- added a pseudo-serializer for FLevelLocals pointers.

This doesn't really write out any info for the pointer, if the level does not match it just errors out.
This is both for quick detection of badly used level data and for automatic restoring of the pointer from the serializer's working level.

This also removed the temporary workarounds in DAutomap and DLevelScript to restore these pointers when a savegame is loaded.
This commit is contained in:
Christoph Oelckers 2019-01-26 21:23:19 +01:00
parent e5139cc325
commit 9b1b6db85d
12 changed files with 152 additions and 90 deletions

View File

@ -3205,7 +3205,8 @@ void DAutomap::Serialize(FSerializer &arc)
("max_h", max_h)
("min_scale_mtof", min_scale_mtof)
("max_scale_mtof", max_scale_mtof)
("mapback", mapback);
("mapback", mapback)
("level", Level);
}

View File

@ -1811,7 +1811,7 @@ void G_DoLoadGame ()
SaveVersion = 0;
void *data = info->CacheLump();
FSerializer arc;
FSerializer arc(nullptr);
if (!arc.OpenReader((const char *)data, info->LumpSize))
{
Printf("Failed to access savegame info\n");
@ -2129,7 +2129,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
insave = true;
try
{
G_SnapshotLevel();
level.SnapshotLevel();
}
catch(CRecoverableError &err)
{
@ -2152,8 +2152,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
}
BufferWriter savepic;
FSerializer savegameinfo; // this is for displayable info about the savegame
FSerializer savegameglobals; // and this for non-level related info that must be saved.
FSerializer savegameinfo(nullptr); // this is for displayable info about the savegame
FSerializer savegameglobals(nullptr); // and this for non-level related info that must be saved.
savegameinfo.OpenWriter(true);
savegameglobals.OpenWriter(save_formatted);

View File

@ -856,7 +856,7 @@ void G_DoCompleted (void)
{ // Remember the level's state for re-entry.
if (!(level.flags2 & LEVEL2_FORGETSTATE))
{
G_SnapshotLevel ();
level.SnapshotLevel ();
// Do not free any global strings this level might reference
// while it's not loaded.
level.Behaviors.LockLevelVarStrings(level.levelnum);
@ -1060,7 +1060,7 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
level.starttime = gametic;
G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
level.UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
int pnumerr = G_FinishTravel ();
if (!level.FromSnapshot)
@ -1109,7 +1109,7 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
E_WorldLoadedUnsafe();
// regular world load (savegames are handled internally)
E_WorldLoaded();
P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
level.DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL)
C_HideConsole ();
@ -1647,19 +1647,19 @@ void G_AirControlChanged ()
//
//==========================================================================
void G_SnapshotLevel ()
void FLevelLocals::SnapshotLevel ()
{
level.info->Snapshot.Clean();
info->Snapshot.Clean();
if (level.info->isValid())
if (info->isValid())
{
FSerializer arc;
FSerializer arc(this);
if (arc.OpenWriter(save_formatted))
{
SaveVersion = SAVEVER;
level.Serialize(arc, false);
level.info->Snapshot = arc.GetCompressedOutput();
Serialize(arc, false);
info->Snapshot = arc.GetCompressedOutput();
}
}
}
@ -1671,24 +1671,24 @@ void G_SnapshotLevel ()
//
//==========================================================================
void G_UnSnapshotLevel (bool hubLoad)
void FLevelLocals::UnSnapshotLevel (bool hubLoad)
{
if (level.info->Snapshot.mBuffer == nullptr)
if (info->Snapshot.mBuffer == nullptr)
return;
if (level.info->isValid())
if (info->isValid())
{
FSerializer arc;
if (!arc.OpenReader(&level.info->Snapshot))
FSerializer arc(this);
if (!arc.OpenReader(&info->Snapshot))
{
I_Error("Failed to load savegame");
return;
}
level.Serialize (arc, hubLoad);
level.FromSnapshot = true;
Serialize (arc, hubLoad);
FromSnapshot = true;
TThinkerIterator<AActor> it(NAME_PlayerPawn);
auto it = GetThinkerIterator<AActor>(NAME_PlayerPawn);
AActor *pawn, *next;
next = it.Next();
@ -1716,11 +1716,11 @@ void G_UnSnapshotLevel (bool hubLoad)
arc.Close();
}
// No reason to keep the snapshot around once the level's been entered.
level.info->Snapshot.Clean();
info->Snapshot.Clean();
if (hubLoad)
{
// Unlock ACS global strings that were locked when the snapshot was made.
level.Behaviors.UnlockLevelVarStrings(level.levelnum);
Behaviors.UnlockLevelVarStrings(level.levelnum);
}
}

View File

@ -490,8 +490,6 @@ void G_ParseMapInfo (FString basemapinfo);
void G_ClearSnapshots (void);
void P_RemoveDefereds ();
void G_SnapshotLevel (void);
void G_UnSnapshotLevel (bool keepPlayers);
void G_ReadSnapshots (FResourceFile *);
void G_WriteSnapshots (TArray<FString> &, TArray<FCompressedBuffer> &);
void G_WriteVisited(FSerializer &arc);

View File

@ -186,6 +186,9 @@ private:
void AddDisplacementForPortal(FLinePortal *portal);
bool ConnectPortalGroups();
public:
void SnapshotLevel();
void UnSnapshotLevel(bool hubLoad);
void FinalizePortals();
bool ChangePortal(line_t *ln, int thisid, int destid);
unsigned GetSkyboxPortal(AActor *actor);
@ -198,6 +201,7 @@ public:
bool CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush);
void ActivateInStasisCeiling(int tag);
bool CreateFloor(sector_t *sec, DFloor::EFloor floortype, line_t *line, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower);
void DoDeferedScripts();
bool EV_DoPlat(int tag, line_t *line, DPlat::EPlatType type, double height, double speed, int delay, int lip, int change);
void EV_StopPlat(int tag, bool remove);

View File

@ -180,7 +180,7 @@ void FSavegameManager::ReadSaveStrings()
continue;
}
void *data = info->CacheLump();
FSerializer arc;
FSerializer arc(nullptr);
if (arc.OpenReader((const char *)data, info->LumpSize))
{
int savever = 0;
@ -469,7 +469,7 @@ unsigned FSavegameManager::ExtractSaveData(int index)
return index;
}
void *data = info->CacheLump();
FSerializer arc;
FSerializer arc(nullptr);
if (arc.OpenReader((const char *)data, info->LumpSize))
{
FString time, pcomment, comment;

View File

@ -671,9 +671,8 @@ public:
SCRIPT_ModulusBy0,
};
DLevelScript(AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
DLevelScript(FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags);
~DLevelScript();
void Serialize(FSerializer &arc);
int RunScript();
@ -780,7 +779,7 @@ private:
friend class DACSThinker;
};
static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
static DLevelScript *P_GetScriptGoing (FLevelLocals *Level, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags);
@ -3264,11 +3263,10 @@ void FBehavior::StartTypedScripts (uint16_t type, AActor *activator, bool always
ptr = &Scripts[i];
if (ptr->Type == type)
{
DLevelScript *runningScript = P_GetScriptGoing (activator, NULL, ptr->Number,
DLevelScript *runningScript = P_GetScriptGoing (&level, activator, NULL, ptr->Number,
ptr, this, &arg1, 1, always ? ACS_ALWAYS : 0);
if (nullptr != runningScript && runNow)
{
runningScript->Level = &level;
runningScript->RunScript();
}
}
@ -3389,7 +3387,6 @@ void DACSThinker::Tick ()
while (script)
{
DLevelScript *next = script->next;
script->Level = &level;
script->RunScript();
script = next;
}
@ -3460,11 +3457,12 @@ void DLevelScript::Serialize(FSerializer &arc)
("cliprectwidth", ClipRectWidth)
("cliprectheight", ClipRectHeight)
("wrapwidth", WrapWidth)
("inmodulescriptnum", InModuleScriptNumber);
("inmodulescriptnum", InModuleScriptNumber)
("level", Level);
if (arc.isReading())
{
activeBehavior = level.Behaviors.GetModule(lib);
activeBehavior = Level->Behaviors.GetModule(lib);
if (nullptr == activeBehavior)
{
@ -3477,19 +3475,12 @@ void DLevelScript::Serialize(FSerializer &arc)
DLevelScript::DLevelScript ()
{
next = prev = nullptr;
if (level.ACSThinker == nullptr)
level.ACSThinker = Create<DACSThinker>();
activefont = SmallFont;
}
DLevelScript::~DLevelScript ()
{
}
void DLevelScript::Unlink ()
{
DACSThinker *controller = level.ACSThinker;
DACSThinker *controller = Level->ACSThinker;
if (controller->LastScript == this)
{
@ -3515,7 +3506,7 @@ void DLevelScript::Unlink ()
void DLevelScript::Link ()
{
DACSThinker *controller = level.ACSThinker;
DACSThinker *controller = Level->ACSThinker;
next = controller->Scripts;
GC::WriteBarrier(this, next);
@ -3535,7 +3526,7 @@ void DLevelScript::Link ()
void DLevelScript::PutLast ()
{
DACSThinker *controller = level.ACSThinker;
DACSThinker *controller = Level->ACSThinker;
if (controller->LastScript == this)
return;
@ -3557,7 +3548,7 @@ void DLevelScript::PutLast ()
void DLevelScript::PutFirst ()
{
DACSThinker *controller = level.ACSThinker;
DACSThinker *controller = Level->ACSThinker;
if (controller->Scripts == this)
return;
@ -10218,10 +10209,10 @@ scriptwait:
#undef PushtoStack
static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
static DLevelScript *P_GetScriptGoing (FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags)
{
DACSThinker *controller = level.ACSThinker;
DACSThinker *controller = l->ACSThinker;
DLevelScript **running;
if (controller && !(flags & ACS_ALWAYS) && (running = controller->RunningScripts.CheckKey(num)) != NULL)
@ -10234,15 +10225,16 @@ static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, cons
return NULL;
}
return Create<DLevelScript> (who, where, num, code, module, args, argcount, flags);
return Create<DLevelScript> (l, who, where, num, code, module, args, argcount, flags);
}
DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
DLevelScript::DLevelScript (FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags)
: activeBehavior (module)
{
if (level.ACSThinker == nullptr)
level.ACSThinker = Create<DACSThinker>();
Level = l;
if (Level->ACSThinker == nullptr)
Level->ACSThinker = Create<DACSThinker>();
script = num;
assert(code->VarCount >= code->ArgCount);
@ -10270,11 +10262,11 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
// goes by while they're in their default state.
if (!(flags & ACS_ALWAYS))
level.ACSThinker->RunningScripts[num] = this;
Level->ACSThinker->RunningScripts[num] = this;
Link();
if (level.flags2 & LEVEL2_HEXENHACK)
if (Level->flags2 & LEVEL2_HEXENHACK)
{
PutLast();
}
@ -10282,9 +10274,8 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
DPrintf(DMSG_SPAMMY, "%s started.\n", ScriptPresentation(num).GetChars());
}
static void SetScriptState (int script, DLevelScript::EScriptState state)
void SetScriptState (DACSThinker *controller, int script, DLevelScript::EScriptState state)
{
DACSThinker *controller = level.ACSThinker;
DLevelScript **running;
if (controller != NULL && (running = controller->RunningScripts.CheckKey(script)) != NULL)
@ -10293,23 +10284,23 @@ static void SetScriptState (int script, DLevelScript::EScriptState state)
}
}
void P_DoDeferedScripts ()
void FLevelLocals::DoDeferedScripts ()
{
const ScriptPtr *scriptdata;
FBehavior *module;
// Handle defered scripts in this step, too
for(int i = level.info->deferred.Size()-1; i>=0; i--)
for(int i = info->deferred.Size()-1; i>=0; i--)
{
acsdefered_t *def = &level.info->deferred[i];
acsdefered_t *def = &info->deferred[i];
switch (def->type)
{
case acsdefered_t::defexecute:
case acsdefered_t::defexealways:
scriptdata = level.Behaviors.FindScript (def->script, module);
scriptdata = Behaviors.FindScript (def->script, module);
if (scriptdata)
{
P_GetScriptGoing ((unsigned)def->playernum < MAXPLAYERS &&
P_GetScriptGoing (this, (unsigned)def->playernum < MAXPLAYERS &&
playeringame[def->playernum] ? players[def->playernum].mo : NULL,
NULL, def->script,
scriptdata, module,
@ -10323,17 +10314,17 @@ void P_DoDeferedScripts ()
break;
case acsdefered_t::defsuspend:
SetScriptState (def->script, DLevelScript::SCRIPT_Suspended);
SetScriptState (ACSThinker, def->script, DLevelScript::SCRIPT_Suspended);
DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars());
break;
case acsdefered_t::defterminate:
SetScriptState (def->script, DLevelScript::SCRIPT_PleaseRemove);
SetScriptState (ACSThinker, def->script, DLevelScript::SCRIPT_PleaseRemove);
DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars());
break;
}
}
level.info->deferred.Clear();
info->deferred.Clear();
}
static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who)
@ -10392,13 +10383,12 @@ int P_StartScript (AActor *who, line_t *where, int script, const char *map, cons
return false;
}
}
DLevelScript *runningScript = P_GetScriptGoing (who, where, script,
DLevelScript *runningScript = P_GetScriptGoing (&level, who, where, script,
scriptdata, module, args, argcount, flags);
if (runningScript != NULL)
{
if (flags & ACS_WANTRESULT)
{
runningScript->Level = &level;
return runningScript->RunScript();
}
return true;
@ -10428,7 +10418,7 @@ void P_SuspendScript (int script, const char *map)
if (strnicmp (level.MapName, map, 8))
addDefered (FindLevelInfo (map), acsdefered_t::defsuspend, script, NULL, 0, NULL);
else
SetScriptState (script, DLevelScript::SCRIPT_Suspended);
SetScriptState (level.ACSThinker, script, DLevelScript::SCRIPT_Suspended);
}
void P_TerminateScript (int script, const char *map)
@ -10436,7 +10426,7 @@ void P_TerminateScript (int script, const char *map)
if (strnicmp (level.MapName, map, 8))
addDefered (FindLevelInfo (map), acsdefered_t::defterminate, script, NULL, 0, NULL);
else
SetScriptState (script, DLevelScript::SCRIPT_PleaseRemove);
SetScriptState (level.ACSThinker, script, DLevelScript::SCRIPT_PleaseRemove);
}
FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def)
@ -10515,11 +10505,11 @@ void ACSProfileInfo::AddRun(unsigned int num_instr)
}
}
void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles)
void FBehaviorContainer::ArrangeScriptProfiles(TArray<ProfileCollector> &profiles)
{
for (unsigned int mod_num = 0; mod_num < level.Behaviors.StaticModules.Size(); ++mod_num)
for (unsigned int mod_num = 0; mod_num < StaticModules.Size(); ++mod_num)
{
FBehavior *module = level.Behaviors.StaticModules[mod_num];
FBehavior *module = StaticModules[mod_num];
ProfileCollector prof;
prof.Module = module;
for (int i = 0; i < module->NumScripts; ++i)
@ -10531,11 +10521,11 @@ void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles)
}
}
void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles)
void FBehaviorContainer::ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles)
{
for (unsigned int mod_num = 0; mod_num < level.Behaviors.StaticModules.Size(); ++mod_num)
for (unsigned int mod_num = 0; mod_num < StaticModules.Size(); ++mod_num)
{
FBehavior *module = level.Behaviors.StaticModules[mod_num];
FBehavior *module = StaticModules[mod_num];
ProfileCollector prof;
prof.Module = module;
for (int i = 0; i < module->NumFunctions; ++i)
@ -10692,8 +10682,8 @@ CCMD(acsprofile)
assert(countof(sort_names) == countof(sort_match_len));
ArrangeScriptProfiles(ScriptProfiles);
ArrangeFunctionProfiles(FuncProfiles);
level.Behaviors.ArrangeScriptProfiles(ScriptProfiles);
level.Behaviors.ArrangeFunctionProfiles(FuncProfiles);
if (argv.argc() > 1)
{

View File

@ -420,8 +420,6 @@ private:
void MarkMapVarStrings() const;
void LockMapVarStrings(int levelnum) const;
friend void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles);
friend void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles);
friend struct FBehaviorContainer;
};
@ -443,6 +441,8 @@ struct FBehaviorContainer
const char *LookupString(uint32_t index);
void StartTypedScripts(uint16_t type, AActor *activator, bool always, int arg1 = 0, bool runNow = false);
void StopMyScripts(AActor *actor);
void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles);
void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles);
};

