mirror of https://github.com/pret/pokecrystal.git
Add C build tools.
This commit is contained in:
parent
604778749d
commit
2ab468268a
57
Makefile
57
Makefile
|
@ -1,13 +1,10 @@
|
|||
PYTHON := python
|
||||
MD5 := md5sum -c --quiet
|
||||
|
||||
.SUFFIXES:
|
||||
.PHONY: all clean crystal crystal11
|
||||
.PHONY: all clean tools crystal crystal11
|
||||
.SECONDEXPANSION:
|
||||
.PRECIOUS: %.2bpp %.1bpp
|
||||
.PRECIOUS: %.2bpp %.1bpp %.pal.bin
|
||||
|
||||
gfx := $(PYTHON) gfx.py
|
||||
includes := $(PYTHON) scan_includes.py
|
||||
|
||||
|
||||
crystal_obj := \
|
||||
|
@ -41,13 +38,18 @@ clean:
|
|||
compare: pokecrystal.gbc pokecrystal11.gbc
|
||||
@$(MD5) roms.md5
|
||||
|
||||
tools: tools/lzcomp tools/png_dimensions tools/scan_includes tools/palette tools/pokemon_animation tools/pokemon_animation_graphics
|
||||
|
||||
tools/%: tools/%.c
|
||||
$(CC) -o $@ $<
|
||||
|
||||
%.asm: ;
|
||||
|
||||
%11.o: dep = $(shell $(includes) $(@D)/$*.asm)
|
||||
%11.o: dep = $(shell tools/scan_includes $(@D)/$*.asm)
|
||||
%11.o: %.asm $$(dep)
|
||||
rgbasm -D CRYSTAL11 -o $@ $<
|
||||
|
||||
%.o: dep = $(shell $(includes) $(@D)/$*.asm)
|
||||
%.o: dep = $(shell tools/scan_includes $(@D)/$*.asm)
|
||||
%.o: %.asm $$(dep)
|
||||
rgbasm -o $@ $<
|
||||
|
||||
|
@ -59,13 +61,38 @@ pokecrystal.gbc: $(crystal_obj)
|
|||
rgblink -n pokecrystal.sym -m pokecrystal.map -o $@ $^
|
||||
rgbfix -Cjv -i BYTE -k 01 -l 0x33 -m 0x10 -p 0 -r 3 -t PM_CRYSTAL $@
|
||||
|
||||
%.png: ;
|
||||
%.2bpp: %.png ; $(gfx) 2bpp $<
|
||||
%.1bpp: %.png ; $(gfx) 1bpp $<
|
||||
%.lz: % ; $(gfx) lz $<
|
||||
|
||||
%.pal: %.2bpp ;
|
||||
gfx/pics/%/normal.pal gfx/pics/%/bitmask.asm gfx/pics/%/frames.asm: gfx/pics/%/front.2bpp ;
|
||||
%.bin: ;
|
||||
%.blk: ;
|
||||
%.tilemap: ;
|
||||
|
||||
%.png: ;
|
||||
%.2bpp: %.png
|
||||
rgbgfx -o $@ $<
|
||||
%.1bpp: %.png
|
||||
rgbgfx -d1 -o $@ $<
|
||||
%.lz: %
|
||||
tools/lzcomp $@ $<
|
||||
|
||||
%.dimensions: %.png
|
||||
tools/png_dimensions $< $@
|
||||
%.tilemap: %.png
|
||||
rgbgfx -t $@ $<
|
||||
%.pal: %.pal.bin
|
||||
tools/palette $< > $@
|
||||
%.pal.bin: %.png
|
||||
rgbgfx -p $@ $<
|
||||
gfx/pics/%/normal.pal: gfx/pics/normal.pal.bin
|
||||
tools/palette -p $< > $@
|
||||
gfx/pics/%/normal.pal.bin: gfx/pics/%/front.png
|
||||
rgbgfx -p $@ $<
|
||||
gfx/pics/%/front.tilemap: gfx/pics/%/front.png
|
||||
rgbgfx -t $@ $<
|
||||
gfx/pics/%/bitmask.asm: gfx/pics/%/front.animated.tilemap gfx/pics/%/front.dimensions
|
||||
tools/pokemon_animation -b $^ > $@
|
||||
gfx/pics/%/frames.asm: gfx/pics/%/front.animated.tilemap gfx/pics/%/front.dimensions
|
||||
tools/pokemon_animation -f $^ > $@
|
||||
gfx/pics/%/front.animated.2bpp: gfx/pics/%/front.2bpp gfx/pics/%/front.dimensions
|
||||
tools/pokemon_animation_graphics -o $@ $^
|
||||
gfx/pics/%/front.animated.tilemap: gfx/pics/%/front.2bpp gfx/pics/%/front.dimensions
|
||||
tools/pokemon_animation_graphics -t $@ $^
|
||||
gfx/pics/%/front.2bpp: gfx/pics/%/front.png
|
||||
rgbgfx -o $@ $<
|
||||
|
|
|
@ -0,0 +1,404 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define COMPRESSION_METHODS 72
|
||||
|
||||
struct command {
|
||||
unsigned command: 3;
|
||||
unsigned count: 12;
|
||||
signed value: 17;
|
||||
};
|
||||
|
||||
int main(int, char **);
|
||||
void error_exit(int, const char *, ...);
|
||||
void bit_flip(const unsigned char *, unsigned short, unsigned char *);
|
||||
unsigned char * read_file_into_buffer(const char *, unsigned short *);
|
||||
void write_commands_to_file(const char *, const struct command *, unsigned, const unsigned char *);
|
||||
void write_command_to_file(FILE *, struct command, const unsigned char *);
|
||||
struct command * compress(const unsigned char *, unsigned short *);
|
||||
struct command * try_compress(const unsigned char *, const unsigned char *, unsigned short *, unsigned);
|
||||
struct command find_best_copy(const unsigned char *, unsigned short, unsigned short, const unsigned char *, unsigned);
|
||||
unsigned short scan_forwards(const unsigned char *, unsigned short, const unsigned char *, unsigned short, short *);
|
||||
unsigned short scan_backwards(const unsigned char *, unsigned short, unsigned short, short *);
|
||||
struct command find_best_repetition(const unsigned char *, unsigned short, unsigned short);
|
||||
struct command pick_best_command(unsigned, struct command, ...);
|
||||
int is_better(struct command, struct command);
|
||||
short command_size(struct command);
|
||||
void optimize(struct command *, unsigned short);
|
||||
void repack(struct command **, unsigned short *);
|
||||
struct command * select_command_sequence(struct command **, const unsigned short *, unsigned, unsigned short *);
|
||||
struct command * merge_command_sequences(const struct command *, unsigned short, const struct command *, unsigned short, unsigned short *);
|
||||
unsigned short compressed_length(const struct command *, unsigned short);
|
||||
|
||||
int main (int argc, char ** argv) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "usage: %s <source file> <compressed output>\n", *argv);
|
||||
return 3;
|
||||
}
|
||||
unsigned short size;
|
||||
unsigned char * file_buffer = read_file_into_buffer(argv[1], &size);
|
||||
struct command * compressed = compress(file_buffer, &size);
|
||||
write_commands_to_file(argv[2], compressed, size, file_buffer);
|
||||
free(file_buffer);
|
||||
free(compressed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void error_exit (int error_code, const char * error, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, error);
|
||||
fputs("error: ", stderr);
|
||||
vfprintf(stderr, error, ap);
|
||||
fputc('\n', stderr);
|
||||
exit(error_code);
|
||||
}
|
||||
|
||||
void bit_flip (const unsigned char * data, unsigned short length, unsigned char * result) {
|
||||
unsigned char new_value, pos;
|
||||
while (length --) {
|
||||
new_value = 0;
|
||||
for (pos = 0; pos < 8; pos ++) new_value |= ((*data >> pos) & 1) << (7 - pos);
|
||||
*(result ++) = new_value;
|
||||
data ++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char * read_file_into_buffer (const char * file, unsigned short * size) {
|
||||
FILE * fp = fopen(file, "rb");
|
||||
if (!fp) error_exit(1, "could not open file %s for reading", file);
|
||||
unsigned char * buf = malloc(32769);
|
||||
int rv = fread(buf, 1, 32769, fp);
|
||||
fclose(fp);
|
||||
if (rv < 0) error_exit(1, "could not read from file %s", file);
|
||||
if (rv > 32768) error_exit(1, "file %s is too big", file);
|
||||
*size = rv;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void write_commands_to_file (const char * file, const struct command * commands, unsigned count, const unsigned char * input_stream) {
|
||||
FILE * fp = fopen(file, "wb");
|
||||
if (!fp) error_exit(1, "could not open file %s for writing", file);
|
||||
while (count --) write_command_to_file(fp, *(commands ++), input_stream);
|
||||
unsigned char terminator = -1;
|
||||
if (fwrite(&terminator, 1, 1, fp) != 1) error_exit(1, "could not write terminator to compressed output");
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void write_command_to_file (FILE * fp, struct command command, const unsigned char * input_stream) {
|
||||
if ((!command.count) || (command.count > 1024)) error_exit(2, "invalid command in output stream");
|
||||
unsigned char buf[4];
|
||||
unsigned char * pos = buf;
|
||||
int n;
|
||||
command.count --;
|
||||
if (command.count < 32)
|
||||
*(pos ++) = (command.command << 5) + command.count;
|
||||
else {
|
||||
*(pos ++) = 224 + (command.command << 2) + (command.count >> 8);
|
||||
*(pos ++) = command.count;
|
||||
}
|
||||
switch (command.command) {
|
||||
case 1: case 2:
|
||||
if ((command.value < 0) || (command.value >= (1 << (command.command << 3)))) error_exit(2, "invalid command in output stream");
|
||||
for (n = 0; n < command.command; n ++) *(pos ++) = command.value >> (n << 3);
|
||||
case 0: case 3:
|
||||
break;
|
||||
default:
|
||||
if ((command.value < -128) || (command.value > 32767)) error_exit(2, "invalid command in output stream");
|
||||
if (command.value < 0)
|
||||
*(pos ++) = command.value ^ 127;
|
||||
else {
|
||||
*(pos ++) = command.value >> 8;
|
||||
*(pos ++) = command.value;
|
||||
}
|
||||
}
|
||||
if (fwrite(buf, 1, pos - buf, fp) != (pos - buf)) error_exit(1, "could not write command to compressed output");
|
||||
if (command.command) return;
|
||||
command.count ++;
|
||||
if (fwrite(input_stream + command.value, 1, command.count, fp) != command.count) error_exit(1, "could not write data to compressed output");
|
||||
}
|
||||
|
||||
struct command * compress (const unsigned char * data, unsigned short * size) {
|
||||
unsigned char * bitflipped = malloc(*size);
|
||||
bit_flip(data, *size, bitflipped);
|
||||
struct command * compressed_sequences[COMPRESSION_METHODS];
|
||||
unsigned short lengths[COMPRESSION_METHODS];
|
||||
unsigned current;
|
||||
for (current = 0; current < COMPRESSION_METHODS; current ++) {
|
||||
lengths[current] = *size;
|
||||
compressed_sequences[current] = try_compress(data, bitflipped, lengths + current, current);
|
||||
}
|
||||
free(bitflipped);
|
||||
struct command * result = select_command_sequence(compressed_sequences, lengths, COMPRESSION_METHODS, size);
|
||||
for (current = 0; current < COMPRESSION_METHODS; current ++) free(compressed_sequences[current]);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct command * try_compress (const unsigned char * data, const unsigned char * bitflipped, unsigned short * length, unsigned flags) {
|
||||
struct command * commands = malloc(sizeof(struct command) * *length);
|
||||
memset(commands, -1, sizeof(struct command) * *length);
|
||||
struct command * current_command = commands;
|
||||
unsigned short position = 0, previous_data = 0;
|
||||
unsigned char lookahead = 0, lookahead_flag = (flags >> 3) % 3;
|
||||
struct command copy, repetition;
|
||||
while (position < *length) {
|
||||
copy = find_best_copy(data, position, *length, bitflipped, flags);
|
||||
repetition = find_best_repetition(data, position, *length);
|
||||
if (flags & 1)
|
||||
*current_command = pick_best_command(2, repetition, copy);
|
||||
else
|
||||
*current_command = pick_best_command(2, copy, repetition);
|
||||
*current_command = pick_best_command(2, (struct command) {.command = 0, .count = 1, .value = position}, *current_command);
|
||||
if (flags & 2) {
|
||||
if (previous_data && (previous_data != 32) && (previous_data != 1024) && (command_size(*current_command) == current_command -> count))
|
||||
*current_command = (struct command) {.command = 0, .count = 1, .value = position};
|
||||
}
|
||||
if (lookahead_flag) {
|
||||
if (lookahead >= lookahead_flag)
|
||||
lookahead = 0;
|
||||
else if (current_command -> command) {
|
||||
lookahead ++;
|
||||
*current_command = (struct command) {.command = 0, .count = 1, .value = position};
|
||||
}
|
||||
}
|
||||
if (current_command -> command)
|
||||
previous_data = 0;
|
||||
else
|
||||
previous_data += current_command -> count;
|
||||
position += (current_command ++) -> count;
|
||||
}
|
||||
optimize(commands, current_command - commands);
|
||||
repack(&commands, length);
|
||||
return commands;
|
||||
}
|
||||
|
||||
struct command find_best_copy (const unsigned char * data, unsigned short position, unsigned short length, const unsigned char * bitflipped, unsigned flags) {
|
||||
struct command simple = {.command = 7};
|
||||
struct command flipped = simple, backwards = simple;
|
||||
short count, offset;
|
||||
if (count = scan_forwards(data + position, length - position, data, position, &offset))
|
||||
simple = (struct command) {.command = 4, .count = count, .value = offset};
|
||||
if (count = scan_forwards(data + position, length - position, bitflipped, position, &offset))
|
||||
flipped = (struct command) {.command = 5, .count = count, .value = offset};
|
||||
if (count = scan_backwards(data, length - position, position, &offset))
|
||||
backwards = (struct command) {.command = 6, .count = count, .value = offset};
|
||||
struct command command;
|
||||
switch (flags / 24) {
|
||||
case 0: command = pick_best_command(3, simple, backwards, flipped); break;
|
||||
case 1: command = pick_best_command(3, backwards, flipped, simple); break;
|
||||
case 2: command = pick_best_command(3, flipped, backwards, simple);
|
||||
}
|
||||
if ((flags & 4) && (command.count > 32)) command.count = 32;
|
||||
return command;
|
||||
}
|
||||
|
||||
unsigned short scan_forwards (const unsigned char * target, unsigned short limit, const unsigned char * source, unsigned short real_position, short * offset) {
|
||||
unsigned short best_match, best_length = 0;
|
||||
unsigned short current_length;
|
||||
unsigned short position;
|
||||
for (position = 0; position < real_position; position ++) {
|
||||
if (source[position] != *target) continue;
|
||||
for (current_length = 0; (current_length < limit) && (source[position + current_length] == target[current_length]); current_length ++);
|
||||
if (current_length > 1024) current_length = 1024;
|
||||
if (current_length < best_length) continue;
|
||||
best_match = position;
|
||||
best_length = current_length;
|
||||
}
|
||||
if (!best_length) return 0;
|
||||
if ((best_match + 128) >= real_position)
|
||||
*offset = best_match - real_position;
|
||||
else
|
||||
*offset = best_match;
|
||||
return best_length;
|
||||
}
|
||||
|
||||
unsigned short scan_backwards (const unsigned char * data, unsigned short limit, unsigned short real_position, short * offset) {
|
||||
if (real_position < limit) limit = real_position;
|
||||
unsigned short best_match, best_length = 0;
|
||||
unsigned short current_length;
|
||||
unsigned short position;
|
||||
for (position = 0; position < real_position; position ++) {
|
||||
if (data[position] != data[real_position]) continue;
|
||||
for (current_length = 0; (current_length < limit) && (data[position - current_length] == data[real_position + current_length]); current_length ++);
|
||||
if (current_length > 1024) current_length = 1024;
|
||||
if (current_length < best_length) continue;
|
||||
best_match = position;
|
||||
best_length = current_length;
|
||||
}
|
||||
if (!best_length) return 0;
|
||||
if ((best_match + 128) >= real_position)
|
||||
*offset = best_match - real_position;
|
||||
else
|
||||
*offset = best_match;
|
||||
return best_length;
|
||||
}
|
||||
|
||||
struct command find_best_repetition (const unsigned char * data, unsigned short position, unsigned short length) {
|
||||
if ((position + 1) >= length) return data[position] ? ((struct command) {.command = 7}) : ((struct command) {.command = 3, .count = 1});
|
||||
unsigned char value[2] = {data[position], data[position + 1]};
|
||||
unsigned repcount, limit = length - position;
|
||||
if (limit > 1024) limit = 1024;
|
||||
for (repcount = 2; (repcount < limit) && (data[position + repcount] == value[repcount & 1]); repcount ++);
|
||||
struct command result;
|
||||
result.count = repcount;
|
||||
if (*value != value[1]) {
|
||||
if (!*value && (repcount < 3)) return (struct command) {.command = 3, .count = 1};
|
||||
result.command = 2;
|
||||
result.value = ((unsigned) (*value)) | (((unsigned) (value[1])) << 8);
|
||||
} else if (*value) {
|
||||
result.command = 1;
|
||||
result.value = *value;
|
||||
} else
|
||||
result.command = 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct command pick_best_command (unsigned count, struct command command, ...) {
|
||||
struct command result = command;
|
||||
va_list ap;
|
||||
va_start(ap, command);
|
||||
while (-- count) {
|
||||
command = va_arg(ap, struct command);
|
||||
if (is_better(command, result)) result = command;
|
||||
}
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
int is_better (struct command new, struct command old) {
|
||||
if (new.command == 7) return 0;
|
||||
if (old.command == 7) return 1;
|
||||
short new_savings = new.count - command_size(new), old_savings = old.count - command_size(old);
|
||||
return new_savings > old_savings;
|
||||
}
|
||||
|
||||
short command_size (struct command command) {
|
||||
short header_size = 1 + (command.count > 32);
|
||||
if (command.command & 4) return header_size + 1 + (command.value >= 0);
|
||||
return header_size + command.command[(short []) {command.count, 1, 2, 0}];
|
||||
}
|
||||
|
||||
void optimize (struct command * commands, unsigned short count) {
|
||||
while (count && (commands -> command == 7)) commands ++, count --;
|
||||
if (count < 2) return;
|
||||
struct command * end = commands + count;
|
||||
struct command * next = commands + 1;
|
||||
while (next < end) {
|
||||
if (next -> command == 7) goto skip;
|
||||
if (
|
||||
!(commands -> command) &&
|
||||
(command_size(*next) == next -> count) &&
|
||||
((commands -> count + next -> count) <= 1024) &&
|
||||
((commands -> count > 32) || ((commands -> count + next -> count) <= 32))
|
||||
) {
|
||||
commands -> count += next -> count;
|
||||
next -> command = 7;
|
||||
goto skip;
|
||||
}
|
||||
if (next -> command != commands -> command) goto accept;
|
||||
switch (commands -> command) {
|
||||
case 0:
|
||||
if ((commands -> value + commands -> count) != next -> value) break;
|
||||
commands -> count += next -> count;
|
||||
next -> command = 7;
|
||||
if (commands -> count <= 1024) goto skip;
|
||||
next -> command = 0;
|
||||
next -> value = commands -> value + 1024;
|
||||
next -> count = commands -> count - 1024;
|
||||
commands -> count = 1024;
|
||||
break;
|
||||
case 1:
|
||||
if (commands -> value != next -> value) break;
|
||||
case 3:
|
||||
if ((commands -> count + next -> count) <= 1024) {
|
||||
commands -> count += next -> count;
|
||||
next -> command = 7;
|
||||
goto skip;
|
||||
}
|
||||
next -> count = (commands -> count + next -> count) - 1024;
|
||||
commands -> count = 1024;
|
||||
break;
|
||||
}
|
||||
accept:
|
||||
commands = next;
|
||||
skip:
|
||||
next ++;
|
||||
}
|
||||
}
|
||||
|
||||
void repack (struct command ** commands, unsigned short * length) {
|
||||
struct command * new_commands = malloc(sizeof(struct command) * *length);
|
||||
struct command * current = new_commands;
|
||||
unsigned short p;
|
||||
for (p = 0; p < *length; p ++) if (p[*commands].command != 7) *(current ++) = p[*commands];
|
||||
free(*commands);
|
||||
*commands = new_commands;
|
||||
*length = current - new_commands;
|
||||
}
|
||||
|
||||
struct command * select_command_sequence (struct command ** sequences, const unsigned short * lengths, unsigned count, unsigned short * final_length) {
|
||||
unsigned short min_sequence = 0, min_length = compressed_length(*sequences, *lengths);
|
||||
unsigned short seq, len;
|
||||
for (seq = 1; seq < count; seq ++) {
|
||||
len = compressed_length(sequences[seq], lengths[seq]);
|
||||
if (len < min_length) {
|
||||
min_sequence = seq;
|
||||
min_length = len;
|
||||
}
|
||||
}
|
||||
*final_length = lengths[min_sequence];
|
||||
struct command * current = malloc(*final_length * sizeof(struct command));
|
||||
memcpy(current, sequences[min_sequence], *final_length * sizeof(struct command));
|
||||
struct command * new;
|
||||
for (seq = 1; seq < count; seq ++) {
|
||||
new = merge_command_sequences(current, *final_length, sequences[(seq + min_sequence) % count], lengths[(seq + min_sequence) % count], final_length);
|
||||
free(current);
|
||||
current = new;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
struct command * merge_command_sequences (const struct command * current, unsigned short current_length, const struct command * new, unsigned short new_length,
|
||||
unsigned short * result_length) {
|
||||
struct command * result = malloc(sizeof(struct command) * (current_length + new_length));
|
||||
struct command * current_command = result;
|
||||
const struct command * saved_current;
|
||||
const struct command * saved_new;
|
||||
unsigned short current_pos, new_pos;
|
||||
while (current_length) {
|
||||
if (current -> count == new -> count) {
|
||||
*(current_command ++) = pick_best_command(2, *(current ++), *(new ++));
|
||||
current_length --;
|
||||
continue;
|
||||
}
|
||||
saved_current = current;
|
||||
saved_new = new;
|
||||
current_pos = (current ++) -> count;
|
||||
new_pos = (new ++) -> count;
|
||||
current_length --;
|
||||
while (current_pos != new_pos)
|
||||
if (current_pos < new_pos) {
|
||||
current_pos += (current ++) -> count;
|
||||
current_length --;
|
||||
} else
|
||||
new_pos += (new ++) -> count;
|
||||
current_pos = compressed_length(saved_current, current - saved_current);
|
||||
new_pos = compressed_length(saved_new, new - saved_new);
|
||||
if (new_pos < current_pos) {
|
||||
memcpy(current_command, saved_new, sizeof(struct command) * (new - saved_new));
|
||||
current_command += new - saved_new;
|
||||
} else {
|
||||
memcpy(current_command, saved_current, sizeof(struct command) * (current - saved_current));
|
||||
current_command += current - saved_current;
|
||||
}
|
||||
}
|
||||
*result_length = current_command - result;
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned short compressed_length (const struct command * commands, unsigned short count) {
|
||||
unsigned short current, total = 0;
|
||||
for (current = 0; current < count; current ++) if (commands[current].command != 7) total += command_size(commands[current]);
|
||||
return total;
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void usage(void) {
|
||||
printf("Usage: palette palfile\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void print_rgb(uint16_t word) {
|
||||
int r, g, b;
|
||||
r = word & 0x1f;
|
||||
g = (word >> 5) & 0x1f;
|
||||
b = (word >> 10) & 0x1f;
|
||||
printf("\tRGB %2d, %2d, %2d\n", r, g, b);
|
||||
}
|
||||
|
||||
void print_pokemon_palette(char* palette_filename) {
|
||||
FILE* f;
|
||||
uint8_t bytes[4];
|
||||
|
||||
f = fopen(palette_filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open file %s\n", palette_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 2, SEEK_SET);
|
||||
fread(bytes, 1, 4, f);
|
||||
fclose(f);
|
||||
|
||||
print_rgb((bytes[1] << 8) | bytes[0]);
|
||||
print_rgb((bytes[3] << 8) | bytes[2]);
|
||||
}
|
||||
|
||||
void print_palette(char* palette_filename) {
|
||||
FILE* f;
|
||||
uint8_t* bytes;
|
||||
long size;
|
||||
int i;
|
||||
|
||||
f = fopen(palette_filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open file %s\n", palette_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
bytes = malloc(size);
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fread(bytes, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
for (i = 0; i + 1 < size; i += 2) {
|
||||
print_rgb((bytes[i + 1] << 8) | bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
bool pokemon;
|
||||
|
||||
while ((ch = getopt(argc, argv, "p")) != -1) {
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
pokemon = true;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
}
|
||||
if (pokemon) {
|
||||
print_pokemon_palette(argv[0]);
|
||||
} else {
|
||||
print_palette(argv[0]);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void usage(void) {
|
||||
fprintf(stderr, "Usage: png_dimensions infile, outfile\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void output_dimensions(char* png_filename, char* out_filename) {
|
||||
FILE* f;
|
||||
int width, height;
|
||||
int i;
|
||||
uint8_t bytes[4];
|
||||
uint8_t output;
|
||||
|
||||
f = fopen(png_filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open file %s\n", png_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// width
|
||||
fseek(f, 16, SEEK_SET);
|
||||
fread(bytes, 1, 4, f);
|
||||
fclose(f);
|
||||
|
||||
width = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
width |= bytes[i] << (8 * (3 - i));
|
||||
}
|
||||
width >>= 3;
|
||||
height = width;
|
||||
|
||||
output = width & 0xf;
|
||||
output |= (height & 0xf) << 4;
|
||||
|
||||
f = fopen(out_filename, "wb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "failed to open file %s\n", out_filename);
|
||||
exit(1);
|
||||
}
|
||||
fwrite(&output, 1, 1, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 3) {
|
||||
usage();
|
||||
}
|
||||
output_dimensions(argv[1], argv[2]);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct Frame {
|
||||
uint8_t* data;
|
||||
int size;
|
||||
int bitmask;
|
||||
};
|
||||
|
||||
struct Frames {
|
||||
struct Frame* frames;
|
||||
int num_frames;
|
||||
int frame_size;
|
||||
};
|
||||
|
||||
struct Bitmask {
|
||||
uint8_t* data;
|
||||
int bitlength;
|
||||
};
|
||||
|
||||
struct Bitmasks {
|
||||
struct Bitmask* bitmasks;
|
||||
int num_bitmasks;
|
||||
};
|
||||
|
||||
|
||||
void make_frames(struct Frames* frames, struct Bitmasks* bitmasks, char* tilemap_filename, char* dimensions_filename);
|
||||
int bitmask_exists(struct Bitmask bitmask, struct Bitmasks bitmasks);
|
||||
void print_frames();
|
||||
|
||||
|
||||
void make_frames(struct Frames* frames, struct Bitmasks* bitmasks, char* tilemap_filename, char* dimensions_filename) {
|
||||
uint8_t* tilemap;
|
||||
uint8_t* this_frame;
|
||||
FILE* f;
|
||||
long size;
|
||||
int width;
|
||||
int height;
|
||||
uint8_t byte;
|
||||
struct Frame* frame;
|
||||
struct Bitmask* bitmask;
|
||||
int frame_size;
|
||||
int num_frames;
|
||||
int i, j;
|
||||
|
||||
f = fopen(tilemap_filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "could not open file %s", tilemap_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
tilemap = malloc(size);
|
||||
fread(tilemap, 1, size, f);
|
||||
fclose(f);
|
||||
|
||||
f = fopen(dimensions_filename, "rb");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "could not open file %s", dimensions_filename);
|
||||
exit(1);
|
||||
}
|
||||
fread(&byte, 1, 1, f);
|
||||
fclose(f);
|
||||
|
||||
width = byte & 0xf;
|
||||
height = byte >> 4;
|
||||
|
||||
frame_size = width * height;
|
||||
|
||||
num_frames = size / frame_size - 1;
|
||||
//fprintf(stderr, "num_frames: %d\n", num_frames);
|
||||
|
||||
bitmasks->bitmasks = malloc((sizeof (struct Bitmask*)) * num_frames);
|
||||
bitmasks->num_bitmasks = 0;
|
||||
|
||||
frames->frames = malloc((sizeof (struct Frame*)) * num_frames);
|
||||
frames->frame_size = frame_size;
|
||||
frames->num_frames = 0;
|
||||
|
||||
this_frame = tilemap + frame_size;
|
||||
for (i = 0; i < num_frames; i++) {
|
||||
frame = (struct Frame*)malloc(sizeof(struct Frame));
|
||||
frame->data = malloc(frame_size);
|
||||
frame->size = 0;
|
||||
bitmask = (struct Bitmask*)malloc(sizeof(struct Bitmask));
|
||||
bitmask->data = malloc((frame_size + 7) / 8);
|
||||
bitmask->bitlength = 0;
|
||||
for (j = 0; j < frame_size; j++) {
|
||||
if (this_frame[j] != tilemap[j]) {
|
||||
frame->data[frame->size] = this_frame[j];
|
||||
frame->size++;
|
||||
bitmask->data[bitmask->bitlength / 8] |= 1;
|
||||
}
|
||||
bitmask->bitlength++;
|
||||
if (bitmask->bitlength % 8 != 0) {
|
||||
bitmask->data[bitmask->bitlength / 8] <<= 1;
|
||||
}
|
||||
}
|
||||
frame->bitmask = bitmask_exists(*bitmask, *bitmasks);
|
||||
if (frame->bitmask == -1) {
|
||||
frame->bitmask = bitmasks->num_bitmasks;
|
||||
bitmasks->bitmasks[bitmasks->num_bitmasks] = *bitmask;
|
||||
bitmasks->num_bitmasks++;
|
||||
} else {
|
||||
free(bitmask->data);
|
||||
free(bitmask);
|
||||
}
|
||||
frames->frames[i] = *frame;
|
||||
frames->num_frames++;
|
||||
this_frame += frame_size;
|
||||
}
|
||||
|
||||
//for (i = 0; i < frames->num_frames; i++) {
|
||||
//free(frames->frames[i].data);
|
||||
//free(frames->frames[i]);
|
||||
//}
|
||||
//free(frames->frames);
|
||||
|
||||
//fprintf(stderr, "num bitmasks: %d", bitmasks->num_bitmasks);
|
||||
//for (i = 0; i < bitmasks->num_bitmasks; i++) {
|
||||
// free(bitmasks->bitmasks[i].data);
|
||||
// fprintf(stderr, "freed bitmask %d\n", i);
|
||||
//free(bitmasks->bitmasks[i]);
|
||||
//}
|
||||
//free(bitmasks->bitmasks);
|
||||
//fprintf(stderr, "freed bitmasks\n");
|
||||
|
||||
free(tilemap);
|
||||
}
|
||||
|
||||
int bitmask_exists(struct Bitmask bitmask, struct Bitmasks bitmasks) {
|
||||
int i, j;
|
||||
struct Bitmask existing;
|
||||
for (i = 0; i < bitmasks.num_bitmasks; i++) {
|
||||
existing = bitmasks.bitmasks[i];
|
||||
if (bitmask.bitlength != existing.bitlength) {
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < (bitmask.bitlength + 7) / 8; j++) {
|
||||
if (bitmask.data[j] != existing.data[j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == (bitmask.bitlength + 7) / 8) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void print_frames(struct Frames* frames) {
|
||||
int i;
|
||||
int j;
|
||||
struct Frame frame;
|
||||
for (i = 0; i < frames->num_frames; i++) {
|
||||
printf("\tdw .frame%d\n", i + 1);
|
||||
}
|
||||
for (i = 0; i < frames->num_frames; i++) {
|
||||
frame = frames->frames[i];
|
||||
printf(".frame%d\n", i + 1);
|
||||
printf("\tdb %d\n", frame.bitmask);
|
||||
if (frame.size > 0) {
|
||||
printf("\tdb %d", frame.data[0]);
|
||||
for (j = 1; j < frame.size; j++) {
|
||||
printf(", %d", frame.data[j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_bitmasks(struct Bitmasks* bitmasks) {
|
||||
int i, j, k;
|
||||
int length;
|
||||
struct Bitmask bitmask;
|
||||
for (i = 0; i < bitmasks->num_bitmasks; i++) {
|
||||
printf("; %d\n", i);
|
||||
bitmask = bitmasks->bitmasks[i];
|
||||
length = (bitmask.bitlength + 7) / 8;
|
||||
for (j = 0; j < length; j++) {
|
||||
printf("\tdb %%");
|
||||
for (k = 0; k < 8; k++) {
|
||||
if ((bitmask.data[j] >> (7 - k)) & 1) {
|
||||
printf("1");
|
||||
} else {
|
||||
printf("0");
|
||||
};
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HOW ARE YOU GENTLEMEN.
|
||||
char* cats (char* head, char* tail) {
|
||||
char* string;
|
||||
string = malloc(strlen(head) + strlen(tail) + 1);
|
||||
strcpy(string, head);
|
||||
strcat(string, tail);
|
||||
return string;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage: pokemon_animation [-b] [-f] tilemap_file dimensions_file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
struct Frames frames = {0};
|
||||
struct Bitmasks bitmasks = {0};
|
||||
int ch;
|
||||
bool use_bitmasks, use_frames;
|
||||
char* tilemap_filename;
|
||||
char* dimensions_filename;
|
||||
|
||||
while ((ch = getopt(argc, argv, "bf")) != -1) {
|
||||
switch (ch) {
|
||||
case 'b':
|
||||
use_bitmasks = true;
|
||||
break;
|
||||
case 'f':
|
||||
use_frames = true;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
tilemap_filename = argv[0];
|
||||
dimensions_filename = argv[1];
|
||||
|
||||
//ext = strrchr(argv[3], '.');
|
||||
//if (!ext || ext == argv[3]) {
|
||||
// fprintf(stderr, "need a file extension to determine what to write to %s", argv[3]);
|
||||
//}
|
||||
|
||||
make_frames(&frames, &bitmasks, tilemap_filename, dimensions_filename);
|
||||
if (use_frames) {
|
||||
print_frames(&frames);
|
||||
}
|
||||
if (use_bitmasks) {
|
||||
print_bitmasks(&bitmasks);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage: pokemon_animation_graphics [-o outfile] [-t mapfile] 2bpp_file dimensions_file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct Tilemap {
|
||||
uint8_t* data;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct Graphic {
|
||||
uint8_t* data;
|
||||
int size;
|
||||
};
|
||||
|
||||
void transpose_tiles(uint8_t* tiles, int width, int size, int tile_size) {
|
||||
int i;
|
||||
int j;
|
||||
uint8_t* new_tiles;
|
||||
new_tiles = malloc(size);
|
||||
for (i = 0; i < size; i++) {
|
||||
j = i / tile_size * width * tile_size;
|
||||
j = j % size + tile_size * (j / size) + i % tile_size;
|
||||
new_tiles[j] = tiles[i];
|
||||
}
|
||||
memcpy(tiles, new_tiles, size);
|
||||
free(new_tiles);
|
||||
}
|
||||
|
||||
int get_tile_index(uint8_t* tile, uint8_t* tiles, int num_tiles) {
|
||||
int i;
|
||||
int j;
|
||||
for (i = 0; i < num_tiles; i++) {
|
||||
for (j = 0; j < 16; j++) {
|
||||
if (tile[j] != tiles[16 * i + j]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == 16) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void create_tilemap(struct Tilemap* tilemap, struct Graphic* graphic, char* graphics_filename, int width, int height) {
|
||||
long graphics_size;
|
||||
uint8_t* graphics;
|
||||
FILE* f;
|
||||
int tile;
|
||||
int num_tiles;
|
||||
int i;
|
||||
|
||||
f = fopen(graphics_filename, "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
graphics_size = ftell(f);
|
||||
rewind(f);
|
||||
graphics = malloc(graphics_size);
|
||||
fread(graphics, 1, graphics_size, f);
|
||||
fclose(f);
|
||||
|
||||
// transpose each frame
|
||||
for (i = 0; i < graphics_size / (width * height); i++) {
|
||||
transpose_tiles(graphics + i * (width * height) * 16, width, width * height * 16, 16);
|
||||
}
|
||||
|
||||
// first frame is naively populated with redundant tiles
|
||||
num_tiles = width * height;
|
||||
tilemap->data = malloc(graphics_size / 16);
|
||||
for (i = 0; i < num_tiles; i++) {
|
||||
tilemap->data[tilemap->size] = i;
|
||||
tilemap->size++;
|
||||
}
|
||||
for (i = width * height; i < graphics_size / 16; i++) {
|
||||
tile = get_tile_index(graphics + i * 16, graphics, i);
|
||||
if (tile == -1) {
|
||||
tilemap->data[tilemap->size] = num_tiles;
|
||||
tilemap->size++;
|
||||
num_tiles++;
|
||||
} else {
|
||||
tilemap->data[tilemap->size] = tile;
|
||||
tilemap->size++;
|
||||
}
|
||||
}
|
||||
|
||||
graphic->data = malloc(tilemap->size * 16);
|
||||
graphic->size = 16 * width * height;
|
||||
memcpy(graphic->data, graphics, graphic->size);
|
||||
for (i = width * height; i < tilemap->size; i++) {
|
||||
tile = get_tile_index(graphics + 16 * i, graphic->data, graphic->size / 16);
|
||||
if (tile == -1) {
|
||||
memcpy(graphic->data + graphic->size, graphics + 16 * i, 16);
|
||||
graphic->size += 16;
|
||||
}
|
||||
}
|
||||
|
||||
free(graphics);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int ch;
|
||||
char* dimensions_filename;
|
||||
char* graphics_filename;
|
||||
char* outfile;
|
||||
char* mapfile;
|
||||
FILE* f;
|
||||
long size;
|
||||
uint8_t bytes[1];
|
||||
int width;
|
||||
int height;
|
||||
struct Graphic graphic = {0};
|
||||
struct Tilemap tilemap = {0};
|
||||
|
||||
while ((ch = getopt(argc, argv, "o:t:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'o':
|
||||
outfile = optarg;
|
||||
break;
|
||||
case 't':
|
||||
mapfile = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
|
||||
graphics_filename = argv[0];
|
||||
dimensions_filename = argv[1];
|
||||
|
||||
f = fopen(dimensions_filename, "rb");
|
||||
fread(bytes, 1, 1, f);
|
||||
fclose(f);
|
||||
width = bytes[0] & 0xf;
|
||||
height = bytes[0] >> 4;
|
||||
|
||||
create_tilemap(&tilemap, &graphic, graphics_filename, width, height);
|
||||
|
||||
if (outfile != NULL) {
|
||||
f = fopen(outfile, "wb");
|
||||
fwrite(graphic.data, 1, graphic.size, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (mapfile != NULL) {
|
||||
f = fopen(mapfile, "wb");
|
||||
fwrite(tilemap.data, 1, tilemap.size, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
free(graphic.data);
|
||||
free(tilemap.data);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void usage(void) {
|
||||
printf("Usage: scan_includes filename\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void scan_file(char* filename) {
|
||||
FILE* f;
|
||||
long size;
|
||||
char* orig;
|
||||
char* buffer;
|
||||
char* include;
|
||||
int length;
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "Could not open file: '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
//fprintf(stderr, "malloc: %s\n", filename);
|
||||
buffer = malloc(size + 1);
|
||||
orig = buffer;
|
||||
fread(buffer, 1, size, f);
|
||||
buffer[size] = '\0';
|
||||
fclose(f);
|
||||
//fprintf(stderr, "read: %s\n", filename);
|
||||
|
||||
for (; (buffer != NULL) && (buffer != 0) && (buffer - orig < size); buffer++) {
|
||||
//fprintf(stderr, "%c", buffer[0]);
|
||||
if (buffer[0] == ';') {
|
||||
buffer = strchr(buffer, '\n');
|
||||
if (buffer == NULL) {
|
||||
fprintf(stderr, "%s: no newline at end of file\n", filename);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
(strncmp(buffer, "INCBIN", 6) == 0) ||
|
||||
(strncmp(buffer, "incbin", 6) == 0)
|
||||
) {
|
||||
buffer = strchr(buffer, '"') + 1;
|
||||
if (buffer == NULL) break;
|
||||
length = strcspn(buffer, "\"");
|
||||
include = malloc(length + 1);
|
||||
strncpy(include, buffer, length);
|
||||
include[length] = '\0';
|
||||
printf("%s ", include);
|
||||
free(include);
|
||||
} else if (
|
||||
(strncmp(buffer, "INCLUDE", 7) == 0) ||
|
||||
(strncmp(buffer, "include", 7) == 0)
|
||||
) {
|
||||
buffer = strchr(buffer, '"') + 1;
|
||||
if (buffer == NULL) break;
|
||||
length = strcspn(buffer, "\"");
|
||||
include = malloc(length + 1);
|
||||
strncpy(include, buffer, length);
|
||||
include[length] = '\0';
|
||||
printf("%s ", include);
|
||||
scan_file(include);
|
||||
free(include);
|
||||
}
|
||||
}
|
||||
|
||||
//fprintf(stderr, "free: %s\n", filename);
|
||||
free(orig);
|
||||
//fprintf(stderr, "freed: %s\n", filename);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
scan_file(argv[1]);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue