aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/savegame.cfsml
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine/savegame.cfsml')
-rw-r--r--engines/sci/engine/savegame.cfsml289
1 files changed, 185 insertions, 104 deletions
diff --git a/engines/sci/engine/savegame.cfsml b/engines/sci/engine/savegame.cfsml
index bc36a5f53d..e9a8edc7e6 100644
--- a/engines/sci/engine/savegame.cfsml
+++ b/engines/sci/engine/savegame.cfsml
@@ -24,16 +24,19 @@
*/
/* Savegame handling for state_t structs. Makes heavy use of cfsml magic. */
-/* DON'T EDIT savegame.c ! Only modify savegame.cfsml, if something needs
+/* DON'T EDIT savegame.cpp ! Only modify savegame.cfsml, if something needs
** to be changed. Refer to freesci/docs/misc/cfsml.spec if you don't understand
** savegame.cfsml. If this doesn't solve your problem, contact the maintainer.
*/
+#include <stdarg.h>
#include "sci/include/sci_memory.h"
#include "sci/include/gfx_operations.h"
#include "sci/include/sfx_engine.h"
#include "sci/include/engine.h"
#include "sci/engine/heap.h"
+#include "common/stream.h"
+#include "common/system.h"
#ifdef _MSC_VER
#include <direct.h>
@@ -50,16 +53,77 @@
** - File input/output state (this is likely not to happen)
*/
+
+const unsigned int PRINTFBUFLEN = 128;
+int WSprintf(Common::WriteStream* str, const char *format, ...) {
+ va_list args;
+ char buf[PRINTFBUFLEN]; // default buffer to prevent new in common case
+ char* writebuf = buf;
+
+ unsigned int s = PRINTFBUFLEN;
+ unsigned int outsize;
+ while (true) {
+ va_start(args, format);
+ outsize = vsnprintf(writebuf, s, format, args);
+ va_end(args);
+
+ if (outsize == s) {
+ if (s > 16384) { // there are limits...
+ delete[] writebuf;
+ warning("Saving failed: line much too long");
+ return 0;
+ }
+ s *= 2;
+ if (writebuf != buf) delete[] writebuf;
+ writebuf = new char[s];
+ } else {
+ break;
+ }
+ }
+
+ uint32 ret = str->write(writebuf, outsize);
+
+ if (writebuf != buf) delete[] writebuf;
+
+ return ret;
+}
+
+// Only supports scanf on a full line
+int SRSscanf(Common::SeekableReadStream* str, const char *format, ...) {
+ assert(strlen(format) > 0 && format[strlen(format)-1] == '\n');
+ va_list args;
+ Common::String line = str->readLine() + "\n";
+
+ va_start(args, format);
+ int ret = vsscanf(line.c_str(), format, args);
+ va_end(args);
+
+ return ret;
+}
+
+int SRSgetc(Common::SeekableReadStream* str) {
+ char c = str->readSByte();
+ if (str->err() || str->eos())
+ return EOF;
+ return c;
+}
+
+char* SRSgets(char* s, int size, Common::SeekableReadStream* str) {
+ return str->readLine_NEW(s, size);
+}
+
+
+
static state_t *_global_save_state;
// Needed for some graphical stuff.
#define FILE_VERSION _global_save_state->savegame_version
-void write_reg_t(FILE *fh, reg_t *foo) {
- fprintf(fh, PREG, PRINT_REG(*foo));
+void write_reg_t(Common::WriteStream *fh, reg_t *foo) {
+ WSprintf(fh, PREG, PRINT_REG(*foo));
}
-int read_reg_t(FILE *fh, reg_t *foo, const char *lastval, int *line, int *hiteof) {
+int read_reg_t(Common::SeekableReadStream *fh, reg_t *foo, const char *lastval, int *line, int *hiteof) {
int segment, offset;
if (sscanf(lastval, PREG, &segment, &offset) < 2) {
@@ -71,22 +135,22 @@ int read_reg_t(FILE *fh, reg_t *foo, const char *lastval, int *line, int *hiteof
return 0;
}
-void write_sci_version(FILE *fh, sci_version_t *foo) {
- fprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo), SCI_VERSION_PATCHLEVEL(*foo));
+void write_sci_version(Common::WriteStream *fh, sci_version_t *foo) {
+ WSprintf(fh, "%d.%03d.%03d", SCI_VERSION_MAJOR(*foo), SCI_VERSION_MINOR(*foo), SCI_VERSION_PATCHLEVEL(*foo));
}
-int read_sci_version(FILE *fh, sci_version_t *foo, const char *lastval, int *line, int *hiteof) {
+int read_sci_version(Common::SeekableReadStream *fh, sci_version_t *foo, const char *lastval, int *line, int *hiteof) {
return version_parse(lastval, foo);
}
-void write_PTN(FILE *fh, parse_tree_node_t *foo) {
+void write_PTN(Common::WriteStream *fh, parse_tree_node_t *foo) {
if (foo->type == PARSE_TREE_NODE_LEAF)
- fprintf(fh, "L%d", foo->content.value);
+ WSprintf(fh, "L%d", foo->content.value);
else
- fprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]);
+ WSprintf(fh, "B(%d,%d)", foo->content.branches[0], foo->content.branches[1]);
}
-int read_PTN(FILE *fh, parse_tree_node_t *foo, const char *lastval, int *line, int *hiteof) {
+int read_PTN(Common::SeekableReadStream *fh, parse_tree_node_t *foo, const char *lastval, int *line, int *hiteof) {
if (lastval[0] == 'L') {
const char *c = lastval + 1;
char *strend;
@@ -140,22 +204,22 @@ int read_PTN(FILE *fh, parse_tree_node_t *foo, const char *lastval, int *line, i
}
-void write_menubar_tp(FILE *fh, menubar_t **foo);
-int read_menubar_tp(FILE *fh, menubar_t **foo, const char *lastval, int *line, int *hiteof);
+void write_menubar_tp(Common::WriteStream *fh, menubar_t **foo);
+int read_menubar_tp(Common::SeekableReadStream *fh, menubar_t **foo, const char *lastval, int *line, int *hiteof);
-void write_mem_obj_tp(FILE *fh, mem_obj_t **foo);
-int read_mem_obj_tp(FILE *fh, mem_obj_t **foo, const char *lastval, int *line, int *hiteof);
+void write_mem_obj_tp(Common::WriteStream *fh, mem_obj_t **foo);
+int read_mem_obj_tp(Common::SeekableReadStream *fh, mem_obj_t **foo, const char *lastval, int *line, int *hiteof);
-void write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo);
-int read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, const char *lastval, int *line, int *hiteof);
+void write_int_hash_map_tp(Common::WriteStream *fh, int_hash_map_t **foo);
+int read_int_hash_map_tp(Common::SeekableReadStream *fh, int_hash_map_t **foo, const char *lastval, int *line, int *hiteof);
-void write_songlib_t(FILE *fh, songlib_t *foo);
-int read_songlib_t(FILE *fh, songlib_t *foo, const char *lastval, int *line, int *hiteof);
+void write_songlib_t(Common::WriteStream *fh, songlib_t *foo);
+int read_songlib_t(Common::SeekableReadStream *fh, songlib_t *foo, const char *lastval, int *line, int *hiteof);
-void write_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo);
-int read_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo, const char *lastval, int *line, int *hiteof);
+void write_int_hash_map_node_tp(Common::WriteStream *fh, int_hash_map_t::node_t **foo);
+int read_int_hash_map_node_tp(Common::SeekableReadStream *fh, int_hash_map_t::node_t **foo, const char *lastval, int *line, int *hiteof);
-int read_song_tp(FILE *fh, song_t **foo, const char *lastval, int *line, int *hiteof);
+int read_song_tp(Common::SeekableReadStream *fh, song_t **foo, const char *lastval, int *line, int *hiteof);
typedef mem_obj_t *mem_obj_ptr;
@@ -254,6 +318,15 @@ RECORD sfx_state_t "sfx_state_t" {
songlib_t songlib;
}
+RECORD SavegameMetadata "SavegameMetadata" {
+ string savegame_name;
+ int savegame_version;
+ string game_version;
+ sci_version_t version;
+ int savegame_date;
+ int savegame_time;
+}
+
RECORD state_t "state_t" {
int savegame_version;
@@ -382,41 +455,45 @@ RECORD dynmem_t "dynmem_t" {
%END CFSML
-void write_songlib_t(FILE *fh, songlib_t *songlib) {
+void write_songlib_t(Common::WriteStream *fh, songlib_t *songlib) {
song_t *seeker = *(songlib->lib);
int songcount = song_lib_count(*songlib);
- fprintf(fh, "{\n");
- fprintf(fh, "songcount = %d\n", songcount);
- fprintf(fh, "list = \n");
- fprintf(fh, "[\n");
+ WSprintf(fh, "{\n");
+ WSprintf(fh, "songcount = %d\n", songcount);
+ WSprintf(fh, "list = \n");
+ WSprintf(fh, "[\n");
while (seeker) {
seeker->restore_time = seeker->it->get_timepos(seeker->it);
%CFSMLWRITE song_t seeker INTO fh;
seeker = seeker->next;
}
- fprintf(fh, "]\n");
- fprintf(fh, "}\n");
+ WSprintf(fh, "]\n");
+ WSprintf(fh, "}\n");
}
-int read_songlib_t(FILE *fh, songlib_t *songlib, const char *lastval, int *line, int *hiteof) {
+int read_songlib_t(Common::SeekableReadStream *fh, songlib_t *songlib, const char *lastval, int *line, int *hiteof) {
int songcount;
int i;
song_t *newsong;
int oldstatus;
- fscanf(fh, "{\n");
- fscanf(fh, "songcount = %d\n", &songcount);
- fscanf(fh, "list = \n");
- fscanf(fh, "[\n");
+ if (strcmp(lastval, "{")) {
+ _cfsml_error("Opening brackets expected at line %d\n", *line);
+ return CFSML_FAILURE;
+ }
+ // FIXME: error checking
+ SRSscanf(fh, "songcount = %d\n", &songcount);
+ SRSscanf(fh, "list = \n");
+ SRSscanf(fh, "[\n");
*line += 4;
song_lib_init(songlib);
for (i = 0; i < songcount; i++) {
%CFSMLREAD song_tp &newsong FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
song_lib_add(*songlib, newsong);
}
- fscanf(fh, "]\n");
- fscanf(fh, "}\n");;
+ SRSscanf(fh, "]\n");
+ SRSscanf(fh, "}\n");
*line += 2;
return 0;
}
@@ -449,17 +526,17 @@ int mem_obj_string_to_enum(const char *str) {
static int bucket_length;
-void write_int_hash_map_tp(FILE *fh, int_hash_map_t **foo) {
+void write_int_hash_map_tp(Common::WriteStream *fh, int_hash_map_t **foo) {
%CFSMLWRITE int_hash_map_t *foo INTO fh;
}
-void write_song_tp(FILE *fh, song_t **foo) {
+void write_song_tp(Common::WriteStream *fh, song_t **foo) {
%CFSMLWRITE song_t *foo INTO fh;
}
song_iterator_t *build_iterator(state_t *s, int song_nr, int type, songit_id_t id);
-int read_song_tp(FILE *fh, song_t **foo, const char *lastval, int *line, int *hiteof) {
+int read_song_tp(Common::SeekableReadStream *fh, song_t **foo, const char *lastval, int *line, int *hiteof) {
char *token;
int assignment;
*foo = (song_t*) malloc(sizeof(song_t));
@@ -471,27 +548,27 @@ int read_song_tp(FILE *fh, song_t **foo, const char *lastval, int *line, int *hi
return 0;
}
-int read_int_hash_map_tp(FILE *fh, int_hash_map_t **foo, const char *lastval, int *line, int *hiteof) {
+int read_int_hash_map_tp(Common::SeekableReadStream *fh, int_hash_map_t **foo, const char *lastval, int *line, int *hiteof) {
*foo = new int_hash_map_t;
%CFSMLREAD int_hash_map_t (*foo) FROM fh ERRVAR *hiteof FIRSTTOKEN lastval LINECOUNTER *line;
(*foo)->holes = NULL;
return 0;
}
-void write_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo) {
+void write_int_hash_map_node_tp(Common::WriteStream *fh, int_hash_map_t::node_t **foo) {
if (!(*foo)) {
- fputs("\\null", fh);
+ WSprintf(fh, "\\null");
} else {
- fprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value);
+ WSprintf(fh,"[\n%d=>%d\n", (*foo)->name, (*foo)->value);
if ((*foo)->next) {
%CFSMLWRITE int_hash_map_node_tp &((*foo)->next) INTO fh;
} else
- fputc('L', fh);
- fputs("]", fh);
+ WSprintf(fh, "L");
+ WSprintf(fh, "]");
}
}
-int read_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo, const char *lastval, int *line, int *hiteof) {
+int read_int_hash_map_node_tp(Common::SeekableReadStream *fh, int_hash_map_t::node_t **foo, const char *lastval, int *line, int *hiteof) {
static char buffer[80];
if (lastval[0] == '\\') {
@@ -505,7 +582,7 @@ int read_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo, const char
do {
(*line)++;
- fgets(buffer, 80, fh);
+ SRSgets(buffer, 80, fh);
if (buffer[0] == 'L') {
(*foo)->next = NULL;
buffer[0] = buffer[1];
@@ -527,16 +604,16 @@ int read_int_hash_map_node_tp(FILE *fh, int_hash_map_t::node_t **foo, const char
return 0;
}
-void write_menubar_tp(FILE *fh, menubar_t **foo) {
+void write_menubar_tp(Common::WriteStream *fh, menubar_t **foo) {
if (*foo) {
%CFSMLWRITE menubar_t (*foo) INTO fh;
} else { // Nothing to write
- fputs("\\null\\", fh);
+ WSprintf(fh, "\\null\\");
}
}
-int read_menubar_tp(FILE *fh, menubar_t **foo, const char *lastval, int *line, int *hiteof) {
+int read_menubar_tp(Common::SeekableReadStream *fh, menubar_t **foo, const char *lastval, int *line, int *hiteof) {
if (lastval[0] == '\\') {
*foo = NULL; // No menu bar
} else {
@@ -546,8 +623,8 @@ int read_menubar_tp(FILE *fh, menubar_t **foo, const char *lastval, int *line, i
return *hiteof;
}
-void write_mem_obj_t(FILE *fh, mem_obj_t *foo) {
- fprintf(fh, "%s\n", mem_obj_string_names[foo->type].name);
+void write_mem_obj_t(Common::WriteStream *fh, mem_obj_t *foo) {
+ WSprintf(fh, "%s\n", mem_obj_string_names[foo->type].name);
%CFSMLWRITE int &foo->segmgr_id INTO fh;
switch (foo->type) {
case MEM_OBJ_SCRIPT:
@@ -579,7 +656,7 @@ void write_mem_obj_t(FILE *fh, mem_obj_t *foo) {
}
}
-int read_mem_obj_t(FILE *fh, mem_obj_t *foo, const char *lastval, int *line, int *hiteof) {
+int read_mem_obj_t(Common::SeekableReadStream *fh, mem_obj_t *foo, const char *lastval, int *line, int *hiteof) {
char buffer[80];
foo->type = mem_obj_string_to_enum(lastval);
if (foo->type < 0) {
@@ -622,15 +699,15 @@ int read_mem_obj_t(FILE *fh, mem_obj_t *foo, const char *lastval, int *line, int
return *hiteof;
}
-void write_mem_obj_tp(FILE *fh, mem_obj_t **foo) {
+void write_mem_obj_tp(Common::WriteStream *fh, mem_obj_t **foo) {
if (*foo) {
%CFSMLWRITE mem_obj_t (*foo) INTO fh;
} else { // Nothing to write
- fputs("\\null\\", fh);
+ WSprintf(fh, "\\null\\");
}
}
-int read_mem_obj_tp(FILE *fh, mem_obj_t **foo, const char *lastval, int *line, int *hiteof) {
+int read_mem_obj_tp(Common::SeekableReadStream *fh, mem_obj_t **foo, const char *lastval, int *line, int *hiteof) {
if (lastval[0] == '\\') {
*foo = NULL; // No menu bar
} else {
@@ -648,12 +725,23 @@ void _gamestate_unfrob(state_t *s) {
}
-int gamestate_save(state_t *s, char *dirname) {
- FILE *fh;
+int gamestate_save(state_t *s, Common::WriteStream *fh, const char* savename) {
sci_dir_t dir;
char *filename;
int fd;
+ tm curTime;
+ g_system->getTimeAndDate(curTime);
+
+ SavegameMetadata *meta = new SavegameMetadata;
+ meta->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION;
+ meta->savegame_name = savename;
+ meta->version = s->version;
+ meta->game_version = s->game_version;
+ meta->savegame_date = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
+ meta->savegame_time = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF);
+ fprintf(stderr, "date/time: %d %d\n", meta->savegame_date, meta->savegame_time);
+
_global_save_state = s;
s->savegame_version = FREESCI_CURRENT_SAVEGAME_VERSION;
s->dyn_views_list_serial = (s->dyn_views)? s->dyn_views->serial : -2;
@@ -665,22 +753,6 @@ int gamestate_save(state_t *s, char *dirname) {
return 1;
}
- scimkdir (dirname, 0700);
-
- if (chdir(dirname)) {
- sciprintf("Could not enter directory '%s'\n", dirname);
- return 1;
- }
-
- sci_init_dir(&dir);
- filename = sci_find_first(&dir, "*");
- while (filename) {
- if (strcmp(filename, "..") && strcmp(filename, "."))
- unlink(filename); // Delete all files in directory
- filename = sci_find_next(&dir);
- }
- sci_finish_find(&dir);
-
/*
if (s->sound_server) {
if ((s->sound_server->save)(s, dirname)) {
@@ -690,19 +762,16 @@ int gamestate_save(state_t *s, char *dirname) {
}
}
*/
- fh = fopen("state", "w" FO_TEXT);
-
// Calculate the time spent with this game
s->game_time = time(NULL) - s->game_start_time.tv_sec;
+ %CFSMLWRITE SavegameMetadata meta INTO fh;
%CFSMLWRITE state_t s INTO fh;
- fclose(fh);
+ delete meta;
_gamestate_unfrob(s);
- chdir("..");
-
return 0;
}
@@ -948,19 +1017,13 @@ static void reconstruct_sounds(state_t *s) {
}
}
-state_t *gamestate_restore(state_t *s, char *dirname) {
- FILE *fh;
+state_t *gamestate_restore(state_t *s, Common::SeekableReadStream *fh) {
int fd;
int i;
int read_eof = 0;
state_t *retval;
songlib_t temp;
- if (chdir(dirname)) {
- sciprintf("Game state '%s' does not exist\n", dirname);
- return NULL;
- }
-
/*
if (s->sound_server) {
if ((s->sound_server->restore)(s, dirname)) {
@@ -978,12 +1041,23 @@ state_t *gamestate_restore(state_t *s, char *dirname) {
_global_save_state = retval;
retval->gfx_state = s->gfx_state;
- fh = fopen("state", "r" FO_TEXT);
- if (!fh) {
- free(retval);
+ SavegameMetadata* meta = new SavegameMetadata;
+ memset(retval, 0, sizeof(SavegameMetadata));
+
+ %CFSMLREAD-ATOMIC SavegameMetadata meta FROM fh ERRVAR read_eof;
+ if ((meta->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) ||
+ (meta->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) {
+ if (meta->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION)
+ sciprintf("Old savegame version detected- can't load\n");
+ else
+ sciprintf("Savegame version is %d- maximum supported is %0d\n", meta->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION);
+
+ delete meta;
return NULL;
}
+ delete meta;
+
// Backwards compatibility settings
retval->dyn_views = NULL;
retval->drop_views = NULL;
@@ -995,19 +1069,6 @@ state_t *gamestate_restore(state_t *s, char *dirname) {
%CFSMLREAD-ATOMIC state_t retval FROM fh ERRVAR read_eof;
- fclose(fh);
-
- if ((retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) || (retval->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) {
- if (retval->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION)
- sciprintf("Old savegame version detected- can't load\n");
- else
- sciprintf("Savegame version is %d- maximum supported is %0d\n", retval->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION);
-
- chdir("..");
- free(retval);
- return NULL;
- }
-
sfx_exit(&s->sound);
_gamestate_unfrob(retval);
@@ -1097,7 +1158,27 @@ state_t *gamestate_restore(state_t *s, char *dirname) {
retval->sound.debug = s->sound.debug;
reconstruct_sounds(retval);
- chdir ("..");
-
return retval;
}
+
+bool get_savegame_metadata(Common::SeekableReadStream* stream, SavegameMetadata* meta) {
+ int read_eof = 0;
+
+ %CFSMLREAD-ATOMIC SavegameMetadata meta FROM stream ERRVAR read_eof;
+
+ if (read_eof)
+ return false;
+
+ if ((meta->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION) ||
+ (meta->savegame_version > FREESCI_CURRENT_SAVEGAME_VERSION)) {
+ if (meta->savegame_version < FREESCI_MINIMUM_SAVEGAME_VERSION)
+ sciprintf("Old savegame version detected- can't load\n");
+ else
+ sciprintf("Savegame version is %d- maximum supported is %0d\n", meta->savegame_version, FREESCI_CURRENT_SAVEGAME_VERSION);
+
+ return false;
+ }
+
+ return true;
+}
+