Support the save format of original System3.x (except System3.5 v1)

Now xsystem35 can load ASD files created by System3.5 (v2+) - System3.9.
The format for saving can be configured with the -saveformat option.
This commit is contained in:
kichikuou
2023-02-23 19:03:23 +09:00
parent 446ffe9466
commit aec06f775a
6 changed files with 205 additions and 72 deletions
+119 -34
View File
@@ -43,7 +43,12 @@ typedef int emscripten_align1_int;
#include "selection.h"
#include "message.h"
#define SAVE_DATAID "System3.5 SavaData(c)ALICE-SOFT"
const char *save_signature[] = {
[SAVEFMT_XSYS35] = "System3.5 SavaData(c)ALICE-SOFT",
[SAVEFMT_SYS36] = "System3.5 SaveData(c)ALICE-SOFT",
[SAVEFMT_SYS38] = "System3.8 SaveData(c)ALICE-SOFT",
};
#define SAVE_DATAVERSION 0x350200
typedef struct {
@@ -108,15 +113,17 @@ typedef struct {
emscripten_align1_int rsv2;
} Ald_sysVarHdr;
static int saveStackInfo(FILE *fp);
static void loadStackInfo(char *buf);
static void* saveStrVar(Ald_strVarHdr *head);
static void loadStrVar(char *buf);
static int saveStackInfo(enum save_format format, FILE *fp);
static void loadStackInfo(enum save_format format, char *buf);
static void* saveStrVar(enum save_format format, Ald_strVarHdr *head);
static void loadStrVar(enum save_format format, char *buf);
static void* saveSysVar(Ald_sysVarHdr *head, int page);
static int loadSysVar(char *buf);
static void* loadGameData(int no, int *status, int *size);
static int saveGameData(int no, char *buf, int size);
static enum save_format save_format = SAVEFMT_XSYS35;
#ifdef __EMSCRIPTEN__
EM_JS(void, scheduleSync, (), {
xsystem35.shell.syncfs();
@@ -125,6 +132,22 @@ EM_JS(void, scheduleSync, (), {
#define scheduleSync()
#endif
int save_setFormat(const char *format_name) {
if (!strcmp(format_name, "xsystem35"))
save_format = SAVEFMT_XSYS35;
else if (!strcmp(format_name, "system36"))
save_format = SAVEFMT_SYS36;
else if (!strcmp(format_name, "system38"))
save_format = SAVEFMT_SYS38;
else if (!strcmp(format_name, "system39"))
save_format = SAVEFMT_SYS38;
else {
WARNING("unknown save format %s", format_name);
return NG;
}
return OK;
}
/* savefile を参照 */
const char *save_get_file(int index) {
return nact->files.save_fname[index];
@@ -380,8 +403,18 @@ int save_loadAll(int no) {
WARNING("endian mismatch");
goto errexit;
}
if (strcmp(SAVE_DATAID, save_base->ID) != 0)
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)) {
format = i;
break;
}
}
if (format < 0) {
WARNING("unrecognized save format");
goto errexit;
}
nact->sel.MsgFontSize = save_base->selMsgSize;
nact->sel.MsgFontColor = save_base->selMsgColor;
nact->sel.WinBackgroundColor = save_base->selBackColor;
@@ -390,10 +423,12 @@ int save_loadAll(int no) {
nact->msg.MsgFontColor = save_base->msgMsgColor;
nact->msg.WinBackgroundColor = save_base->msgBackColor;
nact->msg.WinFrameColor = save_base->msgFrameColor;
sl_jmpFar2(save_base->scoPage, save_base->scoIndex);
sl_jmpFar2(save_base->scoPage - (format == SAVEFMT_XSYS35 ? 0 : 1), save_base->scoIndex);
for (i = 0; i < SELWINMAX; i++) {
int j = (i + 1) % SELWINMAX; // For savedata compatibility
int j = format == SAVEFMT_XSYS35
? (i + 1) % SELWINMAX // For savedata compatibility
: i;
nact->sel.wininfo[j].x = save_base->selWinInfo[i].x;
nact->sel.wininfo[j].y = save_base->selWinInfo[i].y;
nact->sel.wininfo[j].width = save_base->selWinInfo[i].width;
@@ -401,7 +436,9 @@ int save_loadAll(int no) {
// nact->sel.wininfo[i].save = TRUE;
}
for (i = 0; i < MSGWINMAX; i++) {
int j = (i + 1) % MSGWINMAX; // For savedata compatibility
int j = format == SAVEFMT_XSYS35
? (i + 1) % MSGWINMAX // For savedata compatibility
: i;
nact->msg.wininfo[j].x = save_base->msgWinInfo[i].x;
nact->msg.wininfo[j].y = save_base->msgWinInfo[i].y;
nact->msg.wininfo[j].width = save_base->msgWinInfo[i].width;
@@ -410,9 +447,9 @@ int save_loadAll(int no) {
// nact->msg.wininfo[i].save = FALSE;
}
/* スタックのロード */
loadStackInfo(saveTop + save_base->stackinfo);
loadStackInfo(format, saveTop + save_base->stackinfo);
/* 文字列変数のロード */
loadStrVar(saveTop + save_base->varStr);
loadStrVar(format, saveTop + save_base->varStr);
/* 数値・配列変数のロード */
for (i = 0; i < 256; i++) {
if (save_base->varSys[i] != 0) {
@@ -430,6 +467,7 @@ int save_loadAll(int no) {
/* データのセーブ */
int save_saveAll(int no) {
enum save_format format = save_format;
Ald_baseHdr *save_base = calloc(1, sizeof(Ald_baseHdr));
Ald_strVarHdr save_strHdr;
Ald_sysVarHdr save_sysHdr;
@@ -454,7 +492,7 @@ int save_saveAll(int no) {
memset(&save_sysHdr, 0, sizeof(Ald_sysVarHdr));
/* 各種データのセーブ */
strncpy(save_base->ID, SAVE_DATAID, 32);
strncpy(save_base->ID, save_signature[format], 32);
save_base->version = SAVE_DATAVERSION;
save_base->selMsgSize = (uint8_t)nact->sel.MsgFontSize;
save_base->selMsgColor = (uint8_t)nact->sel.MsgFontColor;
@@ -464,11 +502,13 @@ int save_saveAll(int no) {
save_base->msgMsgColor = (uint8_t)nact->msg.MsgFontColor;
save_base->msgBackColor = (uint8_t)nact->msg.WinBackgroundColor;
save_base->msgFrameColor = (uint8_t)nact->msg.WinFrameColor;
save_base->scoPage = sl_getPage();
save_base->scoPage = sl_getPage() + (format == SAVEFMT_XSYS35 ? 0 : 1);
save_base->scoIndex = sl_getIndex();
for (i = 0; i < SELWINMAX; i++) {
int j = (i + 1) % SELWINMAX; // For savedata compatibility
int j = format == SAVEFMT_XSYS35
? (i + 1) % SELWINMAX // For savedata compatibility
: i;
save_base->selWinInfo[i].x = (uint16_t)nact->sel.wininfo[j].x;
save_base->selWinInfo[i].y = (uint16_t)nact->sel.wininfo[j].y;
save_base->selWinInfo[i].width = (uint16_t)nact->sel.wininfo[j].width;
@@ -476,7 +516,9 @@ int save_saveAll(int no) {
}
for (i = 0; i < MSGWINMAX; i++) {
int j = (i + 1) % MSGWINMAX; // For savedata compatibility
int j = format == SAVEFMT_XSYS35
? (i + 1) % MSGWINMAX // For savedata compatibility
: i;
save_base->msgWinInfo[i].x = (uint16_t)nact->msg.wininfo[j].x;
save_base->msgWinInfo[i].y = (uint16_t)nact->msg.wininfo[j].y;
save_base->msgWinInfo[i].width = (uint16_t)nact->msg.wininfo[j].width;
@@ -487,12 +529,12 @@ int save_saveAll(int no) {
/* スタック情報 */
save_base->stackinfo = totalsize;
totalsize += saveStackInfo(fp);
totalsize += saveStackInfo(format, fp);
if (ferror(fp))
goto errexit;
/* 文字列変数 */
if (NULL == (sd_varStr = saveStrVar(&save_strHdr)))
if (NULL == (sd_varStr = saveStrVar(format, &save_strHdr)))
goto errexit;
if (1 != fwrite(&save_strHdr, sizeof(Ald_strVarHdr), 1, fp))
@@ -526,6 +568,49 @@ int save_saveAll(int no) {
if (1 != fwrite(save_base, sizeof(Ald_baseHdr), 1, fp))
goto errexit;
if (format != SAVEFMT_XSYS35) {
// Padding(?)
fseek(fp, 0L, SEEK_END);
for (int i = 0; i < 256; i++)
fputc(0xff, fp);
// Write a mark to indicate that this file was created by xsystem35.
fputs("XS35", fp);
}
if (format == SAVEFMT_SYS38) {
// Stack info
struct stack_info sinfo;
sl_getStackInfo(&sinfo);
int size = sinfo.page_calls * 2 + sinfo.label_calls;
int *buf = calloc(size, sizeof(int));
int *ptr = buf + size;
int *vars = NULL;
int *label_calls = NULL;
struct stack_frame_info *sfi = NULL;
while ((sfi = sl_next_stack_frame(sfi)) != NULL) {
switch (sfi->tag) {
case STACK_NEARCALL:
if (label_calls)
(*label_calls)++;
vars = --ptr;
break;
case STACK_FARCALL:
vars = --ptr;
label_calls = --ptr;
break;
case STACK_VARIABLE:
if (vars)
(*vars)++;
break;
}
}
// Note that only the first `size` byte of the `size` words data is
// written, so information is lost.
fwrite(ptr, size, 1, fp);
fwrite(&size, 4, 1, fp);
free(buf);
fputs("INFS", fp);
}
fclose(fp);
scheduleSync();
free(save_base);
@@ -546,9 +631,9 @@ int save_saveAll(int no) {
}
/* スタック情報のセーブ */
static int saveStackInfo(FILE *fp) {
static int saveStackInfo(enum save_format format, FILE *fp) {
int size;
uint8_t *data = sl_saveStack(&size);
uint8_t *data = sl_saveStack(format, &size);
Ald_stackHdr head = { .size = size };
fwrite(&head, sizeof(head), 1, fp);
@@ -559,22 +644,22 @@ static int saveStackInfo(FILE *fp) {
}
/* スタック情報のロード */
static void loadStackInfo(char *buf) {
static void loadStackInfo(enum save_format format, char *buf) {
Ald_stackHdr *head = (Ald_stackHdr *)buf;
uint8_t *data = buf + sizeof(Ald_stackHdr);
sl_loadStack(data, head->size);
sl_loadStack(format, data, head->size);
}
/* 文字列変数のセーブ */
static void *saveStrVar(Ald_strVarHdr *head) {
static void *saveStrVar(enum save_format format, Ald_strVarHdr *head) {
int bufsize = 65536;
char *buf = malloc(bufsize);
if (!buf)
NOMEMERR();
const int base = format == SAVEFMT_XSYS35 ? 1 : 0;
int offset = 0;
// Do not save svar[0], for backward compatibility.
for (int i = 1; i <= svar_maxindex(); i++) {
for (int i = base; i <= svar_maxindex(); i++) {
const char *s = svar_get(i);
int len = strlen(s);
if (offset + len + 1 > bufsize) {
@@ -588,24 +673,24 @@ static void *saveStrVar(Ald_strVarHdr *head) {
offset += len + 1;
}
head->size = offset;
head->count = svar_maxindex();
head->maxlen = 101; // so that old versions of xystem35 can read this save file
head->count = svar_maxindex() + (1 - base);
head->maxlen = (format == SAVEFMT_XSYS35 ? 101 : 0); // so that old versions of xystem35 can read this save file
return buf;
}
/* 文字列変数のロード */
static void loadStrVar(char *buf) {
static void loadStrVar(enum save_format format, char *buf) {
Ald_strVarHdr *head = (Ald_strVarHdr *)buf;
int cnt, max, i;
const int base = format == SAVEFMT_XSYS35 ? 1 : 0;
cnt = head->count;
if (svar_maxindex() != cnt) {
WARNING("Unexpected number of strings in savedata (%d, expected %d)", cnt, svar_maxindex());
svar_init(cnt);
int cnt = head->count;
if ((1 - base) + svar_maxindex() != cnt) {
WARNING("Unexpected number of strings in savedata (%d, expected %d)", cnt, (1 - base) + svar_maxindex());
svar_init(format == SAVEFMT_XSYS35 ? cnt : cnt - 1);
}
buf += sizeof(Ald_strVarHdr);
for (i = 1; i <= cnt; i++) {
svar_set(i, buf);
for (int i = 0; i < cnt; i++) {
svar_set(i + base, buf);
buf += strlen(buf) + 1;
}
}
+7
View File
@@ -36,8 +36,15 @@
#define SAVE_SAVEOK1 1
#define SAVE_SAVEOK0 0
enum save_format {
SAVEFMT_XSYS35,
SAVEFMT_SYS36, // System3.5 v2.x - System3.6
SAVEFMT_SYS38, // System3.8 - System3.9
};
struct VarRef;
int save_setFormat(const char *format_name);
int save_loadAll(int no);
int save_saveAll(int no);
int save_loadPartial(int no, struct VarRef *vref, int cnt);
+30 -10
View File
@@ -381,9 +381,16 @@ struct save_stack_frame *push_save_stack_frame(int size, struct save_stack_frame
return f;
}
// Serialize the stack to the xsystem35 savedata format (i.e. the stack format
// used in old versions of xsystem35).
uint8_t *sl_saveStack(int *size_out) {
uint8_t *sl_saveStack(enum save_format format, int *size_out) {
if (format != SAVEFMT_XSYS35) {
int size = stack_top - stack_buf;
uint8_t *buf = malloc(size);
memcpy(buf, stack_buf, size);
*size_out = size;
return buf;
}
// Serialize to the stack format used in old versions of xsystem35.
struct save_stack_frame *frame = NULL;
uint8_t *sp = stack_top;
while (sp > stack_buf) {
@@ -487,7 +494,7 @@ uint8_t *sl_saveStack(int *size_out) {
uint8_t *orig_stack = malloc(orig_size);
memcpy(orig_stack, stack_buf, orig_size);
sl_loadStack((uint8_t *)buf, *size_out);
sl_loadStack(format, (uint8_t *)buf, *size_out);
if (stack_top - stack_buf != orig_size || memcmp(stack_buf, orig_stack, orig_size)) {
FILE *fp = fopen("orig_stack", "wb");
@@ -503,18 +510,31 @@ uint8_t *sl_saveStack(int *size_out) {
return (uint8_t *)buf;
}
// Deserialize the stack from the xsystem35 savedata format.
void sl_loadStack(uint8_t *unaligned_data, int size) {
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;
}
// 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;
for (int *p = data + size / sizeof(int); p > data;) {
int type = *--p;
int len = *--p;
int len = p[-2] + 2;
p -= len;
frame = push_save_stack_frame(len + 2, frame);
memcpy(frame->buf, p, (len + 2) * sizeof(int));
frame = push_save_stack_frame(len, frame);
memcpy(frame->buf, p, len * sizeof(int));
}
free(data);
+3 -2
View File
@@ -27,6 +27,7 @@
#include "portab.h"
#include "system.h"
enum save_format;
struct VarRef;
// Stack frame tags. Changing these will break savedata compatibility.
@@ -86,8 +87,8 @@ void sl_returnGoto(int address);
void sl_clearStack(bool restore);
void sl_pushVar(struct VarRef *vref, int cnt);
void sl_popVar(struct VarRef *vref, int cnt);
uint8_t *sl_saveStack(int *size);
void sl_loadStack(uint8_t *data, int size);
uint8_t *sl_saveStack(enum save_format format, int *size);
void sl_loadStack(enum save_format format, uint8_t *data, int size);
void sl_getStackInfo(struct stack_info *info);
void sl_pushTextColor(uint8_t type, uint8_t color);
void sl_pushTextSize(uint8_t type, int size);
+36 -26
View File
@@ -109,46 +109,47 @@ static void sys35_usage(boolean verbose) {
}
puts("Usage: xsystem35 [OPTIONS]\n");
puts("OPTIONS");
puts(" -gamefile file : set game resource file to 'file'");
puts(" -game game : enable game-specific hacks");
puts(" -renderer name : set rendering driver name to 'name'");
puts(" -devcd device : set cdrom device name to 'device'");
puts(" -devmidi device: set midi device name to 'device'");
puts(" -gamefile file : set game resource file to 'file'");
puts(" -game game : enable game-specific hacks");
puts(" -saveformat fmt : save file format. 'xsystem35' (default), 'system36' or 'system39'");
puts(" -renderer name : set rendering driver name to 'name'");
puts(" -devcd device : set cdrom device name to 'device'");
puts(" -devmidi device : set midi device name to 'device'");
puts(" -M? : select output midi methos");
puts(" -M? : select output midi methos");
#ifdef ENABLE_MIDI_SDLMIXER
puts(" -Me : SDL_mixer midi player");
puts(" -Me : SDL_mixer midi player");
#endif
#ifdef ENABLE_MIDI_RAWMIDI
puts(" -Mr : Raw Midi device");
puts(" -Mr : Raw Midi device");
#endif
#ifdef ENABLE_MIDI_SEQMIDI
puts(" -Ms? : Sequenceer device (?:devicenumber)");
puts(" -Ms? : Sequenceer device (?:devicenumber)");
#endif
#ifdef ENABLE_MIDI_PORTMIDI
puts(" -Mp? : ALSA (via PortMidi) (?:devicenumber)");
puts(" -Mp? : ALSA (via PortMidi) (?:devicenumber)");
#endif
puts(" -M0 : Disable MIDI output");
puts(" -M0 : Disable MIDI output");
puts(" -devjoy device : joystick device index (0-)");
puts(" -devjoy device : joystick device index (0-)");
puts(" -ttfont_mincho: set TrueType font for mincho");
puts(" -ttfont_gothic: set TrueType font for mincho");
puts(" -ttfont_mincho : set TrueType font for mincho");
puts(" -ttfont_gothic : set TrueType font for mincho");
#ifdef DEBUG
puts(" -debuglv # : debug level");
puts(" : 1: warings");
puts(" : 2: unimplemented commands");
puts(" : 5: command trace");
puts(" : 6: message trace");
puts(" -debuglv # : debug level");
puts(" : 1: warings");
puts(" : 2: unimplemented commands");
puts(" : 5: command trace");
puts(" : 6: message trace");
#endif
puts(" -noantialias : never use antialiased string");
puts(" -fullscreen : start with fullscreen");
puts(" -integerscale : use integer scaling when resizing");
puts(" -noimagecursor : disable image cursor");
puts(" -version : show version");
puts(" -h : show this message");
puts(" --help : show this message");
puts(" -noantialias : never use antialiased string");
puts(" -fullscreen : start with fullscreen");
puts(" -integerscale : use integer scaling when resizing");
puts(" -noimagecursor : disable image cursor");
puts(" -version : show version");
puts(" -h : show this message");
puts(" --help : show this message");
exit(1);
}
@@ -315,6 +316,10 @@ static void sys35_ParseOption(int *argc, char **argv) {
if (argv[i + 1] != NULL) {
enable_hack_by_gameid(argv[i + 1]);
}
} else if (0 == strcmp(argv[i], "-saveformat")) {
if (argv[i + 1] != NULL) {
save_setFormat(argv[i + 1]);
}
}
}
}
@@ -404,6 +409,11 @@ static void check_profile() {
if (param) {
enable_hack_by_gameid(param);
}
/* Save file format */
param = get_profile("saveformat");
if (param) {
save_setFormat(param);
}
}
#ifdef HAVE_SIGACTION
+10
View File
@@ -30,6 +30,16 @@
# ------------------------------------------------------------
# ------------------------------------------------------------
# The format when writing save files.
# xsystem35 : default
# system36 : compatible with System3.5 v2 - System3.6
# system39 : compatible with System3.8 - System3.9
#saveformat: xsystem35
# ------------------------------------------------------------
# ------------------------------------------------------------
# CD-ROM のデバイス名
#