TINSEL: Handle DW2 saves that had Noir-sized SysVars
With this change we now once again can read pre 2.6 savegames.
Similarly savegames created after this fix will be readable by
ScummVM < 2.6.0. We will also be able to load savegames created
by ScummVM 2.6.x.
The only limitation is that since we now create the same kind
of savegames as older versions again, ScummVM 2.6.x will be
unable to load savegames created after this.
This fixes bug #13897
(cherry picked from commit 366c1e505d
)
This commit is contained in:
parent
a8bbb5f07f
commit
21b72c5bc3
4 changed files with 50 additions and 19 deletions
|
@ -287,7 +287,7 @@ static void syncSoundReel(Common::Serializer &s, SOUNDREELS &sr) {
|
|||
s.syncAsSint32LE(sr.actorCol);
|
||||
}
|
||||
|
||||
static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp) {
|
||||
static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp, int numSystemVars) {
|
||||
s.syncAsUint32LE(sd.SavedSceneHandle);
|
||||
s.syncAsUint32LE(sd.SavedBgroundHandle);
|
||||
for (int i = 0; i < MAX_MOVERS; ++i)
|
||||
|
@ -335,7 +335,7 @@ static void syncSavedData(Common::Serializer &s, SAVED_DATA &sd, int numInterp)
|
|||
s.syncAsUint32LE(sd.SavedTune[i]);
|
||||
s.syncAsByte(sd.bTinselDim);
|
||||
s.syncAsSint32LE(sd.SavedScrollFocus);
|
||||
for (int i = 0; i < SV_TOPVALID; ++i)
|
||||
for (int i = 0; i < numSystemVars; ++i)
|
||||
s.syncAsSint32LE(sd.SavedSystemVars[i]);
|
||||
for (int i = 0; i < MAX_SOUNDREELS; ++i)
|
||||
syncSoundReel(s, sd.SavedSoundReels[i]);
|
||||
|
@ -447,7 +447,7 @@ char *ListEntry(int i, letype which) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static bool DoSync(Common::Serializer &s, int numInterp) {
|
||||
static bool DoSync(Common::Serializer &s, int numInterp, int numSystemVars) {
|
||||
int sg = 0;
|
||||
|
||||
if (TinselVersion >= 2) {
|
||||
|
@ -459,8 +459,7 @@ static bool DoSync(Common::Serializer &s, int numInterp) {
|
|||
_vm->_dialogs->holdItem(INV_NOICON);
|
||||
}
|
||||
|
||||
|
||||
syncSavedData(s, *g_srsd, numInterp);
|
||||
syncSavedData(s, *g_srsd, numInterp, numSystemVars);
|
||||
syncGlobInfo(s); // Glitter globals
|
||||
_vm->_dialogs->syncInvInfo(s); // Inventory data
|
||||
|
||||
|
@ -489,7 +488,7 @@ static bool DoSync(Common::Serializer &s, int numInterp) {
|
|||
if (*g_SaveSceneSsCount != 0) {
|
||||
SAVED_DATA *sdPtr = g_SaveSceneSsData;
|
||||
for (int i = 0; i < *g_SaveSceneSsCount; ++i, ++sdPtr)
|
||||
syncSavedData(s, *sdPtr, numInterp);
|
||||
syncSavedData(s, *sdPtr, numInterp, numSystemVars);
|
||||
|
||||
// Flag that there is a saved scene to return to. Note that in this context 'saved scene'
|
||||
// is a stored scene to return to from another scene, such as from the Summoning Book close-up
|
||||
|
@ -529,23 +528,52 @@ static bool DoRestore() {
|
|||
// for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames
|
||||
int numInterpreters = hdr.numInterpreters;
|
||||
int32 currentPos = f->pos();
|
||||
for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) {
|
||||
// If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
|
||||
int numberOfTries = ((hdr.ver >= 2) ? 1 : 2);
|
||||
int numSystemVars = SV_TOPVALID; // Appropriate for both Noir and DW2
|
||||
for (int tryNumber = 0; tryNumber < numberOfTries; ++tryNumber) {
|
||||
if (tryNumber == 1) {
|
||||
f->seek(currentPos);
|
||||
numInterpreters = 80;
|
||||
// If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts
|
||||
if (hdr.ver < 2) {
|
||||
numInterpreters = 80;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the savegame data
|
||||
if (DoSync(s, numInterpreters))
|
||||
bool successfullSync = DoSync(s, numInterpreters, numSystemVars);
|
||||
|
||||
uint32 id = f->readSint32LE();
|
||||
|
||||
int remaining = f->size() - f->pos();
|
||||
// BUG #13897: Older savegames won't run on ScummVM 2.6.x
|
||||
// The reason being that the system vars for Noir were added increasing the SV_TOPVALID value,
|
||||
// which also affected DW2 unintentionally, creating some v3 savegames that had additional data
|
||||
// stored there. To properly load these savegames, we'll have to try again, with the knowledge
|
||||
// that we have to skip this data.
|
||||
if (hdr.id == DW2_SAVEGAME_ID && hdr.ver == 3 && remaining != 0) {
|
||||
if (tryNumber == 0) {
|
||||
numberOfTries++;
|
||||
// Make the second attempt read the Noir amount of sys-vars, so that the problematic
|
||||
// savegames can be loaded.
|
||||
numSystemVars = SV_TOPVALID_T3;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (successfullSync) {
|
||||
if (id != (uint32)0xFEEDFACE) {
|
||||
error("Incompatible saved game");
|
||||
}
|
||||
|
||||
// Data load was successful (or likely), so break out of loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 id = f->readSint32LE();
|
||||
if (id != (uint32)0xFEEDFACE)
|
||||
error("Incompatible saved game");
|
||||
|
||||
int remainingBytes = f->size() - f->pos();
|
||||
if (remainingBytes != 0) {
|
||||
error("%d bytes of savegame not read", remainingBytes);
|
||||
}
|
||||
bool failed = (f->eos() || f->err());
|
||||
|
||||
delete f;
|
||||
|
@ -628,7 +656,7 @@ static void DoSave() {
|
|||
return;
|
||||
}
|
||||
|
||||
DoSync(s, hdr.numInterpreters);
|
||||
DoSync(s, hdr.numInterpreters, SV_TOPVALID);
|
||||
|
||||
// Write out the special Id for Discworld savegames
|
||||
f->writeUint32LE(0xFEEDFACE);
|
||||
|
|
|
@ -59,7 +59,7 @@ struct SAVED_DATA {
|
|||
uint32 SavedTune[3]; // Music
|
||||
bool bTinselDim;
|
||||
int SavedScrollFocus;
|
||||
int SavedSystemVars[SV_TOPVALID];
|
||||
int SavedSystemVars[SV_TOPVALID_T3];
|
||||
SOUNDREELS SavedSoundReels[MAX_SOUNDREELS];
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ extern int NewestSavedGame();
|
|||
|
||||
// These vars are reset upon engine destruction
|
||||
|
||||
static int g_systemVars[SV_TOPVALID];
|
||||
static int g_systemVars[SV_TOPVALID_T3];
|
||||
static SCNHANDLE g_systemStrings[SS_MAX_VALID];
|
||||
|
||||
//----------------- FUNCTIONS --------------------------------
|
||||
|
@ -57,7 +57,7 @@ void ResetVarsSysVar() {
|
|||
*/
|
||||
|
||||
void InitSysVars() {
|
||||
int initialSystemVars[SV_TOPVALID] = {
|
||||
int initialSystemVars[SV_TOPVALID_T3] = {
|
||||
INV_1, // Default inventory
|
||||
|
||||
10, // Y-offset of Conversation(TOP)
|
||||
|
|
|
@ -83,6 +83,8 @@ typedef enum { SV_DEFAULT_INV,
|
|||
ISV_GHOST_BASE_T2 = 0x2B,
|
||||
ISV_GHOST_COLOR_T2 = 0x2C,
|
||||
|
||||
SV_TOPVALID_T2 = 0x2D,
|
||||
|
||||
SV_SPRITER_SCENE_ID = 0x2F, // Noir, loaded scene
|
||||
ISV_DIVERT_ACTOR_T3 = 0x32,
|
||||
ISV_NO_BLOCKING_T3 = 0x33,
|
||||
|
@ -92,13 +94,14 @@ typedef enum { SV_DEFAULT_INV,
|
|||
SV_SPRITER_SCALE = 0x37, // Noir, scale used for 3D rendering
|
||||
SV_SPRITER_OVERLAY = 0x38, // Noir, if additional model is loaded
|
||||
|
||||
SV_TOPVALID } SYSVARS;
|
||||
SV_TOPVALID_T3 } SYSVARS;
|
||||
|
||||
#define ISV_DIVERT_ACTOR ((TinselVersion == 3) ? ISV_DIVERT_ACTOR_T3 : ISV_DIVERT_ACTOR_T2)
|
||||
#define ISV_NO_BLOCKING ((TinselVersion == 3) ? ISV_NO_BLOCKING_T3 : ISV_NO_BLOCKING_T2)
|
||||
#define ISV_GHOST_ACTOR ((TinselVersion == 3) ? ISV_GHOST_ACTOR_T3 : ISV_GHOST_ACTOR_T2)
|
||||
#define ISV_GHOST_BASE ((TinselVersion == 3) ? ISV_GHOST_BASE_T3 : ISV_GHOST_BASE_T2)
|
||||
#define ISV_GHOST_COLOR ((TinselVersion == 3) ? ISV_GHOST_COLOR_T3 : ISV_GHOST_COLOR_T2)
|
||||
#define SV_TOPVALID ((TinselVersion == 3) ? SV_TOPVALID_T3 : SV_TOPVALID_T2)
|
||||
|
||||
typedef enum {
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue