From 9b1b6db85d73e84c4c445920d4c7f09c51282bff Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 26 Jan 2019 21:23:19 +0100 Subject: [PATCH] - 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. --- src/am_map.cpp | 3 +- src/g_game.cpp | 8 ++-- src/g_level.cpp | 38 ++++++++--------- src/g_level.h | 2 - src/g_levellocals.h | 4 ++ src/menu/loadsavemenu.cpp | 4 +- src/p_acs.cpp | 88 +++++++++++++++++---------------------- src/p_acs.h | 4 +- src/p_saveg.cpp | 1 - src/p_spec.h | 1 - src/serializer.cpp | 84 +++++++++++++++++++++++++++++++++---- src/serializer.h | 5 +++ 12 files changed, 152 insertions(+), 90 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 3116dc5be..8d0fbecfe 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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); } diff --git a/src/g_game.cpp b/src/g_game.cpp index 6c1f32227..cfc8c3789 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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); diff --git a/src/g_level.cpp b/src/g_level.cpp index f2e935700..acb6eaec6 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -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 it(NAME_PlayerPawn); + auto it = GetThinkerIterator(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); } } diff --git a/src/g_level.h b/src/g_level.h index 4c7a91dc1..5e42fcc44 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -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 &, TArray &); void G_WriteVisited(FSerializer &arc); diff --git a/src/g_levellocals.h b/src/g_levellocals.h index b9787f45c..feaa54128 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -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); diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 6361e8e4f..e023f1f88 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -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; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 0cdaad45b..3f61326b6 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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(); 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 (who, where, num, code, module, args, argcount, flags); + return Create (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(); + Level = l; + if (Level->ACSThinker == nullptr) + Level->ACSThinker = Create(); 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 &profiles) +void FBehaviorContainer::ArrangeScriptProfiles(TArray &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 &profiles) } } -void ArrangeFunctionProfiles(TArray &profiles) +void FBehaviorContainer::ArrangeFunctionProfiles(TArray &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) { diff --git a/src/p_acs.h b/src/p_acs.h index f3c724353..21cb61e62 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -420,8 +420,6 @@ private: void MarkMapVarStrings() const; void LockMapVarStrings(int levelnum) const; - friend void ArrangeScriptProfiles(TArray &profiles); - friend void ArrangeFunctionProfiles(TArray &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 &profiles); + void ArrangeFunctionProfiles(TArray &profiles); }; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 689ebc750..56ac0f6c8 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -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(); } diff --git a/src/p_spec.h b/src/p_spec.h index 9fa04582d..9de805f43 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -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 diff --git a/src/serializer.cpp b/src/serializer.cpp index 3f390bf1c..94bb57f78 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -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(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, 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(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, 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; +} + //========================================================================== // // diff --git a/src/serializer.h b/src/serializer.h index 7f8af9b8b..d7d2a1e1a 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -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)