From 288381d8f6b1975662dfd0df5b0f36eec631f8cd Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 16 Dec 2023 12:48:44 +0900 Subject: [PATCH] BanMisc: Use the same save format as AliceSoft's implementation BanMisc.{Save,Load}Struct now serializes structs in the same format as AliceSoft's implementation, rather than in JSON format. The old JSON format can also be loaded for backwards compatibility. --- src/hll/AnteaterADVEngine.c | 4 +- src/hll/BanMisc.c | 135 ++++++++++++++++++++++++++---------- src/hll/iarray.c | 26 +++++-- src/hll/iarray.h | 5 +- 4 files changed, 125 insertions(+), 45 deletions(-) diff --git a/src/hll/AnteaterADVEngine.c b/src/hll/AnteaterADVEngine.c index 70eff94..bde3d2a 100644 --- a/src/hll/AnteaterADVEngine.c +++ b/src/hll/AnteaterADVEngine.c @@ -314,7 +314,7 @@ bool ADVSceneKeeper_AddADVScene(struct page **page) // serialize struct iarray_writer out; iarray_init_writer(&out, "SCN"); - iarray_write_struct(&out, *page); + iarray_write_struct(&out, *page, false); scenes.data[scenes.n++] = iarray_to_page(&out); iarray_free_writer(&out); @@ -341,7 +341,7 @@ bool ADVSceneKeeper_GetADVScene(int index, struct page **page) free_page(*page); } - *page = iarray_read_struct(&in, scene_struct_no()); + *page = iarray_read_struct(&in, scene_struct_no(), false); return !in.error; } diff --git a/src/hll/BanMisc.c b/src/hll/BanMisc.c index 39b43d4..d41780c 100644 --- a/src/hll/BanMisc.c +++ b/src/hll/BanMisc.c @@ -17,39 +17,101 @@ #include #include #include +#include #include "system4/file.h" #include "system4/string.h" +#include "system4/mt19937int.h" #include "hll.h" #include "audio.h" +#include "iarray.h" +#include "little_endian.h" #include "savedata.h" #include "vm/page.h" #include "xsystem4.h" +#define MD11_ENCRYPT_KEY 0x18ec03 +#define MD11_HEADER_SIZE 8 + +static const uint8_t md11_signature[] = { 'M', 'D', 0x01, 0x01 }; + static int BanMisc_SaveStruct(struct page *page, struct string *file_name) { + // serialize + struct iarray_writer out; + iarray_init_writer(&out, NULL); + iarray_write_struct(&out, page, true); + size_t raw_size; + uint8_t *raw_buf = iarray_to_buffer(&out, &raw_size); + iarray_free_writer(&out); + + // compress + unsigned long compressed_size = compressBound(raw_size); + uint8_t *buf = xmalloc(MD11_HEADER_SIZE + compressed_size); + memcpy(buf, md11_signature, sizeof(md11_signature)); + LittleEndian_putDW(buf, 4, raw_size); + uint8_t *compressed = buf + MD11_HEADER_SIZE; + int r = compress2(compressed, &compressed_size, raw_buf, raw_size, Z_DEFAULT_COMPRESSION); + free(raw_buf); + if (r != Z_OK) { + free(buf); + return 0; + } + + // encrypt + mt19937_xorcode(compressed, compressed_size, MD11_ENCRYPT_KEY); + + // write char *path = unix_path(file_name->text); - FILE *fp = file_open_utf8(path, "w"); - if (!fp) { - WARNING("Failed to open file '%s': %s", display_utf0(path), strerror(errno)); - free(path); - return 0; - } - free(path); - - cJSON *json = page_to_json(page); - char *str = cJSON_Print(json); - cJSON_Delete(json); - - bool ok = fputs(str, fp) != EOF; - free(str); - fclose(fp); + bool ok = file_write(path, buf, MD11_HEADER_SIZE + compressed_size); if (!ok) { - WARNING("BanMisc.SaveStruct failed (fputs): %s", strerror(errno)); - return 0; + WARNING("Failed to write file '%s': %s", display_utf0(path), strerror(errno)); } - return 1; + free(buf); + free(path); + return ok; +} + +static struct page *load_md11(int struct_type, uint8_t *buf, size_t len) +{ + if (len < MD11_HEADER_SIZE) { + WARNING("BanMisc.LoadStruct file too short"); + return NULL; + } + unsigned long raw_size = LittleEndian_getDW(buf, 4); + if (raw_size % 4 != 0) { + WARNING("BanMisc.LoadStruct invalid file format"); + return NULL; + } + buf += MD11_HEADER_SIZE; + len -= MD11_HEADER_SIZE; + + // decrypt + mt19937_xorcode(buf, len, MD11_ENCRYPT_KEY); + + // decompress + uint8_t *raw_buf = xmalloc(raw_size); + int r = uncompress(raw_buf, &raw_size, buf, len); + if (r != Z_OK) { + WARNING("BanMisc.LoadStruct uncompress failed: %d", r); + free(raw_buf); + return NULL; + } + + // deserialize + int nr_vars = raw_size / 4; + struct page *tmp_page = alloc_page(ARRAY_PAGE, AIN_ARRAY_INT, nr_vars); + tmp_page->array.rank = 1; + for (int i = 0; i < nr_vars; i++) { + tmp_page->values[i].i = LittleEndian_getDW(raw_buf, i * 4); + } + struct iarray_reader in; + iarray_init_reader(&in, tmp_page, NULL); + struct page *page = iarray_read_struct(&in, struct_type, true); + free_page(tmp_page); + free(raw_buf); + return page; } static int BanMisc_LoadStruct(struct page **_page, struct string *file_name) @@ -60,31 +122,30 @@ static int BanMisc_LoadStruct(struct page **_page, struct string *file_name) } char *path = unix_path(file_name->text); - FILE *fp = file_open_utf8(path, "r"); - if (!fp) { - WARNING("Failed to open file '%s': %s", display_utf0(path), strerror(errno)); - free(path); - return 0; - } + size_t len; + uint8_t *buf = file_read(path, &len); free(path); - - fseek(fp, 0, SEEK_END); - long len = ftell(fp); - fseek(fp, 0, SEEK_SET); - - char *buf = xmalloc(len + 1); - buf[len] = '\0'; - if (fread(buf, len, 1, fp) != 1) { - WARNING("BanMisc.LoadStruct failed (fread): %s", strerror(errno)); - free(buf); + if (!buf) { return 0; } - fclose(fp); - cJSON *json = cJSON_Parse(buf); + if (!memcmp(buf, md11_signature, sizeof(md11_signature))) { + page = load_md11(page->index, buf, len); + free(buf); + if (!page) + return 0; + if (*_page) { + delete_page_vars(*_page); + free_page(*_page); + } + *_page = page; + return 1; + } + // load from JSON (for backward compatibility) + cJSON *json = cJSON_Parse((char *)buf); free(buf); if (!json) { - WARNING("BanMisc.LoadStruct failed to parse JSON"); + WARNING("BanMisc.LoadStruct invalid file format"); return 0; } if (!cJSON_IsArray(json)) { diff --git a/src/hll/iarray.c b/src/hll/iarray.c index 7754ee9..4310dc1 100644 --- a/src/hll/iarray.c +++ b/src/hll/iarray.c @@ -16,6 +16,7 @@ #include #include +#include "little_endian.h" #include "system4.h" #include "system4/string.h" #include "system4/utfsjis.h" @@ -108,7 +109,7 @@ static void iarray_write_value(struct iarray_writer *w, struct ain_type *t, int iarray_write_string(w, heap_get_string(value)); break; case AIN_STRUCT: - iarray_write_struct(w, heap_get_page(value)); + iarray_write_struct(w, heap_get_page(value), false); break; case AIN_ARRAY_INT: case AIN_ARRAY_FLOAT: @@ -122,12 +123,14 @@ static void iarray_write_value(struct iarray_writer *w, struct ain_type *t, int } } -void iarray_write_struct(struct iarray_writer *w, struct page *page) +void iarray_write_struct(struct iarray_writer *w, struct page *page, bool with_type) { assert(page->type == STRUCT_PAGE); struct ain_struct *s = &ain->structures[page->index]; assert(s->nr_members == page->nr_vars); for (int i = 0; i < s->nr_members; i++) { + if (with_type) + iarray_write(w, s->members[i].type.data); iarray_write_value(w, &s->members[i].type, page->values[i].i); } } @@ -202,6 +205,16 @@ struct page *iarray_to_page(struct iarray_writer *w) return page; } +uint8_t *iarray_to_buffer(struct iarray_writer *w, size_t *size_out) +{ + *size_out = w->size * 4; + uint8_t *buf = xmalloc(*size_out); + for (unsigned i = 0; i < w->size; i++) { + LittleEndian_putDW(buf, i * 4, w->data[i]); + } + return buf; +} + bool iarray_init_reader(struct iarray_reader *r, struct page *a, const char *header) { r->data = a->values; @@ -267,7 +280,7 @@ static int32_t iarray_read_member(struct iarray_reader *r, struct ain_type *t) case AIN_STRING: return heap_alloc_string(iarray_read_string(r)); case AIN_STRUCT: - return heap_alloc_page(iarray_read_struct(r, t->struc)); + return heap_alloc_page(iarray_read_struct(r, t->struc, false)); case AIN_ARRAY_INT: case AIN_ARRAY_FLOAT: case AIN_ARRAY_STRING: @@ -279,11 +292,16 @@ static int32_t iarray_read_member(struct iarray_reader *r, struct ain_type *t) } } -struct page *iarray_read_struct(struct iarray_reader *r, int struct_type) +struct page *iarray_read_struct(struct iarray_reader *r, int struct_type, bool with_type) { struct ain_struct *s = &ain->structures[struct_type]; struct page *page = alloc_page(STRUCT_PAGE, struct_type, s->nr_members); for (int i = 0; i < s->nr_members; i++) { + if (with_type) { + int type = iarray_read(r); + if (type != s->members[i].type.data) + VM_ERROR("iarray_read_struct: type mismatch"); + } page->values[i].i = iarray_read_member(r, &s->members[i].type); } return page; diff --git a/src/hll/iarray.h b/src/hll/iarray.h index c6c596f..f5b9c50 100644 --- a/src/hll/iarray.h +++ b/src/hll/iarray.h @@ -40,7 +40,7 @@ void iarray_write_at(struct iarray_writer *w, unsigned pos, int data); void iarray_write_float(struct iarray_writer *w, float data); void iarray_write_string(struct iarray_writer *w, struct string *s); void iarray_write_string_or_null(struct iarray_writer *w, struct string *s); -void iarray_write_struct(struct iarray_writer *w, struct page *page); +void iarray_write_struct(struct iarray_writer *w, struct page *page, bool with_type); void iarray_write_array(struct iarray_writer *w, struct page *page); void iarray_write_point(struct iarray_writer *w, Point *p); void iarray_write_rectangle(struct iarray_writer *w, Rectangle *r); @@ -53,6 +53,7 @@ static inline unsigned iarray_writer_pos(struct iarray_writer *w) } struct page *iarray_to_page(struct iarray_writer *w); +uint8_t *iarray_to_buffer(struct iarray_writer *w, size_t *size_out); struct iarray_reader { union vm_value *data; @@ -71,7 +72,7 @@ int iarray_read(struct iarray_reader *r); float iarray_read_float(struct iarray_reader *r); struct string *iarray_read_string(struct iarray_reader *r); struct string *iarray_read_string_or_null(struct iarray_reader *r); -struct page *iarray_read_struct(struct iarray_reader *r, int struct_type); +struct page *iarray_read_struct(struct iarray_reader *r, int struct_type, bool with_type); struct page *iarray_read_array(struct iarray_reader *r, struct ain_type *t); void iarray_read_point(struct iarray_reader *r, Point *p); void iarray_read_rectangle(struct iarray_reader *r, Rectangle *rect);