diff --git a/CMakeLists.txt b/CMakeLists.txt index f1edbca84..f3d10f78e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ function( add_pk3 PK3_NAME PK3_DIR ) # Phase 1: Create a list of all source files for this PK3 archive, except # for a couple of strife image file names that confuse CMake. file(GLOB_RECURSE PK3_SRCS ${PK3_DIR}/*) - # Exclude from the source list some gzdoom .png files with brackets in the + # Exclude from the source list some files with brackets in the # file names here, because they confuse CMake. # This only affects the list of source files shown in the IDE. # It does not actually remove the files from the PK3 archive. @@ -203,9 +203,9 @@ if( MSVC ) # Function-level linking # Disable run-time type information if ( HAVE_VULKAN ) - set( ALL_C_FLAGS "/GF /Gy /GR- /permissive- /DHAVE_VULKAN" ) + set( ALL_C_FLAGS "/GF /Gy /permissive- /DHAVE_VULKAN" ) else() - set( ALL_C_FLAGS "/GF /Gy /GR- /permissive-" ) + set( ALL_C_FLAGS "/GF /Gy /permissive-" ) endif() # Use SSE 2 as minimum always as the true color drawers needs it for __vectorcall @@ -238,7 +238,6 @@ if( MSVC ) string(REPLACE "/MD " " " CMAKE_C_FLAGS_MINSIZEREL ${CMAKE_C_FLAGS_MINSIZEREL} ) string(REPLACE "/MD " " " CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO} ) string(REPLACE "/MDd " " " CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG} ) - string(REPLACE " /GR" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ) else() set( REL_LINKER_FLAGS "" ) if ( HAVE_VULKAN ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 77b539ae4..1c8cb8f1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -883,6 +883,7 @@ set (PCH_SOURCES sound/s_sndseq.cpp sound/s_doomsound.cpp serializer.cpp + serializer_doom.cpp scriptutil.cpp st_stuff.cpp rendering/v_framebuffer.cpp diff --git a/src/am_map.cpp b/src/am_map.cpp index 9d2ac227d..46397abda 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -40,7 +40,7 @@ #include "gi.h" #include "p_setup.h" #include "c_bind.h" -#include "serializer.h" +#include "serializer_doom.h" #include "r_sky.h" #include "sbar.h" #include "d_player.h" diff --git a/src/common/scripting/core/types.cpp b/src/common/scripting/core/types.cpp index d06c08250..6b8bd4a8f 100644 --- a/src/common/scripting/core/types.cpp +++ b/src/common/scripting/core/types.cpp @@ -38,6 +38,9 @@ #include "types.h" #include "printf.h" #include "textureid.h" +#include "version.h" +#include "info.h" +#include "serializer_doom.h" FTypeTable TypeTable; @@ -1102,9 +1105,7 @@ PSpriteID::PSpriteID() void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const { int32_t val = *(int*)addr; -#ifdef GZDOOM ar.Sprite(key, val, nullptr); -#endif } //========================================================================== @@ -1116,9 +1117,7 @@ void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) c bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const { int32_t val = 0; -#ifdef GZDOOM ar.Sprite(key, val, nullptr); -#endif *(int*)addr = val; return true; } diff --git a/src/g_game.cpp b/src/g_game.cpp index 9fb12d612..cf8ea599c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -67,7 +67,7 @@ #include "r_utility.h" #include "a_morph.h" #include "p_spec.h" -#include "serializer.h" +#include "serializer_doom.h" #include "vm.h" #include "dobjgc.h" #include "gi.h" @@ -1910,7 +1910,7 @@ void G_DoLoadGame () SaveVersion = 0; void *data = info->Lock(); - FSerializer arc(nullptr); + FSerializer arc; if (!arc.OpenReader((const char *)data, info->LumpSize)) { LoadGameError("TXT_FAILEDTOREADSG"); @@ -2342,8 +2342,8 @@ void G_DoSaveGame (bool okForQuicksave, bool forceQuicksave, FString filename, c } BufferWriter savepic; - 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. + FSerializer savegameinfo; // this is for displayable info about the savegame + FSerializer savegameglobals; // 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 50fdb1c51..bd3d86855 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -73,7 +73,7 @@ #include "a_sharedglobal.h" #include "r_utility.h" #include "p_spec.h" -#include "serializer.h" +#include "serializer_doom.h" #include "vm.h" #include "events.h" #include "i_music.h" diff --git a/src/g_statusbar/hudmessages.cpp b/src/g_statusbar/hudmessages.cpp index a708cf168..2f60a1950 100644 --- a/src/g_statusbar/hudmessages.cpp +++ b/src/g_statusbar/hudmessages.cpp @@ -38,7 +38,7 @@ #include "c_cvars.h" #include "v_video.h" #include "cmdlib.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "doomstat.h" #include "vm.h" diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 71d7b8655..98160dd8b 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -182,7 +182,7 @@ void FSavegameManager::ReadSaveStrings() continue; } void *data = info->Lock(); - FSerializer arc(nullptr); + FSerializer arc; 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->Lock(); - FSerializer arc(nullptr); + FSerializer arc; if (arc.OpenReader((const char *)data, info->LumpSize)) { FString comment; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index d84634f5f..4bf46e30f 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -52,7 +52,7 @@ #include "sbar.h" #include "r_utility.h" #include "r_sky.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "g_levellocals.h" #include "events.h" @@ -78,13 +78,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line, line_t * ("activation", line.activation, def->activation) ("special", line.special, def->special) ("alpha", line.alpha, def->alpha) - .Args("args", line.args, def->args, line.special) ("portalindex", line.portalindex, def->portalindex) ("locknumber", line.locknumber, def->locknumber) - ("health", line.health, def->health) + ("health", line.health, def->health); // Unless the map loader is changed the sidedef references will not change between map loads so there's no need to save them. //.Array("sides", line.sidedef, 2) - .EndObject(); + + SerializeArgs(arc, "args", line.args, def->args, line.special); + arc.EndObject(); } return arc; @@ -325,14 +326,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t .Array("specialcolors", p.SpecialColors, def->SpecialColors, 5, true) .Array("additivecolors", p.AdditiveColors, def->AdditiveColors, 5, true) ("gravity", p.gravity, def->gravity) - .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) - .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) ("healthfloor", p.healthfloor, def->healthfloor) ("healthceiling", p.healthceiling, def->healthceiling) ("health3d", p.health3d, def->health3d) // GZDoom exclusive: - .Array("reflect", p.reflect, def->reflect, 2, true) - .EndObject(); + .Array("reflect", p.reflect, def->reflect, 2, true); + + SerializeTerrain(arc, "floorterrain", p.terrainnum[0], &def->terrainnum[0]); + SerializeTerrain(arc, "ceilingterrain", p.terrainnum[1], &def->terrainnum[1]); + arc.EndObject(); } return arc; } @@ -1073,7 +1075,7 @@ void FLevelLocals::SnapshotLevel() if (info->isValid()) { - FSerializer arc(this); + FDoomSerializer arc(this); if (arc.OpenWriter(save_formatted)) { @@ -1098,7 +1100,7 @@ void FLevelLocals::UnSnapshotLevel(bool hubLoad) if (info->isValid()) { - FSerializer arc(this); + FDoomSerializer arc(this); if (!arc.OpenReader(&info->Snapshot)) { I_Error("Failed to load savegame"); diff --git a/src/playsim/a_decals.cpp b/src/playsim/a_decals.cpp index 5aded921a..fc712a5d8 100644 --- a/src/playsim/a_decals.cpp +++ b/src/playsim/a_decals.cpp @@ -40,7 +40,7 @@ #include "decallib.h" #include "c_dispatch.h" #include "d_net.h" -#include "serializer.h" +#include "serializer_doom.h" #include "doomdata.h" #include "g_levellocals.h" #include "vm.h" diff --git a/src/playsim/a_specialspot.cpp b/src/playsim/a_specialspot.cpp index c8261208c..b658c1830 100644 --- a/src/playsim/a_specialspot.cpp +++ b/src/playsim/a_specialspot.cpp @@ -35,7 +35,7 @@ #include "a_specialspot.h" #include "p_local.h" #include "doomstat.h" -#include "serializer.h" +#include "serializer_doom.h" #include "a_pickups.h" #include "vm.h" diff --git a/src/playsim/bots/b_bot.cpp b/src/playsim/bots/b_bot.cpp index 0bdd0cc1e..1f628a88e 100644 --- a/src/playsim/bots/b_bot.cpp +++ b/src/playsim/bots/b_bot.cpp @@ -45,7 +45,7 @@ #include "cmdlib.h" #include "teaminfo.h" #include "d_net.h" -#include "serializer.h" +#include "serializer_doom.h" #include "d_player.h" #include "filesystem.h" #include "vm.h" diff --git a/src/playsim/dthinker.cpp b/src/playsim/dthinker.cpp index 16b7f666f..66061010d 100644 --- a/src/playsim/dthinker.cpp +++ b/src/playsim/dthinker.cpp @@ -35,7 +35,7 @@ #include "dthinker.h" #include "stats.h" #include "p_local.h" -#include "serializer.h" +#include "serializer_doom.h" #include "d_player.h" #include "vm.h" #include "c_dispatch.h" diff --git a/src/playsim/dthinker.h b/src/playsim/dthinker.h index 07d884fc4..680afbc9a 100644 --- a/src/playsim/dthinker.h +++ b/src/playsim/dthinker.h @@ -116,7 +116,7 @@ private: friend struct FThinkerCollection; friend class FThinkerIterator; friend class DObject; - friend class FSerializer; + friend class FDoomSerializer; DThinker *NextThinker = nullptr, *PrevThinker = nullptr; diff --git a/src/playsim/fragglescript/t_prepro.cpp b/src/playsim/fragglescript/t_prepro.cpp index 69e1c4ab1..409faa132 100644 --- a/src/playsim/fragglescript/t_prepro.cpp +++ b/src/playsim/fragglescript/t_prepro.cpp @@ -43,6 +43,7 @@ #include "t_script.h" #include "filesystem.h" #include "serializer.h" +#include "serialize_obj.h" #include "g_levellocals.h" diff --git a/src/playsim/mapthinkers/a_decalfx.cpp b/src/playsim/mapthinkers/a_decalfx.cpp index 76b78ea29..7052373c7 100644 --- a/src/playsim/mapthinkers/a_decalfx.cpp +++ b/src/playsim/mapthinkers/a_decalfx.cpp @@ -34,7 +34,7 @@ #include "decallib.h" #include "a_decalfx.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "a_sharedglobal.h" #include "g_levellocals.h" diff --git a/src/playsim/mapthinkers/a_doors.cpp b/src/playsim/mapthinkers/a_doors.cpp index 7994e8820..4f57770ce 100644 --- a/src/playsim/mapthinkers/a_doors.cpp +++ b/src/playsim/mapthinkers/a_doors.cpp @@ -34,7 +34,7 @@ #include "r_state.h" #include "gi.h" #include "a_keys.h" -#include "serializer.h" +#include "serializer_doom.h" #include "d_player.h" #include "p_spec.h" #include "g_levellocals.h" diff --git a/src/playsim/mapthinkers/a_lighttransfer.cpp b/src/playsim/mapthinkers/a_lighttransfer.cpp index 931de98da..f5185ea59 100644 --- a/src/playsim/mapthinkers/a_lighttransfer.cpp +++ b/src/playsim/mapthinkers/a_lighttransfer.cpp @@ -34,7 +34,7 @@ #include "p_spec.h" #include "a_lighttransfer.h" -#include "serializer.h" +#include "serializer_doom.h" #include "g_levellocals.h" // diff --git a/src/playsim/mapthinkers/a_scroll.cpp b/src/playsim/mapthinkers/a_scroll.cpp index 66cb2731d..6e5ba7ad0 100644 --- a/src/playsim/mapthinkers/a_scroll.cpp +++ b/src/playsim/mapthinkers/a_scroll.cpp @@ -58,7 +58,7 @@ #include #include "actor.h" #include "p_spec.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "p_lnspec.h" #include "r_data/r_interpolate.h" diff --git a/src/playsim/mapthinkers/dsectoreffect.cpp b/src/playsim/mapthinkers/dsectoreffect.cpp index 8fbf3c8a7..25a8826e9 100644 --- a/src/playsim/mapthinkers/dsectoreffect.cpp +++ b/src/playsim/mapthinkers/dsectoreffect.cpp @@ -30,7 +30,8 @@ #include "g_levellocals.h" #include "p_3dmidtex.h" #include "r_data/r_interpolate.h" -#include "serializer.h" +#include "serializer_doom.h" +#include "serialize_obj.h" #include "doomstat.h" #include "vm.h" diff --git a/src/playsim/p_acs.cpp b/src/playsim/p_acs.cpp index f8e3b0193..bbefd5ad5 100644 --- a/src/playsim/p_acs.cpp +++ b/src/playsim/p_acs.cpp @@ -62,7 +62,7 @@ #include "p_setup.h" #include "po_man.h" #include "actorptrselect.h" -#include "serializer.h" +#include "serializer_doom.h" #include "decallib.h" #include "p_terrain.h" #include "version.h" @@ -1873,7 +1873,6 @@ void DPlaneWatcher::Serialize(FSerializer &arc) { Super::Serialize (arc); arc("special", Special) - .Args("args", Args, nullptr, Special) ("sector", Sector) ("ceiling", bCeiling) ("watchd", WatchD) @@ -1882,6 +1881,7 @@ void DPlaneWatcher::Serialize(FSerializer &arc) ("line", Line) ("lineside", LineSide); + SerializeArgs(arc, "args", Args, nullptr, Special); } void DPlaneWatcher::Tick () diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index aae82eafb..c4898686f 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -88,7 +88,7 @@ #include "po_man.h" #include "p_spec.h" #include "p_checkposition.h" -#include "serializer.h" +#include "serializer_doom.h" #include "r_utility.h" #include "thingdef.h" #include "d_player.h" @@ -227,7 +227,6 @@ void AActor::Serialize(FSerializer &arc) A("tics", tics) A("state", state) A("damage", DamageVal) - .Terrain("floorterrain", floorterrain, &def->floorterrain) A("projectilekickback", projectileKickback) A("flags", flags) A("flags2", flags2) @@ -261,7 +260,6 @@ void AActor::Serialize(FSerializer &arc) A("floorclip", Floorclip) A("tid", tid) A("special", special) - .Args("args", args, def->args, special) A("accuracy", accuracy) A("stamina", stamina) ("goal", goal) @@ -367,6 +365,10 @@ void AActor::Serialize(FSerializer &arc) A("spawnorder", SpawnOrder) A("friction", Friction) A("userlights", UserLights); + + SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain); + SerializeArgs(arc, "args", args, def->args, special); + } #undef A diff --git a/src/playsim/p_pspr.cpp b/src/playsim/p_pspr.cpp index 3ccc49f1e..d8e43042a 100644 --- a/src/playsim/p_pspr.cpp +++ b/src/playsim/p_pspr.cpp @@ -39,7 +39,8 @@ #include "templates.h" #include "g_level.h" #include "d_player.h" -#include "serializer.h" +#include "serializer_doom.h" +#include "serialize_obj.h" #include "v_text.h" #include "cmdlib.h" #include "g_levellocals.h" diff --git a/src/playsim/p_switch.cpp b/src/playsim/p_switch.cpp index 7453418cc..e0dd60e80 100644 --- a/src/playsim/p_switch.cpp +++ b/src/playsim/p_switch.cpp @@ -41,7 +41,7 @@ #include "m_random.h" #include "s_sound.h" #include "doomstat.h" -#include "serializer.h" +#include "serializer_doom.h" #include "p_maputl.h" #include "p_spec.h" #include "textures.h" diff --git a/src/playsim/p_user.cpp b/src/playsim/p_user.cpp index 93df0fe1f..47241679b 100644 --- a/src/playsim/p_user.cpp +++ b/src/playsim/p_user.cpp @@ -77,7 +77,7 @@ #include "c_console.h" #include "c_dispatch.h" #include "d_net.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "d_player.h" #include "r_utility.h" diff --git a/src/playsim/po_man.cpp b/src/playsim/po_man.cpp index 5147d77ff..1c6eac3d9 100644 --- a/src/playsim/po_man.cpp +++ b/src/playsim/po_man.cpp @@ -32,7 +32,8 @@ #include "r_data/r_interpolate.h" #include "po_man.h" #include "p_setup.h" -#include "serializer.h" +#include "serializer_doom.h" +#include "serialize_obj.h" #include "p_blockmap.h" #include "p_maputl.h" #include "r_utility.h" diff --git a/src/r_data/r_interpolate.cpp b/src/r_data/r_interpolate.cpp index ed9c7f58a..30dff51c6 100644 --- a/src/r_data/r_interpolate.cpp +++ b/src/r_data/r_interpolate.cpp @@ -38,7 +38,8 @@ #include "r_data/r_interpolate.h" #include "p_local.h" #include "po_man.h" -#include "serializer.h" +#include "serializer_doom.h" +#include "serialize_obj.h" #include "g_levellocals.h" //========================================================================== diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index 3715eab16..1ac18fa8a 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -52,7 +52,7 @@ #include "menu/menu.h" #include "teaminfo.h" #include "r_data/sprites.h" -#include "serializer.h" +#include "serializer_doom.h" #include "wi_stuff.h" #include "a_dynlight.h" #include "types.h" diff --git a/src/scripting/zscript/ast.cpp b/src/scripting/zscript/ast.cpp index 99453901d..ae198dc84 100644 --- a/src/scripting/zscript/ast.cpp +++ b/src/scripting/zscript/ast.cpp @@ -36,6 +36,7 @@ #include "types.h" #include "zcc_parser.h" #include "zcc-parse.h" +#include "printf.h" class FLispString; extern void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *); diff --git a/src/serializer.cpp b/src/serializer.cpp index 5fed64f9a..dca403250 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -64,6 +64,8 @@ bool save_full = false; // for testing. Should be removed afterward. +#include "serializer_internal.h" + //========================================================================== // // This will double-encode already existing UTF-8 content. @@ -73,7 +75,7 @@ bool save_full = false; // for testing. Should be removed afterward. //========================================================================== static TArray out; -static const char *StringToUnicode(const char *cc, int size = -1) +const char *StringToUnicode(const char *cc, int size) { int ch; const uint8_t *c = (const uint8_t*)cc; @@ -101,7 +103,7 @@ static const char *StringToUnicode(const char *cc, int size = -1) return &out[0]; } -static const char *UnicodeToString(const char *cc) +const char *UnicodeToString(const char *cc) { out.Resize((unsigned)strlen(cc) + 1); int ndx = 0; @@ -117,225 +119,6 @@ static const char *UnicodeToString(const char *cc) return &out[0]; } -//========================================================================== -// -// -// -//========================================================================== - -struct FJSONObject -{ - rapidjson::Value *mObject; - rapidjson::Value::MemberIterator mIterator; - int mIndex; - - FJSONObject(rapidjson::Value *v) - { - mObject = v; - if (v->IsObject()) mIterator = v->MemberBegin(); - else if (v->IsArray()) - { - mIndex = 0; - } - } -}; - -//========================================================================== -// -// some wrapper stuff to keep the RapidJSON dependencies out of the global headers. -// FSerializer should not expose any of this. -// -//========================================================================== - -struct FWriter -{ - typedef rapidjson::Writer > Writer; - typedef rapidjson::PrettyWriter > PrettyWriter; - - Writer *mWriter1; - PrettyWriter *mWriter2; - TArray mInObject; - rapidjson::StringBuffer mOutString; - TArray mDObjects; - TMap mObjectMap; - - FWriter(bool pretty) - { - if (!pretty) - { - mWriter1 = new Writer(mOutString); - mWriter2 = nullptr; - } - else - { - mWriter1 = nullptr; - mWriter2 = new PrettyWriter(mOutString); - } - } - - ~FWriter() - { - if (mWriter1) delete mWriter1; - if (mWriter2) delete mWriter2; - } - - - bool inObject() const - { - return mInObject.Size() > 0 && mInObject.Last(); - } - - void StartObject() - { - if (mWriter1) mWriter1->StartObject(); - else if (mWriter2) mWriter2->StartObject(); - } - - void EndObject() - { - if (mWriter1) mWriter1->EndObject(); - else if (mWriter2) mWriter2->EndObject(); - } - - void StartArray() - { - if (mWriter1) mWriter1->StartArray(); - else if (mWriter2) mWriter2->StartArray(); - } - - void EndArray() - { - if (mWriter1) mWriter1->EndArray(); - else if (mWriter2) mWriter2->EndArray(); - } - - void Key(const char *k) - { - if (mWriter1) mWriter1->Key(k); - else if (mWriter2) mWriter2->Key(k); - } - - void Null() - { - if (mWriter1) mWriter1->Null(); - else if (mWriter2) mWriter2->Null(); - } - - void StringU(const char *k, bool encode) - { - if (encode) k = StringToUnicode(k); - if (mWriter1) mWriter1->String(k); - else if (mWriter2) mWriter2->String(k); - } - - void String(const char *k) - { - k = StringToUnicode(k); - if (mWriter1) mWriter1->String(k); - else if (mWriter2) mWriter2->String(k); - } - - void String(const char *k, int size) - { - k = StringToUnicode(k, size); - if (mWriter1) mWriter1->String(k); - else if (mWriter2) mWriter2->String(k); - } - - void Bool(bool k) - { - if (mWriter1) mWriter1->Bool(k); - else if (mWriter2) mWriter2->Bool(k); - } - - void Int(int32_t k) - { - if (mWriter1) mWriter1->Int(k); - else if (mWriter2) mWriter2->Int(k); - } - - void Int64(int64_t k) - { - if (mWriter1) mWriter1->Int64(k); - else if (mWriter2) mWriter2->Int64(k); - } - - void Uint(uint32_t k) - { - if (mWriter1) mWriter1->Uint(k); - else if (mWriter2) mWriter2->Uint(k); - } - - void Uint64(int64_t k) - { - if (mWriter1) mWriter1->Uint64(k); - else if (mWriter2) mWriter2->Uint64(k); - } - - void Double(double k) - { - if (mWriter1) - { - mWriter1->Double(k); - } - else if (mWriter2) - { - mWriter2->Double(k); - } - } - -}; - -//========================================================================== -// -// -// -//========================================================================== - -struct FReader -{ - TArray mObjects; - rapidjson::Document mDoc; - TArray mDObjects; - rapidjson::Value *mKeyValue = nullptr; - bool mObjectsRead = false; - - FReader(const char *buffer, size_t length) - { - mDoc.Parse(buffer, length); - mObjects.Push(FJSONObject(&mDoc)); - } - - rapidjson::Value *FindKey(const char *key) - { - FJSONObject &obj = mObjects.Last(); - - if (obj.mObject->IsObject()) - { - if (key == nullptr) - { - // we are performing an iteration of the object through GetKey. - auto p = mKeyValue; - mKeyValue = nullptr; - return p; - } - else - { - // Find the given key by name; - auto it = obj.mObject->FindMember(key); - if (it == obj.mObject->MemberEnd()) return nullptr; - return &it->value; - } - } - else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size()) - { - return &(*obj.mObject)[obj.mIndex++]; - } - return nullptr; - } -}; - - //========================================================================== // // @@ -409,20 +192,7 @@ void FSerializer::Close() } if (r != nullptr) { - // we must explicitly delete all thinkers in the array which did not get linked into the thinker lists. - // Otherwise these objects may survive a level deletion and point to incorrect data. - for (auto obj : r->mDObjects) - { - auto think = dyn_cast(obj); - if (think != nullptr) - { - if (think->NextThinker == nullptr || think->PrevThinker == nullptr) - { - think->Destroy(); - } - } - } - + CloseReaderCustom(); delete r; r = nullptr; } @@ -612,74 +382,6 @@ void FSerializer::EndArray() } } -//========================================================================== -// -// Special handler for args (because ACS specials' arg0 needs special treatment.) -// -//========================================================================== - -FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int special) -{ - if (isWriting()) - { - if (w->inObject() && defargs != nullptr && !memcmp(args, defargs, 5 * sizeof(int))) - { - return *this; - } - - WriteKey(key); - w->StartArray(); - for (int i = 0; i < 5; i++) - { - if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) - { - w->String(FName(ENamedName(-args[i])).GetChars()); - } - else - { - w->Int(args[i]); - } - } - w->EndArray(); - } - else - { - auto val = r->FindKey(key); - if (val != nullptr) - { - if (val->IsArray()) - { - unsigned int cnt = MIN(val->Size(), 5); - for (unsigned int i = 0; i < cnt; i++) - { - const rapidjson::Value &aval = (*val)[i]; - if (aval.IsInt()) - { - args[i] = aval.GetInt(); - } - else if (i == 0 && aval.IsString()) - { - args[i] = -FName(UnicodeToString(aval.GetString())).GetIndex(); - } - else - { - assert(false && "Integer expected"); - Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'\n", key, i); - mErrors++; - } - } - } - else - { - assert(false && "array expected"); - Printf(TEXTCOLOR_RED "array expected for '%s'\n", key); - mErrors++; - } - } - } - return *this; -} - //========================================================================== // // Special handler for script numbers @@ -726,28 +428,7 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num) //========================================================================== // -// -// -//========================================================================== - -FSerializer &FSerializer::Terrain(const char *key, int &terrain, int *def) -{ - if (isWriting() && def != nullptr && terrain == *def) - { - return *this; - } - FName terr = P_GetTerrainName(terrain); - Serialize(*this, key, terr, nullptr); - if (isReading()) - { - terrain = P_FindTerrain(terr); - } - return *this; -} - -//========================================================================== -// -// +// this is merely a placeholder to satisfy the VM's spriteid type. // //========================================================================== @@ -757,25 +438,14 @@ FSerializer &FSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *d { if (w->inObject() && def != nullptr && *def == spritenum) return *this; WriteKey(key); - w->String(sprites[spritenum].name, 4); + w->Int(spritenum); } else { auto val = r->FindKey(key); - if (val != nullptr) + if (val != nullptr && val->IsInt()) { - if (val->IsString()) - { - uint32_t name = *reinterpret_cast(UnicodeToString(val->GetString())); - for (auto hint = NumStdSprites; hint-- != 0; ) - { - if (sprites[hint].dwName == name) - { - spritenum = hint; - break; - } - } - } + spritenum = val->GetInt(); } } return *this; @@ -1384,72 +1054,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *d // //========================================================================== -template -FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base) -{ - assert(base != nullptr); - if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval) - { - int64_t vv = value == nullptr ? -1 : value - base; - Serialize(arc, key, vv, nullptr); - value = vv < 0 ? nullptr : base + vv; - } - return arc; -} - -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - 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) -{ - return SerializePointer(arc, key, value, defval, players); -} - -template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval) -{ - 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) -{ - if (arc.Level == nullptr) I_Error("Trying to serialize verte without a valid level"); - return SerializePointer(arc, key, value, defval, &arc.Level->vertexes[0]); -} - -//========================================================================== -// -// -// -//========================================================================== - FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval) { if (arc.isWriting()) @@ -1669,7 +1273,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def) { - if (arc.isWriting()) + if (!arc.soundNamesAreUnique) + { + //If sound name here is not reliable, we need to save by index instead. + int id = sid; + Serialize(arc, key, id, nullptr); + if (arc.isReading()) sid = FSoundID(id); + } + else if (arc.isWriting()) { if (!arc.w->inObject() || def == nullptr || sid != *def) { @@ -1705,55 +1316,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI } -//========================================================================== -// -// -// -//========================================================================== - -template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def) -{ - if (arc.isWriting()) - { - if (!arc.w->inObject() || def == nullptr || clst != *def) - { - arc.WriteKey(key); - if (clst == nullptr) - { - arc.w->Null(); - } - else - { - arc.w->String(clst->TypeName.GetChars()); - } - } - } - else - { - auto val = arc.r->FindKey(key); - if (val != nullptr) - { - assert(val->IsString() || val->IsNull()); - if (val->IsString()) - { - clst = PClass::FindActor(UnicodeToString(val->GetString())); - } - else if (val->IsNull()) - { - clst = nullptr; - } - else - { - Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); - clst = nullptr; - arc.mErrors++; - } - } - } - return arc; - -} - //========================================================================== // // almost, but not quite the same as the above. @@ -1808,199 +1370,6 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode) -{ - if (retcode) *retcode = false; - if (arc.isWriting()) - { - if (!arc.w->inObject() || def == nullptr || state != *def) - { - if (retcode) *retcode = true; - arc.WriteKey(key); - if (state == nullptr) - { - arc.w->Null(); - } - else - { - PClassActor *info = FState::StaticFindStateOwner(state); - - if (info != NULL) - { - arc.w->StartArray(); - arc.w->String(info->TypeName.GetChars()); - arc.w->Uint((uint32_t)(state - info->GetStates())); - arc.w->EndArray(); - } - else - { - arc.w->Null(); - } - } - } - } - else - { - auto val = arc.r->FindKey(key); - if (val != nullptr) - { - if (val->IsNull()) - { - if (retcode) *retcode = true; - state = nullptr; - } - else if (val->IsArray()) - { - if (retcode) *retcode = true; - const rapidjson::Value &cls = (*val)[0]; - const rapidjson::Value &ndx = (*val)[1]; - - state = nullptr; - assert(cls.IsString() && ndx.IsUint()); - if (cls.IsString() && ndx.IsUint()) - { - PClassActor *clas = PClass::FindActor(UnicodeToString(cls.GetString())); - if (clas && ndx.GetUint() < (unsigned)clas->GetStateCount()) - { - state = clas->GetStates() + ndx.GetUint(); - } - else - { - // this can actually happen by changing the DECORATE so treat it as a warning, not an error. - state = nullptr; - Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'\n", cls.GetString(), ndx.GetInt(), key); - } - } - else - { - assert(false && "not a state"); - Printf(TEXTCOLOR_RED "data does not represent a state for '%s'\n", key); - arc.mErrors++; - } - } - else if (!retcode) - { - assert(false && "not an array"); - Printf(TEXTCOLOR_RED "array type expected for '%s'\n", key); - arc.mErrors++; - } - } - } - return arc; - -} - -//========================================================================== -// -// -// -//========================================================================== - -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def) -{ - if (arc.isWriting()) - { - if (!arc.w->inObject() || def == nullptr || node != *def) - { - arc.WriteKey(key); - if (node == nullptr) - { - arc.w->Null(); - } - else - { - arc.w->Uint(node->ThisNodeNum); - } - } - } - else - { - auto val = arc.r->FindKey(key); - if (val != nullptr) - { - assert(val->IsUint() || val->IsNull()); - if (val->IsNull()) - { - node = nullptr; - } - else if (val->IsUint()) - { - if (val->GetUint() >= arc.Level->StrifeDialogues.Size()) - { - node = nullptr; - } - else - { - node = arc.Level->StrifeDialogues[val->GetUint()]; - } - } - else - { - Printf(TEXTCOLOR_RED "integer expected for '%s'\n", key); - arc.mErrors++; - node = nullptr; - } - } - } - return arc; - -} - -//========================================================================== -// -// -// -//========================================================================== - -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def) -{ - if (arc.isWriting()) - { - if (!arc.w->inObject() || def == nullptr || pstr != *def) - { - arc.WriteKey(key); - if (pstr == nullptr) - { - arc.w->Null(); - } - else - { - arc.w->String(pstr->GetChars()); - } - } - } - else - { - auto val = arc.r->FindKey(key); - if (val != nullptr) - { - assert(val->IsNull() || val->IsString()); - if (val->IsNull()) - { - pstr = nullptr; - } - else if (val->IsString()) - { - pstr = AActor::mStringPropertyData.Alloc(UnicodeToString(val->GetString())); - } - else - { - Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); - pstr = nullptr; - arc.mErrors++; - } - } - } - return arc; - -} - -//========================================================================== -// -// -// -//========================================================================== - FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString *def) { if (arc.isWriting()) @@ -2043,113 +1412,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString // //========================================================================== -template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def) -{ - if (arc.isWriting()) - { - if (!arc.w->inObject() || def == nullptr || strcmp(pstr, *def)) - { - arc.WriteKey(key); - if (pstr == nullptr) - { - arc.w->Null(); - } - else - { - arc.w->String(pstr); - } - } - } - else - { - auto val = arc.r->FindKey(key); - if (val != nullptr) - { - assert(val->IsNull() || val->IsString()); - if (val->IsNull()) - { - pstr = nullptr; - } - else if (val->IsString()) - { - pstr = copystring(UnicodeToString(val->GetString())); - } - else - { - Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); - pstr = nullptr; - arc.mErrors++; - } - } - } - 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 object that is part of a level may reference a different one. -// -// 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; -} - -//========================================================================== -// -// -// -//========================================================================== - template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def) { if (arc.isWriting()) @@ -2309,3 +1571,5 @@ FSerializer& Serialize(FSerializer& arc, const char* key, FRenderStyle& style, F { return arc.Array(key, &style.BlendOp, def ? &def->BlendOp : nullptr, 4); } + +SaveRecords saveRecords; diff --git a/src/serializer.h b/src/serializer.h index fcfbcaa18..708d17d1e 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -4,28 +4,23 @@ #include #include #include "tarray.h" -#include "r_defs.h" #include "file_zip.h" #include "tflags.h" +#include "vectors.h" +#include "palentry.h" +#include "name.h" #include "dictionary.h" extern bool save_full; -struct ticcmd_t; -struct usercmd_t; - struct FWriter; struct FReader; class PClass; -class PClassActor; -struct FStrifeDialogueNode; class FFont; -struct FState; -struct FDoorAnimation; class FSoundID; -struct FPolyObj; union FRenderStyle; -struct FInterpolator; +class DObject; +class FTextureID; inline bool nullcmp(const void *buffer, size_t length) { @@ -67,22 +62,22 @@ class FSerializer public: FWriter *w = nullptr; FReader *r = nullptr; - FLevelLocals *Level; + bool soundNamesAreUnique = false; // While in GZDoom, sound names are unique, that isn't universally true - let the serializer handle both cases with a flag. unsigned ArraySize(); void WriteKey(const char *key); void WriteObjects(); +private: + virtual void CloseReaderCustom() {} public: - FSerializer(FLevelLocals *l) : Level(l) - {} - ~FSerializer() { mErrors = 0; // The destructor may not throw an exception so silence the error checker. Close(); } + void SetUniqueSoundNames() { soundNamesAreUnique = true; } bool OpenWriter(bool pretty = true); bool OpenReader(const char *buffer, size_t length); bool OpenReader(FCompressedBuffer *input); @@ -96,9 +91,8 @@ public: const char *GetKey(); const char *GetOutput(unsigned *len = nullptr); FCompressedBuffer GetCompressedOutput(); - FSerializer &Args(const char *key, int *args, int *defargs, int special); - FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr); - FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def); + // The sprite serializer is a special case because it is needed by the VM to handle its 'spriteid' type. + virtual FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def); FSerializer &StringPtr(const char *key, const char *&charptr); // This only retrieves the address but creates no permanent copy of the string unlike the regular char* serializer. FSerializer &AddString(const char *key, const char *charptr); const char *GetString(const char *key); @@ -187,6 +181,7 @@ public: }; FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval); + FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval); FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_t *defval); FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint64_t *defval); @@ -204,9 +199,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def); FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def); FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def); -FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &sid, ticcmd_t *def); -FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def); -FSerializer &Serialize(FSerializer &arc, const char *key, FInterpolator &rs, FInterpolator *def); template FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) @@ -243,31 +235,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value, return arc; } -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&clst, PClass **def); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def); -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); +template<> FSerializer& Serialize(FSerializer& arc, const char* key, PClass*& clst, PClass** def); +template<> FSerializer& Serialize(FSerializer& arc, const char* key, FFont*& font, FFont** def); template<> FSerializer &Serialize(FSerializer &arc, const char *key, Dictionary *&dict, Dictionary **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) -{ - return Serialize(arc, key, state, def, nullptr); -} - - inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def) { return arc.Array(key, &p[0], def? &(*def)[0] : nullptr, 3, true); @@ -301,6 +272,41 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TFlags &flags, return Serialize(arc, key, flags.Value, def? &def->Value : nullptr); } +// Automatic save record registration + +struct SaveRecord +{ + const char* GameModule; + void (*Handler)(FSerializer& arc); + + SaveRecord(const char* nm, void (*handler)(FSerializer& arc)); +}; + +struct SaveRecords +{ + TArray records; + + void RunHandlers(const char* gameModule, FSerializer& arc) + { + for (auto record : records) + { + if (!strcmp(gameModule, record->GameModule)) + { + record->Handler(arc); + } + } + } +}; + +extern SaveRecords saveRecords; + +inline SaveRecord::SaveRecord(const char* nm, void (*handler)(FSerializer& arc)) +{ + GameModule = nm; + Handler = handler; + saveRecords.records.Push(this); +} + FString DictionaryToString(const Dictionary &dict); Dictionary *DictionaryFromString(const FString &string); diff --git a/src/serializer_doom.cpp b/src/serializer_doom.cpp new file mode 100644 index 000000000..2ed1e5181 --- /dev/null +++ b/src/serializer_doom.cpp @@ -0,0 +1,573 @@ +#include +#include "rapidjson/rapidjson.h" +#include "rapidjson/writer.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/document.h" +#include "serializer_doom.h" +#include "actor.h" +#include "r_defs.h" +#include "printf.h" +#include "p_lnspec.h" +#include "utf8.h" +#include "g_levellocals.h" +#include "p_conversation.h" +#include "p_terrain.h" + +#include "serializer_internal.h" + +//========================================================================== +// +// we must explicitly delete all thinkers in the array which did not get linked into the thinker lists. +// Otherwise these objects may survive a level deletion and point to incorrect data. +// +//========================================================================== + +void FDoomSerializer::CloseReaderCustom() +{ + for (auto obj : r->mDObjects) + { + auto think = dyn_cast(obj); + if (think != nullptr) + { + if (think->NextThinker == nullptr || think->PrevThinker == nullptr) + { + think->Destroy(); + } + } + } +} + + +//========================================================================== +// +// Special handler for args (because ACS specials' arg0 needs special treatment.) +// +//========================================================================== + +FSerializer &SerializeArgs(FSerializer& arc, const char *key, int *args, int *defargs, int special) +{ + if (arc.isWriting()) + { + auto &w = arc.w; + if (w->inObject() && defargs != nullptr && !memcmp(args, defargs, 5 * sizeof(int))) + { + return arc; + } + + arc.WriteKey(key); + w->StartArray(); + for (int i = 0; i < 5; i++) + { + if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) + { + w->String(FName(ENamedName(-args[i])).GetChars()); + } + else + { + w->Int(args[i]); + } + } + w->EndArray(); + } + else + { + auto &r = arc.r; + auto val = r->FindKey(key); + if (val != nullptr) + { + if (val->IsArray()) + { + unsigned int cnt = MIN(val->Size(), 5); + for (unsigned int i = 0; i < cnt; i++) + { + const rapidjson::Value &aval = (*val)[i]; + if (aval.IsInt()) + { + args[i] = aval.GetInt(); + } + else if (i == 0 && aval.IsString()) + { + args[i] = -FName(UnicodeToString(aval.GetString())).GetIndex(); + } + else + { + assert(false && "Integer expected"); + Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'\n", key, i); + arc.mErrors++; + } + } + } + else + { + assert(false && "array expected"); + Printf(TEXTCOLOR_RED "array expected for '%s'\n", key); + arc.mErrors++; + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &SerializeTerrain(FSerializer &arc, const char *key, int &terrain, int *def) +{ + if (arc.isWriting() && def != nullptr && terrain == *def) + { + return arc; + } + FName terr = P_GetTerrainName(terrain); + Serialize(arc, key, terr, nullptr); + if (arc.isReading()) + { + terrain = P_FindTerrain(terr); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &FDoomSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *def) +{ + if (isWriting()) + { + if (w->inObject() && def != nullptr && *def == spritenum) return *this; + WriteKey(key); + w->String(sprites[spritenum].name, 4); + } + else + { + auto val = r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + uint32_t name = *reinterpret_cast(UnicodeToString(val->GetString())); + for (auto hint = NumStdSprites; hint-- != 0; ) + { + if (sprites[hint].dwName == name) + { + spritenum = hint; + break; + } + } + } + } + } + return *this; +} + + +template<> FSerializer &Serialize(FSerializer &ar, const char *key, FPolyObj *&value, FPolyObj **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || 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 &ar, const char *key, const FPolyObj *&value, const FPolyObj **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || 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 &ar, const char *key, side_t *&value, side_t **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || arc->Level == nullptr) I_Error("Trying to serialize SIDEDEF without a valid level"); + return SerializePointer(ar, key, value, defval, arc->Level->sides.Data()); +} + +template<> FSerializer &Serialize(FSerializer &ar, const char *key, sector_t *&value, sector_t **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || arc->Level == nullptr) I_Error("Trying to serialize sector without a valid level"); + return SerializePointer(*arc, key, value, defval, arc->Level->sectors.Data()); +} + +template<> FSerializer &Serialize(FSerializer &ar, const char *key, const sector_t *&value, const sector_t **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || arc->Level == nullptr) I_Error("Trying to serialize sector without a valid level"); + return SerializePointer(*arc, key, value, defval, arc->Level->sectors.Data()); +} + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) +{ + return SerializePointer(arc, key, value, defval, players); +} + +template<> FSerializer &Serialize(FSerializer &ar, const char *key, line_t *&value, line_t **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || arc->Level == nullptr) I_Error("Trying to serialize linedef without a valid level"); + return SerializePointer(*arc, key, value, defval, arc->Level->lines.Data()); +} + +template<> FSerializer &Serialize(FSerializer &ar, const char *key, vertex_t *&value, vertex_t **defval) +{ + auto arc = dynamic_cast(&ar); + if (!arc || arc->Level == nullptr) I_Error("Trying to serialize vertex without a valid level"); + return SerializePointer(*arc, key, value, defval, arc->Level->vertexes.Data()); +} + +//========================================================================== +// +// +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || clst != *def) + { + arc.WriteKey(key); + if (clst == nullptr) + { + arc.w->Null(); + } + else + { + arc.w->String(clst->TypeName.GetChars()); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + assert(val->IsString() || val->IsNull()); + if (val->IsString()) + { + clst = PClass::FindActor(UnicodeToString(val->GetString())); + } + else if (val->IsNull()) + { + clst = nullptr; + } + else + { + Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); + clst = nullptr; + arc.mErrors++; + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode) +{ + if (retcode) *retcode = false; + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || state != *def) + { + if (retcode) *retcode = true; + arc.WriteKey(key); + if (state == nullptr) + { + arc.w->Null(); + } + else + { + PClassActor *info = FState::StaticFindStateOwner(state); + + if (info != NULL) + { + arc.w->StartArray(); + arc.w->String(info->TypeName.GetChars()); + arc.w->Uint((uint32_t)(state - info->GetStates())); + arc.w->EndArray(); + } + else + { + arc.w->Null(); + } + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + if (retcode) *retcode = true; + state = nullptr; + } + else if (val->IsArray()) + { + if (retcode) *retcode = true; + const rapidjson::Value &cls = (*val)[0]; + const rapidjson::Value &ndx = (*val)[1]; + + state = nullptr; + assert(cls.IsString() && ndx.IsUint()); + if (cls.IsString() && ndx.IsUint()) + { + PClassActor *clas = PClass::FindActor(UnicodeToString(cls.GetString())); + if (clas && ndx.GetUint() < (unsigned)clas->GetStateCount()) + { + state = clas->GetStates() + ndx.GetUint(); + } + else + { + // this can actually happen by changing the DECORATE so treat it as a warning, not an error. + state = nullptr; + Printf(TEXTCOLOR_ORANGE "Invalid state '%s+%d' for '%s'\n", cls.GetString(), ndx.GetInt(), key); + } + } + else + { + assert(false && "not a state"); + Printf(TEXTCOLOR_RED "data does not represent a state for '%s'\n", key); + arc.mErrors++; + } + } + else if (!retcode) + { + assert(false && "not an array"); + Printf(TEXTCOLOR_RED "array type expected for '%s'\n", key); + arc.mErrors++; + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def) +{ + auto doomarc = static_cast(&arc); + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || node != *def) + { + arc.WriteKey(key); + if (node == nullptr) + { + arc.w->Null(); + } + else + { + arc.w->Uint(node->ThisNodeNum); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + assert(val->IsUint() || val->IsNull()); + if (val->IsNull()) + { + node = nullptr; + } + else if (val->IsUint()) + { + if (val->GetUint() >= doomarc->Level->StrifeDialogues.Size()) + { + node = nullptr; + } + else + { + node = doomarc->Level->StrifeDialogues[val->GetUint()]; + } + } + else + { + Printf(TEXTCOLOR_RED "integer expected for '%s'\n", key); + arc.mErrors++; + node = nullptr; + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || pstr != *def) + { + arc.WriteKey(key); + if (pstr == nullptr) + { + arc.w->Null(); + } + else + { + arc.w->String(pstr->GetChars()); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + assert(val->IsNull() || val->IsString()); + if (val->IsNull()) + { + pstr = nullptr; + } + else if (val->IsString()) + { + pstr = AActor::mStringPropertyData.Alloc(UnicodeToString(val->GetString())); + } + else + { + Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); + pstr = nullptr; + arc.mErrors++; + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || strcmp(pstr, *def)) + { + arc.WriteKey(key); + if (pstr == nullptr) + { + arc.w->Null(); + } + else + { + arc.w->String(pstr); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + assert(val->IsNull() || val->IsString()); + if (val->IsNull()) + { + pstr = nullptr; + } + else if (val->IsString()) + { + pstr = copystring(UnicodeToString(val->GetString())); + } + else + { + Printf(TEXTCOLOR_RED "string expected for '%s'\n", key); + pstr = nullptr; + arc.mErrors++; + } + } + } + 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 object that is part of a level may reference a different one. +// +// 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) +{ + auto doomarc = static_cast(&arc); + 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 (doomarc->Level == nullptr || lev != doomarc->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 = doomarc->Level; + } + } + else lev = doomarc->Level; + } + return arc; +} + diff --git a/src/serializer_doom.h b/src/serializer_doom.h new file mode 100644 index 000000000..95256396d --- /dev/null +++ b/src/serializer_doom.h @@ -0,0 +1,61 @@ +#pragma once + +#include "serializer.h" + +class player_t; +struct sector_t; +struct line_t; +struct side_t; +struct vertex_t; +struct ticcmd_t; +struct usercmd_t; +class PClassActor; +struct FStrifeDialogueNode; +struct FState; +struct FDoorAnimation; +struct FPolyObj; +struct FInterpolator; +struct FLevelLocals; + +class FDoomSerializer : public FSerializer +{ + + void CloseReaderCustom() override; + +public: + FLevelLocals* Level; + + FDoomSerializer(FLevelLocals *l) : Level(l) + {} + + FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def) override; + +}; + +FSerializer &SerializeArgs(FSerializer &arc, const char *key, int *args, int *defargs, int special); +FSerializer &SerializeTerrain(FSerializer &arc, const char *key, int &terrain, int *def = nullptr); + +FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval); +FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &sid, ticcmd_t *def); +FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def); +FSerializer &Serialize(FSerializer &arc, const char *key, FInterpolator &rs, FInterpolator *def); + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def); +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, 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) +{ + return Serialize(arc, key, state, def, nullptr); +} diff --git a/src/serializer_internal.h b/src/serializer_internal.h new file mode 100644 index 000000000..7323f23f7 --- /dev/null +++ b/src/serializer_internal.h @@ -0,0 +1,240 @@ +const char* UnicodeToString(const char* cc); +const char* StringToUnicode(const char* cc, int size = -1); + +//========================================================================== +// +// +// +//========================================================================== + +struct FJSONObject +{ + rapidjson::Value* mObject; + rapidjson::Value::MemberIterator mIterator; + int mIndex; + + FJSONObject(rapidjson::Value* v) + { + mObject = v; + if (v->IsObject()) mIterator = v->MemberBegin(); + else if (v->IsArray()) + { + mIndex = 0; + } + } +}; + +//========================================================================== +// +// some wrapper stuff to keep the RapidJSON dependencies out of the global headers. +// FSerializer should not expose any of this, although it is needed by special serializers. +// +//========================================================================== + +struct FWriter +{ + typedef rapidjson::Writer > Writer; + typedef rapidjson::PrettyWriter > PrettyWriter; + + Writer *mWriter1; + PrettyWriter *mWriter2; + TArray mInObject; + rapidjson::StringBuffer mOutString; + TArray mDObjects; + TMap mObjectMap; + + FWriter(bool pretty) + { + if (!pretty) + { + mWriter1 = new Writer(mOutString); + mWriter2 = nullptr; + } + else + { + mWriter1 = nullptr; + mWriter2 = new PrettyWriter(mOutString); + } + } + + ~FWriter() + { + if (mWriter1) delete mWriter1; + if (mWriter2) delete mWriter2; + } + + + bool inObject() const + { + return mInObject.Size() > 0 && mInObject.Last(); + } + + void StartObject() + { + if (mWriter1) mWriter1->StartObject(); + else if (mWriter2) mWriter2->StartObject(); + } + + void EndObject() + { + if (mWriter1) mWriter1->EndObject(); + else if (mWriter2) mWriter2->EndObject(); + } + + void StartArray() + { + if (mWriter1) mWriter1->StartArray(); + else if (mWriter2) mWriter2->StartArray(); + } + + void EndArray() + { + if (mWriter1) mWriter1->EndArray(); + else if (mWriter2) mWriter2->EndArray(); + } + + void Key(const char *k) + { + if (mWriter1) mWriter1->Key(k); + else if (mWriter2) mWriter2->Key(k); + } + + void Null() + { + if (mWriter1) mWriter1->Null(); + else if (mWriter2) mWriter2->Null(); + } + + void StringU(const char *k, bool encode) + { + if (encode) k = StringToUnicode(k); + if (mWriter1) mWriter1->String(k); + else if (mWriter2) mWriter2->String(k); + } + + void String(const char *k) + { + k = StringToUnicode(k); + if (mWriter1) mWriter1->String(k); + else if (mWriter2) mWriter2->String(k); + } + + void String(const char *k, int size) + { + k = StringToUnicode(k, size); + if (mWriter1) mWriter1->String(k); + else if (mWriter2) mWriter2->String(k); + } + + void Bool(bool k) + { + if (mWriter1) mWriter1->Bool(k); + else if (mWriter2) mWriter2->Bool(k); + } + + void Int(int32_t k) + { + if (mWriter1) mWriter1->Int(k); + else if (mWriter2) mWriter2->Int(k); + } + + void Int64(int64_t k) + { + if (mWriter1) mWriter1->Int64(k); + else if (mWriter2) mWriter2->Int64(k); + } + + void Uint(uint32_t k) + { + if (mWriter1) mWriter1->Uint(k); + else if (mWriter2) mWriter2->Uint(k); + } + + void Uint64(int64_t k) + { + if (mWriter1) mWriter1->Uint64(k); + else if (mWriter2) mWriter2->Uint64(k); + } + + void Double(double k) + { + if (mWriter1) + { + mWriter1->Double(k); + } + else if (mWriter2) + { + mWriter2->Double(k); + } + } + +}; + +//========================================================================== +// +// +// +//========================================================================== + +struct FReader +{ + TArray mObjects; + rapidjson::Document mDoc; + TArray mDObjects; + rapidjson::Value *mKeyValue = nullptr; + bool mObjectsRead = false; + + FReader(const char *buffer, size_t length) + { + mDoc.Parse(buffer, length); + mObjects.Push(FJSONObject(&mDoc)); + } + + rapidjson::Value *FindKey(const char *key) + { + FJSONObject &obj = mObjects.Last(); + + if (obj.mObject->IsObject()) + { + if (key == nullptr) + { + // we are performing an iteration of the object through GetKey. + auto p = mKeyValue; + mKeyValue = nullptr; + return p; + } + else + { + // Find the given key by name; + auto it = obj.mObject->FindMember(key); + if (it == obj.mObject->MemberEnd()) return nullptr; + return &it->value; + } + } + else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size()) + { + return &(*obj.mObject)[obj.mIndex++]; + } + return nullptr; + } +}; + +//========================================================================== +// +// +// +//========================================================================== + +template +FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base) +{ + assert(base != nullptr); + if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval) + { + int64_t vv = value == nullptr ? -1 : value - base; + Serialize(arc, key, vv, nullptr); + value = vv < 0 ? nullptr : base + vv; + } + return arc; +} + diff --git a/src/sound/s_doomsound.cpp b/src/sound/s_doomsound.cpp index d99534bad..2bc50b5be 100644 --- a/src/sound/s_doomsound.cpp +++ b/src/sound/s_doomsound.cpp @@ -59,7 +59,7 @@ #include "gstrings.h" #include "gi.h" #include "po_man.h" -#include "serializer.h" +#include "serializer_doom.h" #include "d_player.h" #include "g_levellocals.h" #include "vm.h" diff --git a/src/sound/s_sndseq.cpp b/src/sound/s_sndseq.cpp index 01b30f5e4..80c1eda58 100644 --- a/src/sound/s_sndseq.cpp +++ b/src/sound/s_sndseq.cpp @@ -36,7 +36,7 @@ #include "c_dispatch.h" #include "g_level.h" -#include "serializer.h" +#include "serializer_doom.h" #include "serialize_obj.h" #include "d_player.h" #include "g_levellocals.h" diff --git a/src/utility/m_random.cpp b/src/utility/m_random.cpp index eaf252a67..41fc4e50c 100644 --- a/src/utility/m_random.cpp +++ b/src/utility/m_random.cpp @@ -64,6 +64,7 @@ #include "serializer.h" #include "m_crc32.h" #include "c_dispatch.h" +#include "printf.h" // MACROS ------------------------------------------------------------------