View File

@ -1023,7 +1023,6 @@ void FLevelLocals::Serialize(FSerializer &arc, bool hubload)
AActor::RecreateAllAttachedLights();
InitPortalGroups(this);
automap->Level = this; // Temporary workaround. At the moment this cannot be deserialized yet.
automap->UpdateShowAllLines();
}

View File

@ -630,7 +630,6 @@ bool EV_TeleportSector (int tag, int source_tid, int dest_tid, bool fog, int gro
int P_StartScript (AActor *who, line_t *where, int script, const char *map, const int *args, int argcount, int flags);
void P_SuspendScript (int script, const char *map);
void P_TerminateScript (int script, const char *map);
void P_DoDeferedScripts (void);
//
// [RH] p_quake.c

View File

@ -1450,27 +1450,32 @@ FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval)
{
return SerializePointer(arc, key, value, defval, level.Polyobjects.Data());
if (arc.Level == nullptr) I_Error("Trying to serialize polyobject without a valid level");
return SerializePointer(arc, key, value, defval, arc.Level->Polyobjects.Data());
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval)
{
return SerializePointer<const FPolyObj>(arc, key, value, defval, level.Polyobjects.Data());
if (arc.Level == nullptr) I_Error("Trying to serialize polyobject without a valid level");
return SerializePointer<const FPolyObj>(arc, key, value, defval, arc.Level->Polyobjects.Data());
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval)
{
return SerializePointer(arc, key, value, defval, &level.sides[0]);
if (arc.Level == nullptr) I_Error("Trying to serialize SIDEDEF without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->sides[0]);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval)
{
return SerializePointer(arc, key, value, defval, &level.sectors[0]);
if (arc.Level == nullptr) I_Error("Trying to serialize sector without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->sectors[0]);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval)
{
return SerializePointer<const sector_t>(arc, key, value, defval, &level.sectors[0]);
if (arc.Level == nullptr) I_Error("Trying to serialize sector without a valid level");
return SerializePointer<const sector_t>(arc, key, value, defval, &arc.Level->sectors[0]);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval)
@ -1480,12 +1485,14 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&
template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval)
{
return SerializePointer(arc, key, value, defval, &level.lines[0]);
if (arc.Level == nullptr) I_Error("Trying to serialize linedef without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->lines[0]);
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval)
{
return SerializePointer(arc, key, value, defval, &level.vertexes[0]);
if (arc.Level == nullptr) I_Error("Trying to serialize verte without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->vertexes[0]);
}
//==========================================================================
@ -1968,13 +1975,13 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial
}
else if (val->IsUint())
{
if (val->GetUint() >= level.StrifeDialogues.Size())
if (val->GetUint() >= arc.Level->StrifeDialogues.Size())
{
node = nullptr;
}
else
{
node = level.StrifeDialogues[val->GetUint()];
node = arc.Level->StrifeDialogues[val->GetUint()];
}
}
else
@ -2128,6 +2135,65 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr
return arc;
}
//==========================================================================
//
// This is a bit of a cheat because it never actually writes out the pointer.
// The rules for levels are that they must be self-contained.
// No level and no pbject that is part of a level may reference a different level.
//
// When writing, this merely checks if the rules are obeyed and if not errors out.
// When reading, it assumes that the object was properly written and restores
// the reference from the owning level.
//
// The only exception are null pointers which are allowed
//
//==========================================================================
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FLevelLocals *&lev, FLevelLocals **def)
{
if (arc.isWriting())
{
if (!arc.w->inObject() || lev == nullptr)
{
arc.WriteKey(key);
if (lev == nullptr)
{
arc.w->Null();
}
else
{
// This MUST be the currently serialized level. Anything else will error out here as a sanity check.
if (arc.Level == nullptr || lev != arc.Level)
{
I_Error("Attempt to serialize invalid level reference");
}
if (!arc.w->inObject())
{
arc.w->Bool(true); // In the unlikely case this is used in an array, write a filler.
}
}
}
}
else
{
auto val = arc.r->FindKey(key);
if (val != nullptr)
{
assert(val->IsNull() || val->IsBool());
if (val->IsNull())
{
lev = nullptr;
}
else
{
lev = arc.Level;
}
}
else lev = arc.Level;
}
return arc;
}
//==========================================================================
//
//

View File

@ -65,6 +65,7 @@ class FSerializer
public:
FWriter *w = nullptr;
FReader *r = nullptr;
FLevelLocals *Level;
unsigned ArraySize();
void WriteKey(const char *key);
@ -72,6 +73,9 @@ public:
public:
FSerializer(FLevelLocals *l) : Level(l)
{}
~FSerializer()
{
mErrors = 0; // The destructor may not throw an exception so silence the error checker.
@ -262,6 +266,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimation *&pstr, FDoorAnimation **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FLevelLocals *&font, FLevelLocals **def);
FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode);
template<> inline FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def)