Files
kichikuou_xsystem35-sdl2/src/savedata.c
T
kichikuou 91fa4284c4 Use zero-based indexing for string variables
Surprisingly, 0 is a valid index of the string variables (used only in
Ouji-sama Lv1 as far as I know).

The original System 3.x saves str[0] in save files, but we continue to
save from str[1] for save data compatibility.
2021-12-03 10:57:09 +09:00

773 lines
18 KiB
C

/*
* savedate.c セーブデータの管理
*
* Copyright (C) 1997-1998 Masaki Chikama (Wren) <chikama@kasumi.ipl.mech.nagoya-u.ac.jp>
* 1998- <masaki-c@is.aist-nara.ac.jp>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* $Id: savedata.c,v 1.36 2003/07/21 23:06:47 chikama Exp $ */
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "portab.h"
#include "savedata.h"
#include "scenario.h"
#include "xsystem35.h"
#include "LittleEndian.h"
#include "filecheck.h"
#include "windowframe.h"
#include "selection.h"
#include "message.h"
/* セーブデータ */
static int savefile_sysvar_cnt = SYSVAR_MAX;
static void* saveStackInfo(Ald_stackHdr *head);
static void loadStackInfo(char *buf);
static void* saveStrVar(Ald_strVarHdr *head);
static void loadStrVar(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);
#ifdef __EMSCRIPTEN__
EM_JS(void, scheduleSync, (), {
xsystem35.shell.syncfs();
});
#else
#define scheduleSync()
#endif
/* savefile を参照 */
const char *save_get_file(int index) {
return nact->files.save_fname[index];
}
/* savefile を削除 */
int save_delete_file(int index) {
int ret = unlink(nact->files.save_fname[index]);
if (ret == 0) {
return 1;
}
return 1; /* とりあえず */
}
/* 指定ファイルへの変数の書き込み */
int save_save_var_with_file(char *fname_utf8, int *start, int cnt) {
int status = 0, size, i;
FILE *fp;
WORD *tmp;
tmp = (WORD *)malloc(cnt * sizeof(WORD));
if (tmp == NULL) {
WARNING("Out of memory\n");
return SAVE_SAVEERR;
}
for (i = 0; i < cnt; i++) {
tmp[i] = SDL_SwapLE16((WORD)start[i]);
}
if (NULL == (fp = fc_open(fname_utf8, 'w'))) {
status = SAVE_SAVEERR; goto errexit;
}
size = fwrite(tmp, sizeof(WORD), cnt, fp);
if (size != cnt) {
status = SAVE_OTHERERR;
} else {
status = SAVE_SAVEOK0;
}
fclose(fp);
scheduleSync();
errexit:
free(tmp);
return status;
}
/* 指定ファイルからの変数の読み込み */
int save_load_var_with_file(char *fname_utf8, int *start, int cnt) {
int status = 0, size, i;
FILE *fp;
WORD *tmp;
tmp = (WORD *)malloc(cnt * sizeof(WORD));
if (tmp == NULL) {
WARNING("Out of memory\n");
return SAVE_LOADERR;
}
if (NULL == (fp = fc_open(fname_utf8, 'r'))) {
status = SAVE_LOADERR; goto errexit;
}
size = fread(tmp, sizeof(WORD), cnt, fp);
if (size != cnt) {
status = SAVE_LOADSHORTAGE;
} else {
status = SAVE_LOADOK;
}
for (i = 0; i < cnt; i++) {
start[i] = SDL_SwapLE16(tmp[i]);
}
fclose(fp);
errexit:
free(tmp);
return status;
}
/* 指定ファイルへの文字列の書き込み, start = 1~ */
int save_save_str_with_file(char *fname_utf8, int start, int cnt) {
int status = 0, size, _size,i;
FILE *fp;
char *tmp, *_tmp;
_tmp = tmp = malloc(svar_maxindex() * strvar_len);
if (tmp == NULL) {
WARNING("Out of memory\n");
return SAVE_LOADSHORTAGE;
}
*tmp = 0;
for (i = 0; i < cnt; i++) {
strncpy(tmp, svar_get(start + i), strvar_len - 1);
tmp[strvar_len - 1] = '\0';
tmp += strlen(tmp) + 1;
}
if (NULL == (fp = fc_open(fname_utf8, 'w'))) {
status = SAVE_SAVEERR; goto errexit;
}
size = tmp - _tmp;
_size = fwrite(_tmp, sizeof(char), size , fp);
if (size != _size) {
status = SAVE_OTHERERR;
} else {
status = SAVE_SAVEOK0;
}
fclose(fp);
scheduleSync();
errexit:
free(_tmp);
return status;
}
/* 指定ファイルからの文字列の読み込み */
int save_load_str_with_file(char *fname_utf8, int start, int cnt) {
int status = 0, size, i;
FILE *fp;
char *tmp, *_tmp=NULL;
long filesize;
if (NULL == (fp = fc_open(fname_utf8, 'r'))) {
return SAVE_LOADERR;
}
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
if (filesize == 0) {
return SAVE_LOADERR;
}
tmp = _tmp = (char *)malloc(filesize);
if (tmp == NULL) {
WARNING("Out of memory\n");
return SAVE_LOADERR;
}
fseek(fp, 0L, SEEK_SET);
size = fread(tmp, 1, filesize,fp);
if (size != filesize) {
status = SAVE_LOADSHORTAGE;
} else {
status = SAVE_LOADOK;
}
for (i = 0; i < cnt; i++) {
svar_set(start + i, tmp);
tmp += strlen(tmp) + 1;
}
fclose(fp);
free(_tmp);
return status;
}
/* セーブファイルのコピー */
int save_copyAll(int dstno, int srcno) {
char *saveTop;
int status, filesize;
if (dstno >= SAVE_MAXNUMBER || srcno >= SAVE_MAXNUMBER) {
// fprintf(stderr, "dstno or srcno is outof range\n");
return SAVE_SAVEERR;
}
saveTop = loadGameData(srcno, &status, &filesize);
if (saveTop == NULL)
return SAVE_SAVEERR;
if (((Ald_baseHdr *)saveTop)->version != SAVE_DATAVERSION) {
fprintf(stderr, "save_copyAll(): endian mismatch\n");
free(saveTop);
return SAVE_SAVEERR;
}
status = saveGameData(dstno, saveTop, filesize);
free(saveTop);
return status;
}
/* データの一部ロード */
int save_loadPartial(int no, int page, int offset, int cnt) {
Ald_baseHdr *save_base;
char *vtop;
WORD *tmp;
int *var;
char *saveTop = NULL;
int i, status, filesize;
if (no >= SAVE_MAXNUMBER) {
return SAVE_SAVEERR;
}
if (page == 0) {
cnt = min(cnt, SYSVAR_MAX - offset);
var = sysVar + offset;
} else {
cnt = min(cnt, arrayVarBuffer[page - 1].size - offset);
var = arrayVarBuffer[page - 1].value + offset;
}
saveTop = loadGameData(no, &status, &filesize);
if (saveTop == NULL) {
// fprintf(stderr, "loadGameData() faild\n");
return status;
}
if (filesize <= sizeof(Ald_baseHdr)) {
// fprintf(stderr, "filesize too short\n");
goto errexit;
}
save_base = (Ald_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
fprintf(stderr, "save_loadPartial(): endian mismatch\n");
goto errexit;
}
if (save_base->varSys[page] == 0) {
// fprintf(stderr, "No available Variable\n");
goto errexit;
}
vtop = saveTop + save_base->varSys[page] + sizeof(Ald_sysVarHdr);
tmp = (WORD *)vtop + offset;
for (i = 0; i < cnt; i++) {
*var = *tmp; tmp++; var++;
}
free(saveTop);
return SAVE_LOADOK;
errexit:
if (saveTop != NULL)
free(saveTop);
return SAVE_LOADERR;
}
/* データの一部セーブ */
int save_savePartial(int no, int page, int offset, int cnt) {
Ald_baseHdr *save_base;
WORD *tmp;
char *vtop;
int *var;
char *saveTop = NULL;
int i, status, filesize;
if (no >= SAVE_MAXNUMBER) {
return SAVE_SAVEERR;
}
if (page == 0) {
cnt = min(cnt, SYSVAR_MAX - offset);
var = sysVar + offset;
} else {
if (arrayVarBuffer[page - 1].saveflag == FALSE)
goto errexit;
cnt = min(cnt, arrayVarBuffer[page - 1].size - offset);
var = arrayVarBuffer[page - 1].value + offset;
}
saveTop = loadGameData(no, &status, &filesize);
if (saveTop == NULL)
return status;
if (filesize <= sizeof(Ald_baseHdr))
goto errexit;
save_base = (Ald_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
fprintf(stderr, "save_savePartial(): endian mismatch\n");
goto errexit;
}
vtop = saveTop + save_base->varSys[page] + sizeof(Ald_sysVarHdr);
tmp = (WORD *)vtop + offset;
for (i = 0; i < cnt; i++) {
*tmp = (WORD)*var; tmp++; var++;
}
status = saveGameData(no, saveTop, filesize);
free(saveTop);
return status;
errexit:
if (saveTop != NULL)
free(saveTop);
return SAVE_SAVEERR;
}
/* データのロード */
int save_loadAll(int no) {
Ald_baseHdr *save_base;
char *saveTop = NULL;
int i, status, filesize;
if (no >= SAVE_MAXNUMBER) {
return SAVE_SAVEERR;
}
saveTop = loadGameData(no, &status, &filesize);
if (saveTop == NULL)
return status;
if (filesize <= sizeof(Ald_baseHdr))
goto errexit;
/* 各種データの反映 */
save_base = (Ald_baseHdr *)saveTop;
if (save_base->version != SAVE_DATAVERSION) {
fprintf(stderr, "save_loadAll(): endian mismatch\n");
goto errexit;
}
if (strcmp(SAVE_DATAID, save_base->ID) != 0)
goto errexit;
nact->sel.MsgFontSize = save_base->selMsgSize;
nact->sel.MsgFontColor = save_base->selMsgColor;
nact->sel.WinBackgroundColor = save_base->selBackColor;
nact->sel.WinFrameColor = save_base->selFrameColor;
nact->msg.MsgFontSize = save_base->msgMsgSize;
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);
for (i = 0; i < SELWINMAX; i++) {
selWinInfo[i].x = save_base->selWinInfo[i].x;
selWinInfo[i].y = save_base->selWinInfo[i].y;
selWinInfo[i].width = save_base->selWinInfo[i].width;
selWinInfo[i].height = save_base->selWinInfo[i].height;
// selWinInfo[i].save = TRUE;
}
for (i = 0; i < MSGWINMAX; i++) {
nact->msg.wininfo[i].x = save_base->msgWinInfo[i].x;
nact->msg.wininfo[i].y = save_base->msgWinInfo[i].y;
nact->msg.wininfo[i].width = save_base->msgWinInfo[i].width;
nact->msg.wininfo[i].height = save_base->msgWinInfo[i].height;
// nact->msg.wininfo[i].savedImage = NULL;
// nact->msg.wininfo[i].save = FALSE;
}
/* スタックのロード */
loadStackInfo(saveTop + save_base->stackinfo);
/* 文字列変数のロード */
loadStrVar(saveTop + save_base->varStr);
/* 数値・配列変数のロード */
for (i = 0; i < 256; i++) {
if (save_base->varSys[i] != 0) {
if (SAVE_LOADOK != loadSysVar(saveTop + save_base->varSys[i]))
goto errexit;
}
}
free(saveTop);
return SAVE_LOADOK;
errexit:
free(saveTop);
return SAVE_LOADERR;
}
/* データのセーブ */
int save_saveAll(int no) {
Ald_baseHdr *save_base = calloc(1, sizeof(Ald_baseHdr));
Ald_strVarHdr save_strHdr;
Ald_stackHdr save_stackHdr;
Ald_sysVarHdr save_sysHdr;
char *sd_varStr = NULL;
char *sd_stack = NULL;
char *sd_varSys = NULL;
int i, totalsize = sizeof(Ald_baseHdr);
FILE *fp = NULL;
if (no >= SAVE_MAXNUMBER)
goto errexit;
if (save_base == NULL)
goto errexit;
fc_backup_oldfile(nact->files.save_fname[no]);
fp = fopen(nact->files.save_fname[no], "wb");
if (fp == NULL)
goto errexit;
memset(&save_stackHdr, 0, sizeof(Ald_stackHdr));
memset(&save_strHdr, 0, sizeof(Ald_strVarHdr));
memset(&save_sysHdr, 0, sizeof(Ald_sysVarHdr));
/* 各種データのセーブ */
strncpy(save_base->ID, SAVE_DATAID, 32);
save_base->version = SAVE_DATAVERSION;
save_base->selMsgSize = (BYTE)nact->sel.MsgFontSize;
save_base->selMsgColor = (BYTE)nact->sel.MsgFontColor;
save_base->selBackColor = (BYTE)nact->sel.WinBackgroundColor;
save_base->selFrameColor = (BYTE)nact->sel.WinFrameColor;
save_base->msgMsgSize = (BYTE)nact->msg.MsgFontSize;
save_base->msgMsgColor = (BYTE)nact->msg.MsgFontColor;
save_base->msgBackColor = (BYTE)nact->msg.WinBackgroundColor;
save_base->msgFrameColor = (BYTE)nact->msg.WinFrameColor;
save_base->scoPage = sl_getPage();
save_base->scoIndex = sl_getIndex();
for (i = 0; i < SELWINMAX; i++) {
save_base->selWinInfo[i].x = (WORD)selWinInfo[i].x;
save_base->selWinInfo[i].y = (WORD)selWinInfo[i].y;
save_base->selWinInfo[i].width = (WORD)selWinInfo[i].width;
save_base->selWinInfo[i].height = (WORD)selWinInfo[i].height;
}
for (i = 0; i < MSGWINMAX; i++) {
save_base->msgWinInfo[i].x = (WORD)nact->msg.wininfo[i].x;
save_base->msgWinInfo[i].y = (WORD)nact->msg.wininfo[i].y;
save_base->msgWinInfo[i].width = (WORD)nact->msg.wininfo[i].width;
save_base->msgWinInfo[i].height = (WORD)nact->msg.wininfo[i].height;
}
fseek(fp, sizeof(Ald_baseHdr), SEEK_SET);
/* スタック情報 */
if (NULL == (sd_stack = saveStackInfo(&save_stackHdr)))
goto errexit;
if (1 != fwrite(&save_stackHdr, sizeof(save_stackHdr), 1, fp))
goto errexit;
if (save_stackHdr.size != 0 && 1 != fwrite(sd_stack, save_stackHdr.size, 1, fp))
goto errexit;
save_base->stackinfo = totalsize;
totalsize += save_stackHdr.size + sizeof(Ald_stackHdr);
/* 文字列変数 */
if (NULL == (sd_varStr = saveStrVar(&save_strHdr)))
goto errexit;
if (1 != fwrite(&save_strHdr, sizeof(Ald_strVarHdr), 1, fp))
goto errexit;
if (save_strHdr.size != 0 && 1 != fwrite(sd_varStr, save_strHdr.size, 1, fp))
goto errexit;
save_base->varStr = totalsize;
totalsize += save_strHdr.size + sizeof(Ald_strVarHdr);
free(sd_varStr);
/* 数値変数・配列変数 */
for (i = 0; i < 256; i++) {
sd_varSys = saveSysVar(&save_sysHdr, i);
if (sd_varSys == NULL) {
save_base->varSys[i] = 0;
} else {
if (1 != fwrite(&save_sysHdr, sizeof(Ald_sysVarHdr), 1, fp))
goto errexit;
if (1 != fwrite(sd_varSys, save_sysHdr.size, 1, fp))
goto errexit;
save_base->varSys[i] = totalsize;
totalsize += save_sysHdr.size + sizeof(Ald_sysVarHdr);
free(sd_varSys);
}
}
fseek(fp, 0, SEEK_SET);
if (1 != fwrite(save_base, sizeof(Ald_baseHdr), 1, fp))
goto errexit;
fclose(fp);
scheduleSync();
free(save_base);
return SAVE_SAVEOK1;
errexit:
if (fp != NULL)
fclose(fp);
if (save_base != NULL)
free(save_base);
if (sd_varStr != NULL)
free(sd_varStr);
if (sd_varSys != NULL)
free(sd_varSys);
return SAVE_SAVEERR;
}
/* スタック情報のセーブ */
static void *saveStackInfo(Ald_stackHdr *head) {
int count;
int *info = sl_getStackInfo(&count);
head->size = count * sizeof(int);
return (void *)info;
}
/* スタック情報のロード */
static void loadStackInfo(char *buf) {
Ald_stackHdr *head = (Ald_stackHdr *)buf;
char *data = buf + sizeof(Ald_stackHdr);
sl_putStackInfo((int *)data, head->size / sizeof(int));
}
/* 文字列変数のセーブ */
static void *saveStrVar(Ald_strVarHdr *head) {
int i;
char *tmp, *_tmp;
_tmp = tmp = malloc(svar_maxindex() * strvar_len);
if (tmp == NULL) {
WARNING("Out of memory\n");
return NULL;
}
*tmp = 0;
// Do not save svar[0], for backward compatibility.
for (i = 1; i <= svar_maxindex(); i++) {
strncpy(tmp, svar_get(i), strvar_len - 1);
tmp[strvar_len - 1] = '\0';
tmp += strlen(tmp) + 1;
}
head->size = tmp - _tmp;
head->count = svar_maxindex();
head->maxlen = strvar_len;
return _tmp;
}
/* 文字列変数のロード */
static void loadStrVar(char *buf) {
Ald_strVarHdr *head = (Ald_strVarHdr *)buf;
int cnt, max, i;
cnt = head->count;
max = head->maxlen;
if (svar_maxindex() != cnt || strvar_len != max) {
WARNING("Unexpected number of strings in savedata (%d, expected %d)\n", cnt, svar_maxindex());
svar_init(cnt, max);
}
buf += sizeof(Ald_strVarHdr);
for (i = 1; i <= cnt; i++) {
svar_set(i, buf);
buf += strlen(buf) + 1;
}
}
/* 数値・配列変数のセーブ */
static void *saveSysVar(Ald_sysVarHdr *head, int page) {
int *var;
int cnt, i;
WORD *tmp, *_tmp;
if (page == 0) { /* sysVar */
cnt = min(savefile_sysvar_cnt, SYSVAR_MAX);
var = sysVar;
} else if (!arrayVarBuffer[page - 1].saveflag) {
return NULL;
} else {
cnt = arrayVarBuffer[page - 1].size;
var = arrayVarBuffer[page - 1].value;
if (var == NULL)
return NULL;
}
head->size = cnt * sizeof(WORD);
head->pageNo = page;
tmp = _tmp = (WORD *)malloc(cnt * sizeof(WORD));
if (tmp == NULL) {
WARNING("Out of memory\n");
return NULL;
}
for (i = 0; i < cnt; i++) {
*tmp = (WORD)*var; var++; tmp++;
}
return _tmp;
}
/* 数値・配列変数のロード */
static int loadSysVar(char *buf) {
int i, cnt;
int *var;
WORD *data;
Ald_sysVarHdr *head = (Ald_sysVarHdr *)buf;
int page = head->pageNo;
if (page == 0) {
#if 0
if (head->size != SYSVAR_MAX * sizeof(WORD))
return SAVE_LOADERR;
var = sysVar;
cnt = SYSVAR_MAX;
#endif
var = sysVar;
cnt = savefile_sysvar_cnt = head->size / sizeof(WORD);
} else {
cnt = head->size / sizeof(WORD);
if (arrayVarBuffer[page - 1].size < cnt ||
arrayVarBuffer[page - 1].value == NULL) {
/*
fprintf(stderr, "loadSysVar(): undef array\n");
return SAVE_LOADERR;
*/
if (!v_allocateArrayBuffer(page, cnt, TRUE)) {
fprintf(stderr, "v_allocateArrayBuffer fail\n");
return SAVE_LOADERR;
}
}
var = arrayVarBuffer[page - 1].value;
}
buf += sizeof(Ald_sysVarHdr);
data = (WORD *)buf;
for (i = 0; i < cnt; i++) {
*var++ = *data++;
}
return SAVE_LOADOK;
}
/* ゲームデータのロード
no: セーブファイル番号 0~
*status: ステータス
*size: データの大きさを返すポインタ
あとで free(*buf)するのを忘れないように
*/
static void* loadGameData(int no, int *status, int *size) {
FILE *fp;
long filesize;
char *buf;
fp = fopen(nact->files.save_fname[no], "rb");
if (fp == NULL)
goto errexit;
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
if (filesize == 0)
goto errexit;
buf = (char *)malloc(filesize);
if (buf == NULL)
goto errexit;
fseek(fp, 0L, SEEK_SET);
fread(buf, filesize, 1, fp);
fclose(fp);
*size = (int)filesize;
*status = SAVE_LOADOK;
return buf;
errexit:
if (fp != NULL)
fclose(fp);
*status = SAVE_LOADERR;
return NULL;
}
static int saveGameData(int no, char *buf, int size) {
FILE *fp;
int status = SAVE_SAVEOK1;
fc_backup_oldfile(nact->files.save_fname[no]);
fp = fopen(nact->files.save_fname[no], "wb");
if (fp == NULL) {
return SAVE_SAVEERR;
}
if (1 != fwrite(buf, size, 1, fp)) {
status = SAVE_SAVEERR;
}
fclose(fp);
scheduleSync();
return status;
}
/* 指定ファイルからの画像の読み込み thanx tajiru@wizard */
BYTE* load_cg_with_file(char *fname_utf8, int *status, long *filesize){
int size;
FILE *fp;
static BYTE *tmp;
*status = 0;
if (NULL == (fp = fc_open(fname_utf8, 'r'))) {
*status = SAVE_LOADERR; return NULL;
}
fseek(fp, 0L, SEEK_END);
*filesize = ftell(fp);
if (*filesize == 0) {
*status = SAVE_LOADERR; return NULL;
}
tmp = (char *)malloc(*filesize);
if (tmp == NULL) {
WARNING("Out of memory\n");
*status = SAVE_LOADERR; return NULL;
}
fseek(fp, 0L, SEEK_SET);
size = fread(tmp, 1, *filesize,fp);
if (size != *filesize) {
*status = SAVE_LOADSHORTAGE;
} else {
*status = SAVE_LOADOK;
}
fclose(fp);
return tmp;
}