Files
nunuhara_xsystem4/aindump.c
T
Nunuhara Cabbage 6cd35320af Add aindump utility
Add utility for dumping information from AIN files.

For now this is pretty simplistic. The main use is to dump HLL function
prototypes to help with writing stubs.

Future features:
    * dump to JSON format
    * full AIN dump which could be rebuilt by a complementary tool, e.g.
      to enable editing bytecode for testing purposes.
2019-11-11 00:37:55 -08:00

334 lines
8.1 KiB
C

/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "system4.h"
#include "ain.h"
#include "instructions.h"
#include "little_endian.h"
#include "utfsjis.h"
static void usage(void)
{
puts("Usage: aindump <options> <ainfile>");
puts(" Display information from AIN files.");
puts("");
puts(" -h, --help Display this message and exit");
puts(" -c, --code Dump code section");
puts(" -f, --functions Dump functions section");
puts(" -g, --globals Dump globals section");
puts(" -o, --structures Dump structures section");
puts(" -m, --messages Dump messages section");
puts(" -l, --libraries Dump libraries section");
puts(" --function-types Dump function type section");
puts(" --global-group-names Dump global group names section");
puts(" -e, --enums Dump enums section");
//puts(" -j, --json Dump to JSON format"); // TODO
}
static void print_sjis(const char *s)
{
char *u = sjis2utf(s, strlen(s));
printf("%s", u);
free(u);
}
static void ain_dump_code(struct ain *ain)
{
for (size_t i = 0; i < ain->code_size;) {
uint16_t opcode = LittleEndian_getW(ain->code, i);
const struct instruction *instr = &instructions[opcode];
printf("%s", instr->name);
for (int j = 0; j < instr->nr_args; j++) {
printf(" 0x%x", LittleEndian_getDW(ain->code, i + 2 + j*4));
}
putchar('\n');
i += instruction_width(opcode);
}
}
static void print_arglist(struct ain *ain, struct ain_variable *args, int nr_args)
{
if (!nr_args) {
printf("(void)");
return;
}
putchar('(');
for (int i = 0; i < nr_args; i++) {
if (args[i].data_type == AIN_VOID)
continue;
if (i > 0)
printf(", ");
print_sjis(ain_strtype(ain, args[i].data_type, args[i].struct_type));
putchar(' ');
print_sjis(args[i].name);
}
putchar(')');
}
static void print_function(struct ain *ain, struct ain_function *f)
{
print_sjis(ain_strtype(ain, f->data_type, f->struct_type));
putchar(' ');
print_sjis(f->name);
print_arglist(ain, f->vars, f->nr_args);
putchar('\n');
}
static void ain_dump_functions(struct ain *ain)
{
for (int i = 0; i < ain->nr_functions; i++) {
print_function(ain, &ain->functions[i]);
}
}
static void ain_dump_globals(struct ain *ain)
{
for (int i = 0; i < ain->nr_globals; i++) {
struct ain_global *g = &ain->globals[i];
if (g->data_type == AIN_VOID)
continue;
print_sjis(ain_strtype(ain, g->data_type, g->struct_type));
putchar(' ');
print_sjis(g->name);
putchar('\n');
}
}
static void print_structure(struct ain *ain, struct ain_struct *s)
{
printf("struct ");
print_sjis(s->name);
printf(" {\n");
for (int i = 0; i < s->nr_members; i++) {
struct ain_variable *m = &s->members[i];
if (m->data_type == AIN_VOID)
continue;
printf(" ");
print_sjis(ain_strtype(ain, m->data_type, m->struct_type));
putchar(' ');
print_sjis(m->name);
printf(";\n");
}
printf("};\n\n");
}
static void ain_dump_structures(struct ain *ain)
{
for (int i = 0; i < ain->nr_structures; i++) {
print_structure(ain, &ain->structures[i]);
}
}
static void ain_dump_messages(struct ain *ain)
{
for (int i = 0; i < ain->nr_messages; i++) {
print_sjis(ain->messages[i]->text);
printf("\n---\n");
}
}
static void ain_dump_libraries(struct ain *ain)
{
for (int i = 0; i < ain->nr_libraries; i++) {
printf("--- ");
print_sjis(ain->libraries[i].name);
printf(" ---\n");
for (int j = 0; j < ain->libraries[i].nr_functions; j++) {
struct ain_hll_function *f = &ain->libraries[i].functions[j];
print_sjis(ain_strtype(ain, f->data_type, -1));
putchar(' ');
print_sjis(f->name);
putchar('(');
for (int k = 0; k < f->nr_arguments; k++) {
struct ain_hll_argument *a = &f->arguments[k];
if (a->data_type == AIN_VOID)
continue;
if (k > 0)
printf(", ");
print_sjis(ain_strtype(ain, a->data_type, -1));
putchar(' ');
print_sjis(a->name);
}
printf(")\n");
}
}
}
static void ain_dump_strings(struct ain *ain)
{
for (int i = 0; i < ain->nr_strings; i++) {
print_sjis(ain->strings[i]->text);
printf("\n---\n");
}
}
static void ain_dump_filenames(struct ain *ain)
{
for (int i = 0; i < ain->nr_filenames; i++) {
print_sjis(ain->filenames[i]);
putchar('\n');
}
}
static void ain_dump_functypes(struct ain *ain)
{
for (int i = 0; i < ain->nr_function_types; i++) {
struct ain_function_type *t = &ain->function_types[i];
printf("functype ");
print_sjis(ain_strtype(ain, t->data_type, t->struct_type));
putchar(' ');
print_sjis(t->name);
print_arglist(ain, t->variables, t->nr_arguments);
putchar('\n');
}
}
static void ain_dump_global_group_names(struct ain *ain)
{
for (int i = 0; i < ain->nr_global_groups; i++) {
print_sjis(ain->global_group_names[i]);
putchar('\n');
}
}
static void ain_dump_enums(struct ain *ain)
{
for (int i = 0; i < ain->nr_enums; i++) {
print_sjis(ain->enums[i]);
putchar('\n');
}
}
int main(int argc, char *argv[])
{
bool dump_code = false;
bool dump_functions = false;
bool dump_globals = false;
bool dump_structures = false;
bool dump_messages = false;
bool dump_libraries = false;
bool dump_strings = false;
bool dump_filenames = false;
bool dump_functypes = false;
bool dump_global_group_names = false;
bool dump_enums = false;
int err = AIN_SUCCESS;
struct ain *ain;
while (1) {
static struct option long_options[] = {
{ "help", no_argument, 0, 'h' },
{ "code", no_argument, 0, 'c' },
{ "functions", no_argument, 0, 'f' },
{ "globals", no_argument, 0, 'g' },
{ "structures", no_argument, 0, 'o' },
{ "messages", no_argument, 0, 'm' },
{ "libraries", no_argument, 0, 'l' },
{ "strings", no_argument, 0, 's' },
{ "filenames", no_argument, 0, 'n' },
{ "function-types", no_argument, 0, 't' },
{ "global-group-names", no_argument, 0, 'r' },
{ "enums", no_argument, 0, 'e' },
};
int option_index = 0;
int c;
c = getopt_long(argc, argv, "hcfgomlse", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
usage();
return 0;
case 'c':
dump_code = true;
break;
case 'f':
dump_functions = true;
break;
case 'g':
dump_globals = true;
break;
case 'o':
dump_structures = true;
break;
case 'm':
dump_messages = true;
break;
case 'l':
dump_libraries = true;
break;
case 's':
dump_strings = true;
break;
case 'n':
dump_filenames = true;
break;
case 't':
dump_functypes = true;
break;
case 'r':
dump_global_group_names = true;
break;
case 'e':
dump_enums = true;
break;
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
printf("ERROR: Wrong number of arguments.\n");
usage();
return 1;
}
if (!(ain = ain_open(argv[0], &err))) {
printf("ERROR: %s\n", ain_strerror(err));
return 1;
}
if (dump_structures)
ain_dump_structures(ain);
if (dump_functypes)
ain_dump_functypes(ain);
if (dump_global_group_names)
ain_dump_global_group_names(ain);
if (dump_globals)
ain_dump_globals(ain);
if (dump_libraries)
ain_dump_libraries(ain);
if (dump_functions)
ain_dump_functions(ain);
if (dump_messages)
ain_dump_messages(ain);
if (dump_strings)
ain_dump_strings(ain);
if (dump_filenames)
ain_dump_filenames(ain);
if (dump_enums)
ain_dump_enums(ain);
if (dump_code)
ain_dump_code(ain);
}