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.
This commit is contained in:
Nunuhara Cabbage
2020-02-13 17:50:10 -08:00
parent ab670bd3a9
commit 259b06edf1
4 changed files with 148 additions and 45 deletions
+129 -3
View File
@@ -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 <options> <ainfile>");
@@ -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)
+11
View File
@@ -17,6 +17,7 @@
#ifndef AINDUMP_H
#define AINDUMP_H
#include <stdint.h>
#include <stdio.h>
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);
+8 -3
View File
@@ -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);
}
-39
View File
@@ -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;