Skip to content

Commit

Permalink
Sort Pokemon and trainer sprite palettes, with Makefile-specified exc…
Browse files Browse the repository at this point in the history
…eptions

This avoids the need to define their order via indexed PNG palettes

It also avoids the need to use tools/palfix.py on custom sprites

Fixes #1136
  • Loading branch information
Rangi42 committed Oct 16, 2024
1 parent 80bb1dc commit 2c53a44
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 2,695 deletions.
50 changes: 43 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -207,15 +207,50 @@ gfx/pokemon/girafarig/front.animated.tilemap: gfx/pokemon/girafarig/front.2bpp g
tools/pokemon_animation_graphics --girafarig -t $@ $^


### Misc file-specific graphics rules
### Pokemon and trainer sprite rules

gfx/pokemon/%/back.2bpp: rgbgfx += --columns
gfx/pokemon/%/back.2bpp: gfx/pokemon/%/back.png gfx/pokemon/%/normal.gbcpal
$(RGBGFX) $(rgbgfx) --colors gbc:$(word 2,$^) -o $@ $<
gfx/pokemon/%/front.2bpp: gfx/pokemon/%/front.png gfx/pokemon/%/normal.gbcpal
$(RGBGFX) $(rgbgfx) --colors gbc:$(word 2,$^) -o $@ $<
gfx/pokemon/%/normal.gbcpal: gfx/pokemon/%/front.gbcpal gfx/pokemon/%/back.gbcpal
tools/gbcpal $(tools/gbcpal) $@ $^

gfx/trainers/%.2bpp: rgbgfx += --columns
gfx/trainers/%.2bpp: gfx/trainers/%.png gfx/trainers/%.gbcpal
$(RGBGFX) $(rgbgfx) --colors gbc:$(word 2,$^) -o $@ $<

# Egg does not have a back sprite, so it only uses front.gbcpal
gfx/pokemon/egg/front.2bpp: gfx/pokemon/egg/front.png gfx/pokemon/egg/front.gbcpal
gfx/pokemon/egg/front.2bpp: rgbgfx += --colors gbc:$(word 2,$^)

gfx/pokemon/%/back.2bpp: rgbgfx += --columns --colors embedded
gfx/pokemon/%/front.2bpp: rgbgfx += --colors embedded
# Unown letters share one normal.pal, so they don't already build each normal.gbcpal
$(foreach png, $(wildcard gfx/pokemon/unown_*/front.png),\
$(eval $(png:.png=.2bpp): $(png) $(png:front.png=normal.gbcpal)))
gfx/pokemon/unown_%/front.2bpp: rgbgfx += --colors gbc:$(@:front.2bpp=normal.gbcpal)

gfx/trainers/%.2bpp: rgbgfx += --columns --colors embedded

### Misc file-specific graphics rules

gfx/pokemon/egg/unused_front.2bpp: rgbgfx += --columns

gfx/pokemon/caterpie/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/diglett/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/dugtrio/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/farfetch_d/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/fearow/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/jynx/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/magnemite/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/magneton/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/marill/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/porygon2/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/scyther/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/shellder/normal.gbcpal: tools/gbcpal += --reverse
gfx/pokemon/spearow/normal.gbcpal: tools/gbcpal += --reverse

gfx/trainers/swimmer_m.gbcpal: tools/gbcpal += --reverse

gfx/new_game/shrink1.2bpp: rgbgfx += --columns
gfx/new_game/shrink2.2bpp: rgbgfx += --columns

Expand Down Expand Up @@ -307,15 +342,16 @@ gfx/mobile/stadium2_n64.2bpp: tools/gfx += --trim-whitespace
%.2bpp: %.png
$(RGBGFX) $(rgbgfx) -o $@ $<
$(if $(tools/gfx),\
tools/gfx $(tools/gfx) -o $@ $@)
tools/gfx $(tools/gfx) -o $@ $@ || $$($(RM) $@ && false))

%.1bpp: %.png
$(RGBGFX) $(rgbgfx) --depth 1 -o $@ $<
$(if $(tools/gfx),\
tools/gfx $(tools/gfx) --depth 1 -o $@ $@)
tools/gfx $(tools/gfx) --depth 1 -o $@ $@ || $$($(RM) $@ && false))

%.gbcpal: %.png
$(RGBGFX) --colors embedded -p $@ $<
$(RGBGFX) -p $@ $<
tools/gbcpal $(tools/gbcpal) $@ $@ || $$($(RM) $@ && false)

%.dimensions: %.png
tools/png_dimensions $< $@
508 changes: 254 additions & 254 deletions data/pokemon/palettes.asm

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tools/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
gbcpal
gfx
lzcomp
make_patch
Expand Down
1 change: 1 addition & 0 deletions tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CC := gcc
CFLAGS := -O3 -flto -std=c11 -Wall -Wextra -pedantic

tools := \
gbcpal \
gfx \
lzcomp \
make_patch \
Expand Down
146 changes: 146 additions & 0 deletions tools/gbcpal.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#define PROGRAM_NAME "gbcpal"
#define USAGE_OPTS "[-h|--help] [-r|--reverse] out.gbcpal in.gbcpal..."

