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)