Merge pull request #144 from kichikuou/resume-save

Add support for reading / writing System4 resume save files
This commit is contained in:
Nunuhara Cabbage
2024-02-15 20:34:45 -08:00
committed by GitHub
5 changed files with 593 additions and 24 deletions
+1
View File
@@ -43,6 +43,7 @@ struct config {
bool echo;
float text_x_scale;
bool manual_text_x_scale;
bool rsm_save;
};
extern struct config config;
+568 -18
View File
@@ -17,6 +17,7 @@
// enable private VM interface
#define VM_PRIVATE
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
@@ -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);
}
+22
View File
@@ -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;
+1 -5
View File
@@ -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);