#include "common.h"

bool reverse;

void parse_args(int argc, char *argv[]) {
struct option long_options[] = {
{"reverse", no_argument, 0, 'r'},
{"help", no_argument, 0, 'h'},
{0}
};
for (int opt; (opt = getopt_long(argc, argv, "rh", long_options)) != -1;) {
switch (opt) {
case 'r':
reverse = true;
break;
case 'h':
usage_exit(0);
break;
default:
usage_exit(1);
}
}
}

struct Color {
uint8_t r, g, b;
};

const struct Color BLACK = {0, 0, 0};
const struct Color WHITE = {31, 31, 31};

uint16_t pack_color(struct Color color) {
return (color.b << 10) | (color.g << 5) | color.r;
}

struct Color unpack_color(uint16_t gbc_color) {
return (struct Color){
.r = gbc_color & 0x1f,
.g = (gbc_color >> 5) & 0x1f,
.b = (gbc_color >> 10) & 0x1f,
};
}

double luminance(struct Color color) {
return 0.299 * color.r * color.r + 0.587 * color.g * color.g + 0.114 * color.b * color.b;
}

int compare_colors(const void *color1, const void *color2) {
double lum1 = luminance(*(const struct Color *)color1);
double lum2 = luminance(*(const struct Color *)color2);
// sort lightest to darkest, or darkest to lightest if reversed
return reverse ? (lum1 > lum2) - (lum1 < lum2) : (lum1 < lum2) - (lum1 > lum2);
}

void read_gbcpal(const char *filename, struct Color **colors, size_t *num_colors) {
long filesize;
uint8_t *bytes = read_u8(filename, &filesize);
if (filesize == 0) {
error_exit("%s: empty gbcpal file\n", filename);
}
if (filesize % 2) {
error_exit("%s: invalid gbcpal file\n", filename);
}

size_t new_colors = filesize / 2;
*colors = xrealloc(*colors, (sizeof **colors) * (*num_colors + new_colors));
for (size_t i = 0; i < new_colors; i++) {
uint16_t gbc_color = (bytes[i * 2 + 1] << 8) | bytes[i * 2];
(*colors)[*num_colors + i] = unpack_color(gbc_color);
}
*num_colors += new_colors;

free(bytes);
}

void filter_colors(struct Color **colors, size_t *num_colors) {
size_t num_filtered = 0;
struct Color *filtered = xmalloc((sizeof **colors) * *num_colors);

// filter out black, white, and duplicate colors
for (size_t i = 0; i < *num_colors; i++) {
struct Color color = (*colors)[i];
if (color.r == BLACK.r && color.g == BLACK.g && color.b == BLACK.b) {
continue;
}
if (color.r == WHITE.r && color.g == WHITE.g && color.b == WHITE.b) {
continue;
}
if (num_filtered > 0) {
struct Color last = filtered[num_filtered - 1];
if (color.r == last.r && color.g == last.g && color.b == last.b) {
continue;
}
}
filtered[num_filtered++] = color;
}

free(*colors);
*num_colors = num_filtered;
*colors = filtered;
}

int main(int argc, char *argv[]) {
parse_args(argc, argv);

argc -= optind;
argv += optind;
if (argc < 2) {
usage_exit(1);
}

const char *out_filename = argv[0];

struct Color *colors = NULL;
size_t num_colors = 0;
for (int i = 1; i < argc; i++) {
read_gbcpal(argv[i], &colors, &num_colors);
}

qsort(colors, num_colors, sizeof(*colors), compare_colors);
filter_colors(&colors, &num_colors);

struct Color pal_colors[4] = {
WHITE,
num_colors > 0 ? colors[0] : WHITE,
num_colors > 1 ? colors[1] : num_colors > 0 ? colors[0] : BLACK,
BLACK,
};
if (num_colors > 2) {
error_exit("%s: more than 2 colors besides black and white (%zu)\n", out_filename, num_colors);
}

uint8_t bytes[COUNTOF(pal_colors) * 2] = {0};
for (size_t i = 0; i < COUNTOF(pal_colors); i++) {
uint16_t packed_color = pack_color(pal_colors[i]);
bytes[2 * i] = packed_color & 0xff;
bytes[2 * i + 1] = packed_color >> 8;
}
write_u8(out_filename, bytes, COUNTOF(bytes));

free(colors);
return 0;
}
2 changes: 1 addition & 1 deletion tools/make_patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ struct Buffer *process_template(const char *template_filename, const char *patch
int compare_patch(const void *patch1, const void *patch2) {
unsigned int offset1 = ((const struct Patch *)patch1)->offset;
unsigned int offset2 = ((const struct Patch *)patch2)->offset;
return offset1 > offset2 ? 1 : offset1 < offset2 ? -1 : 0;
return (offset1 > offset2) - (offset1 < offset2);
}

bool verify_completeness(FILE *restrict orig_rom, FILE *restrict new_rom, struct Buffer *patches) {
Expand Down
76 changes: 0 additions & 76 deletions tools/palfix.py

This file was deleted.

Loading

0 comments on commit 2c53a44

Please sign in to comment.