AGS: Engine: support fixing up RoomStatuses restored from older save formats

This allows to properly fixup (upgrade) RoomStatus structs
restored from a older format save in an upgraded game.
For example: game was upgraded from 3.4.0 to 3.6.0, but player
tries loading 3.4.0 save. In this situation the engine must know and
keep track of what version each RoomStatus was saved in.
This is necessary, because RoomStatus can only be updated upon
loading a corresponding room file (when entering a room). This
means that multiple RoomStatus objects will be staying in memory
not being upgraded yet, until player enters particular room.

From upstream 205232af5909e0a257494ff404f36003b004ebc4
This commit is contained in:
Walter Agazzi 2023-03-03 19:40:51 +01:00 committed by Thierry Crozat
parent 2daaef2d48
commit 9a469f1e2b
4 changed files with 45 additions and 11 deletions

View File

@ -533,7 +533,7 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
_GP(thisroom).LocalVariables[i].Value = _G(croom)->interactionVariableValues[i];
// Always copy object and hotspot names for < 3.6.0 games, because they were not settable
if (_G(loaded_game_file_version) < kGameVersion_360_16) {
if ((_G(loaded_game_file_version) < kGameVersion_360_16) || (_G(croom)->contentFormat < kRoomStatSvgVersion_36025)) {
for (size_t cc = 0; cc < _GP(thisroom).Objects.size(); ++cc)
_G(croom)->obj[cc].name = _GP(thisroom).Objects[cc].Name;
for (int cc = 0; cc < MAX_ROOM_HOTSPOTS; cc++)
@ -597,6 +597,8 @@ void load_new_room(int newnum, CharacterInfo *forchar) {
_G(croom)->beenhere = 1;
_G(in_new_room) = 2;
}
// Reset contentFormat hint to avoid doing fixups later
_G(croom)->contentFormat = kRoomStatSvgVersion_Current;
if (_GP(thisroom).EventHandlers == nullptr) {
// legacy interactions

View File

@ -46,6 +46,7 @@ void HotspotState::WriteToSavegame(Shared::Stream *out) const {
}
RoomStatus::RoomStatus() {
contentFormat = kRoomStatSvgVersion_Current; // set current to avoid fixups
beenhere = 0;
numobj = 0;
tsdatasize = 0;
@ -74,6 +75,7 @@ void RoomStatus::ReadFromFile_v321(Stream *in) {
FreeScriptData();
FreeProperties();
contentFormat = kRoomStatSvgVersion_Initial;
beenhere = in->ReadInt32();
numobj = in->ReadInt32();
obj.resize(MAX_ROOM_OBJECTS_v300);
@ -120,7 +122,7 @@ void RoomStatus::ReadRoomObjects_Aligned(Shared::Stream *in) {
}
}
void RoomStatus::ReadFromSavegame(Stream *in, int save_ver) {
void RoomStatus::ReadFromSavegame(Stream *in, RoomStatSvgVersion save_ver) {
FreeScriptData();
FreeProperties();
@ -161,6 +163,14 @@ void RoomStatus::ReadFromSavegame(Stream *in, int save_ver) {
tsdata.resize(tsdatasize);
in->Read(tsdata.data(), tsdatasize);
}
contentFormat = save_ver;
if (save_ver >= kRoomStatSvgVersion_36041) {
contentFormat = (RoomStatSvgVersion)in->ReadInt32();
in->ReadInt32(); // reserved
in->ReadInt32();
in->ReadInt32();
}
}
void RoomStatus::WriteToSavegame(Stream *out) const {
@ -196,6 +206,12 @@ void RoomStatus::WriteToSavegame(Stream *out) const {
out->WriteInt32(static_cast<int32_t>(tsdatasize));
if (tsdatasize)
out->Write(tsdata.data(), tsdatasize);
// kRoomStatSvgVersion_36041
out->WriteInt32(contentFormat);
out->WriteInt32(0); // reserved
out->WriteInt32(0);
out->WriteInt32(0);
}
// JJS: Replacement for the global roomstats array in the original engine.

View File

@ -23,6 +23,7 @@
#define AGS_ENGINE_AC_ROOM_STATUS_H
#include "ags/engine/ac/room_object.h"
#include "ags/engine/game/savegame.h"
#include "ags/shared/game/room_struct.h"
#include "ags/shared/game/interactions.h"
#include "ags/shared/util/string_types.h"
@ -47,8 +48,16 @@ struct HotspotState {
void WriteToSavegame(Shared::Stream *out) const;
};
// This struct is saved in the save games - it contains everything about
// a room that could change
// Savegame data format for RoomStatus
// TODO: fill in other versions (lookup the code history)
enum RoomStatSvgVersion {
kRoomStatSvgVersion_Initial = 0,
kRoomStatSvgVersion_36025 = 3,
kRoomStatSvgVersion_36041 = 4,
kRoomStatSvgVersion_Current = kRoomStatSvgVersion_36041
};
// RoomStatus contains everything about a room that could change at runtime.
struct RoomStatus {
int beenhere = 0;
uint32_t numobj = 0;
@ -76,6 +85,13 @@ struct RoomStatus {
EventBlock misccond;
#endif
// A version of a save this RoomStatus was restored from.
// This is used as a hint when merging RoomStatus with the loaded room file (upon room enter).
// We need this for cases when an old format save is restored within an upgraded game
// (for example, game was upgraded from 3.4.0 to 3.6.0, but player tries loading 3.4.0 save),
// because room files are only loaded once entered, so we cannot fixup all RoomStatuses at once.
RoomStatSvgVersion contentFormat;
RoomStatus();
~RoomStatus();
@ -84,7 +100,7 @@ struct RoomStatus {
void ReadFromFile_v321(Shared::Stream *in);
void ReadRoomObjects_Aligned(Shared::Stream *in);
void ReadFromSavegame(Shared::Stream *in, int save_ver);
void ReadFromSavegame(Shared::Stream *in, RoomStatSvgVersion save_ver);
void WriteToSavegame(Shared::Stream *out) const;
};

View File

@ -883,7 +883,7 @@ HSaveError ReadRoomStates(Stream *in, int32_t cmp_ver, const PreservedParams & /
if (!AssertFormatTagStrict(err, in, "RoomState", true))
return err;
RoomStatus *roomstat = getRoomStatus(id);
roomstat->ReadFromSavegame(in, cmp_ver);
roomstat->ReadFromSavegame(in, (RoomStatSvgVersion)cmp_ver);
if (!AssertFormatTagStrict(err, in, "RoomState", false))
return err;
}
@ -977,7 +977,7 @@ HSaveError ReadThisRoom(Stream *in, int32_t cmp_ver, const PreservedParams & /*p
// read the current troom state, in case they saved in temporary room
if (!in->ReadBool())
_GP(troom).ReadFromSavegame(in, cmp_ver);
_GP(troom).ReadFromSavegame(in, (RoomStatSvgVersion)cmp_ver);
return HSaveError::None();
}
@ -1110,15 +1110,15 @@ struct ComponentHandlers {
},
{
"Room States",
3,
0,
kRoomStatSvgVersion_36041,
kRoomStatSvgVersion_Initial,
WriteRoomStates,
ReadRoomStates
},
{
"Loaded Room State",
3, // must correspond to "Room States"
0,
kRoomStatSvgVersion_36041, // must correspond to "Room States"
kRoomStatSvgVersion_Initial,
WriteThisRoom,
ReadThisRoom
},