mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-08 02:42:34 +00:00
GLK: ADRIFT: Refactoring load serializer to not use longjmp
This commit is contained in:
parent
994afedd33
commit
41c7d31fb6
@ -124,7 +124,7 @@ void SaveSerializer::save() {
|
||||
if (task > 0)
|
||||
writeBool(gs_task_done(_game, task - 1));
|
||||
else
|
||||
writeBool(FALSE);
|
||||
writeBool(false);
|
||||
}
|
||||
|
||||
/* Save NPCs information. */
|
||||
@ -177,8 +177,8 @@ void SaveSerializer::save() {
|
||||
* references.
|
||||
*/
|
||||
flush(TRUE);
|
||||
_callback = NULL;
|
||||
_opaque = NULL;
|
||||
_callback = nullptr;
|
||||
_opaque = nullptr;
|
||||
}
|
||||
|
||||
void SaveSerializer::flush(sc_bool is_final) {
|
||||
@ -225,20 +225,13 @@ void SaveSerializer::writeUint(sc_uint value) {
|
||||
}
|
||||
|
||||
void SaveSerializer::writeBool(sc_bool boolean) {
|
||||
// Write a 1 for TRUE, 0 for FALSE
|
||||
// Write a 1 for TRUE, 0 for false
|
||||
writeString(boolean ? "1" : "0");
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/* TAS input file line counter. */
|
||||
static sc_tafref_t ser_tas = NULL;
|
||||
static sc_int ser_tasline = 0;
|
||||
|
||||
/* Restore error jump buffer. */
|
||||
static jmp_buf ser_tas_error;
|
||||
static sc_var_setref_t new_vars; /* For setjmp safety */
|
||||
static sc_gameref_t new_game; /* For setjmp safety */
|
||||
#define CHECK if (context._break) goto ser_tas_error
|
||||
|
||||
bool LoadSerializer::load() {
|
||||
const sc_filterref_t filter = gs_get_filter(_game);
|
||||
@ -246,144 +239,128 @@ bool LoadSerializer::load() {
|
||||
sc_vartype_t vt_key[3];
|
||||
sc_int index_, var_count;
|
||||
const sc_char *gamename;
|
||||
sc_var_setref_t new_vars = nullptr;
|
||||
sc_gameref_t new_game = nullptr;
|
||||
Context context;
|
||||
|
||||
/* Create a TAF (TAS) reference from callbacks, for reader functions. */
|
||||
// Create a TAF (TAS) reference from callbacks, for reader functions
|
||||
ser_tas = taf_create_tas(_callback, _opaque);
|
||||
if (!ser_tas)
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
/* Reset line counter for error messages. */
|
||||
// Reset line counter for error messages.
|
||||
ser_tasline = 1;
|
||||
|
||||
new_game = NULL;
|
||||
new_vars = NULL;
|
||||
|
||||
/* Set up error handling jump buffer, and handle errors. */
|
||||
if (setjmp(ser_tas_error) != 0) {
|
||||
/* Destroy any temporary _game and variables. */
|
||||
if (new_game)
|
||||
gs_destroy(new_game);
|
||||
if (new_vars)
|
||||
var_destroy(new_vars);
|
||||
|
||||
/* Destroy the TAF (TAS) file and return fail status. */
|
||||
taf_destroy(ser_tas);
|
||||
ser_tas = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the _game name, and compare with the one in the _game. Fail if
|
||||
* they don't match exactly. A tighter check than this would perhaps be
|
||||
* preferable, say, something based on the TAF file header, but this isn't
|
||||
* in the save file format.
|
||||
*/
|
||||
// Read the _game name, and compare with the one in the _game. Fail if they don't match exactly.
|
||||
// A tighter check than this would perhaps be preferable, say, something based on the TAF file
|
||||
// header, but this isn't in the save file format.
|
||||
|
||||
vt_key[0].string = "Globals";
|
||||
vt_key[1].string = "GameName";
|
||||
gamename = prop_get_string(bundle, "S<-ss", vt_key);
|
||||
if (strcmp(readString(), gamename) != 0)
|
||||
longjmp(ser_tas_error, 1);
|
||||
if (strcmp(readString(context), gamename) != 0 || context._break)
|
||||
goto ser_tas_error;
|
||||
|
||||
/* Read and verify the counts in the saved _game. */
|
||||
if (readInt() != gs_room_count(_game)
|
||||
|| readInt() != gs_object_count(_game)
|
||||
|| readInt() != gs_task_count(_game)
|
||||
|| readInt() != gs_event_count(_game)
|
||||
|| readInt() != gs_npc_count(_game))
|
||||
longjmp(ser_tas_error, 1);
|
||||
// Read and verify the counts in the saved _game.
|
||||
if ((readInt(context) != gs_room_count(_game) || context._break)
|
||||
|| (readInt(context) != gs_object_count(_game) || context._break)
|
||||
|| (readInt(context) != gs_task_count(_game) || context._break)
|
||||
|| (readInt(context) != gs_event_count(_game) || context._break)
|
||||
|| (readInt(context) != gs_npc_count(_game) || context._break))
|
||||
goto ser_tas_error;
|
||||
|
||||
/* Create a variables set and _game to restore into. */
|
||||
// Create a variables set and _game to restore into.
|
||||
new_vars = var_create(bundle);
|
||||
new_game = gs_create(new_vars, bundle, filter);
|
||||
var_register_game(new_vars, new_game);
|
||||
|
||||
/* All set to load TAF (TAS) data into the new _game. */
|
||||
// All set to load TAF (TAS) data into the new _game.
|
||||
|
||||
/* Restore the score and player information. */
|
||||
new_game->score = readInt();
|
||||
gs_set_playerroom(new_game, readInt() - 1);
|
||||
gs_set_playerparent(new_game, readInt());
|
||||
gs_set_playerposition(new_game, readInt());
|
||||
// Restore the score and player information.
|
||||
new_game->score = readInt(context); CHECK;
|
||||
gs_set_playerroom(new_game, readInt(context) - 1); CHECK;
|
||||
gs_set_playerparent(new_game, readInt(context)); CHECK;
|
||||
gs_set_playerposition(new_game, readInt(context)); CHECK;
|
||||
|
||||
/* Skip player gender. */
|
||||
(void)readInt();
|
||||
// Skip player gender.
|
||||
(void)readInt(context); CHECK;
|
||||
|
||||
/* Skip encumbrance details, not currently maintained by the _game. */
|
||||
(void)readInt();
|
||||
(void)readInt();
|
||||
(void)readInt();
|
||||
(void)readInt();
|
||||
// Skip encumbrance details, not currently maintained by the _game.
|
||||
(void)readInt(context); CHECK;
|
||||
(void)readInt(context); CHECK;
|
||||
(void)readInt(context); CHECK;
|
||||
(void)readInt(context); CHECK;
|
||||
|
||||
/* Restore rooms information. */
|
||||
for (index_ = 0; index_ < gs_room_count(new_game); index_++)
|
||||
gs_set_room_seen(new_game, index_, readBool());
|
||||
// Restore rooms information.
|
||||
for (index_ = 0; index_ < gs_room_count(new_game); index_++) {
|
||||
gs_set_room_seen(new_game, index_, readBool(context)); CHECK;
|
||||
}
|
||||
|
||||
/* Restore objects information. */
|
||||
// Restore objects information.
|
||||
for (index_ = 0; index_ < gs_object_count(new_game); index_++) {
|
||||
sc_int openable, currentstate;
|
||||
|
||||
/* Bypass mutators for position and parent. Fix later? */
|
||||
new_game->objects[index_].position = readInt();
|
||||
gs_set_object_seen(new_game, index_, readBool());
|
||||
new_game->objects[index_].parent = readInt();
|
||||
// Bypass mutators for position and parent. Fix later?
|
||||
new_game->objects[index_].position = readInt(context); CHECK;
|
||||
gs_set_object_seen(new_game, index_, readBool(context)); CHECK;
|
||||
new_game->objects[index_].parent = readInt(context); CHECK;
|
||||
|
||||
vt_key[0].string = "Objects";
|
||||
vt_key[1].integer = index_;
|
||||
vt_key[2].string = "Openable";
|
||||
openable = prop_get_integer(bundle, "I<-sis", vt_key);
|
||||
gs_set_object_openness(new_game, index_,
|
||||
openable != 0 ? readInt() : 0);
|
||||
gs_set_object_openness(new_game, index_, openable != 0 ? readInt(context) : 0); CHECK;
|
||||
|
||||
vt_key[2].string = "CurrentState";
|
||||
currentstate = prop_get_integer(bundle, "I<-sis", vt_key);
|
||||
gs_set_object_state(new_game, index_,
|
||||
currentstate != 0 ? readInt() : 0);
|
||||
currentstate != 0 ? readInt(context) : 0); CHECK;
|
||||
|
||||
gs_set_object_unmoved(new_game, index_, readBool());
|
||||
gs_set_object_unmoved(new_game, index_, readBool(context)); CHECK;
|
||||
}
|
||||
|
||||
/* Restore tasks information. */
|
||||
// Restore tasks information.
|
||||
for (index_ = 0; index_ < gs_task_count(new_game); index_++) {
|
||||
gs_set_task_done(new_game, index_, readBool());
|
||||
gs_set_task_scored(new_game, index_, readBool());
|
||||
gs_set_task_done(new_game, index_, readBool(context)); CHECK;
|
||||
gs_set_task_scored(new_game, index_, readBool(context)); CHECK;
|
||||
}
|
||||
|
||||
/* Restore events information. */
|
||||
// Restore events information.
|
||||
for (index_ = 0; index_ < gs_event_count(new_game); index_++) {
|
||||
sc_int startertype, task;
|
||||
|
||||
/* Restore first event details. */
|
||||
gs_set_event_time(new_game, index_, readInt());
|
||||
task = readInt();
|
||||
gs_set_event_state(new_game, index_, readInt() + 1);
|
||||
// Restore first event details.
|
||||
gs_set_event_time(new_game, index_, readInt(context)); CHECK;
|
||||
task = readInt(context); CHECK;
|
||||
gs_set_event_state(new_game, index_, readInt(context) + 1); CHECK;
|
||||
|
||||
/* Verify and restore the starter task, if any. */
|
||||
// Verify and restore the starter task, if any.
|
||||
if (task > 0) {
|
||||
vt_key[0].string = "Events";
|
||||
vt_key[1].integer = index_;
|
||||
vt_key[2].string = "StarterType";
|
||||
startertype = prop_get_integer(bundle, "I<-sis", vt_key);
|
||||
if (startertype != 3)
|
||||
longjmp(ser_tas_error, 1);
|
||||
goto ser_tas_error;
|
||||
|
||||
/* Restore task state. */
|
||||
gs_set_task_done(new_game, task - 1, readBool());
|
||||
// Restore task state.
|
||||
gs_set_task_done(new_game, task - 1, readBool(context)); CHECK;
|
||||
} else {
|
||||
(void)readBool(context); CHECK;
|
||||
}
|
||||
else
|
||||
(void)readBool();
|
||||
}
|
||||
|
||||
/* Restore NPCs information. */
|
||||
// Restore NPCs information.
|
||||
for (index_ = 0; index_ < gs_npc_count(new_game); index_++) {
|
||||
sc_int walk;
|
||||
|
||||
gs_set_npc_location(new_game, index_, readInt());
|
||||
gs_set_npc_seen(new_game, index_, readBool());
|
||||
gs_set_npc_location(new_game, index_, readInt(context)); CHECK;
|
||||
gs_set_npc_seen(new_game, index_, readBool(context)); CHECK;
|
||||
for (walk = 0; walk < gs_npc_walkstep_count(new_game, index_); walk++)
|
||||
gs_set_npc_walkstep(new_game, index_, walk, readInt());
|
||||
gs_set_npc_walkstep(new_game, index_, walk, readInt(context)); CHECK;
|
||||
}
|
||||
|
||||
/* Restore each variable. */
|
||||
// Restore each variable.
|
||||
vt_key[0].string = "Variables";
|
||||
var_count = prop_get_child_count(bundle, "I<-s", vt_key);
|
||||
|
||||
@ -400,11 +377,11 @@ bool LoadSerializer::load() {
|
||||
|
||||
switch (var_type) {
|
||||
case TAFVAR_NUMERIC:
|
||||
var_put_integer(new_vars, name, readInt());
|
||||
var_put_integer(new_vars, name, readInt(context)); CHECK;
|
||||
break;
|
||||
|
||||
case TAFVAR_STRING:
|
||||
var_put_string(new_vars, name, readString());
|
||||
var_put_string(new_vars, name, readString(context)); CHECK;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -412,102 +389,108 @@ bool LoadSerializer::load() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore timing information. */
|
||||
var_set_elapsed_seconds(new_vars, readUint());
|
||||
// Restore timing information.
|
||||
var_set_elapsed_seconds(new_vars, readUint(context)); CHECK;
|
||||
|
||||
/* Restore turns count. */
|
||||
new_game->turns = (sc_int)readUint();
|
||||
// Restore turns count.
|
||||
new_game->turns = (sc_int)readUint(context); CHECK;
|
||||
|
||||
/*
|
||||
* Resources tweak -- set requested to match those in the current _game
|
||||
* so that they remain unchanged by the gs_copy() of new_game onto
|
||||
* _game. This way, both the requested and the active resources in the
|
||||
* _game are unchanged by restore.
|
||||
/* Resources tweak -- set requested to match those in the current _game so that they remain
|
||||
* unchanged by the gs_copy() of new_game onto game. This way, both the requested and the
|
||||
* active resources in the game are unchanged by restore.
|
||||
*/
|
||||
new_game->requested_sound = _game->requested_sound;
|
||||
new_game->requested_graphic = _game->requested_graphic;
|
||||
|
||||
/*
|
||||
* Quitter tweak -- set the quit jump buffer in the new _game to be the
|
||||
/* Quitter tweak -- set the quit jump buffer in the new _game to be the
|
||||
* same as the current one, so that it remains unchanged by gs_copy(). The
|
||||
* one in the new _game is still the unset one from gs_create().
|
||||
*/
|
||||
memcpy(&new_game->quitter, &_game->quitter, sizeof(_game->quitter));
|
||||
|
||||
/*
|
||||
* If we got this far, we successfully restored the _game from the file.
|
||||
/* If we got this far, we successfully restored the _game from the file.
|
||||
* As our final act, copy the new _game onto the old one.
|
||||
*/
|
||||
new_game->temporary = _game->temporary;
|
||||
new_game->undo = _game->undo;
|
||||
gs_copy(_game, new_game);
|
||||
|
||||
/* Done with the temporary _game and variables. */
|
||||
// Done with the temporary _game and variables.
|
||||
gs_destroy(new_game);
|
||||
var_destroy(new_vars);
|
||||
|
||||
/* Done with TAF (TAS) file; destroy it and return successfully. */
|
||||
// Done with TAF (TAS) file; destroy it and return successfully
|
||||
taf_destroy(ser_tas);
|
||||
ser_tas = NULL;
|
||||
return TRUE;
|
||||
return true;
|
||||
|
||||
ser_tas_error:
|
||||
// Destroy any temporary _game and variables
|
||||
if (new_game)
|
||||
gs_destroy(new_game);
|
||||
if (new_vars)
|
||||
var_destroy(new_vars);
|
||||
|
||||
// Destroy the TAF (TAS) file and return fail status
|
||||
taf_destroy(ser_tas);
|
||||
return false;
|
||||
}
|
||||
|
||||
const sc_char *LoadSerializer::readString() {
|
||||
const sc_char *LoadSerializer::readString(CONTEXT) {
|
||||
const sc_char *string;
|
||||
|
||||
/* Get the next line, and complain if absent. */
|
||||
string = taf_next_line(ser_tas);
|
||||
if (!string) {
|
||||
sc_error("readString: out of TAS data at line %ld\n", ser_tasline);
|
||||
longjmp(ser_tas_error, 1);
|
||||
LONG_JUMP0
|
||||
}
|
||||
|
||||
ser_tasline++;
|
||||
return string;
|
||||
}
|
||||
|
||||
sc_int LoadSerializer::readInt() {
|
||||
sc_int LoadSerializer::readInt(CONTEXT) {
|
||||
const sc_char *string;
|
||||
sc_int value;
|
||||
|
||||
// Get line, and scan for a single integer; return it
|
||||
string = readString();
|
||||
R0FUNC0(readString, string)
|
||||
if (sscanf(string, "%ld", &value) != 1) {
|
||||
sc_error("readInt: invalid integer at line %ld\n", ser_tasline - 1);
|
||||
longjmp(ser_tas_error, 1);
|
||||
LONG_JUMP0
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
sc_uint LoadSerializer::readUint() {
|
||||
sc_uint LoadSerializer::readUint(CONTEXT) {
|
||||
const sc_char *string;
|
||||
sc_uint value;
|
||||
|
||||
// Get line, and scan for a single integer; return it
|
||||
string = readString();
|
||||
R0FUNC0(readString, string)
|
||||
if (sscanf(string, "%lu", &value) != 1) {
|
||||
sc_error("readUint: invalid integer at line %ld\n", ser_tasline - 1);
|
||||
longjmp(ser_tas_error, 1);
|
||||
LONG_JUMP0
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
sc_bool LoadSerializer::readBool(void) {
|
||||
sc_bool LoadSerializer::readBool(CONTEXT) {
|
||||
const sc_char *string;
|
||||
sc_uint value;
|
||||
|
||||
// Get line, and scan for a single integer; check it's a valid-looking flag, and return it.
|
||||
string = readString();
|
||||
R0FUNC0(readString, string)
|
||||
string = readString(context);
|
||||
if (sscanf(string, "%lu", &value) != 1) {
|
||||
sc_error("readBool:"
|
||||
" invalid boolean at line %ld\n", ser_tasline - 1);
|
||||
longjmp(ser_tas_error, 1);
|
||||
sc_error("readBool: invalid boolean at line %ld\n", ser_tasline - 1);
|
||||
LONG_JUMP0
|
||||
}
|
||||
if (value != 0 && value != 1) {
|
||||
sc_error("readBool:"
|
||||
" warning: suspect boolean at line %ld\n", ser_tasline - 1);
|
||||
sc_error("readBool: warning: suspect boolean at line %ld\n", ser_tasline - 1);
|
||||
LONG_JUMP0
|
||||
}
|
||||
|
||||
return value != 0;
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "common/memstream.h"
|
||||
#include "common/str.h"
|
||||
#include "glk/adrift/scprotos.h"
|
||||
#include "glk/jumps.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Adrift {
|
||||
@ -102,32 +103,34 @@ private:
|
||||
sc_gameref_t _game;
|
||||
sc_read_callbackref_t _callback;
|
||||
void *_opaque;
|
||||
sc_tafref_t ser_tas;
|
||||
sc_int ser_tasline;
|
||||
private:
|
||||
/**
|
||||
* Reads a string
|
||||
*/
|
||||
const sc_char *readString();
|
||||
const sc_char *readString(CONTEXT);
|
||||
|
||||
/**
|
||||
* Read a signed integer
|
||||
*/
|
||||
sc_int readInt();
|
||||
sc_int readInt(CONTEXT);
|
||||
|
||||
/**
|
||||
* Read an unsigned integer
|
||||
*/
|
||||
sc_uint readUint();
|
||||
sc_uint readUint(CONTEXT);
|
||||
|
||||
/**
|
||||
* Read a boolean
|
||||
*/
|
||||
sc_bool readBool();
|
||||
sc_bool readBool(CONTEXT);
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
LoadSerializer(sc_gameref_t game, sc_read_callbackref_t callback, void *opaque) :
|
||||
_game(game), _callback(callback), _opaque(opaque) {
|
||||
_game(game), _callback(callback), _opaque(opaque), ser_tas(nullptr), ser_tasline(0) {
|
||||
assert(callback);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
*/
|
||||
|
||||
#include "glk/alan2/types.h"
|
||||
#include "glk/alan2/jumps.h"
|
||||
#include "glk/jumps.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan2 {
|
||||
|
@ -23,7 +23,7 @@
|
||||
#ifndef GLK_ALAN2_INTER
|
||||
#define GLK_ALAN2_INTER
|
||||
|
||||
#include "glk/alan2/jumps.h"
|
||||
#include "glk/jumps.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan2 {
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include "common/file.h"
|
||||
#include "glk/alan2/types.h"
|
||||
#include "glk/alan2/jumps.h"
|
||||
#include "glk/jumps.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan2 {
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
/* Parse data for ALAN interpreter module. */
|
||||
|
||||
#include "engines/glk/alan2/jumps.h"
|
||||
#include "engines/glk/jumps.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan2 {
|
||||
|
@ -20,15 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_ALAN2_JUMPS
|
||||
#define GLK_ALAN2_JUMPS
|
||||
#ifndef GLK_JUMPS
|
||||
#define GLK_JUMPS
|
||||
|
||||
/* This provides a simplified version of the ScummVM coroutines to allow for automated
|
||||
* breakouts to the main game loop from subroutinese rather than using unportable setjmps
|
||||
*/
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan2 {
|
||||
|
||||
/**
|
||||
* Context used for flagging when a break to the outer game loop
|
||||
@ -64,7 +63,6 @@ struct Context {
|
||||
#define LONG_JUMP { context._break = true; return; }
|
||||
#define LONG_JUMP0 { context._break = true; return 0; }
|
||||
|
||||
} // End of namespace Alan2
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user