Support loading system3.5 v1 save files

This commit is contained in:
kichikuou
2023-03-18 09:51:58 +09:00
parent 912af592ae
commit 64edcfda29
3 changed files with 330 additions and 48 deletions
+208 -12
View File
@@ -23,6 +23,7 @@
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -44,6 +45,7 @@ typedef int emscripten_align1_int;
#include "message.h"
const char *save_signature[] = {
[SAVEFMT_SYS35] = "This is save data for System3.5 Win95 For NACT/ADV system (C) 1996 ALICE-SOFT",
[SAVEFMT_XSYS35] = "System3.5 SavaData(c)ALICE-SOFT",
[SAVEFMT_SYS36] = "System3.5 SaveData(c)ALICE-SOFT",
[SAVEFMT_SYS38] = "System3.8 SaveData(c)ALICE-SOFT",
@@ -473,19 +475,83 @@ int save_copyAll(int dstno, int srcno) {
return status;
}
static int loadpartial_sys35(char *saveTop, int filesize, struct VarRef *vref, int cnt) {
if (filesize < 0xb0)
return SAVE_LOADERR;
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
if (vref->page == 0) {
// SysVar section
int section_offset = LittleEndian_getDW(saveTop, 0x90);
int section_length = LittleEndian_getDW(saveTop, 0x94);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int sysvar_count = LittleEndian_getDW(saveTop, section_offset);
if (sysvar_count > varPage[0].size)
return SAVE_LOADERR; // resizing system page is not supported in sys3.5
int offset = section_offset + 16 + vref->index * 2;
for (int i = 0; i < cnt; i++) {
var[i] = LittleEndian_getW(saveTop, offset);
offset += 2;
}
return SAVE_LOADOK;
}
// Arrays section
int section_offset = LittleEndian_getDW(saveTop, 0xa0);
int section_length = LittleEndian_getDW(saveTop, 0xa4);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int page_count = LittleEndian_getDW(saveTop, section_offset + 4);
int offset = section_offset + 16;
// TODO: section size check
for (int i = 0; i < page_count; i++) {
int page = LittleEndian_getDW(saveTop, offset); offset += 4;
int count = LittleEndian_getDW(saveTop, offset); offset += 4;
int content_offset = LittleEndian_getDW(saveTop, offset); offset += 4;
offset += 4;
if (page != vref->page)
continue;
// TODO page resize
// TODO: section size check
content_offset += vref->index * 2;
for (int j = 0; j < cnt; j++) {
var[j] = LittleEndian_getW(saveTop, content_offset + j * 2);
}
return SAVE_LOADOK;
}
return SAVE_LOADERR;
}
/* データの一部ロード */
int save_loadPartial(int no, struct VarRef *vref, int cnt) {
if (no >= SAVE_MAXNUMBER)
return SAVE_SAVEERR;
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
int status, filesize;
char *saveTop = loadGameData(no, &status, &filesize);
if (!saveTop)
return status;
if (filesize < 0x60)
goto errexit;
enum save_format format = (enum save_format)-1;
for (int i = 0; i < sizeof(save_signature) / sizeof(save_signature[0]); i++) {
if (!strcmp(save_signature[i], saveTop)) {
format = i;
break;
}
}
if (format < 0) {
WARNING("unrecognized save format");
goto errexit;
}
if (format == SAVEFMT_SYS35) {
int result = loadpartial_sys35(saveTop, filesize, vref, cnt);
free(saveTop);
return result;
}
if (filesize <= sizeof(asd_baseHdr))
goto errexit;
@@ -495,6 +561,8 @@ int save_loadPartial(int no, struct VarRef *vref, int cnt) {
goto errexit;
}
cnt = min(cnt, v_sliceSize(vref));
int *var = v_resolveRef(vref);
if (save_base->varSys[vref->page] == 0)
goto errexit;
@@ -551,6 +619,125 @@ int save_savePartial(int no, struct VarRef *vref, int cnt) {
return SAVE_SAVEERR;
}
static int loadall_sys35(char *saveTop, int filesize) {
if (filesize < 0xb0)
return SAVE_LOADERR;
int page = LittleEndian_getDW(saveTop, 0x80);
int addr = LittleEndian_getDW(saveTop, 0x84);
sl_jmpFar2(page - 1, addr);
// SysVar section
int section_offset = LittleEndian_getDW(saveTop, 0x90);
int section_length = LittleEndian_getDW(saveTop, 0x94);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int sysvar_count = LittleEndian_getDW(saveTop, section_offset);
if (sysvar_count > varPage[0].size)
return SAVE_LOADERR; // resizing system page is not supported in sys3.5
int offset = section_offset + 16;
for (int i = 0; i < sysvar_count; i++) {
varPage[0].value[i] = LittleEndian_getW(saveTop, offset);
offset += 2;
}
nact->sel.MsgFontSize = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.MsgFontSize = LittleEndian_getW(saveTop, offset); offset += 2;
cg_vspPB = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.MsgFontColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.MsgFontColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.WinFrameColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.WinBackgroundColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.WinFrameColor = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.WinBackgroundColor = LittleEndian_getW(saveTop, offset); offset += 2;
int left = section_offset + 16 + section_length - offset;
WARNING("0x%x bytes for window info", left);
if (left < 0)
return SAVE_LOADERR;
int win_info_count = left / 32;
if (win_info_count >= SELWINMAX)
return SAVE_LOADERR;
for (int i = 1; i <= win_info_count; i++) {
offset += 2;
nact->sel.wininfo[i].x = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.wininfo[i].y = LittleEndian_getW(saveTop, offset); offset += 2;
int ex = LittleEndian_getW(saveTop, offset); offset += 2;
int ey = LittleEndian_getW(saveTop, offset); offset += 2;
nact->sel.wininfo[i].width = ex + 1 - nact->sel.wininfo[i].x;
nact->sel.wininfo[i].height = ey + 1 - nact->sel.wininfo[i].y;
offset += 6;
}
for (int i = 1; i <= win_info_count; i++) {
offset += 2;
nact->msg.wininfo[i].x = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.wininfo[i].y = LittleEndian_getW(saveTop, offset); offset += 2;
int ex = LittleEndian_getW(saveTop, offset); offset += 2;
int ey = LittleEndian_getW(saveTop, offset); offset += 2;
nact->msg.wininfo[i].width = ex + 1 - nact->msg.wininfo[i].x;
nact->msg.wininfo[i].height = ey + 1 - nact->msg.wininfo[i].y;
offset += 6;
}
//assert(offset == section_offset + 16 + section_length);
// Strings section
section_offset = LittleEndian_getDW(saveTop, 0x98);
section_length = LittleEndian_getDW(saveTop, 0x9c);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int strvar_count = LittleEndian_getDW(saveTop, section_offset);
int strvar_length = LittleEndian_getDW(saveTop, section_offset + 4);
offset = section_offset + 16;
if (section_length != strvar_count * strvar_length)
return SAVE_LOADERR;
if (strvar_count > svar_maxindex())
strvar_count = svar_maxindex();
char *buf = malloc(strvar_length + 1);
buf[strvar_length] = '\0';
for (int i = 1; i <= strvar_count; i++) {
memcpy(buf, saveTop + offset, strvar_length);
svar_set(i, buf);
offset += strvar_length;
}
// Arrays section
section_offset = LittleEndian_getDW(saveTop, 0xa0);
section_length = LittleEndian_getDW(saveTop, 0xa4);
if (section_offset + 16 + section_length > filesize)
return SAVE_LOADERR;
int array_total_size = LittleEndian_getDW(saveTop, section_offset);
int page_count = LittleEndian_getDW(saveTop, section_offset + 4);
offset = section_offset + 16;
// TODO: section size check
int total = 0;
for (int i = 0; i < page_count; i++) {
int page = LittleEndian_getDW(saveTop, offset); offset += 4;
int count = LittleEndian_getDW(saveTop, offset); offset += 4;
int content_offset = LittleEndian_getDW(saveTop, offset); offset += 4;
offset += 4;
total += count;
// TODO page resize
// TODO: section size check
for (int j = 0; j < count; j++) {
varPage[page].value[j] = LittleEndian_getW(saveTop, content_offset + j * 2);
}
}
if (total != array_total_size) {
WARNING("wrong array total size");
} else {
WARNING("correct array total size");
}
// Stack section
section_offset = LittleEndian_getDW(saveTop, 0xa8);
section_length = LittleEndian_getDW(saveTop, 0xac);
if (section_offset + section_length > filesize)
return SAVE_LOADERR;
sl_loadStack(SAVEFMT_SYS35, saveTop + section_offset, section_length);
return SAVE_LOADOK;
}
/* データのロード */
int save_loadAll(int no) {
@@ -561,17 +748,11 @@ int save_loadAll(int no) {
char *saveTop = loadGameData(no, &status, &filesize);
if (!saveTop)
return status;
if (filesize <= sizeof(asd_baseHdr))
if (filesize < 0x60)
goto errexit;
/* 各種データの反映 */
asd_baseHdr *save_base = (asd_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
WARNING("endian mismatch");
goto errexit;
}
enum save_format format = (enum save_format)-1;
for (int i = 0; i < sizeof(save_signature) / sizeof(save_signature[0]); i++) {
if (!strcmp(save_signature[i], save_base->ID)) {
if (!strcmp(save_signature[i], saveTop)) {
format = i;
break;
}
@@ -581,6 +762,21 @@ int save_loadAll(int no) {
goto errexit;
}
if (format == SAVEFMT_SYS35) {
int result = loadall_sys35(saveTop, filesize);
free(saveTop);
return result;
}
if (filesize <= sizeof(asd_baseHdr))
goto errexit;
/* 各種データの反映 */
asd_baseHdr *save_base = (asd_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
WARNING("endian mismatch");
goto errexit;
}
nact->sel.MsgFontSize = save_base->selMsgSize;
nact->sel.MsgFontColor = save_base->selMsgColor;
nact->sel.WinBackgroundColor = save_base->selBackColor;
+1
View File
@@ -38,6 +38,7 @@
enum save_format {
SAVEFMT_XSYS35,
SAVEFMT_SYS35, // System3.5 v1.x
SAVEFMT_SYS36, // System3.5 v2.x - System3.6
SAVEFMT_SYS38, // System3.8 - System3.9
};
+121 -36
View File
@@ -354,7 +354,7 @@ void sl_popVar(struct VarRef *vref, int cnt) {
}
}
enum save_stack_frame_type {
enum xsys35_stack_frame_type {
SAVE_NEARJMP = 1,
SAVE_FARJMP = 2,
SAVE_VARIABLE = 3,
@@ -367,15 +367,15 @@ enum txx_type {
TxxTEXTLOC = 3
};
struct save_stack_frame {
struct save_stack_frame *next;
struct xsys35_stack_frame {
struct xsys35_stack_frame *next;
int len;
int buf[];
};
struct save_stack_frame *push_save_stack_frame(int size, struct save_stack_frame *next) {
struct save_stack_frame *f =
malloc(sizeof(struct save_stack_frame) + sizeof(int) * size);
struct xsys35_stack_frame *push_xsys35_stack_frame(int size, struct xsys35_stack_frame *next) {
struct xsys35_stack_frame *f =
malloc(sizeof(struct xsys35_stack_frame) + sizeof(int) * size);
f->next = next;
f->len = size;
return f;
@@ -391,13 +391,13 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
}
// Serialize to the stack format used in old versions of xsystem35.
struct save_stack_frame *frame = NULL;
struct xsys35_stack_frame *frame = NULL;
uint8_t *sp = stack_top;
while (sp > stack_buf) {
switch (sp[-1]) {
case STACK_FARCALL:
sp -= 7;
frame = push_save_stack_frame(4, frame);
frame = push_xsys35_stack_frame(4, frame);
frame->buf[3] = SAVE_FARJMP;
frame->buf[2] = 2;
frame->buf[1] = LittleEndian_getW(sp, 0) - 1; // page
@@ -405,7 +405,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_NEARCALL:
sp -= 5;
frame = push_save_stack_frame(3, frame);
frame = push_xsys35_stack_frame(3, frame);
frame->buf[2] = SAVE_NEARJMP;
frame->buf[1] = 1;
frame->buf[0] = LittleEndian_getDW(sp, 0); // addr
@@ -422,7 +422,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
int base = frame->buf[frame->len - 4];
// Merge into existing SAVE_VARIABLE frame.
if (page == fpage && index == base - 1) {
frame = realloc(frame, sizeof(struct save_stack_frame) + sizeof(int) * (frame->len + 1));
frame = realloc(frame, sizeof(struct xsys35_stack_frame) + sizeof(int) * (frame->len + 1));
frame->len++;
frame->buf[frame->len - 1] = SAVE_VARIABLE;
frame->buf[frame->len - 2] = count + 1;
@@ -432,7 +432,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
}
}
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_VARIABLE;
frame->buf[3] = 3;
frame->buf[2] = page;
@@ -442,7 +442,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTCOLOR:
sp -= 3;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTCOLOR;
@@ -451,7 +451,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTSIZE:
sp -= 6;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTSIZE;
@@ -460,7 +460,7 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
break;
case STACK_TEXTLOC:
sp -= 9;
frame = push_save_stack_frame(5, frame);
frame = push_xsys35_stack_frame(5, frame);
frame->buf[4] = SAVE_TXXSTATE;
frame->buf[3] = 3;
frame->buf[2] = TxxTEXTLOC;
@@ -472,15 +472,15 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
}
}
int total_len = 0;
for (struct save_stack_frame *f = frame; f; f = f->next)
for (struct xsys35_stack_frame *f = frame; f; f = f->next)
total_len += f->len;
int *buf = malloc(total_len * sizeof(int));
int *p = buf;
for (struct save_stack_frame *f = frame; f;) {
for (struct xsys35_stack_frame *f = frame; f;) {
memcpy(p, f->buf, f->len * sizeof(int));
p += f->len;
struct save_stack_frame *next = f->next;
struct xsys35_stack_frame *next = f->next;
free(f);
f = next;
}
@@ -510,36 +510,22 @@ uint8_t *sl_saveStack(enum save_format format, int *size_out) {
return (uint8_t *)buf;
}
void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
if (format != SAVEFMT_XSYS35) {
if (size > stack_size) {
while (size > stack_size)
stack_size *= 2;
free(stack_buf);
stack_buf = malloc(stack_size);
if (!stack_buf)
NOMEMERR();
}
memcpy(stack_buf, unaligned_data, size);
stack_top = stack_buf + size;
return;
}
static void load_stack_xsys35(uint8_t *unaligned_data, int size) {
// Deserialize from the stack format used in old versions of xsystem35.
int *data = malloc(size);
memcpy(data, unaligned_data, size);
struct save_stack_frame *frame = NULL;
struct xsys35_stack_frame *frame = NULL;
for (int *p = data + size / sizeof(int); p > data;) {
int len = p[-2] + 2;
p -= len;
frame = push_save_stack_frame(len, frame);
frame = push_xsys35_stack_frame(len, frame);
memcpy(frame->buf, p, len * sizeof(int));
}
free(data);
stack_top = stack_buf;
for (struct save_stack_frame *f = frame; f;) {
for (struct xsys35_stack_frame *f = frame; f;) {
switch (f->buf[f->len - 1]) {
case SAVE_NEARJMP:
if (f->len != 3)
@@ -601,12 +587,111 @@ void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
default:
SYSERROR("broken stack data");
}
struct save_stack_frame *next = f->next;
struct xsys35_stack_frame *next = f->next;
free(f);
f = next;
}
}
struct asdv1_stack_frame {
struct asdv1_stack_frame *next;
int len;
uint8_t buf[];
};
struct asdv1_stack_frame *push_asdv1_stack_frame(int size, struct asdv1_stack_frame *next) {
struct asdv1_stack_frame *f =
malloc(sizeof(struct asdv1_stack_frame) + size);
f->next = next;
f->len = size;
return f;
}
static void load_stack_sys35(uint8_t *data, int size) {
struct asdv1_stack_frame *frame = NULL;
for (uint8_t *p = data + size; p > data;) {
int frame_size = 0;
switch (p[-1]) {
case STACK_FARCALL:
frame_size = 13;
break;
case 0xee:
frame_size = 5;
break;
case 0xdd:
frame_size = LittleEndian_getW(p, -3) * 2 + 5;
break;
}
if (!frame_size)
break; // System3.5 save files has some junk data at the stack bottom.
p -= frame_size;
frame = push_asdv1_stack_frame(frame_size, frame);
memcpy(frame->buf, p, frame_size);
}
stack_top = stack_buf;
for (struct asdv1_stack_frame *f = frame; f;) {
switch (f->buf[f->len - 1]) {
case STACK_FARCALL:
stack_reserve(2 + 4 + 1);
if (LittleEndian_getDW(f->buf, 0) != 0)
WARNING("unexpected stack structure");
stack_push_word(LittleEndian_getDW(f->buf, 4));
stack_push_dword(LittleEndian_getDW(f->buf, 8));
stack_push_byte(STACK_FARCALL);
break;
case STACK_NEARCALL:
stack_reserve(4 + 1);
stack_push_dword(LittleEndian_getDW(f->buf, 0));
stack_push_byte(STACK_NEARCALL);
break;
case STACK_VARIABLE:
{
int count = LittleEndian_getW(f->buf, f->len - 3);
int base = LittleEndian_getW(f->buf, f->len - 5);
stack_reserve((2 + 2 + 2 + 1) * count);
for (int i = 0; i < count; i++) {
stack_push_word(base + 1);
stack_push_word(0); // page
stack_push_word(LittleEndian_getW(f->buf, i * 2));
stack_push_byte(STACK_VARIABLE);
}
}
break;
}
struct asdv1_stack_frame *next = f->next;
free(f);
f = next;
}
}
static void load_stack_sys39(uint8_t *data, int size) {
if (size > stack_size) {
while (size > stack_size)
stack_size *= 2;
free(stack_buf);
stack_buf = malloc(stack_size);
if (!stack_buf)
NOMEMERR();
}
memcpy(stack_buf, data, size);
stack_top = stack_buf + size;
}
void sl_loadStack(enum save_format format, uint8_t *unaligned_data, int size) {
switch (format) {
case SAVEFMT_XSYS35:
load_stack_xsys35(unaligned_data, size);
break;
case SAVEFMT_SYS35:
load_stack_sys35(unaligned_data, size);
break;
default:
load_stack_sys39(unaligned_data, size);
break;
}
}
void sl_getStackInfo(struct stack_info *info) {
memset(info, 0, sizeof(struct stack_info));