summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile16
-rw-r--r--tools/common.h37
-rw-r--r--tools/gfx.c271
-rw-r--r--tools/lzcomp.c404
-rw-r--r--tools/palette.c90
-rw-r--r--tools/png_dimensions.c53
-rw-r--r--tools/pokemon_animation.c269
-rw-r--r--tools/pokemon_animation_graphics.c237
-rw-r--r--tools/scan_includes.c130
9 files changed, 1507 insertions, 0 deletions
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 000000000..57a4d84ce
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,16 @@
+.PHONY: all
+
+CFLAGS := -std=c99
+
+all: \
+ lzcomp \
+ png_dimensions \
+ scan_includes \
+ palette \
+ pokemon_animation \
+ pokemon_animation_graphics \
+ gfx
+ @:
+
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $<
diff --git a/tools/common.h b/tools/common.h
new file mode 100644
index 000000000..bc877ccb9
--- /dev/null
+++ b/tools/common.h
@@ -0,0 +1,37 @@
+#ifndef GUARD_COMMON_H
+#define GUARD_COMMON_H
+
+int __getopt_long_i__;
+#define getopt_long(c, v, s, l) getopt_long(c, v, s, l, &__getopt_long_i__)
+
+FILE *fopen_verbose(char *filename, char *mode) {
+ FILE *f = fopen(filename, mode);
+ if (!f) {
+ fprintf(stderr, "Could not open file: \"%s\"\n", filename);
+ }
+ return f;
+}
+
+uint8_t *read_u8(char *filename, int *size) {
+ FILE *f = fopen_verbose(filename, "rb");
+ if (!f) {
+ exit(1);
+ }
+ fseek(f, 0, SEEK_END);
+ *size = ftell(f);
+ rewind(f);
+ uint8_t *data = malloc(*size);
+ fread(data, 1, *size, f);
+ fclose(f);
+ return data;
+}
+
+void write_u8(char *filename, uint8_t *data, int size) {
+ FILE *f = fopen_verbose(filename, "wb");
+ if (f) {
+ fwrite(data, 1, size, f);
+ fclose(f);
+ }
+}
+
+#endif // GUARD_COMMON_H
diff --git a/tools/gfx.c b/tools/gfx.c
new file mode 100644
index 000000000..0e6864e91
--- /dev/null
+++ b/tools/gfx.c
@@ -0,0 +1,271 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "common.h"
+
+static void usage(void) {
+ fprintf(stderr, "Usage: gfx [--trim-whitespace] [--remove-whitespace] [--interleave] [--remove-duplicates [--keep-whitespace]] [--remove-xflip] [--remove-yflip] [-w width] [-d depth] [-h] [-o outfile] infile\n");
+}
+
+static void error(char *message) {
+ fprintf(stderr, message);
+ fprintf(stderr, "\n");
+}
+
+struct Options {
+ int trim_whitespace;
+ int remove_whitespace;
+ int help;
+ char *outfile;
+ int depth;
+ int interleave;
+ int width;
+ int remove_duplicates;
+ int keep_whitespace;
+ int remove_xflip;
+ int remove_yflip;
+};
+
+struct Options Options = {
+ .depth = 2,
+};
+
+void get_args(int argc, char *argv[]) {
+ struct option long_options[] = {
+ {"remove-whitespace", no_argument, &Options.remove_whitespace, 1},
+ {"trim-whitespace", no_argument, &Options.trim_whitespace, 1},
+ {"interleave", no_argument, &Options.interleave, 1},
+ {"remove-duplicates", no_argument, &Options.remove_duplicates, 1},
+ {"keep-whitespace", no_argument, &Options.keep_whitespace, 1},
+ {"remove-xflip", no_argument, &Options.remove_xflip, 1},
+ {"remove-yflip", no_argument, &Options.remove_yflip, 1},
+ {"width", required_argument, 0, 'w'},
+ {"depth", required_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {0}
+ };
+ for (int opt = 0; opt != -1;) {
+ switch (opt = getopt_long(argc, argv, "ho:d:", long_options)) {
+ case 'h':
+ Options.help = true;
+ break;
+ case 'o':
+ Options.outfile = optarg;
+ break;
+ case 'd':
+ Options.depth = strtoul(optarg, NULL, 0);
+ break;
+ case 'w':
+ Options.width = strtoul(optarg, NULL, 0);
+ break;
+ case 0:
+ case -1:
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+}
+
+struct Graphic {
+ int size;
+ uint8_t *data;
+};
+
+bool is_whitespace(uint8_t *tile, int tile_size) {
+ uint8_t WHITESPACE = 0;
+ for (int i = 0; i < tile_size; i++) {
+ if (tile[i] != WHITESPACE) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void trim_whitespace(struct Graphic *graphic) {
+ int tile_size = Options.depth * 8;
+ for (int i = graphic->size - tile_size; i > 0; i -= tile_size) {
+ if (is_whitespace(&graphic->data[i], tile_size)) {
+ graphic->size = i;
+ } else {
+ break;
+ }
+ }
+}
+
+void remove_whitespace(struct Graphic *graphic) {
+ int tile_size = Options.depth * 8;
+ if (Options.interleave) tile_size *= 2;
+ int i = 0;
+ for (int j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (is_whitespace(&graphic->data[j], tile_size)) {
+ j += tile_size;
+ }
+ if (j >= graphic->size) {
+ break;
+ }
+ if (j > i) {
+ memcpy(&graphic->data[i], &graphic->data[j], tile_size);
+ }
+ }
+ graphic->size = i;
+}
+
+bool tile_exists(uint8_t *tile, uint8_t *tiles, int tile_size, int num_tiles) {
+ for (int i = 0; i < num_tiles; i++) {
+ bool match = true;
+ for (int j = 0; j < tile_size; j++) {
+ if (tile[j] != tiles[i * tile_size + j]) {
+ match = false;
+ }
+ }
+ if (match) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void remove_duplicates(struct Graphic *graphic) {
+ int tile_size = Options.depth * 8;
+ if (Options.interleave) tile_size *= 2;
+ int num_tiles = 0;
+ for (int i = 0, j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (tile_exists(&graphic->data[j], graphic->data, tile_size, num_tiles)) {
+ if (Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) {
+ break;
+ }
+ j += tile_size;
+ }
+ if (j >= graphic->size) {
+ break;
+ }
+ if (j > i) {
+ memcpy(&graphic->data[i], &graphic->data[j], tile_size);
+ }
+ num_tiles++;
+ }
+ graphic->size = num_tiles * tile_size;
+}
+
+bool flip_exists(uint8_t *tile, uint8_t *tiles, int tile_size, int num_tiles, bool xflip, bool yflip) {
+ uint8_t *flip = calloc(tile_size, 1);
+ int half_size = tile_size / 2;
+ for (int i = 0; i < tile_size; i++) {
+ int byte = i;
+ if (yflip) {
+ byte = tile_size - 1 - (i ^ 1);
+ if (Options.interleave && i < half_size) {
+ byte = half_size - 1 - (i ^ 1);
+ }
+ }
+ if (xflip) {
+ for (int bit = 0; bit < 8; bit++) {
+ flip[byte] |= ((tile[i] >> bit) & 1) << (7 - bit);
+ }
+ } else {
+ flip[byte] = tile[i];
+ }
+ }
+ if (tile_exists(flip, tiles, tile_size, num_tiles)) {
+ return true;
+ }
+ return false;
+}
+
+void remove_flip(struct Graphic *graphic, bool xflip, bool yflip) {
+ int tile_size = Options.depth * 8;
+ if (Options.interleave) tile_size *= 2;
+ int num_tiles = 0;
+ for (int i = 0, j = 0; i < graphic->size && j < graphic->size; i += tile_size, j += tile_size) {
+ while (flip_exists(&graphic->data[j], graphic->data, tile_size, num_tiles, xflip, yflip)) {
+ if (Options.keep_whitespace && is_whitespace(&graphic->data[j], tile_size)) {
+ break;
+ }
+ j += tile_size;
+ }
+ if (j >= graphic->size) {
+ break;
+ }
+ if (j > i) {
+ memcpy(&graphic->data[i], &graphic->data[j], tile_size);
+ }
+ num_tiles++;
+ }
+ graphic->size = num_tiles * tile_size;
+}
+
+void interleave(struct Graphic *graphic, int width) {
+ int tile_size = Options.depth * 8;
+ int width_tiles = width / 8;
+ int num_tiles = graphic->size / tile_size;
+ uint8_t *interleaved = malloc(graphic->size);
+ for (int i = 0; i < num_tiles; i++) {
+ int tile = i * 2;
+ int row = i / width_tiles;
+ tile -= width_tiles * row;
+ if (row % 2) {
+ tile -= width_tiles;
+ tile += 1;
+ }
+ memcpy(&interleaved[tile * tile_size], &graphic->data[i * tile_size], tile_size);
+ }
+ graphic->size = num_tiles * tile_size;
+ memcpy(graphic->data, interleaved, graphic->size);
+ free(interleaved);
+}
+
+
+int main(int argc, char *argv[]) {
+ get_args(argc, argv);
+ argc -= optind;
+ argv += optind;
+ if (Options.help) {
+ usage();
+ return 0;
+ }
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+ char *infile = argv[0];
+ struct Graphic graphic;
+ graphic.data = read_u8(infile, &graphic.size);
+ if (Options.trim_whitespace) {
+ trim_whitespace(&graphic);
+ }
+ if (Options.interleave) {
+ if (!Options.width) {
+ error("interleave: must set --width to a nonzero value");
+ usage();
+ exit(1);
+ }
+ interleave(&graphic, Options.width);
+ }
+ if (Options.remove_duplicates) {
+ remove_duplicates(&graphic);
+ }
+ if (Options.remove_xflip) {
+ remove_flip(&graphic, true, false);
+ }
+ if (Options.remove_yflip) {
+ remove_flip(&graphic, false, true);
+ }
+ if (Options.remove_xflip && Options.remove_yflip) {
+ remove_flip(&graphic, true, true);
+ }
+ if (Options.remove_whitespace) {
+ remove_whitespace(&graphic);
+ }
+ if (Options.outfile) {
+ write_u8(Options.outfile, graphic.data, graphic.size);
+ }
+ free(graphic.data);
+ return 0;
+}
diff --git a/tools/lzcomp.c b/tools/lzcomp.c
new file mode 100644
index 000000000..1b7b32a5d
--- /dev/null
+++ b/tools/lzcomp.c
@@ -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;
+}
diff --git a/tools/palette.c b/tools/palette.c
new file mode 100644
index 000000000..39837a096
--- /dev/null
+++ b/tools/palette.c
@@ -0,0 +1,90 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <getopt.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;
+}
diff --git a/tools/png_dimensions.c b/tools/png_dimensions.c
new file mode 100644
index 000000000..5bdc12e4c
--- /dev/null
+++ b/tools/png_dimensions.c
@@ -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;
+}
diff --git a/tools/pokemon_animation.c b/tools/pokemon_animation.c
new file mode 100644
index 000000000..0728788ec
--- /dev/null
+++ b/tools/pokemon_animation.c
@@ -0,0 +1,269 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.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(struct Frames* 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;
+ 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;
+
+ uint8_t *first_frame = tilemap;
+ this_frame = tilemap + frame_size;
+ for (i = 0; i < num_frames; i++) {
+ struct Frame *frame = (struct Frame*)malloc(sizeof(struct Frame));
+ frame->data = malloc(frame_size);
+ frame->size = 0;
+ struct Bitmask *bitmask = (struct Bitmask*)malloc(sizeof(struct Bitmask));
+ bitmask->data = calloc((frame_size + 7) / 8, 1);
+ bitmask->bitlength = 0;
+ for (j = 0; j < frame_size; j++) {
+ if (bitmask->bitlength % 8 == 0) {
+ bitmask->data[bitmask->bitlength / 8] = 0;
+ }
+ bitmask->data[bitmask->bitlength / 8] >>= 1;
+ if (this_frame[j] != first_frame[j]) {
+ frame->data[frame->size] = this_frame[j];
+ frame->size++;
+ bitmask->data[bitmask->bitlength / 8] |= (1 << 7);
+ }
+ bitmask->bitlength++;
+ }
+ // I don't remember exactly why this works.
+ // I think it was that the bits are read backwards, but not indexed backwards.
+ int last = bitmask->bitlength - 1;
+ bitmask->data[last / 8] >>= (7 - (last % 8));
+
+ 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;
+ }
+ bool match = true;
+ for (j = 0; j < (bitmask->bitlength + 7) / 8; j++) {
+ if (bitmask->data[j] != existing.data[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void print_frames(struct Frames* frames) {
+ int i;
+ int j;
+ for (i = 0; i < frames->num_frames; i++) {
+ printf("\tdw .frame%d\n", i + 1);
+ }
+ for (i = 0; i < frames->num_frames; i++) {
+ struct Frame *frame = &frames->frames[i];
+ printf(".frame%d\n", i + 1);
+ printf("\tdb $%02x ; bitmask\n", frame->bitmask);
+ if (frame->size > 0) {
+ for (j = 0; j < frame->size; j++) {
+ if (j % 12 == 0) {
+ if (j) {
+ printf("\n");
+ }
+ printf("\tdb $%02x", frame->data[j]);
+ } else {
+ printf(", $%02x", 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;
+}
diff --git a/tools/pokemon_animation_graphics.c b/tools/pokemon_animation_graphics.c
new file mode 100644
index 000000000..f38850fb0
--- /dev/null
+++ b/tools/pokemon_animation_graphics.c
@@ -0,0 +1,237 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <stdint.h>
+
+static void usage(void) {
+ printf("Usage: pokemon_animation_graphics [-o outfile] [-t mapfile] 2bpp_file dimensions_file\n");
+ exit(1);
+}
+
+struct Options {
+ int girafarig;
+};
+
+struct Options Options = {0};
+
+
+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);
+}
+
+bool compare_tile(uint8_t *tile, uint8_t *other) {
+ int j;
+ for (j = 0; j < 16; j++) {
+ if (tile[j] != other[j]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int get_tile_index(uint8_t* tile, uint8_t* tiles, int num_tiles, int preferred_tile_id) {
+ if (preferred_tile_id >= 0 && preferred_tile_id < num_tiles) {
+ uint8_t *other = &tiles[preferred_tile_id * 16];
+ if (compare_tile(tile, other)) {
+ return preferred_tile_id;
+ }
+ }
+ int i;
+ for (i = 0; i < num_tiles; i++) {
+ uint8_t *other = &tiles[i * 16];
+ if (compare_tile(tile, other)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+FILE *fopen_verbose(char *filename, char *mode) {
+ FILE *f = fopen(filename, mode);
+ if (!f) {
+ fprintf(stderr, "Could not open file: \"%s\"\n", filename);
+ }
+ return f;
+}
+
+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 i;
+ int tile;
+
+ f = fopen_verbose(graphics_filename, "rb");
+ if (!f) {
+ exit(1);
+ }
+ fseek(f, 0, SEEK_END);
+ graphics_size = ftell(f);
+ rewind(f);
+ graphics = malloc(graphics_size);
+ fread(graphics, 1, graphics_size, f);
+ fclose(f);
+
+ int num_tiles_per_frame = width * height;
+ int tile_size = 16;
+ int num_frames = graphics_size / (tile_size * num_tiles_per_frame);
+ int frame_size = num_tiles_per_frame * tile_size;
+
+ // transpose each frame
+ for (i = 0; i < num_frames; i++) {
+ transpose_tiles(graphics + i * frame_size, width, frame_size, tile_size);
+ }
+
+ // first frame is naively populated with redundant tiles,
+ // so fill it unconditionally and start from the second frame
+ int num_tiles = width * height;
+ int tilemap_size = graphics_size / tile_size;
+ tilemap->data = malloc(tilemap_size * 2);
+ for (i = 0; i < num_tiles; i++) {
+ tilemap->data[tilemap->size] = i;
+ tilemap->size++;
+ }
+ for (i = num_tiles; i < tilemap_size; i++) {
+ int preferred = i % num_tiles_per_frame;
+ int index = get_tile_index(graphics + i * tile_size, graphics, i, preferred);
+ if (Options.girafarig && index == 0) {
+ tile = num_tiles;
+ } else if (index == -1) {
+ tile = num_tiles++;
+ } else {
+ tile = tilemap->data[index];
+ }
+ tilemap->data[tilemap->size] = tile;
+ tilemap->size++;
+ }
+
+ int graphic_size = tilemap->size * 16;
+ if (Options.girafarig) {
+ // This is probably not needed, but just in case...
+ graphic_size += 16;
+ }
+
+ graphic->data = malloc(graphic_size);
+ 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, i % num_tiles_per_frame);
+ if (tile == -1) {
+ memcpy(graphic->data + graphic->size, graphics + 16 * i, 16);
+ graphic->size += 16;
+ }
+ }
+ if (Options.girafarig) {
+ // Add a duplicate of tile 0 to the end.
+ memcpy(graphic->data + graphic->size, graphics, 16);
+ graphic->size += 16;
+ }
+
+ free(graphics);
+}
+
+int main(int argc, char* argv[]) {
+ int opt;
+ char* dimensions_filename;
+ char* graphics_filename;
+ char* outfile = NULL;
+ char* mapfile = NULL;
+ FILE* f;
+ long size;
+ uint8_t bytes[1];
+ int width;
+ int height;
+ struct Graphic graphic = {0};
+ struct Tilemap tilemap = {0};
+
+ while (1) {
+ struct option long_options[] = {
+ {"girafarig", no_argument, &Options.girafarig, 1},
+ {"tilemap", required_argument, 0, 't'},
+ {"output", required_argument, 0, 'o'},
+ {0}
+ };
+ int long_option_index = 0;
+ int opt = getopt_long(argc, argv, "o:t:", long_options, &long_option_index);
+ if (opt == -1) {
+ break;
+ }
+ switch (opt) {
+ case 0:
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 't':
+ mapfile = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ usage();
+ }
+
+ graphics_filename = argv[0];
+ dimensions_filename = argv[1];
+
+ f = fopen_verbose(dimensions_filename, "rb");
+ if (!f) {
+ exit(1);
+ }
+ 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) {
+ f = fopen_verbose(outfile, "wb");
+ if (f) {
+ fwrite(graphic.data, 1, graphic.size, f);
+ fclose(f);
+ }
+ }
+
+ if (mapfile) {
+ f = fopen_verbose(mapfile, "wb");
+ if (f) {
+ fwrite(tilemap.data, 1, tilemap.size, f);
+ fclose(f);
+ }
+ }
+
+ free(graphic.data);
+ free(tilemap.data);
+
+ return 0;
+}
diff --git a/tools/scan_includes.c b/tools/scan_includes.c
new file mode 100644
index 000000000..b6fcca03a
--- /dev/null
+++ b/tools/scan_includes.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+void usage(void) {
+ printf("Usage: scan_includes [-h] [-s] filename\n"
+ "-h, --help\n"
+ " Print usage and exit\n"
+ "-s, --strict\n"
+ " Fail if a file cannot be read\n");
+}
+
+struct Options {
+ bool help;
+ bool strict;
+};
+
+struct Options Options = {0};
+
+
+void scan_file(char* filename) {
+ FILE* f;
+ long size;
+ char* orig;
+ char* buffer;
+ char* include;
+ int length;
+
+ f = fopen(filename, "r");
+ if (!f) {
+ if (Options.strict) {
+ fprintf(stderr, "Could not open file: '%s'\n", filename);
+ exit(1);
+ } else {
+ return;
+ }
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+
+ buffer = malloc(size + 1);
+ orig = buffer;
+ fread(buffer, 1, size, f);
+ buffer[size] = '\0';
+ fclose(f);
+
+ for (; buffer && (buffer - orig < size); buffer++) {
+ if (buffer[0] == ';') {
+ buffer = strchr(buffer, '\n');
+ if (!buffer) {
+ fprintf(stderr, "%s: no newline at end of file\n", filename);
+ break;
+ }
+ continue;
+ }
+ bool is_include = false;
+ bool is_incbin = false;
+ if ((strncmp(buffer, "INCBIN", 6) == 0) || (strncmp(buffer, "incbin", 6) == 0)) {
+ is_incbin = true;
+ } else if ((strncmp(buffer, "INCLUDE", 7) == 0) || (strncmp(buffer, "include", 7) == 0)) {
+ is_include = true;
+ }
+ if (is_incbin || is_include) {
+ buffer = strchr(buffer, '"') + 1;
+ if (!buffer) {
+ break;
+ }
+ length = strcspn(buffer, "\"");
+ include = malloc(length + 1);
+ strncpy(include, buffer, length);
+ include[length] = '\0';
+ printf("%s ", include);
+ if (is_include) {
+ scan_file(include);
+ }
+ free(include);
+ }
+ }
+
+ free(orig);
+}
+
+void get_args(int argc, char *argv[]) {
+ while (1) {
+ struct option long_options[] = {
+ {"strict", no_argument, 0, 's'},
+ {"help", no_argument, 0, 'h'},
+ {0}
+ };
+ int i = 0;
+ int opt = getopt_long(argc, argv, "sh", long_options, &i);
+
+ if (opt == -1) {
+ break;
+ }
+
+ switch (opt) {
+ case 's':
+ Options.strict = true;
+ break;
+ case 'h':
+ Options.help = true;
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+}
+
+int main(int argc, char* argv[]) {
+ get_args(argc, argv);
+ argc -= optind;
+ argv += optind;
+ if (Options.help) {
+ usage();
+ return 0;
+ }
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+ scan_file(argv[0]);
+ return 0;
+}