diff --git a/include/xsystem4.h b/include/xsystem4.h index bcfc9a9..513a810 100644 --- a/include/xsystem4.h +++ b/include/xsystem4.h @@ -43,6 +43,7 @@ struct config { bool echo; float text_x_scale; bool manual_text_x_scale; + bool rsm_save; }; extern struct config config; diff --git a/src/resume.c b/src/resume.c index 99d4357..385abbe 100644 --- a/src/resume.c +++ b/src/resume.c @@ -17,6 +17,7 @@ // enable private VM interface #define VM_PRIVATE +#include #include #include #include @@ -24,6 +25,7 @@ #include "system4.h" #include "system4/file.h" +#include "system4/savefile.h" #include "system4/string.h" #include "savedata.h" @@ -148,7 +150,229 @@ static cJSON *vm_image_to_json(const char *key) return image; } -int vm_save_image(const char *key, const char *path) +static struct rsave_heap_frame *frame_page_to_rsave(struct page *page, int ref) +{ + struct rsave_heap_frame *o = xcalloc(1, sizeof(struct rsave_heap_frame) + page->nr_vars * sizeof(int32_t)); + o->ref = ref; + struct ain_variable *vars; + if (page->type == GLOBAL_PAGE) { + o->tag = RSAVE_GLOBALS; + o->func.id = -1; + assert(page->nr_vars == ain->nr_globals); + vars = ain->globals; + } else { + struct ain_function *f = &ain->functions[page->index]; + o->tag = RSAVE_LOCALS; + o->func.name = strdup(f->name); + assert(page->nr_vars == f->nr_vars); + vars = f->vars; + } + o->nr_types = page->nr_vars; + o->types = xcalloc(o->nr_types, sizeof(int32_t)); + for (int i = 0; i < o->nr_types; i++) + o->types[i] = vars[i].type.data; + 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 struct rsave_heap_struct *struct_page_to_rsave(struct page *page, int ref) +{ + 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; + 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 : ""); + o->uk = 0; + o->struct_type.name = strdup(s->name); + o->nr_types = s->nr_members; + o->types = xcalloc(o->nr_types, sizeof(int32_t)); + for (int i = 0; i < o->nr_types; i++) + o->types[i] = s->members[i].type.data; + 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 struct rsave_heap_array *array_page_to_rsave(struct page *page, int ref) +{ + 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->rank_minus_1 = page->array.rank - 1; + o->data_type = page->index; + if (page->array.struct_type >= 0) + o->struct_type.name = strdup(ain->structures[page->array.struct_type].name); + else + o->struct_type.name = strdup(""); + o->root_rank = page->array.rank; // FIXME: this is incorrect for subarrays + o->is_not_empty = page->nr_vars ? 1 : 0; + 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) + return rsave_null; + if (heap[i].type == VM_STRING) { + int len = heap[i].s->size + 1; + struct rsave_heap_string *s = xmalloc(sizeof(struct rsave_heap_string) + len); + s->tag = RSAVE_STRING; + s->ref = heap[i].ref; + s->uk = 0; + s->len = len; + memcpy(s->text, heap[i].s->text, len); + return s; + } + struct page *page = heap[i].page; + if (!page) { + // Empty array. + struct rsave_heap_array *o = xcalloc(1, sizeof(struct rsave_heap_array)); + o->tag = RSAVE_ARRAY; + o->ref = heap[i].ref; + o->rank_minus_1 = -1; + + // FIXME: System40.exe populates them but we don't have the type + // information of the array here. + o->data_type = 0; + o->struct_type.name = strdup(""); + + o->root_rank = -1; + o->is_not_empty = 0; + o->nr_slots = 0; + return o; + } + switch (page->type) { + case GLOBAL_PAGE: + case LOCAL_PAGE: + return frame_page_to_rsave(page, heap[i].ref); + case STRUCT_PAGE: + return struct_page_to_rsave(page, heap[i].ref); + case ARRAY_PAGE: + return array_page_to_rsave(page, heap[i].ref); + default: + ERROR("unsupported type %d", page->type); + } +} + +static void save_heap_to_rsave(struct rsave *rs) +{ + rs->nr_heap_objs = heap_size; + rs->heap = xcalloc(heap_size, sizeof(void*)); + for (size_t i = 0; i < heap_size; i++) { + rs->heap[i] = heap_item_to_rsave(i); + } +} + +static void save_call_stack_to_rsave(struct rsave *rs) +{ + rs->nr_call_frames = call_stack_ptr + 1; + rs->call_frames = xcalloc(rs->nr_call_frames, sizeof(struct rsave_call_frame)); + rs->call_frames[0].type = RSAVE_CALL_STACK_BOTTOM; + rs->call_frames[0].local_ptr = -1; + rs->nr_return_records = call_stack_ptr; + rs->return_records = xcalloc(rs->nr_return_records + 1, sizeof(struct rsave_return_record)); + rs->return_records[0].return_addr = -1; + + struct ain_function *prev_func = NULL; + for (int i = 0; i < call_stack_ptr; i++) { + struct function_call *call = &call_stack[i]; + struct ain_function *func = &ain->functions[call->fno]; + if (call->struct_page >= 0) { + rs->call_frames[i + 1].type = RSAVE_METHOD_CALL; + } else if (!strcmp(func->name, "main")) { + rs->call_frames[i + 1].type = RSAVE_ENTRY_POINT; + } else { + rs->call_frames[i + 1].type = RSAVE_FUNCTION_CALL; + } + rs->call_frames[i + 1].local_ptr = call->page_slot; + rs->call_frames[i + 1].struct_ptr = call->struct_page; + if (i > 0) { + rs->return_records[i].return_addr = call->return_address; + rs->return_records[i].local_addr = call->return_address - prev_func->address; + } + rs->return_records[i + 1].caller_func = strdup(func->name); + rs->return_records[i + 1].crc = func->crc; + prev_func = func; + } + rs->ip = rs->return_records[call_stack_ptr]; + rs->ip.return_addr = instr_ptr + 6; + rs->ip.local_addr = instr_ptr - prev_func->address + 6; +} + +static void save_stack_to_rsave(struct rsave *rs) +{ + // For compatibility with the AliceSoft's implementation, exclude two values + // (the SYS_RESUME_SAVE arguments) at the top of the stack. + rs->stack_size = stack_ptr - 2; + + rs->stack = xcalloc(rs->stack_size, sizeof(int32_t)); + for (int i = 0; i < rs->stack_size; i++) { + rs->stack[i] = stack[i].i; + } +} + +static void save_func_names_to_rsave(struct rsave *rs) +{ + rs->nr_func_names = ain->nr_functions; + rs->func_names = xcalloc(rs->nr_func_names, sizeof(char *)); + for (int i = 0; i < ain->nr_functions; i++) { + rs->func_names[i] = strdup(ain->functions[i].name); + } +} + +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; + save->key = strdup(key); + return save; +} + +static struct rsave *vm_image_to_rsave(const char *key) +{ + struct rsave *save = make_rsave(key); + save_heap_to_rsave(save); + save_call_stack_to_rsave(save); + save_stack_to_rsave(save); + save_func_names_to_rsave(save); + return save; +} + +static int save_rsave_image(const char *key, const char *path) +{ + struct rsave *save = vm_image_to_rsave(key); + if (!save) + return 0; + char *full_path = savedir_path(path); + FILE *fp = file_open_utf8(full_path, "wb"); + if (!fp) { + WARNING("Failed to open save file %s: %s", display_utf0(full_path), strerror(errno)); + free(full_path); + rsave_free(save); + return 0; + } + free(full_path); + + bool encrypt = true; + int compression_level = 1; + enum savefile_error error = rsave_write(save, fp, encrypt, compression_level); + if (error != SAVEFILE_SUCCESS) + WARNING("Failed to write save file: %s", savefile_strerror(error)); + fclose(fp); + rsave_free(save); + return 1; +} + +static int save_json_image(const char *key, const char *path) { cJSON *image = vm_image_to_json(key); int r = save_json(path, image); @@ -156,6 +380,14 @@ int vm_save_image(const char *key, const char *path) return r; } +int vm_save_image(const char *key, const char *path) +{ + if (config.rsm_save) + return save_rsave_image(key, path); + else + return save_json_image(key, path); +} + #define _invalid_save_data(file, func, line, fmt, ...) \ _vm_error("*ERROR*(%s:%s:%d): " fmt "\n", file, func, line, ##__VA_ARGS__) #define invalid_save_data(fmt, ...) \ @@ -187,7 +419,7 @@ static cJSON *_type_check(const char *file, const char *func, int line, int type #define type_check(type, json) _type_check(__FILE__, __func__, __LINE__, type, json) -static void load_page(int slot, cJSON *json) +static void load_json_page(int slot, cJSON *json) { int struct_type = 0, rank = 0; @@ -221,7 +453,7 @@ static void load_page(int slot, cJSON *json) heap[slot].type = VM_PAGE; } -static void load_string(int slot, cJSON *json) +static void load_json_string(int slot, cJSON *json) { const char *str = cJSON_GetStringValue(json); heap[slot].s = make_string(str, strlen(str)); @@ -279,7 +511,7 @@ void delegate_list_add(struct delegate_list *list, int slot) list->slots[list->n++] = slot; } -static void load_heap(cJSON *json) +static void load_json_heap(cJSON *json) { struct delegate_list delegates = {0}; delete_heap(); @@ -298,9 +530,9 @@ static void load_heap(cJSON *json) heap[slot].ref = ref; if (cJSON_IsString(value)) { - load_string(slot, value); + load_json_string(slot, value); } else if (cJSON_IsObject(value)) { - load_page(slot, value); + load_json_page(slot, value); if (heap[slot].page->type == DELEGATE_PAGE) delegate_list_add(&delegates, slot); } else if (cJSON_IsNull(value)) { @@ -319,7 +551,137 @@ static void load_heap(cJSON *json) free(delegates.slots); } -static void load_call_stack(cJSON *json) +static int resolve_func_symbol(struct rsave_symbol *sym) +{ + if (!sym->name) + return sym->id; + return ain_get_function(ain, sym->name); +} + +static int resolve_struct_symbol(struct rsave_symbol *sym) +{ + if (!sym->name) + return sym->id; + return ain_get_struct(ain, sym->name); +} + +static void load_rsave_frame(int slot, struct rsave_heap_frame *f) +{ + struct page *page; + int nr_vars; + struct ain_variable *vars; + if (f->tag == RSAVE_GLOBALS) { + page = alloc_page(GLOBAL_PAGE, 0, f->nr_slots); + nr_vars = ain->nr_globals; + vars = ain->globals; + } else { + int func = resolve_func_symbol(&f->func); + page = alloc_page(LOCAL_PAGE, func, f->nr_slots); + nr_vars = ain->functions[func].nr_vars; + vars = ain->functions[func].vars; + } + + // type check + if (nr_vars < f->nr_types) { + invalid_save_data("call frame has too many variables"); + } + for (int i = 0; i < f->nr_types; i++) { + if (f->types[i] != vars[i].type.data) { + invalid_save_data( + "variable type mismatch. Expected %d, got %d", + vars[i].type.data, f->types[i]); + } + } + + for (int i = 0; i < f->nr_slots; i++) { + // TODO: update function pointers using rsave->func_names + page->values[i].i = f->slots[i]; + } + alloc_heap_slot(slot); + heap[slot].ref = f->ref; + heap[slot].type = VM_PAGE; + heap[slot].page = page; +} + +static void load_rsave_string(int slot, struct rsave_heap_string *s) +{ + alloc_heap_slot(slot); + heap[slot].ref = s->ref; + heap[slot].type = VM_STRING; + heap[slot].s = make_string(s->text, s->len - 1); +} + +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].type = VM_PAGE; + heap[slot].page = NULL; + return; + } + struct page *page = alloc_page(ARRAY_PAGE, a->data_type, a->nr_slots); + page->array.struct_type = resolve_struct_symbol(&a->struct_type); + page->array.rank = a->rank_minus_1 + 1; + for (int i = 0; i < a->nr_slots; i++) + page->values[i].i = a->slots[i]; + + alloc_heap_slot(slot); + heap[slot].ref = a->ref; + heap[slot].type = VM_PAGE; + heap[slot].page = page; +} + +static void load_rsave_struct(int slot, struct rsave_heap_struct *s) +{ + int struct_index = resolve_struct_symbol(&s->struct_type); + struct page *page = alloc_page(STRUCT_PAGE, struct_index, s->nr_slots); + page->struc.delegates = NULL; + page->struc.nr_delegates = 0; + + // type check + struct ain_struct *as = &ain->structures[struct_index]; + if (as->nr_members < s->nr_types) { + invalid_save_data("Struct %s has too many members", as->name); + } + for (int i = 0; i < s->nr_types; i++) { + if (s->types[i] != as->members[i].type.data) { + invalid_save_data( + "%s.%s: member type mismatch. Expected %d, got %d", + as->name, as->members[i].name, + as->members[i].type.data, s->types[i]); + } + } + + for (int i = 0; i < s->nr_slots; i++) { + // TODO: update function pointers using rsave->func_names + page->values[i].i = s->slots[i]; + } + + alloc_heap_slot(slot); + heap[slot].ref = s->ref; + heap[slot].type = VM_PAGE; + heap[slot].page = page; +} + +static void load_rsave_heap(struct rsave *save) +{ + delete_heap(); + + for (int slot = 0; slot < save->nr_heap_objs; slot++) { + 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_NULL: break; + } + } +} + +static void load_json_call_stack(cJSON *json) { call_stack_ptr = 0; cJSON *item; @@ -336,7 +698,46 @@ static void load_call_stack(cJSON *json) } } -static void load_stack(cJSON *json) +static void load_rsave_call_stack(struct rsave *save) +{ + call_stack_ptr = 0; + + if (save->nr_return_records == 0 || save->return_records[0].return_addr != -1) + ERROR("invalid resume save"); + struct rsave_return_record *rr = save->return_records; + if (save->nr_return_records == save->nr_call_frames) + rr++; + else if (save->nr_return_records != save->nr_call_frames - 1) + ERROR("invalid resume save"); + + int32_t return_address = -1; + for (int i = 0; i < save->nr_call_frames; i++) { + if (save->call_frames[i].type != RSAVE_CALL_STACK_BOTTOM) { + int fno = ain_get_function(ain, rr->caller_func); + if (fno < 0) + VM_ERROR("Failed to load VM image: function '%s' not found", display_sjis0(rr->caller_func)); + if (ain->functions[fno].crc != rr->crc) + VM_ERROR("Failed to load VM image: CRC mismatch for function '%s'", display_sjis0(rr->caller_func)); + call_stack[call_stack_ptr++] = (struct function_call) { + .fno = fno, + .return_address = return_address, + .page_slot = save->call_frames[i].local_ptr, + .struct_page = save->call_frames[i].struct_ptr, + .delegate = -1, + }; + // Calculate return address from the function address and offset + // to make it robust to ain changes. + return_address = ain->functions[fno].address + rr->local_addr; + } + if (++rr == save->return_records + save->nr_return_records) + rr = &save->ip; + } + // RSM records instruction pointer after the CALLSYS instruction, but + // xsystem4 expects the address of the CALLSYS instruction, so -6. + instr_ptr = return_address - 6; +} + +static void load_json_stack(cJSON *json) { stack_ptr = 0; @@ -345,9 +746,23 @@ static void load_stack(cJSON *json) type_check(cJSON_Number, item); stack_push_value(vm_int(item->valueint)); } + // Pop the arguments of SYS_RESUME_SAVE. + //heap_unref(stack_pop().i); + //heap_unref(stack_pop().i); + stack_pop(); + stack_pop(); } -static cJSON *read_image(const char *keyname, const char *path) +static void load_rsave_stack(struct rsave *save) +{ + stack_ptr = 0; + + for (int i = 0; i < save->stack_size; i++) { + stack_push_value(vm_int(save->stack[i])); + } +} + +static cJSON *read_json_image(const char *keyname, const char *path) { char *full_path = savedir_path(path); char *save_file = file_read(full_path, NULL); @@ -367,23 +782,56 @@ static cJSON *read_image(const char *keyname, const char *path) return type_check(cJSON_Object, save); } -void vm_load_image(const char *key, const char *path) +static void load_json_image(const char *key, const char *path) { - cJSON *save = read_image(key, path); + cJSON *save = read_json_image(key, path); if (!save) { VM_ERROR("Failed to read VM image: '%s'", display_sjis0(path)); } cJSON *ip = type_check(cJSON_Number, cJSON_GetObjectItem(save, "ip")); - load_heap(type_check(cJSON_Array, cJSON_GetObjectItem(save, "heap"))); - load_call_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "call-stack"))); - load_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "stack"))); + load_json_heap(type_check(cJSON_Array, cJSON_GetObjectItem(save, "heap"))); + load_json_call_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "call-stack"))); + load_json_stack(type_check(cJSON_Array, cJSON_GetObjectItem(save, "stack"))); instr_ptr = ip->valueint; cJSON_Delete(save); } -struct page *vm_load_image_comments(const char *key, const char *path, int *success) +static enum savefile_error load_rsave_image(const char *key, const char *path) { - cJSON *save = read_image(key, path); + char *full_path = savedir_path(path); + enum savefile_error error; + struct rsave *save = rsave_read(full_path, &error); + free(full_path); + if (error != SAVEFILE_SUCCESS) + return error; + if (strcmp(key, save->key)) + invalid_save_data("Key doesn't match"); + load_rsave_heap(save); + load_rsave_call_stack(save); + load_rsave_stack(save); + rsave_free(save); + return SAVEFILE_SUCCESS; +} + +void vm_load_image(const char *key, const char *path) +{ + // First, try to read as a rsave. + enum savefile_error error = load_rsave_image(key, path); + switch (error) { + case SAVEFILE_SUCCESS: + return; + case SAVEFILE_INVALID_SIGNATURE: + // If not a System4 resume save file, read as a json. + load_json_image(key, path); + return; + default: + VM_ERROR("Failed to read VM image '%s': %s", display_sjis0(path), savefile_strerror(error)); + } +} + +static struct page *load_json_image_comments(const char *key, const char *path, int *success) +{ + cJSON *save = read_json_image(key, path); if (!save) { *success = 0; return NULL; @@ -418,9 +866,103 @@ struct page *vm_load_image_comments(const char *key, const char *path, int *succ return array; } -int vm_write_image_comments(const char *key, const char *path, struct page *comments) +static struct page *load_rsave_image_comments(const char *key, const char *path, enum savefile_error *error) { - cJSON *save = read_image(key, path); + char *full_path = savedir_path(path); + struct rsave *save = rsave_read(full_path, error); + free(full_path); + if (*error != SAVEFILE_SUCCESS) + return NULL; + if (strcmp(key, save->key)) + invalid_save_data("Key doesn't match"); + if (save->nr_comments == 0) { + rsave_free(save); + return NULL; + } + union vm_value dims = { .i = save->nr_comments }; + struct page *array = alloc_array(1, &dims, AIN_ARRAY_STRING, 0, false); + for (int i = 0; i < dims.i; i++) { + int slot = heap_alloc_slot(VM_STRING); + if (!save->comments[i][0]) { + heap[slot].s = string_ref(&EMPTY_STRING); + } else { + heap[slot].s = make_string(save->comments[i], strlen(save->comments[i])); + } + array->values[i].i = slot; + } + rsave_free(save); + return array; +} + +struct page *vm_load_image_comments(const char *key, const char *path, int *success) +{ + // First, try to read as a rsave. + enum savefile_error error; + struct page *page = load_rsave_image_comments(key, path, &error); + if (error == SAVEFILE_SUCCESS) { + *success = 1; + return page; + } + + if (error == SAVEFILE_INVALID_SIGNATURE) { + // If not a System4 resume save file, read as a json. + return load_json_image_comments(key, path, success); + } + *success = 0; + return NULL; +} + +static int write_rsave_image_comments(const char *key, const char *path, struct page *comments) +{ + char *full_path = savedir_path(path); + enum savefile_error error; + struct rsave *save = rsave_read(full_path, &error); + switch (error) { + case SAVEFILE_SUCCESS: + if (strcmp(key, save->key)) + invalid_save_data("Key doesn't match"); + break; + case SAVEFILE_FILE_ERROR: + save = make_rsave(key); + save->comments_only = true; + break; + default: + free(full_path); + return 0; + } + + for (int i = 0; i < save->nr_comments; i++) { + free(save->comments[i]); + } + free(save->comments); + save->nr_comments = comments->nr_vars; + save->comments = xcalloc(save->nr_comments, sizeof(char*)); + for (int i = 0; i < save->nr_comments; i++) { + save->comments[i] = xstrdup(heap_get_string(comments->values[i].i)->text); + } + + FILE *fp = file_open_utf8(full_path, "wb"); + if (!fp) { + WARNING("Failed to open save file %s: %s", display_utf0(full_path), strerror(errno)); + free(full_path); + rsave_free(save); + return 0; + } + free(full_path); + + bool encrypt = true; + int compression_level = 1; + error = rsave_write(save, fp, encrypt, compression_level); + if (error != SAVEFILE_SUCCESS) + WARNING("Failed to write save file: %s", savefile_strerror(error)); + fclose(fp); + rsave_free(save); + return 1; +} + +static int write_json_image_comments(const char *key, const char *path, struct page *comments) +{ + cJSON *save = read_json_image(key, path); if (!save) { // create blank save save = cJSON_CreateObject(); @@ -440,3 +982,11 @@ int vm_write_image_comments(const char *key, const char *path, struct page *comm cJSON_Delete(save); return 1; } + +int vm_write_image_comments(const char *key, const char *path, struct page *comments) +{ + if (config.rsm_save) + return write_rsave_image_comments(key, path, comments); + else + return write_json_image_comments(key, path, comments); +} diff --git a/src/system4.c b/src/system4.c index 474e757..6f6fd0c 100644 --- a/src/system4.c +++ b/src/system4.c @@ -60,6 +60,7 @@ struct config config = { .echo = false, .text_x_scale = 1.0, .manual_text_x_scale = false, + .rsm_save = false, .bgi_path = NULL, .wai_path = NULL, @@ -175,6 +176,15 @@ static void read_user_config_file(const char *path) } else if (!strcmp(ini[i].name->text, "save-folder")) { free(config.save_dir); config.save_dir = xstrdup(ini_string(&ini[i])->text); + } else if (!strcmp(ini[i].name->text, "save-format")) { + if (!strcmp(ini_string(&ini[i])->text, "json")) { + config.rsm_save = false; + } else if (!strcmp(ini_string(&ini[i])->text, "rsm")) { + config.rsm_save = true; + } else { + WARNING("Invalid value for save-format in config: \"%s\"", + ini_string(&ini[i])->text); + } } ini_free_entry(&ini[i]); } @@ -344,6 +354,7 @@ static void usage(void) puts(" --font-x-scale Specify the x scale for text rendering (1.0 = default scale)"); puts(" -j, --joypad Enable joypad"); puts(" --save-folder Override save folder location"); + puts(" --save-format Specify the resume save file format. json (default) or rsm"); #ifdef DEBUGGER_ENABLED puts(" --nodebug Disable debugger"); puts(" --debug Start in debugger"); @@ -372,6 +383,7 @@ enum { LOPT_FONT_X_SCALE, LOPT_JOYPAD, LOPT_SAVE_FOLDER, + LOPT_SAVE_FORMAT, #ifdef DEBUGGER_ENABLED LOPT_NODEBUG, LOPT_DEBUG, @@ -417,6 +429,7 @@ int main(int argc, char *argv[]) { "font-x-scale", required_argument, 0, LOPT_FONT_X_SCALE }, { "joypad", optional_argument, 0, LOPT_JOYPAD }, { "save-folder", required_argument, 0, LOPT_SAVE_FOLDER }, + { "save-format", required_argument, 0, LOPT_SAVE_FORMAT }, #ifdef DEBUGGER_ENABLED { "nodebug", no_argument, 0, LOPT_NODEBUG }, { "debug", no_argument, 0, LOPT_DEBUG }, @@ -471,6 +484,15 @@ int main(int argc, char *argv[]) case LOPT_SAVE_FOLDER: savedir = optarg; break; + case LOPT_SAVE_FORMAT: + if (!strcmp(optarg, "json")) { + config.rsm_save = false; + } else if (!strcmp(optarg, "rsm")) { + config.rsm_save = true; + } else { + WARNING("Invalid value for --save-format option: \"%s\"", optarg); + } + break; #ifdef DEBUGGER_ENABLED case LOPT_NODEBUG: dbg_enabled = false; diff --git a/src/vm.c b/src/vm.c index 5498784..6b177ee 100644 --- a/src/vm.c +++ b/src/vm.c @@ -530,10 +530,6 @@ static void system_call(enum syscall_code code) int filename_slot = stack_pop().i; int key_slot = stack_pop().i; vm_load_image(heap_get_string(key_slot)->text, heap_get_string(filename_slot)->text); - //heap_unref(stack_pop().i); - //heap_unref(stack_pop().i); - stack_pop(); - stack_pop(); stack_push(0); break; } @@ -998,7 +994,7 @@ static enum opcode execute_instruction(enum opcode opcode) sys_message("Assertion failed at %s:%d: %s\n", display_sjis0(heap_get_string(file)->text), line, - display_sjis0(heap_get_string(expr)->text)); + display_sjis1(heap_get_string(expr)->text)); vm_exit(1); } heap_unref(file); diff --git a/subprojects/libsys4 b/subprojects/libsys4 index c5d3cb9..0409f62 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit c5d3cb955e2595456d5833918e55987f9c91fc88 +Subproject commit 0409f62789bd2f9b4364d4e9a755aaff7725cfba