From 9e2f03d8be87fbe43f925eaf3ae1d7f5e676143a Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 15 Sep 2024 13:25:28 +0900 Subject: [PATCH] Add support for group save format v7 V5 is used until Daiteikoku, and v7 since Rance Quest. Since both are Ain v6, the save version is determined by the presence of the "MainVM" field in AliceStart.ini. --- include/xsystem4.h | 1 + src/savedata.c | 34 ++++++++++++++++++++++++++-------- src/system4.c | 3 +++ subprojects/libsys4 | 2 +- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/xsystem4.h b/include/xsystem4.h index b91536e..b991f9f 100644 --- a/include/xsystem4.h +++ b/include/xsystem4.h @@ -23,6 +23,7 @@ struct config { char *game_name; char *ain_filename; + char *vm_name; char *game_dir; char *save_dir; char *home_dir; diff --git a/src/savedata.c b/src/savedata.c index 38fce9d..ccfd746 100644 --- a/src/savedata.c +++ b/src/savedata.c @@ -88,10 +88,11 @@ static struct gsave_flat_array *collect_flat_arrays(struct page *page, struct gs fa = collect_flat_arrays(heap_get_page(page->values[i].i), fa, save); return fa; } + enum ain_data_type type = variable_type(page, 0, NULL, NULL); fa->nr_values = page->nr_vars; + fa->type = type; fa->values = xcalloc(fa->nr_values, sizeof(struct gsave_array_value)); for (int i = 0; i < page->nr_vars; i++) { - enum ain_data_type type = variable_type(page, i, NULL, NULL); fa->values[i].type = type; fa->values[i].value = add_value_to_gsave(type, page->values[i], save); } @@ -123,6 +124,11 @@ static int32_t add_value_to_gsave(enum ain_data_type type, union vm_value val, s .nr_indices = st->nr_members, .indices = xcalloc(st->nr_members, sizeof(int32_t)), }; + if (save->version >= 7) { + rec.struct_index = gsave_get_struct_def(save, st->name); + if (rec.struct_index < 0) + rec.struct_index = gsave_add_struct_def(save, st); + } for (int i = 0; i < page->nr_vars; i++) { enum ain_data_type type = st->members[i].type.data; int32_t value = add_value_to_gsave(type, page->values[i], save); @@ -166,6 +172,14 @@ static int32_t add_value_to_gsave(enum ain_data_type type, union vm_value val, s } } +static int get_gsave_version(void) +{ + if (AIN_VERSION_GTE(ain, 6, 0)) { + return config.vm_name ? 5 : 7; + } + return AIN_VERSION_GTE(ain, 5, 0) ? 5 : 4; +} + int save_globals(const char *keyname, const char *filename, const char *group_name, int *n_out) { int group = -1; @@ -184,8 +198,7 @@ int save_globals(const char *keyname, const char *filename, const char *group_na nr_vars = ain->nr_globals; } - int gsave_version = AIN_VERSION_GTE(ain, 5, 0) ? 5 : 4; - struct gsave *save = gsave_create(gsave_version, keyname, ain->nr_globals, group_name); + struct gsave *save = gsave_create(get_gsave_version(), keyname, ain->nr_globals, group_name); gsave_add_globals_record(save, nr_vars); struct gsave_global *global = save->globals; @@ -450,27 +463,32 @@ static union vm_value gsave_to_vm_value(struct gsave *save, enum ain_data_type t case AIN_STRING: { int slot = heap_alloc_slot(VM_STRING); - heap[slot].s = string_ref(save->strings[value]); + heap[slot].s = string_ref(value == GSAVE7_EMPTY_STRING ? &EMPTY_STRING : save->strings[value]); return vm_int(slot); } case AIN_STRUCT: { struct gsave_record *rec = &save->records[value]; struct ain_struct *st = &ain->structures[struct_type]; - if (strcmp(rec->struct_name, st->name)) + struct gsave_struct_def *sd = + save->version >= 7 ? &save->struct_defs[rec->struct_index] : NULL; + const char *struct_name = sd ? sd->name : rec->struct_name; + if (strcmp(struct_name, st->name)) VM_ERROR("Bad save file: structure name mismatch"); int slot = alloc_struct(struct_type); struct page *page = heap[slot].page; for (int i = 0; i < rec->nr_indices; i++) { struct gsave_keyval *kv = &save->keyvals[rec->indices[i]]; - int index = struct_member_index(st, kv->name); + const char *field_name = sd ? sd->fields[i].name : kv->name; + enum ain_data_type field_type = sd ? sd->fields[i].type : kv->type; + int index = struct_member_index(st, field_name); if (index < 0) { - WARNING("structure %s has no member named %s", display_sjis0(rec->struct_name), display_sjis1(kv->name)); + WARNING("structure %s has no member named %s", display_sjis0(struct_name), display_sjis1(field_name)); continue; } int struct_type, array_rank; enum ain_data_type data_type = variable_type(page, index, &struct_type, &array_rank); - if (data_type != kv->type) + if (data_type != field_type) VM_ERROR("Bad save file: structure member type mismatch"); union vm_value val = gsave_to_vm_value(save, data_type, struct_type, array_rank, kv->value); page->values[index] = val; diff --git a/src/system4.c b/src/system4.c index 622aa10..f48ed08 100644 --- a/src/system4.c +++ b/src/system4.c @@ -49,6 +49,7 @@ void apply_game_specific_hacks(struct ain *ain); struct config config = { .game_name = NULL, .ain_filename = NULL, + .vm_name = NULL, .game_dir = NULL, .save_dir = NULL, .home_dir = NULL, @@ -133,6 +134,8 @@ static bool read_config(const char *path) config.game_name = strdup(ini_string(&ini[i])->text); } else if (!strcmp(ini[i].name->text, "CodeName")) { config.ain_filename = strdup(ini_string(&ini[i])->text); + } else if (!strcmp(ini[i].name->text, "MainVM")) { + config.vm_name = strdup(ini_string(&ini[i])->text); } else if (!strcmp(ini[i].name->text, "SaveFolder")) { config.save_dir = strdup(ini_string(&ini[i])->text); } else if (!strcmp(ini[i].name->text, "ViewWidth")) { diff --git a/subprojects/libsys4 b/subprojects/libsys4 index 81c1319..9f5feaa 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit 81c13197702309813e61867ef6b3093d7b5565b5 +Subproject commit 9f5feaa240e96cf0eb6bedc60034a2d44ce4a810