From 259b06edf1a1de938ef32f4e8d9c50806f26c3a3 Mon Sep 17 00:00:00 2001 From: Nunuhara Cabbage Date: Thu, 13 Feb 2020 17:50:10 -0800 Subject: [PATCH] aindump: add sorted string/message dump mode Dump strings/messages sorted by function when given the -t,--text option. This is intended to be read back in again by ainedit. --- src/aindump/aindump.c | 132 +++++++++++++++++++++++++++++++++- src/aindump/aindump.h | 11 +++ src/aindump/dasm.c | 11 ++- src/aindump/guess_filenames.c | 39 ---------- 4 files changed, 148 insertions(+), 45 deletions(-) diff --git a/src/aindump/aindump.c b/src/aindump/aindump.c index 73a0f71..ad9abe6 100644 --- a/src/aindump/aindump.c +++ b/src/aindump/aindump.c @@ -28,6 +28,47 @@ #include "system4/string.h" #include "system4/utfsjis.h" +int32_t code_reader_get_arg(struct code_reader *r, int n) +{ + if (n < 0 || n >= r->instr->nr_args) + ERROR("Invalid argument number for '%s': %d", r->instr->name, n); + return LittleEndian_getDW(r->ain->code, r->addr + 2 + n*4); +} + +static void for_each_instruction_default_error(possibly_unused struct code_reader *r, char *msg) +{ + ERROR("%s", msg); +} + +void for_each_instruction(struct ain *ain, void(*fun)(struct code_reader*, void*), void(*err)(struct code_reader*, char*), void *data) +{ + struct code_reader r = { .ain = ain }; + + if (!err) + err = for_each_instruction_default_error; + + for (r.addr = 0; r.addr < ain->code_size;) { + uint16_t opcode = LittleEndian_getW(ain->code, r.addr); + if (opcode >= NR_OPCODES) { + char buf[512]; + snprintf(buf, 512, "Unknown/invalid opcode: %u", opcode); + err(&r, buf); + return; + } + + r.instr = &instructions[opcode]; + if (r.addr + r.instr->nr_args * 4 >= ain->code_size) { + err(&r, "CODE section truncated?"); + return; + } + + fun(&r, data); + + r.addr += instruction_width(r.instr->opcode); + r.instr = NULL; + } +} + static void usage(void) { puts("Usage: aindump "); @@ -36,6 +77,7 @@ static void usage(void) puts(" -h, --help Display this message and exit"); puts(" -c, --code Dump code section"); puts(" -C, --raw-code Dump code section (raw)"); + puts(" -t, --text Dump strings and messages, sorted by function"); puts(" -f, --functions Dump functions section"); puts(" -g, --globals Dump globals section"); puts(" -S, --structures Dump structures section"); @@ -163,6 +205,83 @@ static void ain_dump_structures(FILE *f, struct ain *ain) } } +struct dump_text_data { + FILE *out; + struct ain_function *fun; +}; + +static void dump_text_function(struct dump_text_data *data) +{ + if (!data->fun) + return; + + char *u = sjis2utf(data->fun->name, strlen(data->fun->name)); + fprintf(data->out, "\n; %s\n", u); + free(u); + + data->fun = NULL; +} + +static void dump_text_string(struct dump_text_data *data, struct ain *ain, int no) +{ + if (no < 0 || no >= ain->nr_strings) + ERROR("Invalid string index: %d", no); + + // skip empty string + if (!ain->strings[no]->size) + return; + + dump_text_function(data); + + char *u = escape_string(ain->strings[no]->text); + fprintf(data->out, ";s[%d] = \"%s\"\n", no, u); + free(u); +} + +static void dump_text_message(struct dump_text_data *data, struct ain *ain, int no) +{ + dump_text_function(data); + + if (no < 0 || no >= ain->nr_messages) + ERROR("Invalid message index: %d", no); + + char *u = escape_string(ain->messages[no]->text); + fprintf(data->out, ";m[%d] = \"%s\"\n", no, u); + free(u); +} + +static void dump_text_instruction(struct code_reader *r, void *_data) +{ + int n; + struct dump_text_data *data = _data; + switch (r->instr->opcode) { + case FUNC: + n = code_reader_get_arg(r, 0); + if (n < 0 || n >= r->ain->nr_functions) + ERROR("Invalid function index: %d", n); + data->fun = &r->ain->functions[n]; + break; + case S_PUSH: + dump_text_string(data, r->ain, code_reader_get_arg(r, 0)); + break; + // TODO: other instructions with string arguments + case MSG: + dump_text_message(data, r->ain, code_reader_get_arg(r, 0)); + break; + default: + break; + } +} + +static void ain_dump_text(FILE *f, struct ain *ain) +{ + struct dump_text_data data = { + .out = f, + .fun = NULL + }; + for_each_instruction(ain, dump_text_instruction, NULL, &data); +} + static void ain_dump_messages(FILE *f, struct ain *ain) { for (int i = 0; i < ain->nr_messages; i++) { @@ -342,6 +461,7 @@ int main(int argc, char *argv[]) { bool dump_version = false; bool dump_code = false; + bool dump_text = false; bool dump_functions = false; bool dump_globals = false; bool dump_structures = false; @@ -369,6 +489,7 @@ int main(int argc, char *argv[]) { "ain-version", no_argument, 0, 'V' }, { "code", no_argument, 0, 'c' }, { "raw-code", no_argument, 0, 'C' }, + { "text", no_argument, 0, 't' }, { "functions", no_argument, 0, 'f' }, { "globals", no_argument, 0, 'g' }, { "structures", no_argument, 0, 'S' }, @@ -376,7 +497,7 @@ int main(int argc, char *argv[]) { "libraries", no_argument, 0, 'l' }, { "strings", no_argument, 0, 's' }, { "filenames", no_argument, 0, 'F' }, - { "function-types", no_argument, 0, 't' }, + { "function-types", no_argument, 0, 'T' }, { "delegates", no_argument, 0, 'D' }, { "global-group-names", no_argument, 0, 'r' }, { "enums", no_argument, 0, 'e' }, @@ -390,7 +511,7 @@ int main(int argc, char *argv[]) int option_index = 0; int c; - c = getopt_long(argc, argv, "hVcCfgSmlsFeAdjo:", long_options, &option_index); + c = getopt_long(argc, argv, "hVcCtfgSmlsFeAdjo:", long_options, &option_index); if (c == -1) break; @@ -408,6 +529,9 @@ int main(int argc, char *argv[]) dump_code = true; flags |= DASM_RAW; break; + case 't': + dump_text = true; + break; case 'f': dump_functions = true; break; @@ -429,7 +553,7 @@ int main(int argc, char *argv[]) case 'F': dump_filenames = true; break; - case 't': + case 'T': dump_functypes = true; break; case 'D': @@ -518,6 +642,8 @@ int main(int argc, char *argv[]) ain_dump_enums(output, ain); if (dump_code) disassemble_ain(output, ain, flags); + if (dump_text) + ain_dump_text(output, ain); if (dump_json) ain_dump_json(output, ain); if (dump_map) diff --git a/src/aindump/aindump.h b/src/aindump/aindump.h index 30f1066..c8b5a5f 100644 --- a/src/aindump/aindump.h +++ b/src/aindump/aindump.h @@ -17,6 +17,7 @@ #ifndef AINDUMP_H #define AINDUMP_H +#include #include struct ain; @@ -27,14 +28,24 @@ enum { DASM_NO_STRINGS = 2, }; +struct code_reader { + struct ain *ain; + size_t addr; + const struct instruction *instr; +}; + + // guess_filenames.c void guess_filenames(struct ain *ain); // dasm.c void disassemble_ain(FILE *out, struct ain *ain, unsigned int flags);; +char *escape_string(const char *str); // aindump.c void ain_dump_function(FILE *out, struct ain *ain, struct ain_function *f); +int32_t code_reader_get_arg(struct code_reader *r, int n); +void for_each_instruction(struct ain *ain, void(*fun)(struct code_reader*, void*), void(*err)(struct code_reader*, char*), void *data); // json.c void ain_dump_json(FILE *out, struct ain *ain); diff --git a/src/aindump/dasm.c b/src/aindump/dasm.c index 1836831..37a22a8 100644 --- a/src/aindump/dasm.c +++ b/src/aindump/dasm.c @@ -176,7 +176,7 @@ static void print_sjis(struct dasm_state *dasm, const char *s) free(u); } -static char *prepare_string(const char *str, const char *escape_chars, const char *replace_chars) +static char *_escape_string(const char *str, const char *escape_chars, const char *replace_chars) { int escapes = 0; char *u = sjis2utf(str, strlen(str)); @@ -219,11 +219,16 @@ static char *prepare_string(const char *str, const char *escape_chars, const cha return u; } -static void print_string(struct dasm_state *dasm, const char *str) +char *escape_string(const char *str) { const char escape_chars[] = { '\\', '\"', '\n', 0 }; const char replace_chars[] = { '\\', '\"', 'n', 0 }; - char *u = prepare_string(str, escape_chars, replace_chars); + return _escape_string(str, escape_chars, replace_chars); +} + +static void print_string(struct dasm_state *dasm, const char *str) +{ + char *u = escape_string(str); fprintf(dasm->out, "\"%s\"", u); free(u); } diff --git a/src/aindump/guess_filenames.c b/src/aindump/guess_filenames.c index d28fe42..038b932 100644 --- a/src/aindump/guess_filenames.c +++ b/src/aindump/guess_filenames.c @@ -24,50 +24,11 @@ #include "system4/string.h" #include "system4/utfsjis.h" -struct code_reader { - struct ain *ain; - size_t addr; - const struct instruction *instr; -}; - struct guesser_state { kvec_t(struct ain_function*) functions; char *guess; }; -int32_t code_reader_get_arg(struct code_reader *r, int n) -{ - if (n < 0 || n >= r->instr->nr_args) - ERROR("Invalid argument number for '%s': %d", r->instr->name, n); - return LittleEndian_getDW(r->ain->code, r->addr + 2 + n*4); -} - -void for_each_instruction(struct ain *ain, void(*fun)(struct code_reader*, void*), void(*err)(struct code_reader*, char*), void *data) -{ - struct code_reader r = { .ain = ain }; - - for (r.addr = 0; r.addr < ain->code_size;) { - uint16_t opcode = LittleEndian_getW(ain->code, r.addr); - if (opcode >= NR_OPCODES) { - char buf[512]; - snprintf(buf, 512, "Unknown/invalid opcode: %u", opcode); - err(&r, buf); - return; - } - - r.instr = &instructions[opcode]; - if (r.addr + r.instr->nr_args * 4 >= ain->code_size) { - err(&r, "CODE section truncated?"); - return; - } - - fun(&r, data); - - r.addr += instruction_width(r.instr->opcode); - r.instr = NULL; - } -} - static size_t get_prefix_length(char *a, char *b) { size_t i;