From ddaef07fb59e709a96578a8286898c0629db6d5f Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 4 Aug 2024 12:22:56 +0900 Subject: [PATCH 1/2] resume: Support RSM v9 format This enables using --save-format=rsm for games that use delegates. --- src/resume.c | 78 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/resume.c b/src/resume.c index 6651ad1..415b488 100644 --- a/src/resume.c +++ b/src/resume.c @@ -154,10 +154,11 @@ static cJSON *vm_image_to_json(const char *key) return image; } -static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int ref) +static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int slot) { struct rsave_heap_frame *o = xcalloc(1, sizeof(struct rsave_heap_frame) + page->nr_vars * sizeof(int32_t)); - o->ref = ref; + o->ref = heap[slot].ref; + o->seq = heap[slot].seq; struct ain_variable *vars; if (page->type == GLOBAL_PAGE) { o->tag = RSAVE_GLOBALS; @@ -170,6 +171,7 @@ static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int ref) o->func.name = strdup(f->name); assert(page->nr_vars == f->nr_vars); vars = f->vars; + // TODO: populate o->struct_ptr } o->nr_types = page->nr_vars; o->types = xcalloc(o->nr_types, sizeof(int32_t)); @@ -181,11 +183,12 @@ static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int ref) return o; } -static struct rsave_heap_struct *struct_page_to_rsave(struct page *page, int ref) +static struct rsave_heap_struct *struct_page_to_rsave(struct page *page, int slot) { struct rsave_heap_struct *o = xcalloc(1, sizeof(struct rsave_heap_struct) + page->nr_vars * sizeof(int32_t)); o->tag = RSAVE_STRUCT; - o->ref = ref; + o->ref = heap[slot].ref; + o->seq = heap[slot].seq; struct ain_struct *s = &ain->structures[page->index]; o->ctor.name = strdup(s->constructor >= 0 ? ain->functions[s->constructor].name : ""); o->dtor.name = strdup(s->destructor >= 0 ? ain->functions[s->destructor].name : ""); @@ -201,11 +204,12 @@ static struct rsave_heap_struct *struct_page_to_rsave(struct page *page, int ref return o; } -static struct rsave_heap_array *array_page_to_rsave(struct page *page, int ref) +static struct rsave_heap_array *array_page_to_rsave(struct page *page, int slot) { struct rsave_heap_array *o = xcalloc(1, sizeof(struct rsave_heap_array) + page->nr_vars * sizeof(int32_t)); o->tag = RSAVE_ARRAY; - o->ref = ref; + o->ref = heap[slot].ref; + o->seq = heap[slot].seq; o->rank_minus_1 = page->array.rank - 1; o->data_type = page->index; if (page->array.struct_type >= 0) @@ -220,6 +224,18 @@ static struct rsave_heap_array *array_page_to_rsave(struct page *page, int ref) return o; } +static struct rsave_heap_delegate *delegate_page_to_rsave(struct page *page, int slot) +{ + struct rsave_heap_delegate *o = xcalloc(1, sizeof(struct rsave_heap_delegate) + page->nr_vars * sizeof(int32_t)); + o->tag = RSAVE_DELEGATE; + o->ref = heap[slot].ref; + o->seq = heap[slot].seq; + o->nr_slots = page->nr_vars; + for (int i = 0; i < o->nr_slots; i++) + o->slots[i] = page->values[i].i; + return o; +} + static void *heap_item_to_rsave(int i) { if (!heap[i].ref) @@ -229,6 +245,7 @@ static void *heap_item_to_rsave(int i) struct rsave_heap_string *s = xmalloc(sizeof(struct rsave_heap_string) + len); s->tag = RSAVE_STRING; s->ref = heap[i].ref; + s->seq = heap[i].seq; s->uk = 0; s->len = len; memcpy(s->text, heap[i].s->text, len); @@ -240,6 +257,7 @@ static void *heap_item_to_rsave(int i) struct rsave_heap_array *o = xcalloc(1, sizeof(struct rsave_heap_array)); o->tag = RSAVE_ARRAY; o->ref = heap[i].ref; + o->seq = heap[i].seq; o->rank_minus_1 = -1; // FIXME: System40.exe populates them but we don't have the type @@ -255,11 +273,13 @@ static void *heap_item_to_rsave(int i) switch (page->type) { case GLOBAL_PAGE: case LOCAL_PAGE: - return frame_page_to_rsave(page, heap[i].ref); + return frame_page_to_rsave(page, i); case STRUCT_PAGE: - return struct_page_to_rsave(page, heap[i].ref); + return struct_page_to_rsave(page, i); case ARRAY_PAGE: - return array_page_to_rsave(page, heap[i].ref); + return array_page_to_rsave(page, i); + case DELEGATE_PAGE: + return delegate_page_to_rsave(page, i); default: ERROR("unsupported type %d", page->type); } @@ -334,9 +354,13 @@ static void save_func_names_to_rsave(struct rsave *rs) static struct rsave *make_rsave(const char *key) { struct rsave *save = xcalloc(1, sizeof(struct rsave)); - // FIXME: This is not always correct. RSM version cannot be determined from - // the ain version alone. - save->version = ain->version <= 4 ? 6 : 7; + if (ain->nr_delegates > 0) { + save->version = 9; + } else { + // FIXME: This is not always correct. Pastel Chime Continue is AIN v4 + // but uses RSM v7. + save->version = ain->version <= 4 ? 6 : 7; + } save->key = strdup(key); return save; } @@ -344,6 +368,7 @@ static struct rsave *make_rsave(const char *key) static struct rsave *vm_image_to_rsave(const char *key) { struct rsave *save = make_rsave(key); + save->next_seq = heap_next_seq; save_heap_to_rsave(save); save_call_stack_to_rsave(save); save_stack_to_rsave(save); @@ -480,6 +505,7 @@ static void delete_heap(void) break; } heap[i].ref = 0; + heap[i].seq = 0; } heap_free_ptr = 0; @@ -586,6 +612,7 @@ static void load_rsave_frame(int slot, struct rsave_heap_frame *f) } alloc_heap_slot(slot); heap[slot].ref = f->ref; + heap[slot].seq = f->seq; heap[slot].type = VM_PAGE; heap[slot].page = page; } @@ -594,6 +621,7 @@ static void load_rsave_string(int slot, struct rsave_heap_string *s) { alloc_heap_slot(slot); heap[slot].ref = s->ref; + heap[slot].seq = s->seq; heap[slot].type = VM_STRING; heap[slot].s = make_string(s->text, s->len - 1); } @@ -603,6 +631,7 @@ static void load_rsave_array(int slot, struct rsave_heap_array *a) if (a->rank_minus_1 < 0) { alloc_heap_slot(slot); heap[slot].ref = a->ref; + heap[slot].seq = a->seq; heap[slot].type = VM_PAGE; heap[slot].page = NULL; return; @@ -615,6 +644,7 @@ static void load_rsave_array(int slot, struct rsave_heap_array *a) alloc_heap_slot(slot); heap[slot].ref = a->ref; + heap[slot].seq = a->seq; heap[slot].type = VM_PAGE; heap[slot].page = page; } @@ -645,6 +675,20 @@ static void load_rsave_struct(int slot, struct rsave_heap_struct *s) alloc_heap_slot(slot); heap[slot].ref = s->ref; + heap[slot].seq = s->seq; + heap[slot].type = VM_PAGE; + heap[slot].page = page; +} + +static void load_rsave_delegate(int slot, struct rsave_heap_delegate *d) +{ + struct page *page = alloc_page(DELEGATE_PAGE, 0, d->nr_slots); + for (int i = 0; i < d->nr_slots; i++) + page->values[i].i = d->slots[i]; + + alloc_heap_slot(slot); + heap[slot].ref = d->ref; + heap[slot].seq = d->seq; heap[slot].type = VM_PAGE; heap[slot].page = page; } @@ -657,13 +701,15 @@ static void load_rsave_heap(struct rsave *save) enum rsave_heap_tag *tag = save->heap[slot]; switch (*tag) { case RSAVE_GLOBALS: - case RSAVE_LOCALS: load_rsave_frame(slot, save->heap[slot]); break; - case RSAVE_STRING: load_rsave_string(slot, save->heap[slot]); break; - case RSAVE_ARRAY: load_rsave_array(slot, save->heap[slot]); break; - case RSAVE_STRUCT: load_rsave_struct(slot, save->heap[slot]); break; + case RSAVE_LOCALS: load_rsave_frame(slot, save->heap[slot]); break; + case RSAVE_STRING: load_rsave_string(slot, save->heap[slot]); break; + case RSAVE_ARRAY: load_rsave_array(slot, save->heap[slot]); break; + case RSAVE_STRUCT: load_rsave_struct(slot, save->heap[slot]); break; + case RSAVE_DELEGATE: load_rsave_delegate(slot, save->heap[slot]); break; case RSAVE_NULL: break; } } + heap_next_seq = save->next_seq; } static void load_json_call_stack(cJSON *json) From c4207e4226209db00c5a2f6cc374a153e788faff Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 4 Aug 2024 13:31:05 +0900 Subject: [PATCH 2/2] Store struct_ptr in local page It is currently only used to populate a field in RSM v9 save file. As far as I can tell from examining Rance 01's resume save, this reference does not seem to increment the target's refcount. --- include/vm/page.h | 16 +++++++++++----- src/resume.c | 3 ++- src/vm.c | 5 ++++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/vm/page.h b/include/vm/page.h index 1f289e4..4e827d2 100644 --- a/include/vm/page.h +++ b/include/vm/page.h @@ -66,11 +66,17 @@ struct page { int index; enum ain_data_type a_type; }; - // array-specific metadata - struct { - int struct_type; - int rank; - } array; + union { + // local-specific metadata + struct { + int struct_ptr; + } local; + // array-specific metadata + struct { + int struct_type; + int rank; + } array; + }; int nr_vars; union vm_value values[]; }; diff --git a/src/resume.c b/src/resume.c index 415b488..b7dc249 100644 --- a/src/resume.c +++ b/src/resume.c @@ -171,7 +171,7 @@ static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int slot) o->func.name = strdup(f->name); assert(page->nr_vars == f->nr_vars); vars = f->vars; - // TODO: populate o->struct_ptr + o->struct_ptr = page->local.struct_ptr; } o->nr_types = page->nr_vars; o->types = xcalloc(o->nr_types, sizeof(int32_t)); @@ -592,6 +592,7 @@ static void load_rsave_frame(int slot, struct rsave_heap_frame *f) page = alloc_page(LOCAL_PAGE, func, f->nr_slots); nr_vars = ain->functions[func].nr_vars; vars = ain->functions[func].vars; + page->local.struct_ptr = f->struct_ptr; } // type check diff --git a/src/vm.c b/src/vm.c index 2c6f7a8..0712e3a 100644 --- a/src/vm.c +++ b/src/vm.c @@ -334,6 +334,7 @@ static int _function_call(int fno, int return_address) struct ain_function *f = &ain->functions[fno]; int slot = heap_alloc_slot(VM_PAGE); heap_set_page(slot, alloc_page(LOCAL_PAGE, fno, f->nr_vars)); + heap[slot].page->local.struct_ptr = -1; call_stack[call_stack_ptr++] = (struct function_call) { .fno = fno, @@ -376,7 +377,9 @@ static void function_call(int fno, int return_address) static void method_call(int fno, int return_address) { function_call(fno, return_address); - call_stack[call_stack_ptr-1].struct_page = stack_pop().i; + int struct_page = stack_pop().i; + call_stack[call_stack_ptr-1].struct_page = struct_page; + heap[call_stack[call_stack_ptr-1].page_slot].page->local.struct_ptr = struct_page; } static void vm_execute(void);