From 2b2710b4cc3de9cab60b3b8684b27391a4e59f51 Mon Sep 17 00:00:00 2001 From: Priw8 Date: Thu, 20 Aug 2020 18:43:57 +0200 Subject: [PATCH] thanm: fix negative script id recompiling Turns out that instructions don't refer to scripts by their ID, but by their index in the file, which is not always the same. Why. --- thanm/anmparse.y | 3 ++- thanm/thanm.c | 62 ++++++++++++++++++++++++++++++++++-------------- thanm/thanm.h | 5 +++- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/thanm/anmparse.y b/thanm/anmparse.y index ca19d251..88be8224 100644 --- a/thanm/anmparse.y +++ b/thanm/anmparse.y @@ -434,9 +434,10 @@ Script: reg_reset(state->current_version); script->offset = malloc(sizeof(*script->offset)); script->offset->id = state->script_id++; + script->real_index = state->script_real_index++; symbol_id_pair_t* symbol = (symbol_id_pair_t*)malloc(sizeof(symbol_id_pair_t)); - symbol->id = script->offset->id; + symbol->id = script->real_index; symbol->name = $name; list_append_new(&state->script_names, symbol); if (state->symbolfp != NULL) diff --git a/thanm/thanm.c b/thanm/thanm.c index bad1ee98..c3f07223 100644 --- a/thanm/thanm.c +++ b/thanm/thanm.c @@ -657,12 +657,29 @@ replace_minus( } } +static int +anm_is_valid_script_index( + const anm_archive_t* anm, + int32_t index +) { + anm_entry_t* entry; + anm_script_t* script; + list_for_each(&anm->entries, entry) { + list_for_each(&entry->scripts, script) { + if (script->real_index == index) + return 1; + } + } + return 0; +} + static void anm_stringify_param( FILE* stream, thanm_param_t* param, thanm_instr_t* instr, - int scriptn + const anm_archive_t* anm, + int32_t scriptn ) { char* disp = NULL; char* dest = NULL; @@ -670,21 +687,25 @@ anm_stringify_param( switch(param->type) { case 'o': - sprintf(buf, "offset%u", param->val->val.S); + sprintf(buf, "offset%d", param->val->val.S); replace_minus(buf); dest = buf; break; case 'n': - /* Sprite -1 is used as an empty sprite. */ - if (param->val->val.S == -1) + /* Sprite -1 is actually sometimes used to indicate no sprite. */ + if (param->val->val.S < 0) sprintf(buf, "%d", param->val->val.S); else sprintf(buf, "sprite%d", param->val->val.S); dest = buf; break; case 'N': - sprintf(buf, "script%d", param->val->val.S); - replace_minus(buf); + if (anm_is_valid_script_index(anm, param->val->val.S)) { + sprintf(buf, "script%d", param->val->val.S); + replace_minus(buf); + } else { + sprintf(buf, "%d", param->val->val.S); + } dest = buf; break; default: @@ -811,7 +832,7 @@ thanm_instr_free( static void anm_insert_labels( anm_script_t* script, - int scriptn + int32_t scriptn ) { thanm_instr_t* instr; list_for_each(&script->instrs, instr) { @@ -870,7 +891,7 @@ anm_read_file( archive->map = map_base = file_mmap(in, file_size); map = map_base; - int scriptn = 0; + int32_t scriptn = 0; for (;;) { anm_entry_t* entry = malloc(sizeof(*entry)); anm_header06_t* header = (anm_header06_t*)map; @@ -927,6 +948,7 @@ anm_read_file( (anm_offset_t*)(map + sizeof(*header) + header->sprites * sizeof(uint32_t)); for (uint32_t s = 0; s < header->scripts; ++s) { anm_script_t* script = anm_script_new(); + script->real_index = scriptn; script->offset = &(script_offsets[s]); unsigned char* limit = map; @@ -1106,7 +1128,8 @@ static void anm_stringify_instr( FILE* stream, thanm_instr_t* instr, - int scriptn + const anm_archive_t* anm, + int32_t scriptn ) { seqmap_entry_t* ent = seqmap_get(g_anmmap->ins_names, instr->id); @@ -1117,7 +1140,7 @@ anm_stringify_instr( thanm_param_t* param; list_for_each(&instr->params, param) { - anm_stringify_param(stream, param, instr, scriptn); + anm_stringify_param(stream, param, instr, anm, scriptn); if (!list_is_last_iteration()) { fprintf(stream, ", "); } @@ -1135,7 +1158,6 @@ anm_dump( unsigned int entry_num = 0; anm_entry_t* entry; - int scriptn = 0; int prev_sprite_id = -1; int prev_script_id = -1; list_for_each(&anm->entries, entry) { @@ -1198,13 +1220,17 @@ anm_dump( anm_script_t* script; list_for_each(&entry->scripts, script) { - - sprintf(buf, "script%d", script->offset->id); - replace_minus(buf); + /* We need to use the index of the script in file for the name, because that's + * what instructions that refer to scripts use. I'm unsure what's the actual purpose of the ID field. + * However! I still don't really know how old versions work, and maybe instructions refer to the + * ID field there? If that's the case, it should be enough to just do a check earlier that sets + * real_index to offset->id in old versions (and re-add the replace_minus calls). + * I honestly doubt that this is the case, but I want to leave a note how to change it anyway in case + * it's needed... */ if (script->offset->id - 1 != prev_script_id) { - fprintf(stream, "script %d %s {\n", script->offset->id, buf); + fprintf(stream, "script %d script%d {\n", script->offset->id, script->real_index); } else { - fprintf(stream, "script %s {\n", buf); + fprintf(stream, "script script%d {\n", script->real_index); } prev_script_id = script->offset->id; @@ -1215,7 +1241,7 @@ anm_dump( switch(instr->type) { case THANM_INSTR_INSTR: fprintf(stream, " "); - anm_stringify_instr(stream, instr, scriptn); + anm_stringify_instr(stream, instr, anm, script->real_index); break; case THANM_INSTR_TIME: if (instr->time < 0) @@ -1240,7 +1266,6 @@ anm_dump( } fprintf(stream, "}\n\n"); - ++scriptn; } fprintf(stream, "\n"); @@ -1755,6 +1780,7 @@ anm_create( state.current_version = -1; state.sprite_id = 0; state.script_id = 0; + state.script_real_index = 0; list_init(&state.entries); list_init(&state.globals); list_init(&state.script_names); diff --git a/thanm/thanm.h b/thanm/thanm.h index d27149ac..25835b4f 100644 --- a/thanm/thanm.h +++ b/thanm/thanm.h @@ -42,6 +42,8 @@ extern unsigned int option_force; const id_format_pair_t* anm_get_formats(uint32_t version); typedef struct { + /* The id in the offset struct may not be the real index. */ + int32_t real_index; anm_offset_t* offset; /* instrs of thanm_instr_t format */ list_t instrs; @@ -125,7 +127,8 @@ typedef struct parser_state_t { int32_t current_version; uint32_t offset; uint32_t sprite_id; - uint32_t script_id; + int32_t script_id; + int32_t script_real_index; /* List of anm_entry_t */ list_t entries; anm_entry_t* current_entry;