diff --git a/include/system4/ex.h b/include/system4/ex.h new file mode 100644 index 0000000..21a4000 --- /dev/null +++ b/include/system4/ex.h @@ -0,0 +1,105 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#ifndef SYSTEM4_EX_H +#define SYSTEM4_EX_H + +#include +#include +#include + +enum ex_value_type { + EX_INT = 1, + EX_FLOAT = 2, + EX_STRING = 3, + EX_TABLE = 4, + EX_LIST = 5, + EX_TREE = 6 +}; + +struct ex_value { + enum ex_value_type type; + union { + int32_t i; + float f; + struct string *s; + struct ex_table *t; + struct ex_list *list; + struct ex_tree *tree; + }; +}; + +struct ex_field { + enum ex_value_type type; + struct string *name; + int32_t uk0; + int32_t uk1; + int32_t uk2; + uint32_t nr_subfields; + struct ex_field *subfields; +}; + +struct ex_table { + uint32_t nr_fields; + struct ex_field *fields; + uint32_t nr_columns; + uint32_t nr_rows; + struct ex_value **rows; +}; + +struct ex_list_item { + uint32_t size; + struct ex_value value; +}; + +struct ex_list { + uint32_t nr_items; + struct ex_list_item *items; +}; + +struct ex_leaf { + uint32_t size; + struct string *name; + struct ex_value value; +}; + +struct ex_tree { + struct string *name; + bool is_leaf; + union { + struct { + uint32_t nr_children; + struct ex_tree *children; + }; + struct ex_leaf leaf; + }; +}; + +struct ex_block { + size_t size; + struct string *name; + struct ex_value val; +}; + +struct ex { + uint32_t nr_blocks; + struct ex_block *blocks; +}; + +struct ex *ex_read(const char *path); +void ex_free(struct ex *ex); + +#endif /* SYSTE4_EX_H */ diff --git a/src/ex.c b/src/ex.c new file mode 100644 index 0000000..e726522 --- /dev/null +++ b/src/ex.c @@ -0,0 +1,503 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include "little_endian.h" +#include "system4.h" +#include "system4/ex.h" +#include "system4/string.h" + +uint8_t ex_decode_table[256]; +uint8_t ex_decode_table_inv[256]; + +struct buf_reader { + uint8_t *buf; + size_t size; + size_t index; +}; + +static void buf_init(struct buf_reader *r, uint8_t *buf, size_t size) +{ + r->buf = buf; + r->size = size; + r->index = 0; +} + +static int32_t buf_read_int32(struct buf_reader *r) +{ + if (r->index + 4 > r->size) + ERROR("Out of bounds buffer read"); + int32_t v = LittleEndian_getDW(r->buf, r->index); + r->index += 4; + return v; +} + +static float buf_read_float(struct buf_reader *r) +{ + union { int32_t i; float f; } v; + v.i = buf_read_int32(r); + return v.f; +} + +static struct string *buf_read_pascal_string(struct buf_reader *r) +{ + int32_t len = buf_read_int32(r); + if (len < 0) + ERROR("Invalid string length: %d", len); + struct string *s = make_string((char*)r->buf+r->index, len); + r->index += len; + return s; +} + +static void buf_skip(struct buf_reader *r, size_t off) +{ + r->index = min(r->index + off, r->size); +} + +static size_t buf_remaining(struct buf_reader *r) +{ + return r->size - r->index; +} + +static char *buf_strdata(struct buf_reader *r) +{ + return (char*)r->buf + r->index; +} + +static struct string *ex_read_string(struct buf_reader *r) +{ + struct string *s = buf_read_pascal_string(r); + // TODO: at most 3 bytes padding, no need for full strlen + s->size = strlen(s->text); + // TODO: validate? + return s; +} + +static uint8_t *ex_decode(uint8_t *data, size_t *len, uint32_t *nr_blocks) +{ + struct buf_reader r; + uint32_t compressed_size; + unsigned long uncompressed_size; + + // initialize table + for (int i = 0; i < 256; i++) { + int tmpfor = i; + int tmp = tmpfor; + tmp = (tmp & 0x55) + ((tmp >> 1) & 0x55); + tmp = (tmp & 0x33) + ((tmp >> 2) & 0x33); + tmp = (tmp & 0x0F) + ((tmp >> 4) & 0x0F); + if ((tmp & 0x01) == 0) { + tmpfor = ((tmpfor << (8 - tmp)) | (tmpfor >> tmp)) & 0xFF; + } else { + tmpfor = ((tmpfor >> (8 - tmp)) | (tmpfor << tmp)) & 0xFF; + } + ex_decode_table[i] = tmpfor; + } + // initialize inverse table + for (int i = 0; i < 256; i++) { + ex_decode_table_inv[ex_decode_table[i]] = i; + } + + buf_init(&r, data, *len); + if (strncmp(buf_strdata(&r), "HEAD", 4)) + ERROR("Missing HEAD section marker"); + buf_skip(&r, 8); + + if (strncmp(buf_strdata(&r), "EXTF", 4)) + ERROR("Missing EXTF section marker"); + buf_skip(&r, 8); + + *nr_blocks = buf_read_int32(&r); + + if (strncmp(buf_strdata(&r), "DATA", 4)) { + ERROR("Missing DATA section marker"); + } + buf_skip(&r, 4); + + compressed_size = buf_read_int32(&r); + uncompressed_size = buf_read_int32(&r); + + // decode compressed data + for (size_t i = 0; i < compressed_size; i++) { + data[r.index+i] = ex_decode_table[data[r.index+i]]; + } + + uint8_t *out = xmalloc(uncompressed_size); + int rv = uncompress(out, &uncompressed_size, (uint8_t*)buf_strdata(&r), compressed_size); + switch (rv) { + case Z_BUF_ERROR: ERROR("Uncompress failed: Z_BUF_ERROR"); + case Z_MEM_ERROR: ERROR("Uncompress failed: Z_MEM_ERROR"); + case Z_DATA_ERROR: ERROR("Uncompress failed: Z_DATA_ERROR"); + case Z_OK: break; + default: ERROR("Uncompress failed: %d", rv); + } + + // decompress + *len = uncompressed_size; + return out; +} + +static void ex_read_fields(struct buf_reader *r, struct ex_table *table); +static void ex_read_table(struct buf_reader *r, struct ex_table *table, struct ex_field *fields, uint32_t nr_fields); +static void ex_read_list(struct buf_reader *r, struct ex_list *list); + +static void _ex_read_value(struct buf_reader *r, struct ex_value *value, struct ex_field *fields, uint32_t nr_fields) +{ + switch (value->type) { + case EX_INT: + value->i = buf_read_int32(r); + break; + case EX_FLOAT: + value->f = buf_read_float(r); + break; + case EX_STRING: + value->s = buf_read_pascal_string(r); + break; + case EX_TABLE: + value->t = xcalloc(1, sizeof(struct ex_table)); + // XXX: if nr_fields is zero, we are NOT a sub-table and therefore need to read fields + if (!nr_fields) { + ex_read_fields(r, value->t); + ex_read_table(r, value->t, value->t->fields, value->t->nr_fields); + } else { + ex_read_table(r, value->t, fields, nr_fields); + } + break; + case EX_LIST: + value->list = xcalloc(1, sizeof(struct ex_list)); + ex_read_list(r, value->list); + break; + default: + ERROR("Unhandled value type: %d", value->type); + } +} + +static void ex_read_value(struct buf_reader *r, struct ex_value *value, struct ex_field *fields, uint32_t nr_fields) +{ + value->type = buf_read_int32(r); + _ex_read_value(r, value, fields, nr_fields); +} + +static void ex_read_field(struct buf_reader *r, struct ex_field *field) +{ + field->type = buf_read_int32(r); + if (field->type < EX_INT || field->type > EX_TABLE) + ERROR("Unknown/invalid field type: %d", field->type); + + field->name = ex_read_string(r); + field->uk0 = buf_read_int32(r); + field->uk1 = buf_read_int32(r); + if (field->uk0) + field->uk2 = buf_read_int32(r); + + if (field->type == EX_TABLE) { + field->nr_subfields = buf_read_int32(r); + if (field->nr_subfields > 255) + ERROR("Too many subfields: %u", field->nr_subfields); + + field->subfields = xcalloc(field->nr_subfields, sizeof(struct ex_field)); + for (uint32_t i = 0; i < field->nr_subfields; i++) { + ex_read_field(r, &field->subfields[i]); + } + } +} + +static void ex_read_fields(struct buf_reader *r, struct ex_table *table) +{ + table->nr_fields = buf_read_int32(r); + table->fields = xcalloc(table->nr_fields, sizeof(struct ex_field)); + for (uint32_t i = 0; i < table->nr_fields; i++) { + ex_read_field(r, &table->fields[i]); + } +} + +enum table_layout { + TABLE_DEFAULT, + ROW_MAJOR, + COLUMN_MAJOR +}; + +static enum table_layout table_layout = TABLE_DEFAULT; +static bool read_table = false; + +static void ex_read_table(struct buf_reader *r, struct ex_table *table, struct ex_field *fields, uint32_t nr_fields) +{ + // NOTE: Starting in Evenicle, the rows/columns are reversed + if (table_layout == COLUMN_MAJOR) { + table->nr_rows = buf_read_int32(r); + table->nr_columns = buf_read_int32(r); + } else { + table->nr_columns = buf_read_int32(r); + table->nr_rows = buf_read_int32(r); + } + + // try switching to column-major order if fields don't make sense + if (table_layout == TABLE_DEFAULT && table->nr_columns != nr_fields) { + if (read_table) { + ERROR("Can't switch to column-major order because a table was already read"); + } else if (table->nr_rows == nr_fields) { + WARNING("Switching to column-major order"); + uint32_t tmp = table->nr_columns; + table->nr_columns = table->nr_rows; + table->nr_rows = tmp; + table_layout = COLUMN_MAJOR; + } else { + ERROR("Number of fields doesn't match number of columns: %u, %u", table->nr_columns, nr_fields); + } + } else if (table->nr_columns != nr_fields) { + ERROR("Number of fields doesn't match number of columns: %u, %u", table->nr_columns, nr_fields); + } + + read_table = true; + + table->rows = xcalloc(table->nr_rows, sizeof(struct ex_value*)); + for (uint32_t i = 0; i < table->nr_rows; i++) { + table->rows[i] = xcalloc(table->nr_columns, sizeof(struct ex_value)); + for (uint32_t j = 0; j < table->nr_columns; j++) { + ex_read_value(r, &table->rows[i][j], fields[j].subfields, fields[j].nr_subfields); + if (table->rows[i][j].type != fields[j].type) + ERROR("Column type doesn't match field type: %d/%d", table->rows[i][j], fields[j].type); + } + } +} + +static void ex_read_list(struct buf_reader *r, struct ex_list *list) +{ + list->nr_items = buf_read_int32(r); + list->items = xcalloc(list->nr_items, sizeof(struct ex_list_item)); + for (uint32_t i = 0; i < list->nr_items; i++) { + list->items[i].value.type = buf_read_int32(r); + list->items[i].size = buf_read_int32(r); + _ex_read_value(r, &list->items[i].value, NULL, 0); + // TODO: check size + } +} + +static void ex_read_tree(struct buf_reader *r, struct ex_tree *tree) +{ + tree->name = ex_read_string(r); + uint32_t is_leaf = buf_read_int32(r); + if (is_leaf > 1) + ERROR("tree->is_leaf is not a boolean: %u", is_leaf); + tree->is_leaf = is_leaf; + + if (!tree->is_leaf) { + tree->nr_children = buf_read_int32(r); + tree->children = xcalloc(tree->nr_children, sizeof(struct ex_tree)); + for (uint32_t i = 0; i < tree->nr_children; i++) { + ex_read_tree(r, &tree->children[i]); + } + } else { + tree->leaf.value.type = buf_read_int32(r); + tree->leaf.size = buf_read_int32(r); + tree->leaf.name = ex_read_string(r); + _ex_read_value(r, &tree->leaf.value, NULL, 0); + + int32_t zero = buf_read_int32(r); + if (zero) { + ERROR("Expected 0 after leaf node: 0x%x at 0x%zx", zero, r->index); + } + // TODO: check size + } +} + +static void ex_read_block(struct buf_reader *r, struct ex_block *block) +{ + block->val.type = buf_read_int32(r); + if (block->val.type < EX_INT || block->val.type > EX_TREE) + ERROR("Unknown/invalid block type: %d", block->val.type); + + block->size = buf_read_int32(r); + if (block->size > buf_remaining(r)) + ERROR("Block size extends past end of file: %" SIZE_T_FMT "u", block->size); + + block->name = ex_read_string(r); + + switch (block->val.type) { + case EX_INT: + block->val.i = buf_read_int32(r); + break; + case EX_FLOAT: + block->val.f = buf_read_float(r); + break; + case EX_STRING: + block->val.s = buf_read_pascal_string(r); + break; + case EX_TABLE: + block->val.t = xcalloc(1, sizeof(struct ex_table)); + ex_read_fields(r, block->val.t); + ex_read_table(r, block->val.t, block->val.t->fields, block->val.t->nr_fields); + break; + case EX_LIST: + block->val.list = xcalloc(1, sizeof(struct ex_list)); + ex_read_list(r, block->val.list); + break; + case EX_TREE: + block->val.tree = xcalloc(1, sizeof(struct ex_tree)); + ex_read_tree(r, block->val.tree); + break; + } +} + +struct ex *ex_read(const char *path) +{ + FILE *fp; + long len; + uint8_t *buf = NULL; + + if (!(fp = fopen(path, "rb"))) + ERROR("fopen failed: %s", strerror(errno)); + + fseek(fp, 0, SEEK_END); + len = ftell(fp); + fseek(fp, 0, SEEK_SET); + + buf = xmalloc(len+1); + if (fread(buf, len, 1, fp) != 1) + ERROR("fread failed: %s", strerror(errno)); + if (fclose(fp)) + ERROR("fclose failed: %s", strerror(errno)); + buf[len] = '\0'; + + size_t decoded_len; + uint32_t nr_blocks; + uint8_t *decoded = ex_decode(buf, &decoded_len, &nr_blocks); + + FILE *out = fopen("rance3.dex", "wb"); + fwrite(decoded, decoded_len, 1, out); + fclose(out); + + struct buf_reader r; + buf_init(&r, decoded, decoded_len); + + struct ex *ex = xmalloc(sizeof(struct ex)); + ex->nr_blocks = nr_blocks; + ex->blocks = xcalloc(nr_blocks, sizeof(struct ex_block)); + for (size_t i = 0; i < nr_blocks; i++) { + ex_read_block(&r, &ex->blocks[i]); + } + + free(buf); + free(decoded); + return ex; +} + +static void ex_free_table(struct ex_table *table); +static void ex_free_list(struct ex_list *list); +static void ex_free_tree(struct ex_tree *tree); + +static void ex_free_value(struct ex_value *value) +{ + switch (value->type) { + case EX_STRING: + free_string(value->s); + break; + case EX_TABLE: + ex_free_table(value->t); + break; + case EX_LIST: + ex_free_list(value->list); + break; + case EX_TREE: + ex_free_tree(value->tree); + free(value->tree); + break; + default: + break; + } +} + +static void ex_free_values(struct ex_value *values, uint32_t n) +{ + for (uint32_t i = 0; i < n; i++) { + ex_free_value(&values[i]); + } + free(values); +} + +static void ex_free_fields(struct ex_field *fields, uint32_t n) +{ + for (uint32_t i = 0; i < n; i++) { + free_string(fields[i].name); + ex_free_fields(fields[i].subfields, fields[i].nr_subfields); + } + free(fields); +} + +static void ex_free_table(struct ex_table *table) +{ + ex_free_fields(table->fields, table->nr_fields); + for (uint32_t i = 0; i < table->nr_rows; i++) { + ex_free_values(table->rows[i], table->nr_columns); + } + free(table->rows); + free(table); +} + +static void ex_free_list(struct ex_list *list) +{ + for (uint32_t i = 0; i < list->nr_items; i++) { + ex_free_value(&list->items[i].value); + } + free(list->items); + free(list); +} + +static void ex_free_tree(struct ex_tree *tree) +{ + free_string(tree->name); + + if (tree->is_leaf) { + free_string(tree->leaf.name); + ex_free_value(&tree->leaf.value); + return; + } + + for (uint32_t i = 0; i < tree->nr_children; i++) { + ex_free_tree(&tree->children[i]); + } + free(tree->children); +} + +void ex_free(struct ex *ex) +{ + for (uint32_t i = 0; i < ex->nr_blocks; i++) { + struct ex_block *block = &ex->blocks[i]; + free_string(block->name); + switch (block->val.type) { + case EX_STRING: + free_string(block->val.s); + break; + case EX_TABLE: + ex_free_table(block->val.t); + break; + case EX_LIST: + ex_free_list(block->val.list); + break; + case EX_TREE: + ex_free_tree(block->val.tree); + free(block->val.tree); + break; + default: + break; + } + } +} diff --git a/src/exdump/exdump.c b/src/exdump/exdump.c new file mode 100644 index 0000000..38d3075 --- /dev/null +++ b/src/exdump/exdump.c @@ -0,0 +1,286 @@ +/* Copyright (C) 2019 Nunuhara Cabbage + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "little_endian.h" +#include "system4.h" +#include "system4/ex.h" +#include "system4/string.h" +#include "system4/utfsjis.h" + +int indent_level = 0; + +static void indent(FILE *out) +{ + for (int i = 0; i < indent_level; i++) { + fputc('\t', out); + } +} + +static void print_string(FILE *out, struct string *s) +{ + char *u = sjis2utf(s->text, s->size); + fprintf(out, "%s", u); + free(u); +} + +static const char *ex_strtype(enum ex_value_type type) +{ + switch (type) { + case EX_INT: return "int"; + case EX_FLOAT: return "float"; + case EX_STRING: return "string"; + case EX_TABLE: return "table"; + case EX_LIST: return "list"; + case EX_TREE: return "tree"; + default: return "unknown_type"; + } +} + +static char *_escape_string(const char *str, const char *escape_chars, const char *replace_chars) +{ + int escapes = 0; + char *u = sjis2utf(str, strlen(str)); + + // count number of required escapes + for (int i = 0; u[i]; i++) { + if (u[i] & 0x80) + continue; + for (int j = 0; escape_chars[j]; j++) { + if (u[i] == escape_chars[j]) { + escapes++; + break; + } + } + } + + // add backslash escapes + if (escapes > 0) { + int dst = 0; + int len = strlen(u); + char *tmp = xmalloc(len + escapes + 1); + for (int i = 0; u[i]; i++) { + bool escaped = false; + for (int j = 0; escape_chars[j]; j++) { + if (u[i] == escape_chars[j]) { + tmp[dst++] = '\\'; + tmp[dst++] = replace_chars[j]; + escaped = true; + break; + } + } + if (!escaped) + tmp[dst++] = u[i]; + } + tmp[len+escapes] = '\0'; + free(u); + u = tmp; + } + + return u; +} + +char *escape_string(const char *str) +{ + const char escape_chars[] = { '\\', '\"', '\n', 0 }; + const char replace_chars[] = { '\\', '\"', 'n', 0 }; + return _escape_string(str, escape_chars, replace_chars); +} + +static void ex_dump_string(FILE *out, struct string *str) +{ + char *u = escape_string(str->text); + fprintf(out, "\"%s\"", u); + free(u); +} + +static void ex_dump_field(FILE *out, struct ex_field *field) +{ + fprintf(out, "%s ", ex_strtype(field->type)); + print_string(out, field->name); + if (field->uk0 || field->uk1) { + if (field->uk0) + fprintf(out, "[%d,%d,%d]", field->uk0, field->uk1, field->uk2); + else + fprintf(out, "[%d,%d]", field->uk0, field->uk1); + } + + if (field->nr_subfields) { + fprintf(out, " { "); + for (uint32_t i = 0; i < field->nr_subfields; i++) { + ex_dump_field(out, &field->subfields[i]); + if (i+1 < field->nr_subfields) + fprintf(out, ", "); + } + fprintf(out, " }"); + } +} + +static void ex_dump_table(FILE *out, struct ex_table *table); +static void ex_dump_list(FILE *out, struct ex_list *list, bool in_line); + +static void ex_dump_value(FILE *out, struct ex_value *val) +{ + switch (val->type) { + case EX_INT: fprintf(out, "%d", val->i); break; + case EX_FLOAT: fprintf(out, "%f", val->f); break; + case EX_STRING: ex_dump_string(out, val->s); break; + case EX_TABLE: ex_dump_table(out, val->t); break; + case EX_LIST: ex_dump_list(out, val->list, true); break; + case EX_TREE: ERROR("TODO DUMP TREE"); break; + } +} + +static void ex_dump_row(FILE *out, struct ex_value *row, uint32_t nr_columns) +{ + fprintf(out, "{ "); + for (uint32_t i = 0; i < nr_columns; i++) { + ex_dump_value(out, &row[i]); + if (i+1 < nr_columns) + fprintf(out, ", "); + } + fprintf(out, " }"); +} + +static void ex_dump_table(FILE *out, struct ex_table *table) +{ + bool toplevel = !!table->nr_fields; + fputc('{', out); + if (toplevel) + fputc('\n', out); + + indent_level++; + if (table->nr_fields) { + indent(out); + fprintf(out, "{ "); + for (uint32_t i = 0; i < table->nr_fields; i++) { + ex_dump_field(out, &table->fields[i]); + if (i+1 < table->nr_fields) + fprintf(out, ", "); + } + fprintf(out, " }\n"); + } + for (uint32_t i = 0; i < table->nr_rows; i++) { + if (toplevel) + indent(out); + else + fputc(' ', out); + ex_dump_row(out, table->rows[i], table->nr_columns); + if (i+1 < table->nr_rows) + fputc(',', out); + else if (!toplevel) + fputc(' ', out); + if (toplevel) + fputc('\n', out); + } + indent_level--; + + indent(out); + fputc('}', out); +} + +static void ex_dump_list(FILE *out, struct ex_list *list, bool in_line) +{ + fputc('{', out); + fputc(in_line ? ' ' : '\n', out); + + indent_level++; + for (uint32_t i = 0; i < list->nr_items; i++) { + if (!in_line) + indent(out); + ex_dump_value(out, &list->items[i].value); + if (i+1 < list->nr_items) + fputc(',', out); + fputc(in_line ? ' ' : '\n', out); + } + indent_level--; + + fputc('}', out); +} + +static void ex_dump_tree(FILE *out, struct ex_tree *tree, int level) +{ + if (tree->is_leaf) { + if (tree->leaf.value.type == EX_TABLE) + fprintf(out, "(table) "); + else if (tree->leaf.value.type == EX_LIST) + fprintf(out, "(list) "); + else if (tree->leaf.value.type == EX_TREE) + fprintf(out, "(tree) "); // shouldn't happen? + ex_dump_value(out, &tree->leaf.value); + return; + } + + fprintf(out, "{\n"); + + indent_level++; + for (uint32_t i = 0; i < tree->nr_children; i++) { + indent(out); + print_string(out, tree->children[i].name); + fprintf(out, " = "); + ex_dump_tree(out, &tree->children[i], level+1); + fprintf(out, ",\n"); + } + indent_level--; + + indent(out); + fprintf(out, "}"); +} + +void ex_dump(FILE *out, struct ex *ex) +{ + indent_level = 0; + for (uint32_t i = 0; i < ex->nr_blocks; i++) { + struct ex_block *block = &ex->blocks[i]; + + // type name = + fprintf(out, "%s ", ex_strtype(block->val.type)); + print_string(out, block->name); + fprintf(out, " = "); + + // rvalue + switch (block->val.type) { + case EX_INT: fprintf(out, "%d", block->val.i); break; + case EX_FLOAT: fprintf(out, "%f", block->val.f); break; + case EX_STRING: ex_dump_string(out, block->val.s); break; + case EX_TABLE: ex_dump_table(out, block->val.t); break; + case EX_LIST: ex_dump_list(out, block->val.list, false); break; + case EX_TREE: ex_dump_tree(out, block->val.tree, 0); break; + } + + fputc(';', out); + if (i+1 < ex->nr_blocks) + fprintf(out, "\n\n"); + } +} + +int main(int argc, char *argv[]) +{ + if (argc != 2) + ERROR("Wrong number of arguments."); + + struct ex *ex = ex_read(argv[1]); + ex_dump(stdout, ex); + ex_free(ex); + + return 0; +} diff --git a/src/exdump/meson.build b/src/exdump/meson.build new file mode 100644 index 0000000..5f3d7ed --- /dev/null +++ b/src/exdump/meson.build @@ -0,0 +1,8 @@ +exdump = ['../cJSON.c', + 'exdump.c', +] + +executable('exdump', exdump, + dependencies : [libm, zlib], + include_directories : incdir, + link_with : libsys4) diff --git a/src/meson.build b/src/meson.build index 18b6b25..99b50a7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,6 +2,7 @@ system4 = ['ain.c', 'ald.c', 'cg.c', + 'ex.c', 'instructions.c', 'qnt.c', 'string.c', @@ -68,3 +69,4 @@ executable('xsystem4', xsystem4, subdir('aindump') subdir('ainedit') +subdir('exdump')