From 9313a99e1231220533ef49c5ba3aa3cefecec695 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Sep 2016 13:26:34 +0200 Subject: [PATCH 01/59] - started implementing a JSON based serializer. Unfortunately it is far too slow to be of any real use. --- src/CMakeLists.txt | 1 + src/actor.h | 6 +- src/dobject.h | 5 + src/dthinker.h | 3 + src/g_level.cpp | 4 + src/g_shared/a_soundenvironment.cpp | 2 +- src/json.cpp | 1585 +++++++++++++++++ src/p_saveg.cpp | 9 +- src/p_setup.cpp | 15 +- src/p_terrain.cpp | 21 +- src/p_terrain.h | 1 + src/r_defs.h | 6 +- src/r_state.h | 3 +- src/rapidjson/allocators.h | 271 +++ src/rapidjson/document.h | 2575 +++++++++++++++++++++++++++ src/rapidjson/encodedstream.h | 299 ++++ src/rapidjson/encodings.h | 716 ++++++++ src/rapidjson/error/en.h | 74 + src/rapidjson/error/error.h | 155 ++ src/rapidjson/filereadstream.h | 99 + src/rapidjson/filewritestream.h | 104 ++ src/rapidjson/fwd.h | 151 ++ src/rapidjson/internal/biginteger.h | 290 +++ src/rapidjson/internal/diyfp.h | 258 +++ src/rapidjson/internal/dtoa.h | 245 +++ src/rapidjson/internal/ieee754.h | 78 + src/rapidjson/internal/itoa.h | 304 ++++ src/rapidjson/internal/meta.h | 181 ++ src/rapidjson/internal/pow10.h | 55 + src/rapidjson/internal/regex.h | 701 ++++++++ src/rapidjson/internal/stack.h | 230 +++ src/rapidjson/internal/strfunc.h | 58 + src/rapidjson/internal/strtod.h | 269 +++ src/rapidjson/internal/swap.h | 46 + src/rapidjson/istreamwrapper.h | 115 ++ src/rapidjson/memorybuffer.h | 70 + src/rapidjson/memorystream.h | 71 + src/rapidjson/msinttypes/inttypes.h | 316 ++++ src/rapidjson/msinttypes/stdint.h | 300 ++++ src/rapidjson/ostreamwrapper.h | 81 + src/rapidjson/pointer.h | 1358 ++++++++++++++ src/rapidjson/prettywriter.h | 261 +++ src/rapidjson/rapidjson.h | 615 +++++++ src/rapidjson/reader.h | 1879 +++++++++++++++++++ src/rapidjson/schema.h | 2006 +++++++++++++++++++++ src/rapidjson/stream.h | 179 ++ src/rapidjson/stringbuffer.h | 117 ++ src/rapidjson/writer.h | 616 +++++++ src/s_sound.cpp | 4 +- src/tflags.h | 1 + 50 files changed, 16774 insertions(+), 35 deletions(-) create mode 100644 src/json.cpp create mode 100644 src/rapidjson/allocators.h create mode 100644 src/rapidjson/document.h create mode 100644 src/rapidjson/encodedstream.h create mode 100644 src/rapidjson/encodings.h create mode 100644 src/rapidjson/error/en.h create mode 100644 src/rapidjson/error/error.h create mode 100644 src/rapidjson/filereadstream.h create mode 100644 src/rapidjson/filewritestream.h create mode 100644 src/rapidjson/fwd.h create mode 100644 src/rapidjson/internal/biginteger.h create mode 100644 src/rapidjson/internal/diyfp.h create mode 100644 src/rapidjson/internal/dtoa.h create mode 100644 src/rapidjson/internal/ieee754.h create mode 100644 src/rapidjson/internal/itoa.h create mode 100644 src/rapidjson/internal/meta.h create mode 100644 src/rapidjson/internal/pow10.h create mode 100644 src/rapidjson/internal/regex.h create mode 100644 src/rapidjson/internal/stack.h create mode 100644 src/rapidjson/internal/strfunc.h create mode 100644 src/rapidjson/internal/strtod.h create mode 100644 src/rapidjson/internal/swap.h create mode 100644 src/rapidjson/istreamwrapper.h create mode 100644 src/rapidjson/memorybuffer.h create mode 100644 src/rapidjson/memorystream.h create mode 100644 src/rapidjson/msinttypes/inttypes.h create mode 100644 src/rapidjson/msinttypes/stdint.h create mode 100644 src/rapidjson/ostreamwrapper.h create mode 100644 src/rapidjson/pointer.h create mode 100644 src/rapidjson/prettywriter.h create mode 100644 src/rapidjson/rapidjson.h create mode 100644 src/rapidjson/reader.h create mode 100644 src/rapidjson/schema.h create mode 100644 src/rapidjson/stream.h create mode 100644 src/rapidjson/stringbuffer.h create mode 100644 src/rapidjson/writer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 190a9560a..55183af61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1007,6 +1007,7 @@ set( FASTMATH_SOURCES ) set (PCH_SOURCES + json.cpp actorptrselect.cpp am_map.cpp b_bot.cpp diff --git a/src/actor.h b/src/actor.h index 522405dd6..89c747880 100644 --- a/src/actor.h +++ b/src/actor.h @@ -582,7 +582,8 @@ public: ~AActor (); void Serialize (FArchive &arc); - + void Serialize(FSerializer &arc); + static AActor *StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false); inline AActor *GetDefault () const @@ -1179,8 +1180,9 @@ public: private: static AActor *TIDHash[128]; static inline int TIDHASH (int key) { return key & 127; } +public: static FSharedStringArena mStringPropertyData; - +private: friend class FActorIterator; friend bool P_IsTIDUsed(int tid); diff --git a/src/dobject.h b/src/dobject.h index 682e9bb08..787a07359 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -40,6 +40,7 @@ class PClass; class FArchive; +class FSerializer; class DObject; /* @@ -465,6 +466,10 @@ public: void SerializeUserVars(FArchive &arc); virtual void Serialize (FArchive &arc); + + void SerializeUserVars(FSerializer &arc); + virtual void Serialize(FSerializer &arc); + void ClearClass() { Class = NULL; diff --git a/src/dthinker.h b/src/dthinker.h index ac5adc431..b68c2e320 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -43,6 +43,7 @@ class player_t; struct pspdef_s; struct FState; class DThinker; +class FSerializer; class FThinkerIterator; @@ -83,6 +84,7 @@ public: DestroyThinkersInList(FreshThinkers[statnum]); } static void SerializeAll (FArchive &arc, bool keepPlayers); + static void SerializeThinkers(FSerializer &arc, bool keepPlayers); static void MarkRoots(); static DThinker *FirstThinker (int statnum); @@ -94,6 +96,7 @@ private: static void DestroyMostThinkersInList (FThinkerList &list, int stat); static int TickThinkers (FThinkerList *list, FThinkerList *dest); // Returns: # of thinkers ticked static void SaveList(FArchive &arc, DThinker *node); + static void SaveList(FSerializer &arc, DThinker *node); void Remove(); static FThinkerList Thinkers[MAX_STATNUM+2]; // Current thinkers diff --git a/src/g_level.cpp b/src/g_level.cpp index 60e9b0699..e71ecc1a1 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1482,6 +1482,8 @@ void G_AirControlChanged () void G_SerializeLevel (FArchive &arc, bool hubLoad) { int i = level.totaltime; + + unsigned tm = I_MSTime(); Renderer->StartSerialize(arc); if (arc.IsLoading()) P_DestroyThinkers(hubLoad); @@ -1599,6 +1601,8 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad) } } Renderer->EndSerialize(arc); + unsigned tt = I_MSTime(); + Printf("Serialization took %d ms\n", tt - tm); } //========================================================================== diff --git a/src/g_shared/a_soundenvironment.cpp b/src/g_shared/a_soundenvironment.cpp index 645298da8..ba0171157 100644 --- a/src/g_shared/a_soundenvironment.cpp +++ b/src/g_shared/a_soundenvironment.cpp @@ -59,7 +59,7 @@ void ASoundEnvironment::PostBeginPlay () void ASoundEnvironment::Activate (AActor *activator) { - zones[Sector->ZoneNumber].Environment = S_FindEnvironment ((args[0]<<8) | (args[1])); + Zones[Sector->ZoneNumber].Environment = S_FindEnvironment ((args[0]<<8) | (args[1])); } // Deactivate just exists so that you can flag the thing as dormant in an editor diff --git a/src/json.cpp b/src/json.cpp new file mode 100644 index 000000000..b49c006d1 --- /dev/null +++ b/src/json.cpp @@ -0,0 +1,1585 @@ +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 // disable this insanity which is bound to make the code break over time. +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 + +//#define PRETTY + +#include "rapidjson/rapidjson.h" +#include "rapidjson/writer.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/document.h" +#include "r_defs.h" +#include "r_local.h" +#include "p_lnspec.h" +#include "i_system.h" +#include "w_wad.h" +#include "p_terrain.h" +#include "c_dispatch.h" +#include "p_setup.h" +#include "p_conversation.h" +#include "dsectoreffect.h" +#include "actor.h" +#include "r_data/r_interpolate.h" +#include "g_shared/a_sharedglobal.h" + +//========================================================================== +// +// +// +//========================================================================== +FSerializer &Serialize(FSerializer &arc, const char *key, FName &value); + + +//========================================================================== +// +// +// +//========================================================================== +typedef rapidjson::Value FJSONValue; + +struct FJSONObject +{ + rapidjson::Value *mObject; + rapidjson::Value::MemberIterator mIterator; + + FJSONObject(rapidjson::Value *v) + { + mObject = v; + mIterator = v->MemberEnd(); + } +}; + + + +class FSerializer +{ +#ifndef PRETTY + typedef rapidjson::Writer > Writer; +#else + typedef rapidjson::PrettyWriter > Writer; +#endif + +public: + Writer *mWriter = nullptr; + TArray mInObject; + rapidjson::StringBuffer mOutString; + TArray mDObjects; + TMap mObjectMap; + TArray mObjects; + rapidjson::Value mDocObj; // just because RapidJSON is stupid and does not allow direct access to what's in the document. + + int ArraySize() + { + if (mObjects.Last().mObject->IsArray()) + { + return mObjects.Last().mObject->Size(); + } + else + { + return 0; + } + } + + rapidjson::Value *FindKey(const char *key) + { + FJSONObject &obj = mObjects.Last(); + if (obj.mObject->IsObject()) + { + // This will continue the search from the last found key, assuming + // that key are being read in the same order in which they were written. + // As long as this is the case this will reduce time here significantly. + // Do at most as many iterations as the object has members. Otherwise the key cannot be present. + for (rapidjson::SizeType i = 0; i < obj.mObject->MemberCount(); i++) + { + if (obj.mIterator == obj.mObject->MemberEnd()) obj.mIterator = obj.mObject->MemberBegin(); + else obj.mIterator++; + if (!strcmp(key, obj.mIterator->name.GetString())) + { + return &obj.mIterator->value; + } + } + } + else if (obj.mObject->IsArray()) + { + // todo: Get element at current index and increment. + } + return nullptr; + } + +public: + + bool OpenWriter(bool pretty) + { + if (mWriter != nullptr || mObjects.Size() > 0) + { + return false; + } + mWriter = new Writer(mOutString); + return true; + } + + bool OpenReader(rapidjson::Document *doc) + { + if (mWriter != nullptr || mObjects.Size() > 0 || !doc->IsObject()) + { + return false; + } + mDocObj = doc->GetObject(); + mObjects.Push(FJSONObject(&mDocObj)); + return true; + } + + bool isReading() const + { + return mWriter == nullptr; + } + + bool isWriting() const + { + return mWriter != nullptr; + } + + void WriteKey(const char *key) + { + if (mInObject.Size() > 0 && mInObject.Last()) + { + mWriter->Key(key); + } + } + + FSerializer &BeginObject(const char *name) + { + if (isWriting()) + { + WriteKey(name); + mWriter->StartObject(); + mInObject.Push(true); + } + else + { + auto val = FindKey(name); + if (val != nullptr) + { + if (val->IsObject()) + { + mObjects.Push(FJSONObject(val)); + } + else + { + I_Error("Object expected for '%s'", name); + } + } + else + { + I_Error("'%s' not found", name); + } + } + return *this; + } + + FSerializer &EndObject() + { + if (isWriting()) + { + mWriter->EndObject(); + mInObject.Pop(); + } + else + { + mObjects.Pop(); + } + return *this; + } + + FSerializer &BeginArray(const char *name) + { + if (isWriting()) + { + WriteKey(name); + mWriter->StartArray(); + mInObject.Push(false); + } + else + { + auto val = FindKey(name); + if (val != nullptr) + { + if (val->IsArray()) + { + mObjects.Push(FJSONObject(val)); + } + else + { + I_Error("Array expected for '%s'", name); + } + } + else + { + I_Error("'%s' not found", name); + } + } + return *this; + } + + FSerializer &EndArray() + { + if (isWriting()) + { + mWriter->EndArray(); + mInObject.Pop(); + } + else + { + mObjects.Pop(); + } + return *this; + } + + + //========================================================================== + // + // Special handler for args (because ACS specials' arg0 needs special treatment.) + // + //========================================================================== + + FSerializer &Args(const char *key, int *args, int special) + { + if (isWriting()) + { + WriteKey(key); + mWriter->StartArray(); + for (int i = 0; i < 5; i++) + { + if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) + { + mWriter->String(FName(ENamedName(-args[i])).GetChars()); + } + else + { + mWriter->Int(args[i]); + } + } + mWriter->EndArray(); + } + else + { + auto val = 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(aval.GetString()); + } + else + { + I_Error("Integer expected for '%s[%d]'", key, i); + } + } + } + } + else + { + I_Error("array expected for '%s'", key); + } + } + return *this; + } + + template + FSerializer &operator()(const char *key, T &obj) + { + return Serialize(*this, key, obj); + } + + template + FSerializer &Array(const char *key, T *obj, int count) + { + BeginArray(key); + for (int i = 0; i < count; i++) + { + Serialize(*this, nullptr, obj[i]); + } + return EndArray(); + } + + FSerializer &Terrain(const char *key, int &terrain) + { + FName terr = P_GetTerrainName(terrain); + Serialize(*this, key, terr); + if (isReading()) + { + terrain = P_FindTerrain(terr); + } + return *this; + } + + FSerializer &Sprite(const char *key, uint16_t &spritenum) + { + if (isWriting()) + { + WriteKey(key); + mWriter->String(sprites[spritenum].name, 4); + } + return *this; + } +}; + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, bool &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Bool(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsBool()) + { + value = val->GetBool(); + } + else + { + I_Error("boolean type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Int64(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsInt64()) + { + value = val->GetInt64(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Uint64(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsUint64()) + { + value = val->GetUint64(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Int(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsInt()) + { + value = val->GetInt(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Uint(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsUint()) + { + value = val->GetUint(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value) +{ + int32_t vv = value; + Serialize(arc, key, vv); + value = (int8_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, uint8_t &value) +{ + uint32_t vv = value; + Serialize(arc, key, vv); + value = (uint8_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value) +{ + int32_t vv = value; + Serialize(arc, key, vv); + value = (int16_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value) +{ + uint32_t vv = value; + Serialize(arc, key, vv); + value = (uint16_t)vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, double &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->Double(value); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsDouble()) + { + value = val->GetDouble(); + } + else + { + I_Error("float type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, float &value) +{ + double vv = value; + Serialize(arc, key, vv); + value = (float)vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value) +{ + ptrdiff_t vv = value == nullptr? -1 : value - sides; + Serialize(arc, key, vv); + value = vv < 0 ? nullptr : sides + vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value) +{ + ptrdiff_t vv = value == nullptr ? -1 : value - sectors; + Serialize(arc, key, vv); + value = vv < 0 ? nullptr : sectors + vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value) +{ + ptrdiff_t vv = value == nullptr ? -1 : value - players; + Serialize(arc, key, vv); + value = vv < 0 ? nullptr : players + vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value) +{ + ptrdiff_t vv = value == nullptr ? -1 : value - lines; + Serialize(arc, key, vv); + value = vv < 0 ? nullptr : lines + vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value) +{ + if (arc.isWriting()) + { + if (!value.Exists()) + { + arc.WriteKey(key); + arc.mWriter->Null(); + return arc; + } + if (value.isNull()) + { + // save 'no texture' in a more space saving way + arc.WriteKey(key); + arc.mWriter->Int(0); + return arc; + } + FTextureID chk = value; + if (chk.GetIndex() >= TexMan.NumTextures()) chk.SetNull(); + FTexture *pic = TexMan[chk]; + const char *name; + + if (Wads.GetLinkedTexture(pic->SourceLump) == pic) + { + name = Wads.GetLumpFullName(pic->SourceLump); + } + else + { + name = pic->Name; + } + arc.WriteKey(key); + arc.mWriter->StartObject(); + arc.mWriter->Key("name"); + arc.mWriter->String(name); + arc.mWriter->Key("usetype"); + arc.mWriter->Int(pic->UseType); + arc.mWriter->EndObject(); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsObject()) + { + const rapidjson::Value &nameval = (*val)["name"]; + const rapidjson::Value &typeval = (*val)["type"]; + if (nameval.IsString() && typeval.IsInt()) + { + value = TexMan.GetTexture(nameval.GetString(), typeval.GetInt()); + } + else + { + I_Error("object does not represent a texture for '%s'", key); + } + } + else if (val->IsNull()) + { + value.SetInvalid(); + } + else if (val->IsInt() && val->GetInt() == 0) + { + value.SetNull(); + } + else + { + I_Error("object does not represent a texture for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value) +{ + if (arc.isWriting()) + { + int ndx = -1; + if (value != nullptr) + { + int *pndx = arc.mObjectMap.CheckKey(value); + if (pndx != nullptr) ndx = *pndx; + else + { + ndx = arc.mDObjects.Push(value); + arc.mObjectMap[value] = ndx; + } + } + Serialize(arc, key, ndx); + } + else + { + } + return arc; +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, T *&value) +{ + DObject *v = static_cast(value); + Serialize(arc, key, v); + value = static_cast(v); + return arc; +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value) +{ + DObject *v = static_cast(value); + Serialize(arc, key, v); + value = static_cast(v); + return arc; +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value) +{ + arc.BeginArray(key); + if (arc.isReading()) + { + value.Resize(arc.ArraySize()); + } + for (unsigned i = 0; i < value.Size(); i++) + { + Serialize(arc, nullptr, value[i]); + } + return arc.EndArray(); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p) +{ + return arc.Array(key, &p[0], 3); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, DRotator &p) +{ + return arc.Array(key, &p[0], 3); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, DVector2 &p) +{ + return arc.Array(key, &p[0], 2); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, DAngle &p) +{ + return Serialize(arc, key, p.Degrees); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FName &value) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->String(value.GetChars()); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + value = val->GetString(); + } + else + { + I_Error("String expected for '%s'", key); + } + } + } + return arc; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line) +{ + return arc.BeginObject(key) + ("flags", line.flags) + ("activation", line.activation) + ("special", line.special) + ("alpha", line.alpha) + .Args("args", line.args, line.special) + ("portalindex", line.portalindex) + .Array("sides", line.sidedef, 2) + .EndObject(); + + // no need to store the sidedef references. Unless the map loader is changed they will not change between map loads. +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t::part &part) +{ + return arc.BeginObject(key) + ("xoffset", part.xOffset) + ("yoffset", part.yOffset) + ("xscale", part.xScale) + ("yscale", part.yScale) + ("texture", part.texture) + ("interpolation", part.interpolation) + .EndObject(); +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side) +{ + return arc.BeginObject(key) + .Array("textures", side.textures, 3) + ("light", side.Light) + ("flags", side.Flags) + ("leftside", side.LeftSide) + ("rightside", side.RightSide) + ("index", side.Index) + ("attacheddecals", side.AttachedDecals) + .EndObject(); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FLinkedSector &ls) +{ + return arc.BeginObject(key) + ("sector", ls.Sector) + ("type", ls.Type) + .EndObject(); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t::splane &p) +{ + return arc.BeginObject(key) + ("xoffs", p.xform.xOffs) + ("yoffs", p.xform.yOffs) + ("xscale", p.xform.xScale) + ("yscale", p.xform.yScale) + ("angle", p.xform.Angle) + ("baseyoffs", p.xform.baseyOffs) + ("baseangle", p.xform.baseAngle) + ("flags", p.Flags) + ("light", p.Light) + ("texture", p.Texture) + ("texz", p.TexZ) + ("alpha", p.alpha) + .EndObject(); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p) +{ + arc.BeginObject(key) + ("normal", p.normal) + ("d", p.D) + .EndObject(); + + if (arc.isReading() && p.normal.Z != 0) + { + p.negiC = 1 / p.normal.Z; + } + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe) +{ + return Serialize(arc, key, pe.d); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + arc.mWriter->StartObject(); + arc.mWriter->Key("color"); + arc.mWriter->Uint(cm->Color); + arc.mWriter->Key("fade"); + arc.mWriter->Uint(cm->Fade); + arc.mWriter->Key("desat"); + arc.mWriter->Uint(cm->Desaturate); + arc.mWriter->EndObject(); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsObject()) + { + const rapidjson::Value &colorval = (*val)["color"]; + const rapidjson::Value &fadeval = (*val)["fade"]; + const rapidjson::Value &desatval = (*val)["desat"]; + if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint()) + { + cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint()); + } + else + { + I_Error("object does not represent a colormap for '%s'", key); + } + } + else + { + I_Error("object does not represent a colormap for '%s'", key); + } + } + } + return arc; +} + + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p) +{ + return arc.BeginObject(key) + ("floorplane", p.floorplane) + ("ceilingplane", p.ceilingplane) + ("lightlevel", p.lightlevel) + ("special", p.special) + ("soundtraversed", p.soundtraversed) + ("seqtype", p.seqType) + ("seqname", p.SeqName) + ("friction", p.friction) + ("movefactor", p.movefactor) + ("stairlock", p.stairlock) + ("prevsec", p.prevsec) + ("nextsec", p.nextsec) + .Array("planes", p.planes, 2) + ("heightsec", p.heightsec) + ("bottommap", p.bottommap) + ("midmap", p.midmap) + ("topmap", p.topmap) + ("damageamount", p.damageamount) + ("damageinterval", p.damageinterval) + ("leakydamage", p.leakydamage) + ("damagetype", p.damagetype) + ("sky", p.sky) + ("moreflags", p.MoreFlags) + ("flags", p.Flags) + .Array("portals", p.Portals, 2) + ("zonenumber", p.ZoneNumber) + .Array("interpolations", p.interpolations, 4) + ("soundtarget", p.SoundTarget) + ("secacttarget", p.SecActTarget) + ("floordata", p.floordata) + ("ceilingdata", p.ceilingdata) + ("lightingdata", p.lightingdata) + ("fakefloor_sectors", p.e->FakeFloor.Sectors) + ("midtexf_lines", p.e->Midtex.Floor.AttachedLines) + ("midtexf_sectors", p.e->Midtex.Floor.AttachedSectors) + ("midtexc_lines", p.e->Midtex.Ceiling.AttachedLines) + ("midtexc_sectors", p.e->Midtex.Ceiling.AttachedSectors) + ("linked_floor", p.e->Linked.Floor.Sectors) + ("linked_ceiling", p.e->Linked.Ceiling.Sectors) + ("colormap", p.ColorMap) + .Terrain("floorterrain", p.terrainnum[0]) + .Terrain("ceilingterrain", p.terrainnum[1]) + .EndObject(); +} + + +FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss) +{ + BYTE by; + + if (arc.isWriting() && hasglnodes) + { + TArray encoded((numsubsectors + 5) / 6); + int p = 0; + for (int i = 0; i < numsubsectors; i += 6) + { + by = 0; + for (int j = 0; j < 6; j++) + { + if (i + j < numsubsectors && (subsectors[i + j].flags & SSECF_DRAWN)) + { + by |= (1 << j); + } + } + if (by < 10) by += '0'; + else if (by < 36) by += 'A' - 10; + else if (by < 62) by += 'a' - 36; + else if (by == 62) by = '-'; + else if (by == 63) by = '+'; + encoded[p++] = by; + } + arc.mWriter->Key(key); + arc.mWriter->StartArray(); + arc.mWriter->Int(numvertexes); + arc.mWriter->Int(numsubsectors); + arc.mWriter->Int(numnodes); + arc.mWriter->String(&encoded[0], (numsubsectors + 5) / 6); + arc.mWriter->EndArray(); + } + else + { + int num_verts, num_subs, num_nodes; + } + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c) +{ + int id = (arc.isReading() || c == nullptr) ? 0 : c->ID; + Serialize(arc, key, id); + if (arc.isReading()) + { + c = S_FindEnvironment(id); + } + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z) +{ + return Serialize(arc, key, z.Environment); +} + +//============================================================================ +// +// Save a line portal for savegames. +// +//============================================================================ + +FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port) +{ + return arc.BeginObject(key) + ("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("type", port.mType) + ("flags", port.mFlags) + ("defflags", port.mDefFlags) + ("align", port.mAlign) + .EndObject(); +} + +//============================================================================ +// +// Save a sector portal for savegames. +// +//============================================================================ + +FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port) +{ + return arc.BeginObject(key) + ("type", port.mType) + ("flags", port.mFlags) + ("partner", port.mPartner) + ("plane", port.mPlane) + ("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("planez", port.mPlaneZ) + ("skybox", port.mSkybox) + .EndObject(); +} + + +void DThinker::SaveList(FSerializer &arc, DThinker *node) +{ + if (node != NULL) + { + while (!(node->ObjectFlags & OF_Sentinel)) + { + assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); + ::Serialize(arc, nullptr, node); + node = node->NextThinker; + } + } +} + +void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) +{ + DThinker *thinker; + BYTE stat; + int statcount; + int i; + + if (arc.isWriting()) + { + arc.BeginArray("thinkers"); + for (i = 0; i <= MAX_STATNUM; i++) + { + arc.BeginArray(nullptr); + SaveList(arc, Thinkers[i].GetHead()); + SaveList(arc, FreshThinkers[i].GetHead()); + arc.EndArray(); + } + arc.EndArray(); + } + else + { + } +} + + +FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style) +{ + return arc.BeginObject(key) + ("blendop", style.BlendOp) + ("srcalpha", style.SrcAlpha) + ("dstalpha", style.DestAlpha) + ("flags", style.Flags) + .EndObject(); +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TFlags &flags) +{ + return Serialize(arc, key, flags.Value); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + const char *sn = (const char*)sid; + if (sn != nullptr) arc.mWriter->String(sn); + else arc.mWriter->Null(); + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + sid = val->GetString(); + } + else if (val->IsNull()) + { + sid = 0; + } + else + { + I_Error("string type expected for '%s'", key); + } + } + } + return arc; + +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state) +{ + if (arc.isWriting()) + { + arc.WriteKey(key); + if (state == nullptr) + { + arc.mWriter->Null(); + } + else + { + PClassActor *info = FState::StaticFindStateOwner(state); + + if (info != NULL) + { + arc.mWriter->StartArray(); + arc.mWriter->String(info->TypeName.GetChars()); + arc.mWriter->Uint((uint32_t)(state - info->OwnedStates)); + arc.mWriter->EndArray(); + } + else + { + arc.mWriter->Null(); + } + } + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + state = nullptr; + } + else if (val->IsArray()) + { + //rapidjson::Value cls = (*val)[0]; + //rapidjson::Value ndx = (*val)[1]; + } + else + { + I_Error("array type expected for '%s'", key); + } + } + } + return arc; + +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node) +{ + uint32_t convnum; + if (arc.isWriting()) + { + arc.WriteKey(key); + if (node == nullptr) + { + arc.mWriter->Null(); + } + else + { + arc.mWriter->Uint(node->ThisNodeNum); + } + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + node = nullptr; + } + else if (val->IsUint()) + { + if (val->GetUint() >= StrifeDialogues.Size()) + { + node = NULL; + } + else + { + node = StrifeDialogues[val->GetUint()]; + } + } + else + { + I_Error("integer expected for '%s'", key); + } + } + } + return arc; + +} + +FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr) +{ + uint32_t convnum; + if (arc.isWriting()) + { + arc.WriteKey(key); + if (pstr == nullptr) + { + arc.mWriter->Null(); + } + else + { + arc.mWriter->String(pstr->GetChars()); + } + } + else + { + auto val = arc.FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + pstr = nullptr; + } + else if (val->IsString()) + { + pstr = AActor::mStringPropertyData.Alloc(val->GetString()); + } + else + { + I_Error("string expected for '%s'", key); + } + } + } + return arc; + +} + + +/* +{ +FString tagstr; +if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; +arc << tagstr; +if (arc.IsLoading()) +{ +if (tagstr.Len() == 0) Tag = NULL; +else Tag = mStringPropertyData.Alloc(tagstr); +} +} +*/ + + +void SerializeWorld(FSerializer &arc) +{ + arc.Array("linedefs", lines, numlines) + .Array("sidedefs", sides, numsides) + .Array("sectors", sectors, numsectors) + ("subsectors", subsectors) + ("zones", Zones) + ("lineportals", linePortals) + ("sectorportals", sectorPortals); +} + +void DObject::SerializeUserVars(FSerializer &arc) +{ + PSymbolTable *symt; + FName varname; + DWORD count, j; + int *varloc = NULL; + + symt = &GetClass()->Symbols; + + if (arc.isWriting()) + { + // Write all fields that aren't serialized by native code. + //GetClass()->WriteValue(arc, this); + } + else + { + //GetClass()->ReadValue(arc, this); + } +} + +void DObject::Serialize(FSerializer &arc) +{ + ObjectFlags |= OF_SerialSuccess; +} + +void SerializeObjects(FSerializer &arc) +{ + if (arc.isWriting()) + { + arc.BeginArray("objects"); + for (unsigned i = 0; i < arc.mDObjects.Size(); i++) + { + arc.BeginObject(nullptr); + arc.WriteKey("classtype"); + arc.mWriter->String(arc.mDObjects[i]->GetClass()->TypeName.GetChars()); + arc.mDObjects[i]->Serialize(arc); + arc.EndObject(); + } + arc.EndArray(); + } +} + +//========================================================================== +// +// AActor :: Serialize +// +//========================================================================== + +void AActor::Serialize(FSerializer &arc) +{ + int damage = 0; // just a placeholder until the insanity surrounding the damage property can be fixed + + Super::Serialize(arc); + + arc + .Sprite("sprite", sprite) + ("pos", __Pos) + ("angles", Angles) + ("frame", frame) + ("scale", Scale) + ("renderstyle", RenderStyle) + ("renderflags", renderflags) + ("picnum", picnum) + ("floorpic", floorpic) + ("ceilingpic", ceilingpic) + ("tidtohate", TIDtoHate) + ("lastlookpn", LastLookPlayerNumber) + ("lastlookactor", LastLookActor) + ("effects", effects) + ("alpha", Alpha) + ("fillcolor", fillcolor) + ("sector", Sector) + ("floorz", floorz) + ("ceilingz", ceilingz) + ("dropoffz", dropoffz) + ("floorsector", floorsector) + ("ceilingsector", ceilingsector) + ("radius", radius) + ("height", Height) + ("ppassheight", projectilepassheight) + ("vel", Vel) + ("tics", tics) + ("state", state) + ("damage", damage) + .Terrain("floorterrain", floorterrain) + ("projectilekickback", projectileKickback) + ("flags", flags) + ("flags2", flags2) + ("flags3", flags3) + ("flags4", flags4) + ("flags5", flags5) + ("flags6", flags6) + ("flags7", flags7) + ("weaponspecial", weaponspecial) + ("special1", special1) + ("special2", special2) + ("specialf1", specialf1) + ("specialf2", specialf2) + ("health", health) + ("movedir", movedir) + ("visdir", visdir) + ("movecount", movecount) + ("strafecount", strafecount) + ("target", target) + ("lastenemy", lastenemy) + ("lastheard", LastHeard) + ("reactiontime", reactiontime) + ("threshold", threshold) + ("player", player) + ("spawnpoint", SpawnPoint) + ("spawnangle", SpawnAngle) + ("starthealth", StartHealth) + ("skillrespawncount", skillrespawncount) + ("tracer", tracer) + ("floorclip", Floorclip) + ("tid", tid) + ("special", special) + .Args("args", args, special) + ("accuracy", accuracy) + ("stamina", stamina) + ("goal", goal) + ("waterlevel", waterlevel) + ("minmissilechance", MinMissileChance) + ("spawnflags", SpawnFlags) + ("inventory", Inventory) + ("inventoryid", InventoryID) + ("floatbobphase", FloatBobPhase) + ("translation", Translation) + ("seesound", SeeSound) + ("attacksound", AttackSound) + ("paimsound", PainSound) + ("deathsound", DeathSound) + ("activesound", ActiveSound) + ("usesound", UseSound) + ("bouncesound", BounceSound) + ("wallbouncesound", WallBounceSound) + ("crushpainsound", CrushPainSound) + ("speed", Speed) + ("floatspeed", FloatSpeed) + ("mass", Mass) + ("painchance", PainChance) + ("spawnstate", SpawnState) + ("seestate", SeeState) + ("meleestate", MeleeState) + ("missilestate", MissileState) + ("maxdropoffheight", MaxDropOffHeight) + ("maxstepheight", MaxStepHeight) + ("bounceflags", BounceFlags) + ("bouncefactor", bouncefactor) + ("wallbouncefactor", wallbouncefactor) + ("bouncecount", bouncecount) + ("maxtargetrange", maxtargetrange) + ("meleethreshold", meleethreshold) + ("meleerange", meleerange) + ("damagetype", DamageType) + ("damagetypereceived", DamageTypeReceived) + ("paintype", PainType) + ("deathtype", DeathType) + ("gravity", Gravity) + ("fastchasestrafecount", FastChaseStrafeCount) + ("master", master) + ("smokecounter", smokecounter) + ("blockingmobj", BlockingMobj) + ("blockingline", BlockingLine) + ("visibletoteam", VisibleToTeam) + ("pushfactor", pushfactor) + ("species", Species) + ("score", Score) + ("designatedteam", DesignatedTeam) + ("lastpush", lastpush) + ("lastbump", lastbump) + ("painthreshold", PainThreshold) + ("damagefactor", DamageFactor) + ("damagemultiply", DamageMultiply) + ("waveindexxy", WeaveIndexXY) + ("weaveindexz", WeaveIndexZ) + ("pdmgreceived", PoisonDamageReceived) + ("pdurreceived", PoisonDurationReceived) + ("ppreceived", PoisonPeriodReceived) + ("poisoner", Poisoner) + ("posiondamage", PoisonDamage) + ("poisonduration", PoisonDuration) + ("poisonperiod", PoisonPeriod) + ("poisondamagetype", PoisonDamageType) + ("poisondmgtypereceived", PoisonDamageTypeReceived) + ("conversationroot", ConversationRoot) + ("conversation", Conversation) + ("friendplayer", FriendPlayer) + ("telefogsourcetype", TeleFogSourceType) + ("telefogdesttype", TeleFogDestType) + ("ripperlevel", RipperLevel) + ("riplevelmin", RipLevelMin) + ("riplevelmax", RipLevelMax) + ("devthreshold", DefThreshold) + ("spriteangle", SpriteAngle) + ("spriterotation", SpriteRotation) + ("alternative", alternative) + ("tag", Tag); + + +} + + +CCMD(writejson) +{ + DWORD t = I_MSTime(); + FSerializer arc; + arc.OpenWriter(true); + arc.BeginObject(nullptr); + DThinker::SerializeThinkers(arc, false); + SerializeWorld(arc); + SerializeObjects(arc); + arc.EndObject(); + DWORD tt = I_MSTime(); + Printf("JSON generation took %d ms\n", tt - t); + FILE *f = fopen("out.json", "wb"); + fwrite(arc.mOutString.GetString(), 1, arc.mOutString.GetSize(), f); + fclose(f); + DWORD ttt = I_MSTime(); + Printf("JSON save took %d ms\n", ttt - tt); + rapidjson::Document doc; + doc.Parse(arc.mOutString.GetString()); + DWORD tttt = I_MSTime(); + Printf("JSON parse took %d ms\n", tttt - ttt); + doc.ParseInsitu((char*)arc.mOutString.GetString()); + DWORD ttttt = I_MSTime(); + Printf("JSON parse insitu took %d ms\n", ttttt - tttt); +} diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index a6c34857f..3bbae666a 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -440,18 +440,15 @@ void P_SerializeWorld (FArchive &arc) } // do zones + unsigned numzones = Zones.Size(); arc << numzones; if (arc.IsLoading()) { - if (zones != NULL) - { - delete[] zones; - } - zones = new zone_t[numzones]; + Zones.Resize(numzones); } - for (i = 0, zn = zones; i < numzones; ++i, ++zn) + for (i = 0, zn = &Zones[0]; i < numzones; ++i, ++zn) { arc << zn->Environment; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index d8d95d277..0ff6b06b3 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -137,8 +137,7 @@ line_t* lines; int numsides; side_t* sides; -int numzones; -zone_t* zones; +TArray Zones; node_t * gamenodes; int numgamenodes; @@ -816,8 +815,7 @@ void P_FloodZones () P_FloodZone (§ors[i], z++); } } - numzones = z; - zones = new zone_t[z]; + Zones.Resize(z); reverb = S_FindEnvironment(level.DefaultEnvironment); if (reverb == NULL) { @@ -826,7 +824,7 @@ void P_FloodZones () } for (i = 0; i < z; ++i) { - zones[i].Environment = reverb; + Zones[i].Environment = reverb; } } @@ -3549,12 +3547,7 @@ void P_FreeLevelData () polyobjs = NULL; } po_NumPolyobjs = 0; - if (zones != NULL) - { - delete[] zones; - zones = NULL; - } - numzones = 0; + Zones.Clear(); P_FreeStrifeConversations (); if (level.Scrolls != NULL) { diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index ca5a8b998..c7c2848c0 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -713,19 +713,24 @@ int P_FindTerrain (FName name) return -1; } +FName P_GetTerrainName(int terrainnum) +{ + if (terrainnum < 0 || terrainnum >= (int)Terrains.Size()) + { + return NAME_Null; + } + else + { + return Terrains[terrainnum].Name; + } +} + void P_SerializeTerrain(FArchive &arc, int &terrainnum) { FName val; if (arc.IsStoring()) { - if (terrainnum < 0 || terrainnum >= (int)Terrains.Size()) - { - val = NAME_Null; - } - else - { - val = Terrains[terrainnum].Name; - } + val = P_GetTerrainName(terrainnum); arc << val; } else diff --git a/src/p_terrain.h b/src/p_terrain.h index 2a15eb976..77d57da6c 100644 --- a/src/p_terrain.h +++ b/src/p_terrain.h @@ -125,5 +125,6 @@ extern TArray Terrains; class FArchive; int P_FindTerrain(FName name); void P_SerializeTerrain(FArchive &arc, int &terrainnum); +FName P_GetTerrainName(int terrainnum); #endif //__P_TERRAIN_H__ diff --git a/src/r_defs.h b/src/r_defs.h index f27ac2716..44114de3c 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -275,7 +275,7 @@ struct secplane_t // the plane is defined as a*x + b*y + c*z + d = 0 // ic is 1/c, for faster Z calculations -private: +//private: // restore when JSON serializer is done. DVector3 normal; double D, negiC; // negative iC because that also saves a negation in all methods using this. public: @@ -1205,8 +1205,8 @@ struct line_t private: DVector2 delta; // precalculated v2 - v1 for side checking public: - DWORD flags; - DWORD activation; // activation type + uint32_t flags; + uint32_t activation; // activation type int special; int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) double alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) diff --git a/src/r_state.h b/src/r_state.h index fe3a21060..b66ad57eb 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -65,8 +65,7 @@ extern line_t* lines; extern int numsides; extern side_t* sides; -extern int numzones; -extern zone_t* zones; +extern TArray Zones; extern node_t * gamenodes; extern int numgamenodes; diff --git a/src/rapidjson/allocators.h b/src/rapidjson/allocators.h new file mode 100644 index 000000000..98affe03f --- /dev/null +++ b/src/rapidjson/allocators.h @@ -0,0 +1,271 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator()); + if (ChunkHeader* chunk = reinterpret_cast(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/src/rapidjson/document.h b/src/rapidjson/document.h new file mode 100644 index 000000000..19f5a6a5f --- /dev/null +++ b/src/rapidjson/document.h @@ -0,0 +1,2575 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include + +RAPIDJSON_DIAG_PUSH +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#if __GNUC__ >= 6 +RAPIDJSON_DIAG_OFF(terminate) // ignore throwing RAPIDJSON_ASSERT in RAPIDJSON_NOEXCEPT functions +#endif +#endif // __GNUC__ + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::iterator, std::random_access_iterator_tag +#endif + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +struct GenericMember { + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator + : public std::iterator >::Type> { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef std::iterator BaseType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + //! Pointer to (const) GenericMember + typedef typename BaseType::pointer Pointer; + //! Reference to (const) GenericMember + typedef typename BaseType::reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef typename BaseType::difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + bool operator==(ConstIterator that) const { return ptr_ == that.ptr_; } + bool operator!=(ConstIterator that) const { return ptr_ != that.ptr_; } + bool operator<=(ConstIterator that) const { return ptr_ <= that.ptr_; } + bool operator>=(ConstIterator that) const { return ptr_ >= that.ptr_; } + bool operator< (ConstIterator that) const { return ptr_ < that.ptr_; } + bool operator> (ConstIterator that) const { return ptr_ > that.ptr_; } + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +struct GenericMemberIterator; + +//! non-const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +struct GenericMemberIterator { + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template > +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_ASSERT(type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \see CopyFrom() + */ + template< typename SourceAllocator > + GenericValue(const GenericValue& rhs, Allocator & allocator); + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } + break; + + case kObjectFlag: + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + break; + + case kCopyStringFlag: + Allocator::Free(const_cast(GetStringPointer())); + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + RAPIDJSON_ASSERT(this != &rhs); + this->~GenericValue(); + RawAssign(rhs); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Linear time complexity (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if ((*this)[i] != rhs[i]) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast(std::numeric_limits::max())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast(std::numeric_limits::min())) + && (d < static_cast(std::numeric_limits::max())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-std::numeric_limits::max()) + || a > static_cast(std::numeric_limits::max())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + + // This will generate -Wexit-time-destructors in clang + // static GenericValue NullValue; + // return NullValue; + + // Use static buffer and placement-new to prevent destruction + static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + + ObjectData& o = data_.o; + if (o.size >= o.capacity) { + if (o.capacity == 0) { + o.capacity = kDefaultObjectCapacity; + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); + } + else { + SizeType oldCapacity = o.capacity; + o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); + } + } + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); + o.size++; + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy + --data_.o.size; + return m; + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); + std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(f); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(s.data(), SizeType(s.size()), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (const GenericValue* v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + kTrueFlag = kTrueType | kBoolFlag, + kFalseFlag = kFalseType | kBoolFlag, + kNumberIntFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag, + kNumberUintFlag = kNumberType | kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag, + kNumberInt64Flag = kNumberType | kNumberFlag | kInt64Flag, + kNumberUint64Flag = kNumberType | kNumberFlag | kUint64Flag, + kNumberDoubleFlag = kNumberType | kNumberFlag | kDoubleFlag, + kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, + kConstStringFlag = kStringType | kStringFlag, + kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = 16; + static const SizeType kDefaultObjectCapacity = 16; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + } + std::memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template , typename StackAllocator = CrtAllocator> +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(static_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + +// defined here due to the dependency on GenericDocument +template +template +inline +GenericValue::GenericValue(const GenericValue& rhs, Allocator& allocator) +{ + switch (rhs.GetType()) { + case kObjectType: + case kArrayType: { // perform deep copy via SAX Handler + GenericDocument d(&allocator); + rhs.Accept(d); + RawAssign(*d.stack_.template Pop(1)); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } else { + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + } + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } +} + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { return value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/src/rapidjson/encodedstream.h b/src/rapidjson/encodedstream.h new file mode 100644 index 000000000..145068386 --- /dev/null +++ b/src/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/rapidjson/encodings.h b/src/rapidjson/encodings.h new file mode 100644 index 000000000..baa7c2b17 --- /dev/null +++ b/src/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFF >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define COPY() os.Put(c = is.Take()) +#define TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define TAIL() COPY(); TRANS(0x70) + Ch c; + COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: TAIL(); return result; + case 3: TAIL(); TAIL(); return result; + case 4: COPY(); TRANS(0x50); TAIL(); return result; + case 5: COPY(); TRANS(0x10); TAIL(); TAIL(); return result; + case 6: TAIL(); TAIL(); TAIL(); return result; + case 10: COPY(); TRANS(0x20); TAIL(); return result; + case 11: COPY(); TRANS(0x60); TAIL(); TAIL(); return result; + default: return false; + } +#undef COPY +#undef TRANS +#undef TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put((v & 0x3FF) | 0xDC00); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, (v & 0x3FF) | 0xDC00); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(is.Take()); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + RAPIDJSON_FORCEINLINE static void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + RAPIDJSON_FORCEINLINE static bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + RAPIDJSON_FORCEINLINE static bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/src/rapidjson/error/en.h b/src/rapidjson/error/en.h new file mode 100644 index 000000000..2db838bff --- /dev/null +++ b/src/rapidjson/error/en.h @@ -0,0 +1,74 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/src/rapidjson/error/error.h b/src/rapidjson/error/error.h new file mode 100644 index 000000000..95cb31a72 --- /dev/null +++ b/src/rapidjson/error/error.h @@ -0,0 +1,155 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literial to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). + operator bool() const { return !IsError(); } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/src/rapidjson/filereadstream.h b/src/rapidjson/filereadstream.h new file mode 100644 index 000000000..b56ea13b3 --- /dev/null +++ b/src/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/rapidjson/filewritestream.h b/src/rapidjson/filewritestream.h new file mode 100644 index 000000000..6378dd60e --- /dev/null +++ b/src/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/src/rapidjson/fwd.h b/src/rapidjson/fwd.h new file mode 100644 index 000000000..e8104e841 --- /dev/null +++ b/src/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +struct GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/src/rapidjson/internal/biginteger.h b/src/rapidjson/internal/biginteger.h new file mode 100644 index 000000000..9d3e88c99 --- /dev/null +++ b/src/rapidjson/internal/biginteger.h @@ -0,0 +1,290 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include // for _umul128 +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + BigInteger(const char* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + void AppendDecimal64(const char* begin, const char* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + static uint64_t ParseUint64(const char* begin, const char* end) { + uint64_t r = 0; + for (const char* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= '0' && *p <= '9'); + r = r * 10u + static_cast(*p - '0'); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/src/rapidjson/internal/diyfp.h b/src/rapidjson/internal/diyfp.h new file mode 100644 index 000000000..c9fefdc61 --- /dev/null +++ b/src/rapidjson/internal/diyfp.h @@ -0,0 +1,258 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && defined(_M_AMD64) +#include +#pragma intrinsic(_BitScanReverse64) +#pragma intrinsic(_umul128) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { +#if defined(_MSC_VER) && defined(_M_AMD64) + unsigned long index; + _BitScanReverse64(&index, f); + return DiyFp(f << (63 - index), e - (63 - index)); +#elif defined(__GNUC__) && __GNUC__ >= 4 + int s = __builtin_clzll(f); + return DiyFp(f << s, e - s); +#else + DiyFp res = *this; + while (!(res.f & (static_cast(1) << 63))) { + res.f <<= 1; + res.e--; + } + return res; +#endif + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + unsigned index = (static_cast(exp) + 348u) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); + } + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/src/rapidjson/internal/dtoa.h b/src/rapidjson/internal/dtoa.h new file mode 100644 index 000000000..8d6350e62 --- /dev/null +++ b/src/rapidjson/internal/dtoa.h @@ -0,0 +1,245 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline unsigned CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + unsigned kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, static_cast(kPow10[kappa]) << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -static_cast(kappa); + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 9 ? kPow10[-static_cast(kappa)] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/src/rapidjson/internal/ieee754.h b/src/rapidjson/internal/ieee754.h new file mode 100644 index 000000000..82bb0b99e --- /dev/null +++ b/src/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static unsigned EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return static_cast(order) + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/src/rapidjson/internal/itoa.h b/src/rapidjson/internal/itoa.h new file mode 100644 index 000000000..01a4e7e72 --- /dev/null +++ b/src/rapidjson/internal/itoa.h @@ -0,0 +1,304 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + if (value >= kTen8) + *buffer++ = cDigitsLut[d4 + 1]; + + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/src/rapidjson/internal/meta.h b/src/rapidjson/internal/meta.h new file mode 100644 index 000000000..5a9aaa428 --- /dev/null +++ b/src/rapidjson/internal/meta.h @@ -0,0 +1,181 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(__GNUC__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/src/rapidjson/internal/pow10.h b/src/rapidjson/internal/pow10.h new file mode 100644 index 000000000..02f475d70 --- /dev/null +++ b/src/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/src/rapidjson/internal/regex.h b/src/rapidjson/internal/regex.h new file mode 100644 index 000000000..422a5240b --- /dev/null +++ b/src/rapidjson/internal/regex.h @@ -0,0 +1,701 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(implicit-fallthrough) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef typename Encoding::Ch Ch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream > ds(ss); + Parse(ds); + } + + ~GenericRegex() { + Allocator::Free(stateSet_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + + template + bool Match(InputStream& is) const { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) const { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) const { + return SearchWithAnchoring(is, anchorBegin_, anchorEnd_); + } + + bool Search(const Ch* s) const { + GenericStringStream is(s); + return Search(is); + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + template + class DecodedStream { + public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + + private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Allocator allocator; + Stack operandStack(&allocator, 256); // Frag + Stack operatorStack(&allocator, 256); // Operator + Stack atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + + // Preallocate buffer for SearchWithAnchoring() + RAPIDJSON_ASSERT(stateSet_ == 0); + if (stateCount_ > 0) { + stateSet_ = static_cast(states_.GetAllocator().Malloc(GetStateSetSize())); + state0_.template Reserve(stateCount_); + state1_.template Reserve(stateCount_); + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + default: + RAPIDJSON_ASSERT(op == kOneOrMore); + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const { + RAPIDJSON_ASSERT(IsValid()); + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == kAnyCharacterClass || + (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) const { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) { + stateSet_[index >> 5] |= (1 << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = GetRange(rangeIndex); + if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + uint32_t* stateSet_; // allocated by states_.GetAllocator() + mutable Stack state0_; + mutable Stack state1_; + bool anchorBegin_; + bool anchorEnd_; +}; + +typedef GenericRegex > Regex; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/src/rapidjson/internal/stack.h b/src/rapidjson/internal/stack.h new file mode 100644 index 000000000..022c9aab4 --- /dev/null +++ b/src/rapidjson/internal/stack.h @@ -0,0 +1,230 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(stackTop_ + sizeof(T) * count > stackEnd_)) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_ + sizeof(T) * count <= stackEnd_); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/src/rapidjson/internal/strfunc.h b/src/rapidjson/internal/strfunc.h new file mode 100644 index 000000000..de41d8f9c --- /dev/null +++ b/src/rapidjson/internal/strfunc.h @@ -0,0 +1,58 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/src/rapidjson/internal/strtod.h b/src/rapidjson/internal/strtod.h new file mode 100644 index 000000000..289c413b0 --- /dev/null +++ b/src/rapidjson/internal/strtod.h @@ -0,0 +1,269 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +inline bool StrtodDiyFp(const char* decimals, size_t length, size_t decimalPosition, int exp, double* result) { + uint64_t significand = 0; + size_t i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < length; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > '5')) + break; + significand = significand * 10u + static_cast(decimals[i] - '0'); + } + + if (i < length && decimals[i] >= '5') // Rounding + significand++; + + size_t remaining = length - i; + const unsigned kUlpShift = 3; + const unsigned kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + const int dExp = static_cast(decimalPosition) - static_cast(i) + exp; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp - 1; + RAPIDJSON_ASSERT(adjustment >= 0 && adjustment < 7); + v = v * kPow10[adjustment]; + if (length + static_cast(adjustment)> 19u) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const unsigned effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + unsigned precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + unsigned scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + static_cast(kUlp); + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + static_cast(precisionSize)); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +inline double StrtodBigInteger(double approx, const char* decimals, size_t length, size_t decimalPosition, int exp) { + const BigInteger dInt(decimals, length); + const int dExp = static_cast(decimalPosition) - static_cast(length) + exp; + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +inline double StrtodFullPrecision(double d, int p, const char* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result; + if (StrtodFast(d, p, &result)) + return result; + + // Trim leading zeros + while (*decimals == '0' && length > 1) { + length--; + decimals++; + decimalPosition--; + } + + // Trim trailing zeros + while (decimals[length - 1] == '0' && length > 1) { + length--; + decimalPosition--; + exp++; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 780; + if (static_cast(length) > kMaxDecimalDigit) { + int delta = (static_cast(length) - kMaxDecimalDigit); + exp += delta; + decimalPosition -= static_cast(delta); + length = kMaxDecimalDigit; + } + + // If too small, underflow to zero + if (int(length) + exp < -324) + return 0.0; + + if (StrtodDiyFp(decimals, length, decimalPosition, exp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, length, decimalPosition, exp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/src/rapidjson/internal/swap.h b/src/rapidjson/internal/swap.h new file mode 100644 index 000000000..666e49f97 --- /dev/null +++ b/src/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/src/rapidjson/istreamwrapper.h b/src/rapidjson/istreamwrapper.h new file mode 100644 index 000000000..f5fe28977 --- /dev/null +++ b/src/rapidjson/istreamwrapper.h @@ -0,0 +1,115 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicIStreamWrapper(StreamType& stream) : stream_(stream), count_(), peekBuffer_() {} + + Ch Peek() const { + typename StreamType::int_type c = stream_.peek(); + return RAPIDJSON_LIKELY(c != StreamType::traits_type::eof()) ? static_cast(c) : '\0'; + } + + Ch Take() { + typename StreamType::int_type c = stream_.get(); + if (RAPIDJSON_LIKELY(c != StreamType::traits_type::eof())) { + count_++; + return static_cast(c); + } + else + return '\0'; + } + + // tellg() may return -1 when failed. So we count by ourself. + size_t Tell() const { return count_; } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + RAPIDJSON_ASSERT(sizeof(Ch) == 1); // Only usable for byte stream. + int i; + bool hasError = false; + for (i = 0; i < 4; ++i) { + typename StreamType::int_type c = stream_.get(); + if (c == StreamType::traits_type::eof()) { + hasError = true; + stream_.clear(); + break; + } + peekBuffer_[i] = static_cast(c); + } + for (--i; i >= 0; --i) + stream_.putback(peekBuffer_[i]); + return !hasError ? peekBuffer_ : 0; + } + +private: + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + StreamType& stream_; + size_t count_; //!< Number of characters read. Note: + mutable Ch peekBuffer_[4]; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/src/rapidjson/memorybuffer.h b/src/rapidjson/memorybuffer.h new file mode 100644 index 000000000..39bee1dec --- /dev/null +++ b/src/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/src/rapidjson/memorystream.h b/src/rapidjson/memorystream.h new file mode 100644 index 000000000..1d71d8a4f --- /dev/null +++ b/src/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/src/rapidjson/msinttypes/inttypes.h b/src/rapidjson/msinttypes/inttypes.h new file mode 100644 index 000000000..18111286b --- /dev/null +++ b/src/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/src/rapidjson/msinttypes/stdint.h b/src/rapidjson/msinttypes/stdint.h new file mode 100644 index 000000000..3d4477b9a --- /dev/null +++ b/src/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/src/rapidjson/ostreamwrapper.h b/src/rapidjson/ostreamwrapper.h new file mode 100644 index 000000000..6f4667c08 --- /dev/null +++ b/src/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/src/rapidjson/pointer.h b/src/rapidjson/pointer.h new file mode 100644 index 000000000..0206ac1c8 --- /dev/null +++ b/src/rapidjson/pointer.h @@ -0,0 +1,1358 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "internal/itoa.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + if (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = buffer[i]; + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + v = &(--v->MemberEnd())->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + Value& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericStringRef(t->name, t->length)); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // Adjust pointers to name buffer + std::ptrdiff_t diff = nameBuffer_ - rhs.nameBuffer_; + for (Token *t = tokens_; t != tokens_ + rhs.tokenCount_; ++t) + t->name += diff; + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(hexDigits[u >> 4]); + os_.Put(hexDigits[u & 15]); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/src/rapidjson/prettywriter.h b/src/rapidjson/prettywriter.h new file mode 100644 index 000000000..c6f0216e9 --- /dev/null +++ b/src/rapidjson/prettywriter.h @@ -0,0 +1,261 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of ouptut os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::WriteString(str, length); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::WriteString(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndObject(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::WriteEndArray(); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::os_->Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::WriteRawValue(json, length); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/rapidjson/rapidjson.h b/src/rapidjson/rapidjson.h new file mode 100644 index 000000000..053b2ce43 --- /dev/null +++ b/src/rapidjson/rapidjson.h @@ -0,0 +1,615 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overriden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && defined(_M_ARM) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. Currently the default uses 4 bytes + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#if RAPIDJSON_64BIT == 1 +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#else +#define RAPIDJSON_ALIGN(x) (((x) + 3u) & ~3u) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2 optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2 or SSE4.2 SIMD extensions on modern Intel-compatible + processors. + + To enable these optimizations, two different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + \c RAPIDJSON_SSE42 takes precedence, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Adopt from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y + +#if defined(__GNUC__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(x) new x +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/rapidjson/reader.h b/src/rapidjson/reader.h new file mode 100644 index 000000000..19f8849b1 --- /dev/null +++ b/src/rapidjson/reader.h @@ -0,0 +1,1879 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cvtsi128_si32(_mm_cmpistrm(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY)); + if (r != 0) { // some of characters is non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_SSE2 + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(stackAllocator, stackCapacity), parseResult_() {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n'); + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the inital '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const char* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is), stackStream(reader.stack_) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(char c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const char* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& is) : Base(reader, is) {} + ~NumberStream() {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + useNanOrInf = true; + if (RAPIDJSON_LIKELY(Consume(s, 'N') && Consume(s, 'a') && Consume(s, 'N'))) { + d = std::numeric_limits::quiet_NaN(); + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I') && Consume(s, 'n') && Consume(s, 'f'))) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(d >= 1.7976931348623157e307)) // DBL_MAX / 10.0 + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (Consume(s, 'e') || Consume(s, 'E')) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (exp >= 214748364) { // Issue #313: prevent overflow exponent + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + StringStream srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const char* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingStartState = 0, + IterativeParsingFinishState, + IterativeParsingErrorState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingKeyValueDelimiterState, + IterativeParsingMemberValueState, + IterativeParsingMemberDelimiterState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingElementDelimiterState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState + }; + + enum { cIterativeParsingStateCount = IterativeParsingValueState + 1 }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + } + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/src/rapidjson/schema.h b/src/rapidjson/schema.h new file mode 100644 index 000000000..8497d3031 --- /dev/null +++ b/src/rapidjson/schema.h @@ -0,0 +1,2006 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 0 +#endif + +#if !RAPIDJSON_SCHEMA_USE_INTERNALREGEX && !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) && (__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 1 +#else +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +#if RAPIDJSON_SCHEMA_VERBOSE +#include "stringbuffer.h" +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeyword(const char* keyword) { + printf("Fail keyword: %s\n", keyword); +} + +inline void PrintInvalidKeyword(const wchar_t* keyword) { + wprintf(L"Fail keyword: %ls\n", keyword); +} + +inline void PrintInvalidDocument(const char* document) { + printf("Fail document: %s\n\n", document); +} + +inline void PrintInvalidDocument(const wchar_t* document) { + wprintf(L"Fail document: %ls\n\n", document); +} + +inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) { + printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) { + wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword) +#else +#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) +#endif + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(keyword)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidKeyword = keyword.GetString();\ + RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword.GetString());\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, const SchemaType* s) : + factory(f), + schema(s), + valueSchema(), + invalidKeyword(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) + factory.DestroySchemaValidator(validators[i]); + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + const SchemaType* schema; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator) : + allocator_(allocator), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false) + { + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + if (!value.IsObject()) + return; + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256 + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + + if (schemaDocument) { + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + } + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = GetTypeless(); + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document); + } + } + + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + } + + ~Schema() { + if (allocator_) { + allocator_->Free(enum_); + } + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + allocator_->Free(pattern_); + } +#endif + } + + bool BeginValue(Context& context) const { + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = GetTypeless(); + else + RAPIDJSON_INVALID_KEYWORD_RETURN(GetItemsString()); + } + else + context.valueSchema = GetTypeless(); + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + else if (!patternValid && !otherValid) // kPatternValidatorWithAdditionalProperty) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternPropertiesString()); + } + + if (enum_) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetEnumString()); + foundEnum:; + } + + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAllOfString()); + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAnyOfString()); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + else + oneValid = true; + } + if (!oneValid) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetOneOfString()); + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetNotString()); + + return true; + } + + bool Null(Context& context) const { + if (!(type_ & (1 << kNullSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + if (!(type_ & (1 << kNumberSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + if (!(type_ & (1 << kStringSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinLengthString()); + if (count > maxLength_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxLengthString()); + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetPatternString()); + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + if (!(type_ & (1 << kObjectSchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + } + + SizeType index; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (additionalPropertiesSchema_ && context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = GetTypeless(); + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = GetTypeless(); + return true; + } + + if (context.patternPropertiesSchemaCount == 0) // patternProperties are not additional properties + RAPIDJSON_INVALID_KEYWORD_RETURN(GetAdditionalPropertiesString()); + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + if (hasRequired_) + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required) + if (!context.propertyExist[index]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetRequiredString()); + + if (memberCount < minProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinPropertiesString()); + + if (memberCount > maxProperties_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxPropertiesString()); + + if (hasDependencies_) { + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) + if (context.propertyExist[sourceIndex]) { + if (properties_[sourceIndex].dependencies) { + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (properties_[sourceIndex].dependencies[targetIndex] && !context.propertyExist[targetIndex]) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + else if (properties_[sourceIndex].dependenciesSchema) + if (!context.validators[properties_[sourceIndex].dependenciesValidatorIndex]->IsValid()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetDependenciesString()); + } + } + + return true; + } + + bool StartArray(Context& context) const { + if (!(type_ & (1 << kArraySchemaType))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + context.arrayElementIndex = 0; + context.inArray = true; + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + context.inArray = false; + + if (elementCount < minItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinItemsString()); + + if (elementCount > maxItems_) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaxItemsString()); + + return true; + } + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, sizeof(s) / sizeof(Ch) - 1);\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + static const SchemaType* GetTypeless() { + static SchemaType typeless(0, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), 0); + return &typeless; + } + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString()); + if (!r->IsValid()) { + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + return pattern->Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) + try { + return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { return 0; } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + context.validatorCount = validatorCount_; + + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema); + } + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i]); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsUint64()) { + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsUint64()) + /* do nothing */; // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetTypeString()); + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + } + else if (maximum_.IsInt64()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); // i >= 0 > maximum_ + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMinimumString()); + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMaximumString()); + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = std::floor(a / b); + double r = a - q * b; + if (r > 0.0) + RAPIDJSON_INVALID_KEYWORD_RETURN(GetMultipleOfString()); + return true; + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = buffer[i]; + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + if (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + */ + explicit GenericSchemaDocument(const ValueType& document, IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize) + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator()); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call AddRefSchema() if there are $ref. + CreateSchemaRecursive(&root_, PointerType(), document, document); + + // Resolve $ref + while (!schemaRef_.Empty()) { + SchemaRefEntry* refEntry = schemaRef_.template Pop(1); + if (const SchemaType* s = GetSchema(refEntry->target)) { + if (refEntry->schema) + *refEntry->schema = s; + + // Create entry in map if not exist + if (!GetSchema(refEntry->source)) { + new (schemaMap_.template Push()) SchemaEntry(refEntry->source, const_cast(s), false, allocator_); + } + } + refEntry->~SchemaRefEntry(); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + +private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + struct SchemaRefEntry { + SchemaRefEntry(const PointerType& s, const PointerType& t, const SchemaType** outSchema, Allocator *allocator) : source(s, allocator), target(t, allocator), schema(outSchema) {} + PointerType source; + PointerType target; + const SchemaType** schema; + }; + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + if (schema) + *schema = SchemaType::GetTypeless(); + + if (v.GetType() == kObjectType) { + const SchemaType* s = GetSchema(pointer); + if (!s) + CreateSchema(schema, pointer, v, document); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document); + } + + void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document) { + RAPIDJSON_ASSERT(pointer.IsValid()); + if (v.IsObject()) { + if (!HandleRefSchema(pointer, schema, v, document)) { + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_); + new (schemaMap_.template Push()) SchemaEntry(pointer, s, true, allocator_); + if (schema) + *schema = s; + } + } + } + + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document) { + static const Ch kRefString[] = { '$', 'r', 'e', 'f', '\0' }; + static const ValueType kRefValue(kRefString, 4); + + typename ValueType::ConstMemberIterator itr = v.FindMember(kRefValue); + if (itr == v.MemberEnd()) + return false; + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len > 0) { + const Ch* s = itr->value.GetString(); + SizeType i = 0; + while (i < len && s[i] != '#') // Find the first # + i++; + + if (i > 0) { // Remote reference, resolve immediately + if (remoteProvider_) { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(s, i)) { + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const SchemaType* sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + return true; + } + } + } + } + } + else if (s[i] == '#') { // Local reference, defer resolution + PointerType pointer(&s[i], len - i, allocator_); + if (pointer.IsValid()) { + if (const ValueType* nv = pointer.Get(document)) + if (HandleRefSchema(source, schema, *nv, document)) + return true; + + new (schemaRef_.template Push()) SchemaRefEntry(source, pointer, schema, allocator_); + return true; + } + } + } + } + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer from $ref and schema which holds the $ref +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator +{ +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + outputHandler_(outputHandler), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(0) +#endif + { + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + valid_ = true; + } + + //! Checks whether the current state is valid. + // Implementation of ISchemaValidator + virtual bool IsValid() const { return valid_; } + + //! Gets the JSON pointer pointed to the invalid schema. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : schemaDocument_->GetPointer(&CurrentSchema()); + } + + //! Gets the keyword of invalid schema. + const Ch* GetInvalidSchemaKeyword() const { + return schemaStack_.Empty() ? 0 : CurrentContext().invalidKeyword; + } + + //! Gets the JSON pointer pointed to the invalid value. + PointerType GetInvalidDocumentPointer() const { + return documentStack_.Empty() ? PointerType() : PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + internal::PrintInvalidDocument(documentStack_.template Bottom());\ +RAPIDJSON_MULTILINEMACRO_END +#else +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() +#endif + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if (!BeginValue() || !CurrentSchema().method arg1) {\ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\ + return valid_ = false;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + return valid_ = EndValue() && outputHandler_.method arg2 + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext() ), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + return valid_ = outputHandler_.StartObject(); + } + + bool Key(const Ch* str, SizeType len, bool copy) { + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + return valid_ = outputHandler_.Key(str, len, copy); + } + + bool EndObject(SizeType memberCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + return valid_ = outputHandler_.StartArray(); + } + + bool EndArray(SizeType elementCount) { + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount)) return valid_ = false; + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_ +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root) { + return new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, +#if RAPIDJSON_SCHEMA_VERBOSE + depth_ + 1, +#endif + &GetStateAllocator()); + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + return StateAllocator::Free(p); + } + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth, +#endif + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + outputHandler_(GetNullHandler()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + valid_(true) +#if RAPIDJSON_SCHEMA_VERBOSE + , depth_(depth) +#endif + { + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator()); + return *stateAllocator_; + } + + bool BeginValue() { + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext())) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + if (CurrentContext().valueSchema) + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i]); + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + if (!CurrentSchema().EndValue(CurrentContext())) + return false; + +#if RAPIDJSON_SCHEMA_VERBOSE + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb); + + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom()); +#endif + + uint64_t h = CurrentContext().arrayUniqueness ? static_cast(CurrentContext().hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + if (context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) + RAPIDJSON_INVALID_KEYWORD_RETURN(SchemaType::GetUniqueItemsString()); + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, &schema); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static OutputHandler& GetNullHandler() { + static OutputHandler nullHandler; + return nullHandler; + } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + OutputHandler& outputHandler_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + bool valid_; +#if RAPIDJSON_SCHEMA_VERBOSE + unsigned depth_; +#endif +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/src/rapidjson/stream.h b/src/rapidjson/stream.h new file mode 100644 index 000000000..fef82c252 --- /dev/null +++ b/src/rapidjson/stream.h @@ -0,0 +1,179 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/src/rapidjson/stringbuffer.h b/src/rapidjson/stringbuffer.h new file mode 100644 index 000000000..78f34d209 --- /dev/null +++ b/src/rapidjson/stringbuffer.h @@ -0,0 +1,117 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/src/rapidjson/writer.h b/src/rapidjson/writer.h new file mode 100644 index 000000000..c5a3b98a9 --- /dev/null +++ b/src/rapidjson/writer.h @@ -0,0 +1,616 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#endif + +#ifdef _MSC_VER +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + static const size_t kDefaultLevelDepth = 32; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename TargetEncoding::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + for (size_t i = 0; i < length; i++) { + RAPIDJSON_ASSERT(json[i] != '\0'); + PutUnsafe(*os_, json[i]); + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + os_->Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19 }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x19) == 0x19 + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + +RAPIDJSON_NAMESPACE_END + +#ifdef _MSC_VER +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 2149f7814..54c8b5cf8 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2033,8 +2033,8 @@ static void S_SetListener(SoundListener &listener, AActor *listenactor) listener.velocity.Zero(); listener.position = listenactor->SoundPos(); listener.underwater = listenactor->waterlevel == 3; - assert(zones != NULL); - listener.Environment = zones[listenactor->Sector->ZoneNumber].Environment; + assert(Zones.Size() > listenactor->Sector->ZoneNumber); + listener.Environment = Zones[listenactor->Sector->ZoneNumber].Environment; listener.valid = true; } else diff --git a/src/tflags.h b/src/tflags.h index 0c496b228..5f44c8391 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -98,6 +98,7 @@ private: template Self operator& (X value) const { return Self::FromInt (Value & value); } template Self operator^ (X value) const { return Self::FromInt (Value ^ value); } +public: // to be removed. TT Value; }; From a5628518c16b27b36fd8def20d4dfc39477bba19 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 18 Sep 2016 16:41:34 +0200 Subject: [PATCH 02/59] - cut down on data size by not saving trivial defaults. --- src/json.cpp | 382 ++++++++++++++++++++++++++---------------------- src/p_setup.cpp | 10 ++ 2 files changed, 218 insertions(+), 174 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index b49c006d1..fe4a2d5d4 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -23,6 +23,10 @@ #include "r_data/r_interpolate.h" #include "g_shared/a_sharedglobal.h" +TArray loadsectors; +TArray loadlines; +TArray loadsides; + //========================================================================== // // @@ -192,7 +196,7 @@ public: return *this; } - FSerializer &BeginArray(const char *name) + bool BeginArray(const char *name) { if (isWriting()) { @@ -216,10 +220,10 @@ public: } else { - I_Error("'%s' not found", name); + return false; } } - return *this; + return true; } FSerializer &EndArray() @@ -302,15 +306,25 @@ public: return Serialize(*this, key, obj); } + template + FSerializer &operator()(const char *key, T &obj, T & def) + { + if (isWriting() && !memcmp(&obj, &def, sizeof(T))) return *this; + return Serialize(*this, key, obj); + } + template FSerializer &Array(const char *key, T *obj, int count) { - BeginArray(key); - for (int i = 0; i < count; i++) + if (BeginArray(key)) { - Serialize(*this, nullptr, obj[i]); + for (int i = 0; i < count; i++) + { + Serialize(*this, nullptr, obj[i]); + } + EndArray(); } - return EndArray(); + return *this; } FSerializer &Terrain(const char *key, int &terrain) @@ -720,9 +734,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value) { if (arc.isWriting()) { - int ndx = -1; if (value != nullptr) { + int ndx; int *pndx = arc.mObjectMap.CheckKey(value); if (pndx != nullptr) ndx = *pndx; else @@ -730,11 +744,19 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value) ndx = arc.mDObjects.Push(value); arc.mObjectMap[value] = ndx; } + Serialize(arc, key, ndx); } - Serialize(arc, key, ndx); } else { + auto val = arc.FindKey(key); + if (val != nullptr) + { + } + else + { + value = nullptr; + } } return arc; } @@ -760,9 +782,18 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value) template FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value) { - arc.BeginArray(key); + if (arc.isWriting()) + { + if (value.Size() == 0) return arc; // do not save empty arrays + } + bool res = arc.BeginArray(key); if (arc.isReading()) { + if (!res) + { + value.Clear(); + return arc; + } value.Resize(arc.ArraySize()); } for (unsigned i = 0; i < value.Size(); i++) @@ -833,7 +864,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line) ("alpha", line.alpha) .Args("args", line.args, line.special) ("portalindex", line.portalindex) - .Array("sides", line.sidedef, 2) + //.Array("sides", line.sidedef, 2) .EndObject(); // no need to store the sidedef references. Unless the map loader is changed they will not change between map loads. @@ -869,9 +900,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side) .Array("textures", side.textures, 3) ("light", side.Light) ("flags", side.Flags) - ("leftside", side.LeftSide) - ("rightside", side.RightSide) - ("index", side.Index) + //("leftside", side.LeftSide) + //("rightside", side.RightSide) + //("index", side.Index) ("attacheddecals", side.AttachedDecals) .EndObject(); } @@ -926,14 +957,11 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm) if (arc.isWriting()) { arc.WriteKey(key); - arc.mWriter->StartObject(); - arc.mWriter->Key("color"); + arc.mWriter->StartArray(); arc.mWriter->Uint(cm->Color); - arc.mWriter->Key("fade"); arc.mWriter->Uint(cm->Fade); - arc.mWriter->Key("desat"); arc.mWriter->Uint(cm->Desaturate); - arc.mWriter->EndObject(); + arc.mWriter->EndArray(); } else { @@ -942,9 +970,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm) { if (val->IsObject()) { - const rapidjson::Value &colorval = (*val)["color"]; - const rapidjson::Value &fadeval = (*val)["fade"]; - const rapidjson::Value &desatval = (*val)["desat"]; + const rapidjson::Value &colorval = (*val)[0]; + const rapidjson::Value &fadeval = (*val)[1]; + const rapidjson::Value &desatval = (*val)[2]; if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint()) { cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint()); @@ -980,10 +1008,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p) ("prevsec", p.prevsec) ("nextsec", p.nextsec) .Array("planes", p.planes, 2) - ("heightsec", p.heightsec) - ("bottommap", p.bottommap) - ("midmap", p.midmap) - ("topmap", p.topmap) + //("heightsec", p.heightsec) + //("bottommap", p.bottommap) + //("midmap", p.midmap) + //("topmap", p.topmap) ("damageamount", p.damageamount) ("damageinterval", p.damageinterval) ("leakydamage", p.leakydamage) @@ -1017,34 +1045,37 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss) { BYTE by; - if (arc.isWriting() && hasglnodes) + if (arc.isWriting()) { - TArray encoded((numsubsectors + 5) / 6); - int p = 0; - for (int i = 0; i < numsubsectors; i += 6) + if (hasglnodes) { - by = 0; - for (int j = 0; j < 6; j++) + TArray encoded((numsubsectors + 5) / 6); + int p = 0; + for (int i = 0; i < numsubsectors; i += 6) { - if (i + j < numsubsectors && (subsectors[i + j].flags & SSECF_DRAWN)) + by = 0; + for (int j = 0; j < 6; j++) { - by |= (1 << j); + if (i + j < numsubsectors && (subsectors[i + j].flags & SSECF_DRAWN)) + { + by |= (1 << j); + } } + if (by < 10) by += '0'; + else if (by < 36) by += 'A' - 10; + else if (by < 62) by += 'a' - 36; + else if (by == 62) by = '-'; + else if (by == 63) by = '+'; + encoded[p++] = by; } - if (by < 10) by += '0'; - else if (by < 36) by += 'A' - 10; - else if (by < 62) by += 'a' - 36; - else if (by == 62) by = '-'; - else if (by == 63) by = '+'; - encoded[p++] = by; + arc.mWriter->Key(key); + arc.mWriter->StartArray(); + arc.mWriter->Int(numvertexes); + arc.mWriter->Int(numsubsectors); + arc.mWriter->Int(numnodes); + arc.mWriter->String(&encoded[0], (numsubsectors + 5) / 6); + arc.mWriter->EndArray(); } - arc.mWriter->Key(key); - arc.mWriter->StartArray(); - arc.mWriter->Int(numvertexes); - arc.mWriter->Int(numsubsectors); - arc.mWriter->Int(numnodes); - arc.mWriter->String(&encoded[0], (numsubsectors + 5) / 6); - arc.mWriter->EndArray(); } else { @@ -1403,154 +1434,157 @@ void SerializeObjects(FSerializer &arc) // //========================================================================== +#define A(a,b) ((a), (b), def->b) + void AActor::Serialize(FSerializer &arc) { int damage = 0; // just a placeholder until the insanity surrounding the damage property can be fixed + AActor *def = GetDefault(); Super::Serialize(arc); arc .Sprite("sprite", sprite) - ("pos", __Pos) - ("angles", Angles) - ("frame", frame) - ("scale", Scale) - ("renderstyle", RenderStyle) - ("renderflags", renderflags) - ("picnum", picnum) - ("floorpic", floorpic) - ("ceilingpic", ceilingpic) - ("tidtohate", TIDtoHate) - ("lastlookpn", LastLookPlayerNumber) + A("pos", __Pos) + A("angles", Angles) + A("frame", frame) + A("scale", Scale) + A("renderstyle", RenderStyle) + A("renderflags", renderflags) + A("picnum", picnum) + A("floorpic", floorpic) + A("ceilingpic", ceilingpic) + A("tidtohate", TIDtoHate) + A("lastlookpn", LastLookPlayerNumber) ("lastlookactor", LastLookActor) - ("effects", effects) - ("alpha", Alpha) - ("fillcolor", fillcolor) - ("sector", Sector) - ("floorz", floorz) - ("ceilingz", ceilingz) - ("dropoffz", dropoffz) - ("floorsector", floorsector) - ("ceilingsector", ceilingsector) - ("radius", radius) - ("height", Height) - ("ppassheight", projectilepassheight) - ("vel", Vel) - ("tics", tics) + A("effects", effects) + A("alpha", Alpha) + A("fillcolor", fillcolor) + A("sector", Sector) + A("floorz", floorz) + A("ceilingz", ceilingz) + A("dropoffz", dropoffz) + A("floorsector", floorsector) + A("ceilingsector", ceilingsector) + A("radius", radius) + A("height", Height) + A("ppassheight", projectilepassheight) + A("vel", Vel) + A("tics", tics) ("state", state) ("damage", damage) .Terrain("floorterrain", floorterrain) - ("projectilekickback", projectileKickback) - ("flags", flags) - ("flags2", flags2) - ("flags3", flags3) - ("flags4", flags4) - ("flags5", flags5) - ("flags6", flags6) - ("flags7", flags7) - ("weaponspecial", weaponspecial) - ("special1", special1) - ("special2", special2) - ("specialf1", specialf1) - ("specialf2", specialf2) - ("health", health) - ("movedir", movedir) - ("visdir", visdir) - ("movecount", movecount) - ("strafecount", strafecount) + A("projectilekickback", projectileKickback) + A("flags", flags) + A("flags2", flags2) + A("flags3", flags3) + A("flags4", flags4) + A("flags5", flags5) + A("flags6", flags6) + A("flags7", flags7) + A("weaponspecial", weaponspecial) + A("special1", special1) + A("special2", special2) + A("specialf1", specialf1) + A("specialf2", specialf2) + A("health", health) + A("movedir", movedir) + A("visdir", visdir) + A("movecount", movecount) + A("strafecount", strafecount) ("target", target) ("lastenemy", lastenemy) ("lastheard", LastHeard) - ("reactiontime", reactiontime) - ("threshold", threshold) - ("player", player) - ("spawnpoint", SpawnPoint) - ("spawnangle", SpawnAngle) - ("starthealth", StartHealth) - ("skillrespawncount", skillrespawncount) + A("reactiontime", reactiontime) + A("threshold", threshold) + A("player", player) + A("spawnpoint", SpawnPoint) + A("spawnangle", SpawnAngle) + A("starthealth", StartHealth) + A("skillrespawncount", skillrespawncount) ("tracer", tracer) - ("floorclip", Floorclip) - ("tid", tid) - ("special", special) + A("floorclip", Floorclip) + A("tid", tid) + A("special", special) .Args("args", args, special) - ("accuracy", accuracy) - ("stamina", stamina) + A("accuracy", accuracy) + A("stamina", stamina) ("goal", goal) - ("waterlevel", waterlevel) - ("minmissilechance", MinMissileChance) - ("spawnflags", SpawnFlags) + A("waterlevel", waterlevel) + A("minmissilechance", MinMissileChance) + A("spawnflags", SpawnFlags) ("inventory", Inventory) - ("inventoryid", InventoryID) - ("floatbobphase", FloatBobPhase) - ("translation", Translation) - ("seesound", SeeSound) - ("attacksound", AttackSound) - ("paimsound", PainSound) - ("deathsound", DeathSound) - ("activesound", ActiveSound) - ("usesound", UseSound) - ("bouncesound", BounceSound) - ("wallbouncesound", WallBounceSound) - ("crushpainsound", CrushPainSound) - ("speed", Speed) - ("floatspeed", FloatSpeed) - ("mass", Mass) - ("painchance", PainChance) - ("spawnstate", SpawnState) - ("seestate", SeeState) - ("meleestate", MeleeState) - ("missilestate", MissileState) - ("maxdropoffheight", MaxDropOffHeight) - ("maxstepheight", MaxStepHeight) - ("bounceflags", BounceFlags) - ("bouncefactor", bouncefactor) - ("wallbouncefactor", wallbouncefactor) - ("bouncecount", bouncecount) - ("maxtargetrange", maxtargetrange) - ("meleethreshold", meleethreshold) - ("meleerange", meleerange) - ("damagetype", DamageType) - ("damagetypereceived", DamageTypeReceived) - ("paintype", PainType) - ("deathtype", DeathType) - ("gravity", Gravity) - ("fastchasestrafecount", FastChaseStrafeCount) + A("inventoryid", InventoryID) + A("floatbobphase", FloatBobPhase) + A("translation", Translation) + A("seesound", SeeSound) + A("attacksound", AttackSound) + A("paimsound", PainSound) + A("deathsound", DeathSound) + A("activesound", ActiveSound) + A("usesound", UseSound) + A("bouncesound", BounceSound) + A("wallbouncesound", WallBounceSound) + A("crushpainsound", CrushPainSound) + A("speed", Speed) + A("floatspeed", FloatSpeed) + A("mass", Mass) + A("painchance", PainChance) + A("spawnstate", SpawnState) + A("seestate", SeeState) + A("meleestate", MeleeState) + A("missilestate", MissileState) + A("maxdropoffheight", MaxDropOffHeight) + A("maxstepheight", MaxStepHeight) + A("bounceflags", BounceFlags) + A("bouncefactor", bouncefactor) + A("wallbouncefactor", wallbouncefactor) + A("bouncecount", bouncecount) + A("maxtargetrange", maxtargetrange) + A("meleethreshold", meleethreshold) + A("meleerange", meleerange) + A("damagetype", DamageType) + A("damagetypereceived", DamageTypeReceived) + A("paintype", PainType) + A("deathtype", DeathType) + A("gravity", Gravity) + A("fastchasestrafecount", FastChaseStrafeCount) ("master", master) - ("smokecounter", smokecounter) + A("smokecounter", smokecounter) ("blockingmobj", BlockingMobj) - ("blockingline", BlockingLine) - ("visibletoteam", VisibleToTeam) - ("pushfactor", pushfactor) - ("species", Species) - ("score", Score) - ("designatedteam", DesignatedTeam) - ("lastpush", lastpush) - ("lastbump", lastbump) - ("painthreshold", PainThreshold) - ("damagefactor", DamageFactor) - ("damagemultiply", DamageMultiply) - ("waveindexxy", WeaveIndexXY) - ("weaveindexz", WeaveIndexZ) - ("pdmgreceived", PoisonDamageReceived) - ("pdurreceived", PoisonDurationReceived) - ("ppreceived", PoisonPeriodReceived) - ("poisoner", Poisoner) - ("posiondamage", PoisonDamage) - ("poisonduration", PoisonDuration) - ("poisonperiod", PoisonPeriod) - ("poisondamagetype", PoisonDamageType) - ("poisondmgtypereceived", PoisonDamageTypeReceived) - ("conversationroot", ConversationRoot) - ("conversation", Conversation) - ("friendplayer", FriendPlayer) - ("telefogsourcetype", TeleFogSourceType) - ("telefogdesttype", TeleFogDestType) - ("ripperlevel", RipperLevel) - ("riplevelmin", RipLevelMin) - ("riplevelmax", RipLevelMax) - ("devthreshold", DefThreshold) - ("spriteangle", SpriteAngle) - ("spriterotation", SpriteRotation) + A("blockingline", BlockingLine) + A("visibletoteam", VisibleToTeam) + A("pushfactor", pushfactor) + A("species", Species) + A("score", Score) + A("designatedteam", DesignatedTeam) + A("lastpush", lastpush) + A("lastbump", lastbump) + A("painthreshold", PainThreshold) + A("damagefactor", DamageFactor) + A("damagemultiply", DamageMultiply) + A("waveindexxy", WeaveIndexXY) + A("weaveindexz", WeaveIndexZ) + A("pdmgreceived", PoisonDamageReceived) + A("pdurreceived", PoisonDurationReceived) + A("ppreceived", PoisonPeriodReceived) + A("poisoner", Poisoner) + A("posiondamage", PoisonDamage) + A("poisonduration", PoisonDuration) + A("poisonperiod", PoisonPeriod) + A("poisondamagetype", PoisonDamageType) + A("poisondmgtypereceived", PoisonDamageTypeReceived) + A("conversationroot", ConversationRoot) + A("conversation", Conversation) + A("friendplayer", FriendPlayer) + A("telefogsourcetype", TeleFogSourceType) + A("telefogdesttype", TeleFogDestType) + A("ripperlevel", RipperLevel) + A("riplevelmin", RipLevelMin) + A("riplevelmax", RipLevelMax) + A("devthreshold", DefThreshold) + A("spriteangle", SpriteAngle) + A("spriterotation", SpriteRotation) ("alternative", alternative) ("tag", Tag); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 0ff6b06b3..99bb01330 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -124,6 +124,7 @@ glsegextra_t* glsegextras; int numsectors; sector_t* sectors; +TArray loadsectors; int numsubsectors; subsector_t* subsectors; @@ -133,9 +134,11 @@ node_t* nodes; int numlines; line_t* lines; +TArray loadlines; int numsides; side_t* sides; +TArray loadsides; TArray Zones; @@ -4195,6 +4198,13 @@ void P_SetupLevel (const char *lumpname, int position) MapThingsUserDataIndex.Clear(); MapThingsUserData.Clear(); + loadsectors.Resize(numsectors); + memcpy(&loadsectors[0], sectors, numsectors * sizeof(sector_t)); + loadlines.Resize(numlines); + memcpy(&loadlines[0], lines, numlines * sizeof(line_t)); + loadsides.Resize(numsides); + memcpy(&loadsides[0], sides, numsides * sizeof(side_t)); + if (glsegextras != NULL) { delete[] glsegextras; From 718614a8209b5e7a6aa7fc7385b60a4dde6cfcb5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 01:07:51 +0200 Subject: [PATCH 03/59] - cleanup --- src/CMakeLists.txt | 2 + src/dobject.h | 2 + src/info.cpp | 3 + src/json.cpp | 1514 +++++++------------------------------------- src/p_mobj.cpp | 1 - src/s_advsound.cpp | 2 +- src/s_sound.cpp | 2 +- src/s_sound.h | 8 + src/serializer.cpp | 1255 ++++++++++++++++++++++++++++++++++++ 9 files changed, 1517 insertions(+), 1272 deletions(-) create mode 100644 src/serializer.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 55183af61..cd08d9e1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1124,6 +1124,7 @@ set (PCH_SOURCES po_man.cpp portal.cpp r_utility.cpp + serializer.cpp sc_man.cpp st_stuff.cpp statistics.cpp @@ -1414,6 +1415,7 @@ source_group("Games\\Strife Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DI source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+") source_group("Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/math/.+") source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+") +source_group("RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/rapidjson/.+") source_group("Render Core\\Render Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.h$") source_group("Render Core\\Render Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.cpp$") source_group("Render Data\\Resource Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.h$") diff --git a/src/dobject.h b/src/dobject.h index 787a07359..7f177c8d4 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -416,6 +416,8 @@ public: template friend inline FArchive &operator<<(FArchive &arc, TObjPtr &o); template friend inline void GC::Mark(TObjPtr &obj); + template friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value, TObjPtr *); + friend class DObject; }; diff --git a/src/info.cpp b/src/info.cpp index 7198a5de4..f70124f8b 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -375,6 +375,9 @@ void PClassActor::InitializeNativeDefaults() else { memset (Defaults, 0, Size); + // Non-DECORATE properties that must be set. + ((AActor*)Defaults)->DamageMultiply = 1.; // fixme: Make this a DECORATE property. + ((AActor*)Defaults)->ConversationRoot = -1; } } diff --git a/src/json.cpp b/src/json.cpp index fe4a2d5d4..7dd969daa 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1,353 +1,33 @@ -#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 // disable this insanity which is bound to make the code break over time. -#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 -#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 - -//#define PRETTY - -#include "rapidjson/rapidjson.h" -#include "rapidjson/writer.h" -#include "rapidjson/prettywriter.h" -#include "rapidjson/stringbuffer.h" -#include "rapidjson/document.h" -#include "r_defs.h" +#include "serializer.h" #include "r_local.h" -#include "p_lnspec.h" -#include "i_system.h" -#include "w_wad.h" -#include "p_terrain.h" -#include "c_dispatch.h" #include "p_setup.h" -#include "p_conversation.h" -#include "dsectoreffect.h" -#include "actor.h" -#include "r_data/r_interpolate.h" -#include "g_shared/a_sharedglobal.h" - -TArray loadsectors; -TArray loadlines; -TArray loadsides; +#include "c_dispatch.h" +#include "i_system.h" +#include "a_sharedglobal.h" //========================================================================== // // // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FName &value); - -//========================================================================== -// -// -// -//========================================================================== -typedef rapidjson::Value FJSONValue; - -struct FJSONObject +FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line, line_t *def) { - rapidjson::Value *mObject; - rapidjson::Value::MemberIterator mIterator; - - FJSONObject(rapidjson::Value *v) + if (arc.BeginObject(key)) { - mObject = v; - mIterator = v->MemberEnd(); + arc("flags", line.flags, def->flags) + ("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) + // no need to store the sidedef references. Unless the map loader is changed they will not change between map loads. + //.Array("sides", line.sidedef, 2) + .EndObject(); } -}; + return arc; - - -class FSerializer -{ -#ifndef PRETTY - typedef rapidjson::Writer > Writer; -#else - typedef rapidjson::PrettyWriter > Writer; -#endif - -public: - Writer *mWriter = nullptr; - TArray mInObject; - rapidjson::StringBuffer mOutString; - TArray mDObjects; - TMap mObjectMap; - TArray mObjects; - rapidjson::Value mDocObj; // just because RapidJSON is stupid and does not allow direct access to what's in the document. - - int ArraySize() - { - if (mObjects.Last().mObject->IsArray()) - { - return mObjects.Last().mObject->Size(); - } - else - { - return 0; - } - } - - rapidjson::Value *FindKey(const char *key) - { - FJSONObject &obj = mObjects.Last(); - if (obj.mObject->IsObject()) - { - // This will continue the search from the last found key, assuming - // that key are being read in the same order in which they were written. - // As long as this is the case this will reduce time here significantly. - // Do at most as many iterations as the object has members. Otherwise the key cannot be present. - for (rapidjson::SizeType i = 0; i < obj.mObject->MemberCount(); i++) - { - if (obj.mIterator == obj.mObject->MemberEnd()) obj.mIterator = obj.mObject->MemberBegin(); - else obj.mIterator++; - if (!strcmp(key, obj.mIterator->name.GetString())) - { - return &obj.mIterator->value; - } - } - } - else if (obj.mObject->IsArray()) - { - // todo: Get element at current index and increment. - } - return nullptr; - } - -public: - - bool OpenWriter(bool pretty) - { - if (mWriter != nullptr || mObjects.Size() > 0) - { - return false; - } - mWriter = new Writer(mOutString); - return true; - } - - bool OpenReader(rapidjson::Document *doc) - { - if (mWriter != nullptr || mObjects.Size() > 0 || !doc->IsObject()) - { - return false; - } - mDocObj = doc->GetObject(); - mObjects.Push(FJSONObject(&mDocObj)); - return true; - } - - bool isReading() const - { - return mWriter == nullptr; - } - - bool isWriting() const - { - return mWriter != nullptr; - } - - void WriteKey(const char *key) - { - if (mInObject.Size() > 0 && mInObject.Last()) - { - mWriter->Key(key); - } - } - - FSerializer &BeginObject(const char *name) - { - if (isWriting()) - { - WriteKey(name); - mWriter->StartObject(); - mInObject.Push(true); - } - else - { - auto val = FindKey(name); - if (val != nullptr) - { - if (val->IsObject()) - { - mObjects.Push(FJSONObject(val)); - } - else - { - I_Error("Object expected for '%s'", name); - } - } - else - { - I_Error("'%s' not found", name); - } - } - return *this; - } - - FSerializer &EndObject() - { - if (isWriting()) - { - mWriter->EndObject(); - mInObject.Pop(); - } - else - { - mObjects.Pop(); - } - return *this; - } - - bool BeginArray(const char *name) - { - if (isWriting()) - { - WriteKey(name); - mWriter->StartArray(); - mInObject.Push(false); - } - else - { - auto val = FindKey(name); - if (val != nullptr) - { - if (val->IsArray()) - { - mObjects.Push(FJSONObject(val)); - } - else - { - I_Error("Array expected for '%s'", name); - } - } - else - { - return false; - } - } - return true; - } - - FSerializer &EndArray() - { - if (isWriting()) - { - mWriter->EndArray(); - mInObject.Pop(); - } - else - { - mObjects.Pop(); - } - return *this; - } - - - //========================================================================== - // - // Special handler for args (because ACS specials' arg0 needs special treatment.) - // - //========================================================================== - - FSerializer &Args(const char *key, int *args, int special) - { - if (isWriting()) - { - WriteKey(key); - mWriter->StartArray(); - for (int i = 0; i < 5; i++) - { - if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) - { - mWriter->String(FName(ENamedName(-args[i])).GetChars()); - } - else - { - mWriter->Int(args[i]); - } - } - mWriter->EndArray(); - } - else - { - auto val = 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(aval.GetString()); - } - else - { - I_Error("Integer expected for '%s[%d]'", key, i); - } - } - } - } - else - { - I_Error("array expected for '%s'", key); - } - } - return *this; - } - - template - FSerializer &operator()(const char *key, T &obj) - { - return Serialize(*this, key, obj); - } - - template - FSerializer &operator()(const char *key, T &obj, T & def) - { - if (isWriting() && !memcmp(&obj, &def, sizeof(T))) return *this; - return Serialize(*this, key, obj); - } - - template - FSerializer &Array(const char *key, T *obj, int count) - { - if (BeginArray(key)) - { - for (int i = 0; i < count; i++) - { - Serialize(*this, nullptr, obj[i]); - } - EndArray(); - } - return *this; - } - - FSerializer &Terrain(const char *key, int &terrain) - { - FName terr = P_GetTerrainName(terrain); - Serialize(*this, key, terr); - if (isReading()) - { - terrain = P_FindTerrain(terr); - } - return *this; - } - - FSerializer &Sprite(const char *key, uint16_t &spritenum) - { - if (isWriting()) - { - WriteKey(key); - mWriter->String(sprites[spritenum].name, 4); - } - return *this; - } -}; +} //========================================================================== // @@ -355,26 +35,119 @@ public: // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, bool &value) +FSerializer &Serialize(FSerializer &arc, const char *key, side_t::part &part, side_t::part *def) { - if (arc.isWriting()) + if (arc.canSkip() && def != nullptr && !memcmp(&part, def, sizeof(part))) { - arc.WriteKey(key); - arc.mWriter->Bool(value); + return arc; } - else + + if (arc.BeginObject(key)) { - auto val = arc.FindKey(key); - if (val != nullptr) + arc("xoffset", part.xOffset, def->xOffset) + ("yoffset", part.yOffset, def->yOffset) + ("xscale", part.xScale, def->xScale) + ("yscale", part.yScale, def->yScale) + ("texture", part.texture, def->texture) + ("interpolation", part.interpolation) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side, side_t *def) +{ + if (arc.BeginObject(key)) + { + arc.Array("textures", side.textures, def->textures, 3, true) + ("light", side.Light, def->Light) + ("flags", side.Flags, def->Flags) + //("leftside", side.LeftSide) + //("rightside", side.RightSide) + //("index", side.Index) + ("attacheddecals", side.AttachedDecals) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FLinkedSector &ls, FLinkedSector *def) +{ + if (arc.BeginObject(key)) + { + arc("sector", ls.Sector) + ("type", ls.Type) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t::splane &p, sector_t::splane *def) +{ + if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) + { + return arc; + } + + if (arc.BeginObject(key)) + { + arc("xoffs", p.xform.xOffs, def->xform.xOffs) + ("yoffs", p.xform.yOffs, def->xform.yOffs) + ("xscale", p.xform.xScale, def->xform.xScale) + ("yscale", p.xform.yScale, def->xform.yScale) + ("angle", p.xform.Angle, def->xform.Angle) + ("baseyoffs", p.xform.baseyOffs, def->xform.baseyOffs) + ("baseangle", p.xform.baseAngle, def->xform.baseAngle) + ("flags", p.Flags, def->Flags) + ("light", p.Light, def->Light) + ("texture", p.Texture, def->Texture) + ("texz", p.TexZ, def->TexZ) + ("alpha", p.alpha, def->alpha) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def) +{ + if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) + { + return arc; + } + + if (arc.BeginObject(key)) + { + arc("normal", p.normal, def->normal) + ("d", p.D, def->D) + .EndObject(); + + if (arc.isReading() && p.normal.Z != 0) { - if (val->IsBool()) - { - value = val->GetBool(); - } - else - { - I_Error("boolean type expected for '%s'", key); - } + p.negiC = 1 / p.normal.Z; } } return arc; @@ -386,664 +159,62 @@ FSerializer &Serialize(FSerializer &arc, const char *key, bool &value) // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value) +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t *def) { - if (arc.isWriting()) + if (arc.BeginObject(key)) { - arc.WriteKey(key); - arc.mWriter->Int64(value); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsInt64()) - { - value = val->GetInt64(); - } - else - { - I_Error("integer type expected for '%s'", key); - } - } - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->Uint64(value); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsUint64()) - { - value = val->GetUint64(); - } - else - { - I_Error("integer type expected for '%s'", key); - } - } + arc("floorplane", p.floorplane, def->floorplane) + ("ceilingplane", p.ceilingplane, def->ceilingplane) + ("lightlevel", p.lightlevel, def->lightlevel) + ("special", p.special, def->special) + ("soundtraversed", p.soundtraversed, def->soundtraversed) + ("seqtype", p.seqType, def->seqType) + ("seqname", p.SeqName, def->SeqName) + ("friction", p.friction, def->friction) + ("movefactor", p.movefactor, def->movefactor) + ("stairlock", p.stairlock, def->stairlock) + ("prevsec", p.prevsec, def->prevsec) + ("nextsec", p.nextsec, def->nextsec) + .Array("planes", p.planes, def->planes, 2, true) + //("heightsec", p.heightsec) + //("bottommap", p.bottommap) + //("midmap", p.midmap) + //("topmap", p.topmap) + ("damageamount", p.damageamount, def->damageamount) + ("damageinterval", p.damageinterval, def->damageinterval) + ("leakydamage", p.leakydamage, def->leakydamage) + ("damagetype", p.damagetype, def->damagetype) + ("sky", p.sky, def->sky) + ("moreflags", p.MoreFlags, def->MoreFlags) + ("flags", p.Flags, def->Flags) + .Array("portals", p.Portals, def->Portals, 2, true) + ("zonenumber", p.ZoneNumber, def->ZoneNumber) + .Array("interpolations", p.interpolations, 4, true) + ("soundtarget", p.SoundTarget) + ("secacttarget", p.SecActTarget) + ("floordata", p.floordata) + ("ceilingdata", p.ceilingdata) + ("lightingdata", p.lightingdata) + ("fakefloor_sectors", p.e->FakeFloor.Sectors) + ("midtexf_lines", p.e->Midtex.Floor.AttachedLines) + ("midtexf_sectors", p.e->Midtex.Floor.AttachedSectors) + ("midtexc_lines", p.e->Midtex.Ceiling.AttachedLines) + ("midtexc_sectors", p.e->Midtex.Ceiling.AttachedSectors) + ("linked_floor", p.e->Linked.Floor.Sectors) + ("linked_ceiling", p.e->Linked.Ceiling.Sectors) + ("colormap", p.ColorMap, def->ColorMap) + .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) + .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) + .EndObject(); } return arc; } -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->Int(value); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsInt()) - { - value = val->GetInt(); - } - else - { - I_Error("integer type expected for '%s'", key); - } - } - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->Uint(value); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsUint()) - { - value = val->GetUint(); - } - else - { - I_Error("integer type expected for '%s'", key); - } - } - } - return arc; -} - - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value) -{ - int32_t vv = value; - Serialize(arc, key, vv); - value = (int8_t)vv; - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, uint8_t &value) -{ - uint32_t vv = value; - Serialize(arc, key, vv); - value = (uint8_t)vv; - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value) -{ - int32_t vv = value; - Serialize(arc, key, vv); - value = (int16_t)vv; - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value) -{ - uint32_t vv = value; - Serialize(arc, key, vv); - value = (uint16_t)vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, double &value) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->Double(value); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsDouble()) - { - value = val->GetDouble(); - } - else - { - I_Error("float type expected for '%s'", key); - } - } - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, float &value) -{ - double vv = value; - Serialize(arc, key, vv); - value = (float)vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value) -{ - ptrdiff_t vv = value == nullptr? -1 : value - sides; - Serialize(arc, key, vv); - value = vv < 0 ? nullptr : sides + vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value) -{ - ptrdiff_t vv = value == nullptr ? -1 : value - sectors; - Serialize(arc, key, vv); - value = vv < 0 ? nullptr : sectors + vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value) -{ - ptrdiff_t vv = value == nullptr ? -1 : value - players; - Serialize(arc, key, vv); - value = vv < 0 ? nullptr : players + vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value) -{ - ptrdiff_t vv = value == nullptr ? -1 : value - lines; - Serialize(arc, key, vv); - value = vv < 0 ? nullptr : lines + vv; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value) -{ - if (arc.isWriting()) - { - if (!value.Exists()) - { - arc.WriteKey(key); - arc.mWriter->Null(); - return arc; - } - if (value.isNull()) - { - // save 'no texture' in a more space saving way - arc.WriteKey(key); - arc.mWriter->Int(0); - return arc; - } - FTextureID chk = value; - if (chk.GetIndex() >= TexMan.NumTextures()) chk.SetNull(); - FTexture *pic = TexMan[chk]; - const char *name; - - if (Wads.GetLinkedTexture(pic->SourceLump) == pic) - { - name = Wads.GetLumpFullName(pic->SourceLump); - } - else - { - name = pic->Name; - } - arc.WriteKey(key); - arc.mWriter->StartObject(); - arc.mWriter->Key("name"); - arc.mWriter->String(name); - arc.mWriter->Key("usetype"); - arc.mWriter->Int(pic->UseType); - arc.mWriter->EndObject(); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsObject()) - { - const rapidjson::Value &nameval = (*val)["name"]; - const rapidjson::Value &typeval = (*val)["type"]; - if (nameval.IsString() && typeval.IsInt()) - { - value = TexMan.GetTexture(nameval.GetString(), typeval.GetInt()); - } - else - { - I_Error("object does not represent a texture for '%s'", key); - } - } - else if (val->IsNull()) - { - value.SetInvalid(); - } - else if (val->IsInt() && val->GetInt() == 0) - { - value.SetNull(); - } - else - { - I_Error("object does not represent a texture for '%s'", key); - } - } - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value) -{ - if (arc.isWriting()) - { - if (value != nullptr) - { - int ndx; - int *pndx = arc.mObjectMap.CheckKey(value); - if (pndx != nullptr) ndx = *pndx; - else - { - ndx = arc.mDObjects.Push(value); - arc.mObjectMap[value] = ndx; - } - Serialize(arc, key, ndx); - } - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - } - else - { - value = nullptr; - } - } - return arc; -} - -template -FSerializer &Serialize(FSerializer &arc, const char *key, T *&value) -{ - DObject *v = static_cast(value); - Serialize(arc, key, v); - value = static_cast(v); - return arc; -} - -template -FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value) -{ - DObject *v = static_cast(value); - Serialize(arc, key, v); - value = static_cast(v); - return arc; -} - -template -FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value) -{ - if (arc.isWriting()) - { - if (value.Size() == 0) return arc; // do not save empty arrays - } - bool res = arc.BeginArray(key); - if (arc.isReading()) - { - if (!res) - { - value.Clear(); - return arc; - } - value.Resize(arc.ArraySize()); - } - for (unsigned i = 0; i < value.Size(); i++) - { - Serialize(arc, nullptr, value[i]); - } - return arc.EndArray(); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p) -{ - return arc.Array(key, &p[0], 3); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, DRotator &p) -{ - return arc.Array(key, &p[0], 3); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, DVector2 &p) -{ - return arc.Array(key, &p[0], 2); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, DAngle &p) -{ - return Serialize(arc, key, p.Degrees); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FName &value) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->String(value.GetChars()); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsString()) - { - value = val->GetString(); - } - else - { - I_Error("String expected for '%s'", key); - } - } - } - return arc; -} - - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line) -{ - return arc.BeginObject(key) - ("flags", line.flags) - ("activation", line.activation) - ("special", line.special) - ("alpha", line.alpha) - .Args("args", line.args, line.special) - ("portalindex", line.portalindex) - //.Array("sides", line.sidedef, 2) - .EndObject(); - - // no need to store the sidedef references. Unless the map loader is changed they will not change between map loads. -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, side_t::part &part) -{ - return arc.BeginObject(key) - ("xoffset", part.xOffset) - ("yoffset", part.yOffset) - ("xscale", part.xScale) - ("yscale", part.yScale) - ("texture", part.texture) - ("interpolation", part.interpolation) - .EndObject(); -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side) -{ - return arc.BeginObject(key) - .Array("textures", side.textures, 3) - ("light", side.Light) - ("flags", side.Flags) - //("leftside", side.LeftSide) - //("rightside", side.RightSide) - //("index", side.Index) - ("attacheddecals", side.AttachedDecals) - .EndObject(); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FLinkedSector &ls) -{ - return arc.BeginObject(key) - ("sector", ls.Sector) - ("type", ls.Type) - .EndObject(); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t::splane &p) -{ - return arc.BeginObject(key) - ("xoffs", p.xform.xOffs) - ("yoffs", p.xform.yOffs) - ("xscale", p.xform.xScale) - ("yscale", p.xform.yScale) - ("angle", p.xform.Angle) - ("baseyoffs", p.xform.baseyOffs) - ("baseangle", p.xform.baseAngle) - ("flags", p.Flags) - ("light", p.Light) - ("texture", p.Texture) - ("texz", p.TexZ) - ("alpha", p.alpha) - .EndObject(); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p) -{ - arc.BeginObject(key) - ("normal", p.normal) - ("d", p.D) - .EndObject(); - - if (arc.isReading() && p.normal.Z != 0) - { - p.negiC = 1 / p.normal.Z; - } - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe) -{ - return Serialize(arc, key, pe.d); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - arc.mWriter->StartArray(); - arc.mWriter->Uint(cm->Color); - arc.mWriter->Uint(cm->Fade); - arc.mWriter->Uint(cm->Desaturate); - arc.mWriter->EndArray(); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsObject()) - { - const rapidjson::Value &colorval = (*val)[0]; - const rapidjson::Value &fadeval = (*val)[1]; - const rapidjson::Value &desatval = (*val)[2]; - if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint()) - { - cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint()); - } - else - { - I_Error("object does not represent a colormap for '%s'", key); - } - } - else - { - I_Error("object does not represent a colormap for '%s'", key); - } - } - } - return arc; -} - - -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p) -{ - return arc.BeginObject(key) - ("floorplane", p.floorplane) - ("ceilingplane", p.ceilingplane) - ("lightlevel", p.lightlevel) - ("special", p.special) - ("soundtraversed", p.soundtraversed) - ("seqtype", p.seqType) - ("seqname", p.SeqName) - ("friction", p.friction) - ("movefactor", p.movefactor) - ("stairlock", p.stairlock) - ("prevsec", p.prevsec) - ("nextsec", p.nextsec) - .Array("planes", p.planes, 2) - //("heightsec", p.heightsec) - //("bottommap", p.bottommap) - //("midmap", p.midmap) - //("topmap", p.topmap) - ("damageamount", p.damageamount) - ("damageinterval", p.damageinterval) - ("leakydamage", p.leakydamage) - ("damagetype", p.damagetype) - ("sky", p.sky) - ("moreflags", p.MoreFlags) - ("flags", p.Flags) - .Array("portals", p.Portals, 2) - ("zonenumber", p.ZoneNumber) - .Array("interpolations", p.interpolations, 4) - ("soundtarget", p.SoundTarget) - ("secacttarget", p.SecActTarget) - ("floordata", p.floordata) - ("ceilingdata", p.ceilingdata) - ("lightingdata", p.lightingdata) - ("fakefloor_sectors", p.e->FakeFloor.Sectors) - ("midtexf_lines", p.e->Midtex.Floor.AttachedLines) - ("midtexf_sectors", p.e->Midtex.Floor.AttachedSectors) - ("midtexc_lines", p.e->Midtex.Ceiling.AttachedLines) - ("midtexc_sectors", p.e->Midtex.Ceiling.AttachedSectors) - ("linked_floor", p.e->Linked.Floor.Sectors) - ("linked_ceiling", p.e->Linked.Ceiling.Sectors) - ("colormap", p.ColorMap) - .Terrain("floorterrain", p.terrainnum[0]) - .Terrain("ceilingterrain", p.terrainnum[1]) - .EndObject(); -} - - -FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss) +FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subsector_t **) { BYTE by; + const char *str; if (arc.isWriting()) { @@ -1068,26 +239,37 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss) else if (by == 63) by = '+'; encoded[p++] = by; } - arc.mWriter->Key(key); - arc.mWriter->StartArray(); - arc.mWriter->Int(numvertexes); - arc.mWriter->Int(numsubsectors); - arc.mWriter->Int(numnodes); - arc.mWriter->String(&encoded[0], (numsubsectors + 5) / 6); - arc.mWriter->EndArray(); + encoded[p] = 0; + str = &encoded[0]; + if (arc.BeginArray(key)) + { + arc(nullptr, numvertexes) + (nullptr, numsubsectors) + .StringPtr(nullptr, str) + .EndArray(); + } } } else { - int num_verts, num_subs, num_nodes; + int num_verts, num_subs; + + if (arc.BeginArray(key)) + { + arc(nullptr, num_verts) + (nullptr, num_subs) + .StringPtr(nullptr, str) + .EndArray(); + } + } return arc; } -FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c) +FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c, ReverbContainer **def) { int id = (arc.isReading() || c == nullptr) ? 0 : c->ID; - Serialize(arc, key, id); + Serialize(arc, key, id, nullptr); if (arc.isReading()) { c = S_FindEnvironment(id); @@ -1095,9 +277,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c) return arc; } -FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z) +FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def) { - return Serialize(arc, key, z.Environment); + return Serialize(arc, key, z.Environment, nullptr); } //============================================================================ @@ -1106,17 +288,20 @@ FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z) // //============================================================================ -FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port) +FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port, FLinePortal *def) { - return arc.BeginObject(key) - ("origin", port.mOrigin) - ("destination", port.mDestination) - ("displacement", port.mDisplacement) - ("type", port.mType) - ("flags", port.mFlags) - ("defflags", port.mDefFlags) - ("align", port.mAlign) - .EndObject(); + if (arc.BeginObject(key)) + { + arc("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("type", port.mType) + ("flags", port.mFlags) + ("defflags", port.mDefFlags) + ("align", port.mAlign) + .EndObject(); + } + return arc; } //============================================================================ @@ -1125,19 +310,22 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port) // //============================================================================ -FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port) +FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, FSectorPortal *def) { - return arc.BeginObject(key) - ("type", port.mType) - ("flags", port.mFlags) - ("partner", port.mPartner) - ("plane", port.mPlane) - ("origin", port.mOrigin) - ("destination", port.mDestination) - ("displacement", port.mDisplacement) - ("planez", port.mPlaneZ) - ("skybox", port.mSkybox) - .EndObject(); + if (arc.BeginObject(key)) + { + arc("type", port.mType) + ("flags", port.mFlags) + ("partner", port.mPartner) + ("plane", port.mPlane) + ("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("planez", port.mPlaneZ) + ("skybox", port.mSkybox) + .EndObject(); + } + return arc; } @@ -1148,7 +336,7 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node) while (!(node->ObjectFlags & OF_Sentinel)) { assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); - ::Serialize(arc, nullptr, node); + ::Serialize(arc, nullptr, node, nullptr); node = node->NextThinker; } } @@ -1156,9 +344,9 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node) void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) { - DThinker *thinker; - BYTE stat; - int statcount; + //DThinker *thinker; + //BYTE stat; + //int statcount; int i; if (arc.isWriting()) @@ -1179,207 +367,12 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) } -FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style) -{ - return arc.BeginObject(key) - ("blendop", style.BlendOp) - ("srcalpha", style.SrcAlpha) - ("dstalpha", style.DestAlpha) - ("flags", style.Flags) - .EndObject(); -} - -template -FSerializer &Serialize(FSerializer &arc, const char *key, TFlags &flags) -{ - return Serialize(arc, key, flags.Value); -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - const char *sn = (const char*)sid; - if (sn != nullptr) arc.mWriter->String(sn); - else arc.mWriter->Null(); - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsString()) - { - sid = val->GetString(); - } - else if (val->IsNull()) - { - sid = 0; - } - else - { - I_Error("string type expected for '%s'", key); - } - } - } - return arc; - -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state) -{ - if (arc.isWriting()) - { - arc.WriteKey(key); - if (state == nullptr) - { - arc.mWriter->Null(); - } - else - { - PClassActor *info = FState::StaticFindStateOwner(state); - - if (info != NULL) - { - arc.mWriter->StartArray(); - arc.mWriter->String(info->TypeName.GetChars()); - arc.mWriter->Uint((uint32_t)(state - info->OwnedStates)); - arc.mWriter->EndArray(); - } - else - { - arc.mWriter->Null(); - } - } - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsNull()) - { - state = nullptr; - } - else if (val->IsArray()) - { - //rapidjson::Value cls = (*val)[0]; - //rapidjson::Value ndx = (*val)[1]; - } - else - { - I_Error("array type expected for '%s'", key); - } - } - } - return arc; - -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node) -{ - uint32_t convnum; - if (arc.isWriting()) - { - arc.WriteKey(key); - if (node == nullptr) - { - arc.mWriter->Null(); - } - else - { - arc.mWriter->Uint(node->ThisNodeNum); - } - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsNull()) - { - node = nullptr; - } - else if (val->IsUint()) - { - if (val->GetUint() >= StrifeDialogues.Size()) - { - node = NULL; - } - else - { - node = StrifeDialogues[val->GetUint()]; - } - } - else - { - I_Error("integer expected for '%s'", key); - } - } - } - return arc; - -} - -FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr) -{ - uint32_t convnum; - if (arc.isWriting()) - { - arc.WriteKey(key); - if (pstr == nullptr) - { - arc.mWriter->Null(); - } - else - { - arc.mWriter->String(pstr->GetChars()); - } - } - else - { - auto val = arc.FindKey(key); - if (val != nullptr) - { - if (val->IsNull()) - { - pstr = nullptr; - } - else if (val->IsString()) - { - pstr = AActor::mStringPropertyData.Alloc(val->GetString()); - } - else - { - I_Error("string expected for '%s'", key); - } - } - } - return arc; - -} - - -/* -{ -FString tagstr; -if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; -arc << tagstr; -if (arc.IsLoading()) -{ -if (tagstr.Len() == 0) Tag = NULL; -else Tag = mStringPropertyData.Alloc(tagstr); -} -} -*/ - void SerializeWorld(FSerializer &arc) { - arc.Array("linedefs", lines, numlines) - .Array("sidedefs", sides, numsides) - .Array("sectors", sectors, numsectors) + arc.Array("linedefs", lines, &loadlines[0], numlines) + .Array("sidedefs", sides, &loadsides[0], numsides) + .Array("sectors", sectors, &loadsectors[0], numsectors) ("subsectors", subsectors) ("zones", Zones) ("lineportals", linePortals) @@ -1390,7 +383,7 @@ void DObject::SerializeUserVars(FSerializer &arc) { PSymbolTable *symt; FName varname; - DWORD count, j; + //DWORD count, j; int *varloc = NULL; symt = &GetClass()->Symbols; @@ -1411,23 +404,6 @@ void DObject::Serialize(FSerializer &arc) ObjectFlags |= OF_SerialSuccess; } -void SerializeObjects(FSerializer &arc) -{ - if (arc.isWriting()) - { - arc.BeginArray("objects"); - for (unsigned i = 0; i < arc.mDObjects.Size(); i++) - { - arc.BeginObject(nullptr); - arc.WriteKey("classtype"); - arc.mWriter->String(arc.mDObjects[i]->GetClass()->TypeName.GetChars()); - arc.mDObjects[i]->Serialize(arc); - arc.EndObject(); - } - arc.EndArray(); - } -} - //========================================================================== // // AActor :: Serialize @@ -1444,7 +420,7 @@ void AActor::Serialize(FSerializer &arc) Super::Serialize(arc); arc - .Sprite("sprite", sprite) + .Sprite("sprite", sprite, &def->sprite) A("pos", __Pos) A("angles", Angles) A("frame", frame) @@ -1471,9 +447,9 @@ void AActor::Serialize(FSerializer &arc) A("ppassheight", projectilepassheight) A("vel", Vel) A("tics", tics) - ("state", state) + A("state", state) ("damage", damage) - .Terrain("floorterrain", floorterrain) + .Terrain("floorterrain", floorterrain, &def->floorterrain) A("projectilekickback", projectileKickback) A("flags", flags) A("flags2", flags2) @@ -1506,7 +482,7 @@ void AActor::Serialize(FSerializer &arc) A("floorclip", Floorclip) A("tid", tid) A("special", special) - .Args("args", args, special) + .Args("args", args, def->args, special) A("accuracy", accuracy) A("stamina", stamina) ("goal", goal) @@ -1568,7 +544,7 @@ void AActor::Serialize(FSerializer &arc) A("pdmgreceived", PoisonDamageReceived) A("pdurreceived", PoisonDurationReceived) A("ppreceived", PoisonPeriodReceived) - A("poisoner", Poisoner) + ("poisoner", Poisoner) A("posiondamage", PoisonDamage) A("poisonduration", PoisonDuration) A("poisonperiod", PoisonPeriod) @@ -1586,7 +562,7 @@ void AActor::Serialize(FSerializer &arc) A("spriteangle", SpriteAngle) A("spriterotation", SpriteRotation) ("alternative", alternative) - ("tag", Tag); + A("tag", Tag); } @@ -1596,24 +572,24 @@ CCMD(writejson) { DWORD t = I_MSTime(); FSerializer arc; - arc.OpenWriter(true); + arc.OpenWriter(); arc.BeginObject(nullptr); DThinker::SerializeThinkers(arc, false); SerializeWorld(arc); - SerializeObjects(arc); + arc.WriteObjects(); arc.EndObject(); DWORD tt = I_MSTime(); Printf("JSON generation took %d ms\n", tt - t); FILE *f = fopen("out.json", "wb"); - fwrite(arc.mOutString.GetString(), 1, arc.mOutString.GetSize(), f); + unsigned siz; + const char *str = arc.GetOutput(&siz); + fwrite(str, 1, siz, f); fclose(f); + /* DWORD ttt = I_MSTime(); Printf("JSON save took %d ms\n", ttt - tt); - rapidjson::Document doc; - doc.Parse(arc.mOutString.GetString()); + FDocument doc(arc.w->mOutString.GetString(), arc.w->mOutString.GetSize()); DWORD tttt = I_MSTime(); Printf("JSON parse took %d ms\n", tttt - ttt); - doc.ParseInsitu((char*)arc.mOutString.GetString()); - DWORD ttttt = I_MSTime(); - Printf("JSON parse insitu took %d ms\n", ttttt - tttt); + */ } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6d8ce4418..e4702bf80 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4118,7 +4118,6 @@ AActor *AActor::StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t a actor->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 if (G_SkillProperty(SKILLP_FastMonsters) && actor->GetClass()->FastSpeed >= 0) actor->Speed = actor->GetClass()->FastSpeed; - actor->DamageMultiply = 1.; // set subsector and/or block links actor->LinkToWorld (SpawningMapThing); diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 8bc1b4868..3d0fe1bac 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2174,7 +2174,7 @@ void AAmbientSound::Tick () loop = CHAN_LOOP; } - if (ambient->sound != 0) + if (ambient->sound != FSoundID(0)) { // The second argument scales the ambient sound's volume. // 0 and 100 are normal volume. The maximum volume level diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 54c8b5cf8..949dee7f8 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -3021,7 +3021,7 @@ CCMD (cachesound) for (int i = 1; i < argv.argc(); ++i) { FSoundID sfxnum = argv[i]; - if (sfxnum != 0) + if (sfxnum != FSoundID(0)) { S_CacheSound (&S_sfx[sfxnum]); } diff --git a/src/s_sound.h b/src/s_sound.h index e157bbaa1..774d859c8 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -126,6 +126,14 @@ public: ID = S_FindSound(name.GetChars()); return *this; } + bool operator !=(FSoundID other) const + { + return ID != other.ID; + } + bool operator !=(int other) const + { + return ID != other; + } operator int() const { return ID; diff --git a/src/serializer.cpp b/src/serializer.cpp new file mode 100644 index 000000000..9f85da9b6 --- /dev/null +++ b/src/serializer.cpp @@ -0,0 +1,1255 @@ +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 // disable this insanity which is bound to make the code break over time. +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag + +//#define PRETTY + +#include "rapidjson/rapidjson.h" +#include "rapidjson/writer.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/document.h" +#include "serializer.h" +#include "r_data/colormaps.h" +#include "r_data/r_interpolate.h" +#include "r_defs.h" +#include "r_state.h" +#include "p_lnspec.h" +#include "i_system.h" +#include "w_wad.h" +#include "p_terrain.h" +#include "c_dispatch.h" +#include "p_setup.h" +#include "p_conversation.h" +#include "dsectoreffect.h" +#include "d_player.h" +#include "r_data/r_interpolate.h" +#include "g_shared/a_sharedglobal.h" + +char nulspace[1024 * 1024 * 4]; + +//========================================================================== +// +// +// +//========================================================================== + +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 +{ + // Since this is done by template parameters, we'd have to template the entire serializer to allow this switch at run time. Argh! +#ifndef PRETTY + typedef rapidjson::Writer > Writer; +#else + typedef rapidjson::PrettyWriter > Writer; +#endif + + Writer mWriter; + TArray mInObject; + rapidjson::StringBuffer mOutString; + TArray mDObjects; + TMap mObjectMap; + + bool inObject() const + { + return mInObject.Size() > 0 && inObject(); + } +}; + +//========================================================================== +// +// +// +//========================================================================== + +struct FReader +{ + TArray mObjects; + rapidjson::Value mDocObj; // just because RapidJSON is stupid and does not allow direct access to what's in the document. + + FReader(const char *buffer, size_t length) + { + rapidjson::Document doc; + doc.Parse(buffer, length); + mDocObj = doc.GetObject(); + mObjects.Push(FJSONObject(&mDocObj)); + } + + rapidjson::Value *FindKey(const char *key) + { + FJSONObject &obj = mObjects.Last(); + + if (obj.mObject->IsObject()) + { + if (obj.mIterator != obj.mObject->MemberEnd()) + { + if (!strcmp(key, obj.mIterator->name.GetString())) + { + return &(obj.mIterator++)->value; + } + } + } + else if (obj.mObject->IsArray()) + { + return &obj.mObject[obj.mIndex++]; + } + return nullptr; + } +}; + + +//========================================================================== +// +// +// +//========================================================================== + +bool FSerializer::OpenWriter() +{ + if (w != nullptr || r == nullptr) return false; + w = new FWriter; + + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FSerializer::OpenReader(const char *buffer, size_t length) +{ + if (w != nullptr || r == nullptr) return false; + r = new FReader(buffer, length); + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FSerializer::Close() +{ + if (w != nullptr) + { + delete w; + w = nullptr; + } + if (r != nullptr) + { + delete r; + r = nullptr; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +int FSerializer::ArraySize() +{ + if (r != nullptr && r->mObjects.Last().mObject->IsArray()) + { + return r->mObjects.Last().mObject->Size(); + } + else + { + return 0; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FSerializer::canSkip() const +{ + return isWriting() && w->inObject(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FSerializer::WriteKey(const char *key) +{ + if (isWriting() && w->inObject()) + { + if (key == nullptr) + { + I_Error("missing element name"); + } + w->mWriter.Key(key); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FSerializer::BeginObject(const char *name) +{ + if (isWriting()) + { + WriteKey(name); + w->mWriter.StartObject(); + w->mInObject.Push(true); + } + else + { + auto val = r->FindKey(name); + if (val != nullptr) + { + if (val->IsObject()) + { + r->mObjects.Push(FJSONObject(val)); + } + else + { + I_Error("Object expected for '%s'", name); + } + } + else + { + return false; + } + } + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FSerializer::EndObject() +{ + if (isWriting()) + { + if (w->inObject()) + { + w->mWriter.EndObject(); + w->mInObject.Pop(); + } + else + { + I_Error("EndObject call not inside an object"); + } + } + else + { + r->mObjects.Pop(); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FSerializer::BeginArray(const char *name) +{ + if (isWriting()) + { + WriteKey(name); + w->mWriter.StartArray(); + w->mInObject.Push(false); + } + else + { + auto val = r->FindKey(name); + if (val != nullptr) + { + if (val->IsArray()) + { + r->mObjects.Push(FJSONObject(val)); + } + else + { + I_Error("Array expected for '%s'", name); + } + } + else + { + return false; + } + } + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FSerializer::EndArray() +{ + if (isWriting()) + { + if (w->inObject()) + { + w->mWriter.EndArray(); + w->mInObject.Pop(); + } + else + { + I_Error("EndArray call not inside an array"); + } + } + else + { + r->mObjects.Pop(); + } +} + +//========================================================================== +// +// 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->mWriter.StartArray(); + for (int i = 0; i < 5; i++) + { + if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) + { + w->mWriter.String(FName(ENamedName(-args[i])).GetChars()); + } + else + { + w->mWriter.Int(args[i]); + } + } + w->mWriter.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(aval.GetString()); + } + else + { + I_Error("Integer expected for '%s[%d]'", key, i); + } + } + } + else + { + I_Error("array expected for '%s'", key); + } + } + } + return *this; +} + +//========================================================================== +// +// +// +//========================================================================== + +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; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &FSerializer::Sprite(const char *key, uint16_t &spritenum, uint16_t *def) +{ + if (isWriting()) + { + if (w->inObject() && def != nullptr && *def == spritenum) return *this; + WriteKey(key); + w->mWriter.String(sprites[spritenum].name, 4); + } + else + { + auto val = r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + int name = *reinterpret_cast(val->GetString()); + for (auto hint = NumStdSprites; hint-- != 0; ) + { + if (sprites[hint].dwName == name) + { + spritenum = hint; + break; + } + } + } + } + } + return *this; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) +{ + if (isWriting()) + { + WriteKey(key); + w->mWriter.String(charptr); + } + else + { + auto val = r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + charptr = val->GetString(); + } + } + } + return *this; +} + +//========================================================================== +// +// Writes out all collected objects +// +//========================================================================== + +void FSerializer::WriteObjects() +{ + if (isWriting()) + { + BeginArray("objects"); + for (unsigned i = 0; i < w->mDObjects.Size(); i++) + { + BeginObject(nullptr); + WriteKey("classtype"); + w->mWriter.String(w->mDObjects[i]->GetClass()->TypeName.GetChars()); + w->mDObjects[i]->Serialize(*this); + EndObject(); + } + EndArray(); + } +} + +const char *FSerializer::GetOutput(unsigned *len) +{ + if (len != nullptr) + { + *len = (unsigned)w->mOutString.GetSize(); + } + return w->mOutString.GetString(); +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Bool(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsBool()) + { + value = val->GetBool(); + } + else + { + I_Error("boolean type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_t *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Int64(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsInt64()) + { + value = val->GetInt64(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint64_t *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Uint64(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsUint64()) + { + value = val->GetUint64(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_t *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Int(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsInt()) + { + value = val->GetInt(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint32_t *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Uint(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsUint()) + { + value = val->GetUint(); + } + else + { + I_Error("integer type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value, int8_t *defval) +{ + int32_t vv = value; + int32_t vvd = defval? *defval : value-1; + Serialize(arc, key, vv, &vvd); + value = (int8_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, uint8_t &value, uint8_t *defval) +{ + uint32_t vv = value; + uint32_t vvd = defval ? *defval : value - 1; + Serialize(arc, key, vv, &vvd); + value = (uint8_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value, int16_t *defval) +{ + int32_t vv = value; + int32_t vvd = defval ? *defval : value - 1; + Serialize(arc, key, vv, &vvd); + value = (int16_t)vv; + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint16_t *defval) +{ + uint32_t vv = value; + uint32_t vvd = defval ? *defval : value - 1; + Serialize(arc, key, vv, &vvd); + value = (uint16_t)vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.Double(value); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsDouble()) + { + value = val->GetDouble(); + } + else + { + I_Error("float type expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval) +{ + double vv = value; + double vvd = defval ? *defval : value - 1; + Serialize(arc, key, vv, &vvd); + value = (float)vv; + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +template +FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base) +{ + if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval) + { + ptrdiff_t vv = value == nullptr ? -1 : value - base; + Serialize(arc, key, vv, nullptr); + value = vv < 0 ? nullptr : base + vv; + } + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) +{ + return SerializePointer(arc, key, value, defval, sides); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval) +{ + return SerializePointer(arc, key, value, defval, sectors); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) +{ + return SerializePointer(arc, key, value, defval, players); +} + +FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval) +{ + return SerializePointer(arc, key, value, defval, lines); +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + if (!value.Exists()) + { + arc.WriteKey(key); + arc.w->mWriter.Null(); + return arc; + } + if (value.isNull()) + { + // save 'no texture' in a more space saving way + arc.WriteKey(key); + arc.w->mWriter.Int(0); + return arc; + } + FTextureID chk = value; + if (chk.GetIndex() >= TexMan.NumTextures()) chk.SetNull(); + FTexture *pic = TexMan[chk]; + const char *name; + + if (Wads.GetLinkedTexture(pic->SourceLump) == pic) + { + name = Wads.GetLumpFullName(pic->SourceLump); + } + else + { + name = pic->Name; + } + arc.WriteKey(key); + arc.w->mWriter.StartArray(); + arc.w->mWriter.String(name); + arc.w->mWriter.Int(pic->UseType); + arc.w->mWriter.EndObject(); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsObject()) + { + const rapidjson::Value &nameval = (*val)[0]; + const rapidjson::Value &typeval = (*val)[1]; + if (nameval.IsString() && typeval.IsInt()) + { + value = TexMan.GetTexture(nameval.GetString(), typeval.GetInt()); + } + else + { + I_Error("object does not represent a texture for '%s'", key); + } + } + else if (val->IsNull()) + { + value.SetInvalid(); + } + else if (val->IsInt() && val->GetInt() == 0) + { + value.SetNull(); + } + else + { + I_Error("object does not represent a texture for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// This never uses defval and instead uses 'null' as default +// because object pointers cannot be safely defaulted to anything else. +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/) +{ + if (arc.isWriting()) + { + if (value != nullptr) + { + int ndx; + int *pndx = arc.w->mObjectMap.CheckKey(value); + if (pndx != nullptr) ndx = *pndx; + else + { + ndx = arc.w->mDObjects.Push(value); + arc.w->mObjectMap[value] = ndx; + } + Serialize(arc, key, ndx, nullptr); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + } + else + { + value = nullptr; + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + arc.w->mWriter.String(value.GetChars()); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + value = val->GetString(); + } + else + { + I_Error("String expected for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def) +{ + if (arc.isWriting()) + { + if (arc.w->inObject() && def != nullptr && cm->Color == (*def)->Color && cm->Fade == (*def)->Fade && cm->Desaturate == (*def)->Desaturate) + { + return arc; + } + + arc.WriteKey(key); + arc.w->mWriter.StartArray(); + arc.w->mWriter.Uint(cm->Color); + arc.w->mWriter.Uint(cm->Fade); + arc.w->mWriter.Uint(cm->Desaturate); + arc.w->mWriter.EndArray(); + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsObject()) + { + const rapidjson::Value &colorval = (*val)[0]; + const rapidjson::Value &fadeval = (*val)[1]; + const rapidjson::Value &desatval = (*val)[2]; + if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint()) + { + cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint()); + } + else + { + I_Error("object does not represent a colormap for '%s'", key); + } + } + else + { + I_Error("object does not represent a colormap for '%s'", key); + } + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def) +{ + if (arc.isWriting()) + { + if (arc.w->inObject() && def != nullptr && sid != *def) + { + arc.WriteKey(key); + const char *sn = (const char*)sid; + if (sn != nullptr) arc.w->mWriter.String(sn); + else arc.w->mWriter.Null(); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + sid = val->GetString(); + } + else if (val->IsNull()) + { + sid = 0; + } + else + { + I_Error("string type expected for '%s'", key); + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +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); + arc.w->mWriter.String(clst->TypeName.GetChars()); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + clst = PClass::FindActor(val->GetString()); + } + else + { + I_Error("string type expected for '%s'", key); + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def) +{ + if (arc.isWriting()) + { + if (arc.w->inObject() && def != nullptr && state != *def) + { + arc.WriteKey(key); + if (state == nullptr) + { + arc.w->mWriter.Null(); + } + else + { + PClassActor *info = FState::StaticFindStateOwner(state); + + if (info != NULL) + { + arc.w->mWriter.StartArray(); + arc.w->mWriter.String(info->TypeName.GetChars()); + arc.w->mWriter.Uint((uint32_t)(state - info->OwnedStates)); + arc.w->mWriter.EndArray(); + } + else + { + arc.w->mWriter.Null(); + } + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + state = nullptr; + } + else if (val->IsArray()) + { + //rapidjson::Value cls = (*val)[0]; + //rapidjson::Value ndx = (*val)[1]; + } + else + { + I_Error("array type expected for '%s'", key); + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +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->mWriter.Null(); + } + else + { + arc.w->mWriter.Uint(node->ThisNodeNum); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + node = nullptr; + } + else if (val->IsUint()) + { + if (val->GetUint() >= StrifeDialogues.Size()) + { + node = NULL; + } + else + { + node = StrifeDialogues[val->GetUint()]; + } + } + else + { + I_Error("integer expected for '%s'", key); + } + } + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +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->mWriter.Null(); + } + else + { + arc.w->mWriter.String(pstr->GetChars()); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + pstr = nullptr; + } + else if (val->IsString()) + { + pstr = AActor::mStringPropertyData.Alloc(val->GetString()); + } + else + { + I_Error("string expected for '%s'", key); + } + } + } + return arc; + +} + From 967ed48fd35bd3cefe03519b2613f5f3f554c8a4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 01:48:48 +0200 Subject: [PATCH 04/59] - fixing. --- src/json.cpp | 2 +- src/serializer.cpp | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/json.cpp b/src/json.cpp index 7dd969daa..3c455d52c 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -220,7 +220,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subs { if (hasglnodes) { - TArray encoded((numsubsectors + 5) / 6); + TArray encoded(1 + (numsubsectors + 5) / 6); int p = 0; for (int i = 0; i < numsubsectors; i += 6) { diff --git a/src/serializer.cpp b/src/serializer.cpp index 9f85da9b6..db467c21a 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -71,9 +71,12 @@ struct FWriter TArray mDObjects; TMap mObjectMap; + FWriter() : mWriter(mOutString) + {} + bool inObject() const { - return mInObject.Size() > 0 && inObject(); + return mInObject.Size() > 0 && mInObject.Last(); } }; @@ -127,7 +130,7 @@ struct FReader bool FSerializer::OpenWriter() { - if (w != nullptr || r == nullptr) return false; + if (w != nullptr || r != nullptr) return false; w = new FWriter; return true; @@ -141,7 +144,7 @@ bool FSerializer::OpenWriter() bool FSerializer::OpenReader(const char *buffer, size_t length) { - if (w != nullptr || r == nullptr) return false; + if (w != nullptr || r != nullptr) return false; r = new FReader(buffer, length); return true; } @@ -321,7 +324,7 @@ void FSerializer::EndArray() { if (isWriting()) { - if (w->inObject()) + if (!w->inObject()) { w->mWriter.EndArray(); w->mInObject.Pop(); @@ -859,7 +862,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe arc.w->mWriter.StartArray(); arc.w->mWriter.String(name); arc.w->mWriter.Int(pic->UseType); - arc.w->mWriter.EndObject(); + arc.w->mWriter.EndArray(); } } else From c665cc53f9aa455a156a6dfe7b13ec9d165437ef Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 10:34:54 +0200 Subject: [PATCH 05/59] - moved new code to its proper location and started moving the replaced old archive code to a placeholder file for easy removal later. --- src/actor.h | 1 + src/dobject.cpp | 3 +- src/dthinker.cpp | 4 + src/dthinker.h | 1 + src/farchive.cpp | 145 --------- src/g_level.cpp | 131 -------- src/json.cpp | 496 +---------------------------- src/p_mobj.cpp | 388 +++++++++++------------ src/p_saveg.cpp | 775 ++++++++++++++++++++++++++++++--------------- src/p_saveg.h | 2 + src/p_states.cpp | 65 ---- src/serializer.cpp | 16 +- src/zzz_old.cpp | 697 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1431 insertions(+), 1293 deletions(-) create mode 100644 src/zzz_old.cpp diff --git a/src/actor.h b/src/actor.h index d01f8db88..fcfeb4fce 100644 --- a/src/actor.h +++ b/src/actor.h @@ -585,6 +585,7 @@ public: void Serialize (FArchive &arc); void Serialize(FSerializer &arc); + void PostSerialize(); static AActor *StaticSpawn (PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false); diff --git a/src/dobject.cpp b/src/dobject.cpp index 82014c133..c70a51e9a 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -48,6 +48,7 @@ #include "a_sharedglobal.h" #include "dsectoreffect.h" #include "farchive.h" +#include "serializer.h" ClassReg DObject::RegistrationInfo = { @@ -479,7 +480,7 @@ void DObject::SerializeUserVars(FArchive &arc) } } -void DObject::Serialize (FArchive &arc) +void DObject::Serialize(FSerializer &arc) { ObjectFlags |= OF_SerialSuccess; } diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 176df6c18..0efcecbdd 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -261,6 +261,10 @@ void DThinker::PostBeginPlay () { } +void DThinker::PostSerialize() +{ +} + DThinker *DThinker::FirstThinker (int statnum) { DThinker *node; diff --git a/src/dthinker.h b/src/dthinker.h index b68c2e320..d423c3454 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -70,6 +70,7 @@ public: virtual ~DThinker (); virtual void Tick (); virtual void PostBeginPlay (); // Called just before the first tick + virtual void PostSerialize(); size_t PropagateMark(); void ChangeStatNum (int statnum); diff --git a/src/farchive.cpp b/src/farchive.cpp index de3e248a4..8bb39b35f 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -1085,109 +1085,6 @@ FArchive &FArchive::SerializeObject (DObject *&object, PClass *type) } } -FArchive &FArchive::WriteObject (DObject *obj) -{ - player_t *player; - BYTE id[2]; - - if (obj == NULL) - { - id[0] = NULL_OBJ; - Write (id, 1); - } - else if (obj == (DObject*)~0) - { - id[0] = M1_OBJ; - Write (id, 1); - } - else if (obj->ObjectFlags & OF_EuthanizeMe) - { - // Objects that want to die are not saved to the archive, but - // we leave the pointers to them alone. - id[0] = NULL_OBJ; - Write (id, 1); - } - else - { - PClass *type = obj->GetClass(); - DWORD *classarcid; - - if (type == RUNTIME_CLASS(DObject)) - { - //I_Error ("Tried to save an instance of DObject.\n" - // "This should not happen.\n"); - id[0] = NULL_OBJ; - Write (id, 1); - } - else if (NULL == (classarcid = ClassToArchive.CheckKey(type))) - { - // No instances of this class have been written out yet. - // Write out the class, then write out the object. If this - // is an actor controlled by a player, make note of that - // so that it can be overridden when moving around in a hub. - if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && - (player = static_cast(obj)->player) && - player->mo == obj) - { - id[0] = NEW_PLYR_CLS_OBJ; - id[1] = (BYTE)(player - players); - Write (id, 2); - } - else - { - id[0] = NEW_CLS_OBJ; - Write (id, 1); - } - WriteClass (type); -// Printf ("Make class %s (%u)\n", type->Name, m_File->Tell()); - MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); - } - else - { - // An instance of this class has already been saved. If - // this object has already been written, save a reference - // to the saved object. Otherwise, save a reference to the - // class, then save the object. Again, if this is a player- - // controlled actor, remember that. - DWORD *objarcid = ObjectToArchive.CheckKey(obj); - - if (objarcid == NULL) - { - - if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && - (player = static_cast(obj)->player) && - player->mo == obj) - { - id[0] = NEW_PLYR_OBJ; - id[1] = (BYTE)(player - players); - Write (id, 2); - } - else - { - id[0] = NEW_OBJ; - Write (id, 1); - } - WriteCount (*classarcid); -// Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell()); - MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); - } - else - { - id[0] = OLD_OBJ; - Write (id, 1); - WriteCount (*objarcid); - } - } - } - return *this; -} - FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) { BYTE objHead; @@ -1545,45 +1442,3 @@ void FArchive::UserReadClass (PClass *&type) } } -FArchive &operator<< (FArchive &arc, sector_t *&sec) -{ - return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); -} - -FArchive &operator<< (FArchive &arc, const sector_t *&sec) -{ - return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); -} - -FArchive &operator<< (FArchive &arc, line_t *&line) -{ - return arc.SerializePointer (lines, (BYTE **)&line, sizeof(*lines)); -} - -FArchive &operator<< (FArchive &arc, vertex_t *&vert) -{ - return arc.SerializePointer (vertexes, (BYTE **)&vert, sizeof(*vertexes)); -} - -FArchive &operator<< (FArchive &arc, side_t *&side) -{ - return arc.SerializePointer (sides, (BYTE **)&side, sizeof(*sides)); -} - -FArchive &operator<<(FArchive &arc, DAngle &ang) -{ - arc << ang.Degrees; - return arc; -} - -FArchive &operator<<(FArchive &arc, DVector3 &vec) -{ - arc << vec.X << vec.Y << vec.Z; - return arc; -} - -FArchive &operator<<(FArchive &arc, DVector2 &vec) -{ - arc << vec.X << vec.Y; - return arc; -} diff --git a/src/g_level.cpp b/src/g_level.cpp index e71ecc1a1..4388fc00c 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1474,137 +1474,6 @@ void G_AirControlChanged () } } -//========================================================================== -// -// -//========================================================================== - -void G_SerializeLevel (FArchive &arc, bool hubLoad) -{ - int i = level.totaltime; - - unsigned tm = I_MSTime(); - - Renderer->StartSerialize(arc); - if (arc.IsLoading()) P_DestroyThinkers(hubLoad); - - arc << level.flags - << level.flags2 - << level.fadeto - << level.found_secrets - << level.found_items - << level.killed_monsters - << level.gravity - << level.aircontrol - << level.teamdamage - << level.maptime - << i; - - // Hub transitions must keep the current total time - if (!hubLoad) - level.totaltime = i; - - arc << level.skytexture1 << level.skytexture2; - if (arc.IsLoading()) - { - sky1texture = level.skytexture1; - sky2texture = level.skytexture2; - R_InitSkyMap(); - } - - G_AirControlChanged (); - - BYTE t; - - // Does this level have scrollers? - if (arc.IsStoring ()) - { - t = level.Scrolls ? 1 : 0; - arc << t; - } - else - { - arc << t; - if (level.Scrolls) - { - delete[] level.Scrolls; - level.Scrolls = NULL; - } - if (t) - { - level.Scrolls = new FSectorScrollValues[numsectors]; - memset (level.Scrolls, 0, sizeof(level.Scrolls)*numsectors); - } - } - - FBehavior::StaticSerializeModuleStates (arc); - if (arc.IsLoading()) interpolator.ClearInterpolations(); - P_SerializeWorld(arc); - P_SerializeThinkers (arc, hubLoad); - P_SerializeWorldActors(arc); // serializing actor pointers in the world data must be done after SerializeWorld has restored the entire sector state, otherwise LinkToWorld may fail. - P_SerializePolyobjs (arc); - P_SerializeSubsectors(arc); - StatusBar->Serialize (arc); - - arc << level.total_monsters << level.total_items << level.total_secrets; - - // Does this level have custom translations? - FRemapTable *trans; - WORD w; - if (arc.IsStoring ()) - { - for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i) - { - trans = translationtables[TRANSLATION_LevelScripted][i]; - if (trans != NULL && !trans->IsIdentity()) - { - w = WORD(i); - arc << w; - trans->Serialize(arc); - } - } - w = 0xffff; - arc << w; - } - else - { - while (arc << w, w != 0xffff) - { - trans = translationtables[TRANSLATION_LevelScripted].GetVal(w); - if (trans == NULL) - { - trans = new FRemapTable; - translationtables[TRANSLATION_LevelScripted].SetVal(w, trans); - } - trans->Serialize(arc); - } - } - - // This must be saved, too, of course! - FCanvasTextureInfo::Serialize (arc); - AM_SerializeMarkers(arc); - - P_SerializePlayers (arc, hubLoad); - P_SerializeSounds (arc); - if (arc.IsLoading()) - { - for (i = 0; i < numsectors; i++) - { - P_Recalculate3DFloors(§ors[i]); - } - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].mo != NULL) - { - players[i].mo->SetupWeaponSlots(); - } - } - } - Renderer->EndSerialize(arc); - unsigned tt = I_MSTime(); - Printf("Serialization took %d ms\n", tt - tm); -} - //========================================================================== // // Archives the current level diff --git a/src/json.cpp b/src/json.cpp index 3c455d52c..c3fb88247 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -11,324 +11,6 @@ // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line, line_t *def) -{ - if (arc.BeginObject(key)) - { - arc("flags", line.flags, def->flags) - ("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) - // no need to store the sidedef references. Unless the map loader is changed they will not change between map loads. - //.Array("sides", line.sidedef, 2) - .EndObject(); - } - return arc; - -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, side_t::part &part, side_t::part *def) -{ - if (arc.canSkip() && def != nullptr && !memcmp(&part, def, sizeof(part))) - { - return arc; - } - - if (arc.BeginObject(key)) - { - arc("xoffset", part.xOffset, def->xOffset) - ("yoffset", part.yOffset, def->yOffset) - ("xscale", part.xScale, def->xScale) - ("yscale", part.yScale, def->yScale) - ("texture", part.texture, def->texture) - ("interpolation", part.interpolation) - .EndObject(); - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side, side_t *def) -{ - if (arc.BeginObject(key)) - { - arc.Array("textures", side.textures, def->textures, 3, true) - ("light", side.Light, def->Light) - ("flags", side.Flags, def->Flags) - //("leftside", side.LeftSide) - //("rightside", side.RightSide) - //("index", side.Index) - ("attacheddecals", side.AttachedDecals) - .EndObject(); - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, FLinkedSector &ls, FLinkedSector *def) -{ - if (arc.BeginObject(key)) - { - arc("sector", ls.Sector) - ("type", ls.Type) - .EndObject(); - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t::splane &p, sector_t::splane *def) -{ - if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) - { - return arc; - } - - if (arc.BeginObject(key)) - { - arc("xoffs", p.xform.xOffs, def->xform.xOffs) - ("yoffs", p.xform.yOffs, def->xform.yOffs) - ("xscale", p.xform.xScale, def->xform.xScale) - ("yscale", p.xform.yScale, def->xform.yScale) - ("angle", p.xform.Angle, def->xform.Angle) - ("baseyoffs", p.xform.baseyOffs, def->xform.baseyOffs) - ("baseangle", p.xform.baseAngle, def->xform.baseAngle) - ("flags", p.Flags, def->Flags) - ("light", p.Light, def->Light) - ("texture", p.Texture, def->Texture) - ("texz", p.TexZ, def->TexZ) - ("alpha", p.alpha, def->alpha) - .EndObject(); - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def) -{ - if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) - { - return arc; - } - - if (arc.BeginObject(key)) - { - arc("normal", p.normal, def->normal) - ("d", p.D, def->D) - .EndObject(); - - if (arc.isReading() && p.normal.Z != 0) - { - p.negiC = 1 / p.normal.Z; - } - } - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t *def) -{ - if (arc.BeginObject(key)) - { - arc("floorplane", p.floorplane, def->floorplane) - ("ceilingplane", p.ceilingplane, def->ceilingplane) - ("lightlevel", p.lightlevel, def->lightlevel) - ("special", p.special, def->special) - ("soundtraversed", p.soundtraversed, def->soundtraversed) - ("seqtype", p.seqType, def->seqType) - ("seqname", p.SeqName, def->SeqName) - ("friction", p.friction, def->friction) - ("movefactor", p.movefactor, def->movefactor) - ("stairlock", p.stairlock, def->stairlock) - ("prevsec", p.prevsec, def->prevsec) - ("nextsec", p.nextsec, def->nextsec) - .Array("planes", p.planes, def->planes, 2, true) - //("heightsec", p.heightsec) - //("bottommap", p.bottommap) - //("midmap", p.midmap) - //("topmap", p.topmap) - ("damageamount", p.damageamount, def->damageamount) - ("damageinterval", p.damageinterval, def->damageinterval) - ("leakydamage", p.leakydamage, def->leakydamage) - ("damagetype", p.damagetype, def->damagetype) - ("sky", p.sky, def->sky) - ("moreflags", p.MoreFlags, def->MoreFlags) - ("flags", p.Flags, def->Flags) - .Array("portals", p.Portals, def->Portals, 2, true) - ("zonenumber", p.ZoneNumber, def->ZoneNumber) - .Array("interpolations", p.interpolations, 4, true) - ("soundtarget", p.SoundTarget) - ("secacttarget", p.SecActTarget) - ("floordata", p.floordata) - ("ceilingdata", p.ceilingdata) - ("lightingdata", p.lightingdata) - ("fakefloor_sectors", p.e->FakeFloor.Sectors) - ("midtexf_lines", p.e->Midtex.Floor.AttachedLines) - ("midtexf_sectors", p.e->Midtex.Floor.AttachedSectors) - ("midtexc_lines", p.e->Midtex.Ceiling.AttachedLines) - ("midtexc_sectors", p.e->Midtex.Ceiling.AttachedSectors) - ("linked_floor", p.e->Linked.Floor.Sectors) - ("linked_ceiling", p.e->Linked.Ceiling.Sectors) - ("colormap", p.ColorMap, def->ColorMap) - .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) - .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) - .EndObject(); - } - return arc; -} - - -FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subsector_t **) -{ - BYTE by; - const char *str; - - if (arc.isWriting()) - { - if (hasglnodes) - { - TArray encoded(1 + (numsubsectors + 5) / 6); - int p = 0; - for (int i = 0; i < numsubsectors; i += 6) - { - by = 0; - for (int j = 0; j < 6; j++) - { - if (i + j < numsubsectors && (subsectors[i + j].flags & SSECF_DRAWN)) - { - by |= (1 << j); - } - } - if (by < 10) by += '0'; - else if (by < 36) by += 'A' - 10; - else if (by < 62) by += 'a' - 36; - else if (by == 62) by = '-'; - else if (by == 63) by = '+'; - encoded[p++] = by; - } - encoded[p] = 0; - str = &encoded[0]; - if (arc.BeginArray(key)) - { - arc(nullptr, numvertexes) - (nullptr, numsubsectors) - .StringPtr(nullptr, str) - .EndArray(); - } - } - } - else - { - int num_verts, num_subs; - - if (arc.BeginArray(key)) - { - arc(nullptr, num_verts) - (nullptr, num_subs) - .StringPtr(nullptr, str) - .EndArray(); - } - - } - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c, ReverbContainer **def) -{ - int id = (arc.isReading() || c == nullptr) ? 0 : c->ID; - Serialize(arc, key, id, nullptr); - if (arc.isReading()) - { - c = S_FindEnvironment(id); - } - return arc; -} - -FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def) -{ - return Serialize(arc, key, z.Environment, nullptr); -} - -//============================================================================ -// -// Save a line portal for savegames. -// -//============================================================================ - -FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port, FLinePortal *def) -{ - if (arc.BeginObject(key)) - { - arc("origin", port.mOrigin) - ("destination", port.mDestination) - ("displacement", port.mDisplacement) - ("type", port.mType) - ("flags", port.mFlags) - ("defflags", port.mDefFlags) - ("align", port.mAlign) - .EndObject(); - } - return arc; -} - -//============================================================================ -// -// Save a sector portal for savegames. -// -//============================================================================ - -FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, FSectorPortal *def) -{ - if (arc.BeginObject(key)) - { - arc("type", port.mType) - ("flags", port.mFlags) - ("partner", port.mPartner) - ("plane", port.mPlane) - ("origin", port.mOrigin) - ("destination", port.mDestination) - ("displacement", port.mDisplacement) - ("planez", port.mPlaneZ) - ("skybox", port.mSkybox) - .EndObject(); - } - return arc; -} - - void DThinker::SaveList(FSerializer &arc, DThinker *node) { if (node != NULL) @@ -368,17 +50,6 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) -void SerializeWorld(FSerializer &arc) -{ - arc.Array("linedefs", lines, &loadlines[0], numlines) - .Array("sidedefs", sides, &loadsides[0], numsides) - .Array("sectors", sectors, &loadsectors[0], numsectors) - ("subsectors", subsectors) - ("zones", Zones) - ("lineportals", linePortals) - ("sectorportals", sectorPortals); -} - void DObject::SerializeUserVars(FSerializer &arc) { PSymbolTable *symt; @@ -399,174 +70,9 @@ void DObject::SerializeUserVars(FSerializer &arc) } } -void DObject::Serialize(FSerializer &arc) -{ - ObjectFlags |= OF_SerialSuccess; -} - -//========================================================================== -// -// AActor :: Serialize -// -//========================================================================== - -#define A(a,b) ((a), (b), def->b) - -void AActor::Serialize(FSerializer &arc) -{ - int damage = 0; // just a placeholder until the insanity surrounding the damage property can be fixed - AActor *def = GetDefault(); - - Super::Serialize(arc); - - arc - .Sprite("sprite", sprite, &def->sprite) - A("pos", __Pos) - A("angles", Angles) - A("frame", frame) - A("scale", Scale) - A("renderstyle", RenderStyle) - A("renderflags", renderflags) - A("picnum", picnum) - A("floorpic", floorpic) - A("ceilingpic", ceilingpic) - A("tidtohate", TIDtoHate) - A("lastlookpn", LastLookPlayerNumber) - ("lastlookactor", LastLookActor) - A("effects", effects) - A("alpha", Alpha) - A("fillcolor", fillcolor) - A("sector", Sector) - A("floorz", floorz) - A("ceilingz", ceilingz) - A("dropoffz", dropoffz) - A("floorsector", floorsector) - A("ceilingsector", ceilingsector) - A("radius", radius) - A("height", Height) - A("ppassheight", projectilepassheight) - A("vel", Vel) - A("tics", tics) - A("state", state) - ("damage", damage) - .Terrain("floorterrain", floorterrain, &def->floorterrain) - A("projectilekickback", projectileKickback) - A("flags", flags) - A("flags2", flags2) - A("flags3", flags3) - A("flags4", flags4) - A("flags5", flags5) - A("flags6", flags6) - A("flags7", flags7) - A("weaponspecial", weaponspecial) - A("special1", special1) - A("special2", special2) - A("specialf1", specialf1) - A("specialf2", specialf2) - A("health", health) - A("movedir", movedir) - A("visdir", visdir) - A("movecount", movecount) - A("strafecount", strafecount) - ("target", target) - ("lastenemy", lastenemy) - ("lastheard", LastHeard) - A("reactiontime", reactiontime) - A("threshold", threshold) - A("player", player) - A("spawnpoint", SpawnPoint) - A("spawnangle", SpawnAngle) - A("starthealth", StartHealth) - A("skillrespawncount", skillrespawncount) - ("tracer", tracer) - A("floorclip", Floorclip) - A("tid", tid) - A("special", special) - .Args("args", args, def->args, special) - A("accuracy", accuracy) - A("stamina", stamina) - ("goal", goal) - A("waterlevel", waterlevel) - A("minmissilechance", MinMissileChance) - A("spawnflags", SpawnFlags) - ("inventory", Inventory) - A("inventoryid", InventoryID) - A("floatbobphase", FloatBobPhase) - A("translation", Translation) - A("seesound", SeeSound) - A("attacksound", AttackSound) - A("paimsound", PainSound) - A("deathsound", DeathSound) - A("activesound", ActiveSound) - A("usesound", UseSound) - A("bouncesound", BounceSound) - A("wallbouncesound", WallBounceSound) - A("crushpainsound", CrushPainSound) - A("speed", Speed) - A("floatspeed", FloatSpeed) - A("mass", Mass) - A("painchance", PainChance) - A("spawnstate", SpawnState) - A("seestate", SeeState) - A("meleestate", MeleeState) - A("missilestate", MissileState) - A("maxdropoffheight", MaxDropOffHeight) - A("maxstepheight", MaxStepHeight) - A("bounceflags", BounceFlags) - A("bouncefactor", bouncefactor) - A("wallbouncefactor", wallbouncefactor) - A("bouncecount", bouncecount) - A("maxtargetrange", maxtargetrange) - A("meleethreshold", meleethreshold) - A("meleerange", meleerange) - A("damagetype", DamageType) - A("damagetypereceived", DamageTypeReceived) - A("paintype", PainType) - A("deathtype", DeathType) - A("gravity", Gravity) - A("fastchasestrafecount", FastChaseStrafeCount) - ("master", master) - A("smokecounter", smokecounter) - ("blockingmobj", BlockingMobj) - A("blockingline", BlockingLine) - A("visibletoteam", VisibleToTeam) - A("pushfactor", pushfactor) - A("species", Species) - A("score", Score) - A("designatedteam", DesignatedTeam) - A("lastpush", lastpush) - A("lastbump", lastbump) - A("painthreshold", PainThreshold) - A("damagefactor", DamageFactor) - A("damagemultiply", DamageMultiply) - A("waveindexxy", WeaveIndexXY) - A("weaveindexz", WeaveIndexZ) - A("pdmgreceived", PoisonDamageReceived) - A("pdurreceived", PoisonDurationReceived) - A("ppreceived", PoisonPeriodReceived) - ("poisoner", Poisoner) - A("posiondamage", PoisonDamage) - A("poisonduration", PoisonDuration) - A("poisonperiod", PoisonPeriod) - A("poisondamagetype", PoisonDamageType) - A("poisondmgtypereceived", PoisonDamageTypeReceived) - A("conversationroot", ConversationRoot) - A("conversation", Conversation) - A("friendplayer", FriendPlayer) - A("telefogsourcetype", TeleFogSourceType) - A("telefogdesttype", TeleFogDestType) - A("ripperlevel", RipperLevel) - A("riplevelmin", RipLevelMin) - A("riplevelmax", RipLevelMax) - A("devthreshold", DefThreshold) - A("spriteangle", SpriteAngle) - A("spriterotation", SpriteRotation) - ("alternative", alternative) - A("tag", Tag); -} - +void SerializeWorld(FSerializer &arc); CCMD(writejson) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 3ecf5c76c..2459f79af 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -70,6 +70,7 @@ #include "po_man.h" #include "p_spec.h" #include "p_checkposition.h" +#include "serializer.h" // MACROS ------------------------------------------------------------------ @@ -155,214 +156,195 @@ AActor::~AActor () // //========================================================================== -void AActor::Serialize(FArchive &arc) +#define A(a,b) ((a), (b), def->b) + +void AActor::Serialize(FSerializer &arc) { + AActor *def = GetDefault(); + Super::Serialize(arc); - if (arc.IsStoring()) - { - arc.WriteSprite(sprite); - } - else - { - sprite = arc.ReadSprite(); - } - - arc << __Pos - << Angles.Yaw - << Angles.Pitch - << Angles.Roll - << frame - << Scale - << RenderStyle - << renderflags - << picnum - << floorpic - << ceilingpic - << TIDtoHate - << LastLookPlayerNumber - << LastLookActor - << effects - << Alpha - << fillcolor - << Sector - << floorz - << ceilingz - << dropoffz - << floorsector - << ceilingsector - << radius - << Height - << projectilepassheight - << Vel - << tics - << state - << DamageVal; - if (DamageVal == 0x40000000 || DamageVal == -1) - { - DamageVal = -1; - DamageFunc = GetDefault()->DamageFunc; - } - else - { - DamageFunc = nullptr; - } - P_SerializeTerrain(arc, floorterrain); - arc << projectileKickback - << flags - << flags2 - << flags3 - << flags4 - << flags5 - << flags6 - << flags7 - << weaponspecial - << special1 - << special2 - << specialf1 - << specialf2 - << health - << movedir - << visdir - << movecount - << strafecount - << target - << lastenemy - << LastHeard - << reactiontime - << threshold - << player - << SpawnPoint - << SpawnAngle - << StartHealth - << skillrespawncount - << tracer - << Floorclip - << tid - << special; - if (P_IsACSSpecial(special)) - { - P_SerializeACSScriptNumber(arc, args[0], false); - } - else - { - arc << args[0]; - } - arc << args[1] << args[2] << args[3] << args[4]; - arc << accuracy << stamina; - arc << goal - << waterlevel - << MinMissileChance - << SpawnFlags - << Inventory - << InventoryID; - arc << FloatBobPhase - << Translation - << SeeSound - << AttackSound - << PainSound - << DeathSound - << ActiveSound - << UseSound - << BounceSound - << WallBounceSound - << CrushPainSound - << Speed - << FloatSpeed - << Mass - << PainChance - << SpawnState - << SeeState - << MeleeState - << MissileState - << MaxDropOffHeight - << MaxStepHeight - << BounceFlags - << bouncefactor - << wallbouncefactor - << bouncecount - << maxtargetrange - << meleethreshold - << meleerange - << DamageType; - arc << DamageTypeReceived; - arc << PainType - << DeathType; - arc << Gravity - << FastChaseStrafeCount - << master - << smokecounter - << BlockingMobj - << BlockingLine - << VisibleToTeam // [BB] - << pushfactor - << Species - << Score; - arc << DesignatedTeam; - arc << lastpush << lastbump - << PainThreshold - << DamageFactor; - arc << DamageMultiply; - arc << WeaveIndexXY << WeaveIndexZ - << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner - << PoisonDamage << PoisonDuration << PoisonPeriod; - arc << PoisonDamageType << PoisonDamageTypeReceived; - arc << ConversationRoot << Conversation; - arc << FriendPlayer; - arc << TeleFogSourceType - << TeleFogDestType; - arc << RipperLevel - << RipLevelMin - << RipLevelMax; - arc << DefThreshold; - if (SaveVersion >= 4549) - { - arc << SpriteAngle; - arc << SpriteRotation; - } - - if (SaveVersion >= 4550) - { - arc << alternative; - } - - { - FString tagstr; - if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; - arc << tagstr; - if (arc.IsLoading()) - { - if (tagstr.Len() == 0) Tag = NULL; - else Tag = mStringPropertyData.Alloc(tagstr); - } - } - - if (arc.IsLoading ()) - { - touching_sectorlist = NULL; - LinkToWorld(false, Sector); - - AddToHash (); - SetShade (fillcolor); - if (player) - { - if (playeringame[player - players] && - player->cls != NULL && - !(flags4 & MF4_NOSKIN) && - state->sprite == GetDefaultByType (player->cls)->SpawnState->sprite) - { // Give player back the skin - sprite = skins[player->userinfo.GetSkin()].sprite; - } - if (Speed == 0) - { - Speed = GetDefault()->Speed; - } - } - ClearInterpolation(); - UpdateWaterLevel(false); - } + arc + .Sprite("sprite", sprite, &def->sprite) + A("pos", __Pos) + A("angles", Angles) + A("frame", frame) + A("scale", Scale) + A("renderstyle", RenderStyle) + A("renderflags", renderflags) + A("picnum", picnum) + A("floorpic", floorpic) + A("ceilingpic", ceilingpic) + A("tidtohate", TIDtoHate) + A("lastlookpn", LastLookPlayerNumber) + ("lastlookactor", LastLookActor) + A("effects", effects) + A("alpha", Alpha) + A("fillcolor", fillcolor) + A("sector", Sector) + A("floorz", floorz) + A("ceilingz", ceilingz) + A("dropoffz", dropoffz) + A("floorsector", floorsector) + A("ceilingsector", ceilingsector) + A("radius", radius) + A("height", Height) + A("ppassheight", projectilepassheight) + A("vel", Vel) + A("tics", tics) + A("state", state) + A("damage", DamageVal) + .Terrain("floorterrain", floorterrain, &def->floorterrain) + A("projectilekickback", projectileKickback) + A("flags", flags) + A("flags2", flags2) + A("flags3", flags3) + A("flags4", flags4) + A("flags5", flags5) + A("flags6", flags6) + A("flags7", flags7) + A("weaponspecial", weaponspecial) + A("special1", special1) + A("special2", special2) + A("specialf1", specialf1) + A("specialf2", specialf2) + A("health", health) + A("movedir", movedir) + A("visdir", visdir) + A("movecount", movecount) + A("strafecount", strafecount) + ("target", target) + ("lastenemy", lastenemy) + ("lastheard", LastHeard) + A("reactiontime", reactiontime) + A("threshold", threshold) + A("player", player) + A("spawnpoint", SpawnPoint) + A("spawnangle", SpawnAngle) + A("starthealth", StartHealth) + A("skillrespawncount", skillrespawncount) + ("tracer", tracer) + A("floorclip", Floorclip) + A("tid", tid) + A("special", special) + .Args("args", args, def->args, special) + A("accuracy", accuracy) + A("stamina", stamina) + ("goal", goal) + A("waterlevel", waterlevel) + A("minmissilechance", MinMissileChance) + A("spawnflags", SpawnFlags) + ("inventory", Inventory) + A("inventoryid", InventoryID) + A("floatbobphase", FloatBobPhase) + A("translation", Translation) + A("seesound", SeeSound) + A("attacksound", AttackSound) + A("paimsound", PainSound) + A("deathsound", DeathSound) + A("activesound", ActiveSound) + A("usesound", UseSound) + A("bouncesound", BounceSound) + A("wallbouncesound", WallBounceSound) + A("crushpainsound", CrushPainSound) + A("speed", Speed) + A("floatspeed", FloatSpeed) + A("mass", Mass) + A("painchance", PainChance) + A("spawnstate", SpawnState) + A("seestate", SeeState) + A("meleestate", MeleeState) + A("missilestate", MissileState) + A("maxdropoffheight", MaxDropOffHeight) + A("maxstepheight", MaxStepHeight) + A("bounceflags", BounceFlags) + A("bouncefactor", bouncefactor) + A("wallbouncefactor", wallbouncefactor) + A("bouncecount", bouncecount) + A("maxtargetrange", maxtargetrange) + A("meleethreshold", meleethreshold) + A("meleerange", meleerange) + A("damagetype", DamageType) + A("damagetypereceived", DamageTypeReceived) + A("paintype", PainType) + A("deathtype", DeathType) + A("gravity", Gravity) + A("fastchasestrafecount", FastChaseStrafeCount) + ("master", master) + A("smokecounter", smokecounter) + ("blockingmobj", BlockingMobj) + A("blockingline", BlockingLine) + A("visibletoteam", VisibleToTeam) + A("pushfactor", pushfactor) + A("species", Species) + A("score", Score) + A("designatedteam", DesignatedTeam) + A("lastpush", lastpush) + A("lastbump", lastbump) + A("painthreshold", PainThreshold) + A("damagefactor", DamageFactor) + A("damagemultiply", DamageMultiply) + A("waveindexxy", WeaveIndexXY) + A("weaveindexz", WeaveIndexZ) + A("pdmgreceived", PoisonDamageReceived) + A("pdurreceived", PoisonDurationReceived) + A("ppreceived", PoisonPeriodReceived) + ("poisoner", Poisoner) + A("posiondamage", PoisonDamage) + A("poisonduration", PoisonDuration) + A("poisonperiod", PoisonPeriod) + A("poisondamagetype", PoisonDamageType) + A("poisondmgtypereceived", PoisonDamageTypeReceived) + A("conversationroot", ConversationRoot) + A("conversation", Conversation) + A("friendplayer", FriendPlayer) + A("telefogsourcetype", TeleFogSourceType) + A("telefogdesttype", TeleFogDestType) + A("ripperlevel", RipperLevel) + A("riplevelmin", RipLevelMin) + A("riplevelmax", RipLevelMax) + A("devthreshold", DefThreshold) + A("spriteangle", SpriteAngle) + A("spriterotation", SpriteRotation) + ("alternative", alternative) + A("tag", Tag); } +#undef A + +//========================================================================== +// +// This must be done after the world is set up. +// +//========================================================================== + +void AActor::PostSerialize() +{ + touching_sectorlist = NULL; + LinkToWorld(false, Sector); + + AddToHash(); + SetShade(fillcolor); + if (player) + { + if (playeringame[player - players] && + player->cls != NULL && + !(flags4 & MF4_NOSKIN) && + state->sprite == GetDefaultByType(player->cls)->SpawnState->sprite) + { // Give player back the skin + sprite = skins[player->userinfo.GetSkin()].sprite; + } + if (Speed == 0) + { + Speed = GetDefault()->Speed; + } + } + ClearInterpolation(); + UpdateWaterLevel(false); +} + + + AActor::AActor () throw() { } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 3bbae666a..550e9455d 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -3,7 +3,8 @@ ** Code for serializing the world state in an archive ** **--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit +** Copyright 1998-2016 Randy Heit +** Copyright 2005-2016 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -55,18 +56,22 @@ #include "p_lnspec.h" #include "p_acs.h" #include "p_terrain.h" +#include "am_map.h" +#include "r_data/r_translate.h" +#include "sbar.h" +#include "r_utility.h" +#include "r_sky.h" +#include "r_renderer.h" +#include "serializer.h" + +// just the stuff that already got converted to FSerializer so that it can be seen as 'done' when searching. +#include "zzz_old.cpp" void CopyPlayer (player_t *dst, player_t *src, const char *name); static void ReadOnePlayer (FArchive &arc, bool skipload); static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload); static void SpawnExtraPlayers (); -inline FArchive &operator<< (FArchive &arc, FLinkedSector &link) -{ - arc << link.Sector << link.Type; - return arc; -} - // // P_ArchivePlayers // @@ -339,179 +344,6 @@ static void SpawnExtraPlayers () } } -// -// P_ArchiveWorld -// -void P_SerializeWorld (FArchive &arc) -{ - int i, j; - sector_t *sec; - line_t *li; - zone_t *zn; - - // do sectors - for (i = 0, sec = sectors; i < numsectors; i++, sec++) - { - arc << sec->floorplane - << sec->ceilingplane; - arc << sec->lightlevel; - arc << sec->special; - arc << sec->soundtraversed - << sec->seqType - << sec->friction - << sec->movefactor - << sec->stairlock - << sec->prevsec - << sec->nextsec - << sec->planes[sector_t::floor] - << sec->planes[sector_t::ceiling] - << sec->heightsec - << sec->bottommap << sec->midmap << sec->topmap - << sec->gravity; - P_SerializeTerrain(arc, sec->terrainnum[0]); - P_SerializeTerrain(arc, sec->terrainnum[1]); - arc << sec->damageamount; - arc << sec->damageinterval - << sec->leakydamage - << sec->damagetype - << sec->sky - << sec->MoreFlags - << sec->Flags - << sec->Portals[sector_t::floor] << sec->Portals[sector_t::ceiling] - << sec->ZoneNumber; - arc << sec->interpolations[0] - << sec->interpolations[1] - << sec->interpolations[2] - << sec->interpolations[3] - << sec->SeqName; - - sec->e->Serialize(arc); - if (arc.IsStoring ()) - { - arc << sec->ColorMap->Color - << sec->ColorMap->Fade; - BYTE sat = sec->ColorMap->Desaturate; - arc << sat; - } - else - { - PalEntry color, fade; - BYTE desaturate; - arc << color << fade - << desaturate; - sec->ColorMap = GetSpecialLights (color, fade, desaturate); - } - } - - // do lines - for (i = 0, li = lines; i < numlines; i++, li++) - { - arc << li->flags - << li->activation - << li->special - << li->alpha; - - if (P_IsACSSpecial(li->special)) - { - P_SerializeACSScriptNumber(arc, li->args[0], false); - } - else - { - arc << li->args[0]; - } - arc << li->args[1] << li->args[2] << li->args[3] << li->args[4]; - - arc << li->portalindex; - for (j = 0; j < 2; j++) - { - if (li->sidedef[j] == NULL) - continue; - - side_t *si = li->sidedef[j]; - arc << si->textures[side_t::top] - << si->textures[side_t::mid] - << si->textures[side_t::bottom] - << si->Light - << si->Flags - << si->LeftSide - << si->RightSide - << si->Index; - } - } - - // do zones - unsigned numzones = Zones.Size(); - arc << numzones; - - if (arc.IsLoading()) - { - Zones.Resize(numzones); - } - - for (i = 0, zn = &Zones[0]; i < numzones; ++i, ++zn) - { - arc << zn->Environment; - } - - arc << linePortals << sectorPortals; - P_CollectLinkedPortals(); -} - -void P_SerializeWorldActors(FArchive &arc) -{ - int i; - sector_t *sec; - line_t *line; - - for (i = 0, sec = sectors; i < numsectors; i++, sec++) - { - arc << sec->SoundTarget - << sec->SecActTarget - << sec->floordata - << sec->ceilingdata - << sec->lightingdata; - } - for (auto &s : sectorPortals) - { - arc << s.mSkybox; - } - for (i = 0, line = lines; i < numlines; i++, line++) - { - for (int s = 0; s < 2; s++) - { - if (line->sidedef[s] != NULL) - { - DBaseDecal::SerializeChain(arc, &line->sidedef[s]->AttachedDecals); - } - } - } -} - -void extsector_t::Serialize(FArchive &arc) -{ - arc << FakeFloor.Sectors - << Midtex.Floor.AttachedLines - << Midtex.Floor.AttachedSectors - << Midtex.Ceiling.AttachedLines - << Midtex.Ceiling.AttachedSectors - << Linked.Floor.Sectors - << Linked.Ceiling.Sectors; -} - -FArchive &operator<< (FArchive &arc, side_t::part &p) -{ - arc << p.xOffset << p.yOffset << p.interpolation << p.texture - << p.xScale << p.yScale;// << p.Light; - return arc; -} - -FArchive &operator<< (FArchive &arc, sector_t::splane &p) -{ - arc << p.xform.xOffs << p.xform.yOffs << p.xform.xScale << p.xform.yScale - << p.xform.Angle << p.xform.baseyOffs << p.xform.baseAngle - << p.Flags << p.Light << p.Texture << p.TexZ << p.alpha; - return arc; -} // @@ -537,6 +369,411 @@ void P_DestroyThinkers(bool hubLoad) DThinker::DestroyAllThinkers(); } +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, line_t &line, line_t *def) +{ + if (arc.BeginObject(key)) + { + arc("flags", line.flags, def->flags) + ("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) + // 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(); + } + return arc; + +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t::part &part, side_t::part *def) +{ + if (arc.canSkip() && def != nullptr && !memcmp(&part, def, sizeof(part))) + { + return arc; + } + + if (arc.BeginObject(key)) + { + arc("xoffset", part.xOffset, def->xOffset) + ("yoffset", part.yOffset, def->yOffset) + ("xscale", part.xScale, def->xScale) + ("yscale", part.yScale, def->yScale) + ("texture", part.texture, def->texture) + ("interpolation", part.interpolation) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, side_t &side, side_t *def) +{ + if (arc.BeginObject(key)) + { + arc.Array("textures", side.textures, def->textures, 3, true) + ("light", side.Light, def->Light) + ("flags", side.Flags, def->Flags) + // These also remain identical across map loads + //("leftside", side.LeftSide) + //("rightside", side.RightSide) + //("index", side.Index) + ("attacheddecals", side.AttachedDecals) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FLinkedSector &ls, FLinkedSector *def) +{ + if (arc.BeginObject(key)) + { + arc("sector", ls.Sector) + ("type", ls.Type) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t::splane &p, sector_t::splane *def) +{ + if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) + { + return arc; + } + + if (arc.BeginObject(key)) + { + arc("xoffs", p.xform.xOffs, def->xform.xOffs) + ("yoffs", p.xform.yOffs, def->xform.yOffs) + ("xscale", p.xform.xScale, def->xform.xScale) + ("yscale", p.xform.yScale, def->xform.yScale) + ("angle", p.xform.Angle, def->xform.Angle) + ("baseyoffs", p.xform.baseyOffs, def->xform.baseyOffs) + ("baseangle", p.xform.baseAngle, def->xform.baseAngle) + ("flags", p.Flags, def->Flags) + ("light", p.Light, def->Light) + ("texture", p.Texture, def->Texture) + ("texz", p.TexZ, def->TexZ) + ("alpha", p.alpha, def->alpha) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def) +{ + if (arc.canSkip() && def != nullptr && !memcmp(&p, def, sizeof(p))) + { + return arc; + } + + if (arc.BeginObject(key)) + { + arc("normal", p.normal, def->normal) + ("d", p.D, def->D) + .EndObject(); + + if (arc.isReading() && p.normal.Z != 0) + { + p.negiC = 1 / p.normal.Z; + } + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t *def) +{ + if (arc.BeginObject(key)) + { + arc("floorplane", p.floorplane, def->floorplane) + ("ceilingplane", p.ceilingplane, def->ceilingplane) + ("lightlevel", p.lightlevel, def->lightlevel) + ("special", p.special, def->special) + ("soundtraversed", p.soundtraversed, def->soundtraversed) + ("seqtype", p.seqType, def->seqType) + ("seqname", p.SeqName, def->SeqName) + ("friction", p.friction, def->friction) + ("movefactor", p.movefactor, def->movefactor) + ("stairlock", p.stairlock, def->stairlock) + ("prevsec", p.prevsec, def->prevsec) + ("nextsec", p.nextsec, def->nextsec) + .Array("planes", p.planes, def->planes, 2, true) + // These cannot change during play. + //("heightsec", p.heightsec) + //("bottommap", p.bottommap) + //("midmap", p.midmap) + //("topmap", p.topmap) + ("damageamount", p.damageamount, def->damageamount) + ("damageinterval", p.damageinterval, def->damageinterval) + ("leakydamage", p.leakydamage, def->leakydamage) + ("damagetype", p.damagetype, def->damagetype) + ("sky", p.sky, def->sky) + ("moreflags", p.MoreFlags, def->MoreFlags) + ("flags", p.Flags, def->Flags) + .Array("portals", p.Portals, def->Portals, 2, true) + ("zonenumber", p.ZoneNumber, def->ZoneNumber) + .Array("interpolations", p.interpolations, 4, true) + ("soundtarget", p.SoundTarget) + ("secacttarget", p.SecActTarget) + ("floordata", p.floordata) + ("ceilingdata", p.ceilingdata) + ("lightingdata", p.lightingdata) + ("fakefloor_sectors", p.e->FakeFloor.Sectors) + ("midtexf_lines", p.e->Midtex.Floor.AttachedLines) + ("midtexf_sectors", p.e->Midtex.Floor.AttachedSectors) + ("midtexc_lines", p.e->Midtex.Ceiling.AttachedLines) + ("midtexc_sectors", p.e->Midtex.Ceiling.AttachedSectors) + ("linked_floor", p.e->Linked.Floor.Sectors) + ("linked_ceiling", p.e->Linked.Ceiling.Sectors) + ("colormap", p.ColorMap, def->ColorMap) + .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) + .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// RecalculateDrawnSubsectors +// +// In case the subsector data is unusable this function tries to reconstruct +// if from the linedefs' ML_MAPPED info. +// +//========================================================================== + +void RecalculateDrawnSubsectors() +{ + for (int i = 0; inumlines; j++) + { + if (sub->firstline[j].linedef != NULL && + (sub->firstline[j].linedef->flags & ML_MAPPED)) + { + sub->flags |= SSECF_DRAWN; + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subsector_t **) +{ + BYTE by; + const char *str; + + if (arc.isWriting()) + { + if (hasglnodes) + { + TArray encoded(1 + (numsubsectors + 5) / 6); + int p = 0; + for (int i = 0; i < numsubsectors; i += 6) + { + by = 0; + for (int j = 0; j < 6; j++) + { + if (i + j < numsubsectors && (subsectors[i + j].flags & SSECF_DRAWN)) + { + by |= (1 << j); + } + } + if (by < 10) by += '0'; + else if (by < 36) by += 'A' - 10; + else if (by < 62) by += 'a' - 36; + else if (by == 62) by = '-'; + else if (by == 63) by = '+'; + encoded[p++] = by; + } + encoded[p] = 0; + str = &encoded[0]; + if (arc.BeginArray(key)) + { + arc(nullptr, numvertexes) + (nullptr, numsubsectors) + .StringPtr(nullptr, str) + .EndArray(); + } + } + } + else + { + int num_verts, num_subs; + bool success = false; + if (arc.BeginArray(key)) + { + arc(nullptr, num_verts) + (nullptr, num_subs) + .StringPtr(nullptr, str) + .EndArray(); + + if (num_verts == numvertexes && num_subs == numsubsectors && hasglnodes) + { + success = true; + for (int i = 0; str[i] != 0; i++) + { + by = str[i]; + if (by >= '0' && by <= '9') by -= '0'; + else if (by >= 'A' && by <= 'Z') by -= 'A' - 10; + else if (by >= 'a' && by <= 'z') by -= 'a' - 36; + else if (by == '-') by = 62; + else if (by == '+') by = 63; + else + { + success = false; + break; + } + } + } + if (hasglnodes && !success) + { + RecalculateDrawnSubsectors(); + } + } + + } + return arc; +} + +//============================================================================ +// +// Save a line portal for savegames. +// +//============================================================================ + +FSerializer &Serialize(FSerializer &arc, const char *key, FLinePortal &port, FLinePortal *def) +{ + if (arc.BeginObject(key)) + { + arc("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("type", port.mType) + ("flags", port.mFlags) + ("defflags", port.mDefFlags) + ("align", port.mAlign) + .EndObject(); + } + return arc; +} + +//============================================================================ +// +// Save a sector portal for savegames. +// +//============================================================================ + +FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, FSectorPortal *def) +{ + if (arc.BeginObject(key)) + { + arc("type", port.mType) + ("flags", port.mFlags) + ("partner", port.mPartner) + ("plane", port.mPlane) + ("origin", port.mOrigin) + ("destination", port.mDestination) + ("displacement", port.mDisplacement) + ("planez", port.mPlaneZ) + ("skybox", port.mSkybox) + .EndObject(); + } + return arc; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, ReverbContainer *&c, ReverbContainer **def) +{ + int id = (arc.isReading() || c == nullptr) ? 0 : c->ID; + Serialize(arc, key, id, nullptr); + if (arc.isReading()) + { + c = S_FindEnvironment(id); + } + return arc; +} + +FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def) +{ + return Serialize(arc, key, z.Environment, nullptr); +} + +//============================================================================ +// +// +// +//============================================================================ + +void SerializeWorld(FSerializer &arc) +{ + // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON + arc.Array("linedefs", lines, &loadlines[0], numlines) + .Array("sidedefs", sides, &loadsides[0], numsides) + .Array("sectors", sectors, &loadsectors[0], numsectors) + ("subsectors", subsectors) + ("zones", Zones) + ("lineportals", linePortals) + ("sectorportals", sectorPortals); + + if (arc.isReading()) P_CollectLinkedPortals(); +} + + //========================================================================== // // ArchiveSounds @@ -625,96 +862,132 @@ void P_SerializePolyobjs (FArchive &arc) //========================================================================== // -// RecalculateDrawnSubsectors -// -// In case the subsector data is unusable this function tries to reconstruct -// if from the linedefs' ML_MAPPED info. // //========================================================================== -void RecalculateDrawnSubsectors() +void G_SerializeLevel(FArchive &arc, bool hubLoad) { - for(int i=0;iStartSerialize(arc); + if (arc.IsLoading()) P_DestroyThinkers(hubLoad); + + arc << level.flags + << level.flags2 + << level.fadeto + << level.found_secrets + << level.found_items + << level.killed_monsters + << level.gravity + << level.aircontrol + << level.teamdamage + << level.maptime + << i; + + // Hub transitions must keep the current total time + if (!hubLoad) + level.totaltime = i; + + arc << level.skytexture1 << level.skytexture2; + if (arc.IsLoading()) { - subsector_t *sub = &subsectors[i]; - for(unsigned int j=0;jnumlines;j++) - { - if (sub->firstline[j].linedef != NULL && - (sub->firstline[j].linedef->flags & ML_MAPPED)) - { - sub->flags |= SSECF_DRAWN; - } - } + sky1texture = level.skytexture1; + sky2texture = level.skytexture2; + R_InitSkyMap(); } -} -//========================================================================== -// -// ArchiveSubsectors -// -//========================================================================== + G_AirControlChanged(); -void P_SerializeSubsectors(FArchive &arc) -{ - int num_verts, num_subs, num_nodes; - BYTE by; + BYTE t; + // Does this level have scrollers? if (arc.IsStoring()) { - if (hasglnodes) - { - arc << numvertexes << numsubsectors << numnodes; // These are only for verification - for(int i=0;iSerialize(arc); + + arc << level.total_monsters << level.total_items << level.total_secrets; + + // Does this level have custom translations? + FRemapTable *trans; + WORD w; + if (arc.IsStoring()) + { + for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i) + { + trans = translationtables[TRANSLATION_LevelScripted][i]; + if (trans != NULL && !trans->IsIdentity()) { - arc << by; - for(int j=0;j<8;j++) - { - if ((by & (1<Serialize(arc); + } + } + w = 0xffff; + arc << w; + } + else + { + while (arc << w, w != 0xffff) + { + trans = translationtables[TRANSLATION_LevelScripted].GetVal(w); + if (trans == NULL) + { + trans = new FRemapTable; + translationtables[TRANSLATION_LevelScripted].SetVal(w, trans); + } + trans->Serialize(arc); + } + } + + // This must be saved, too, of course! + FCanvasTextureInfo::Serialize(arc); + AM_SerializeMarkers(arc); + + P_SerializePlayers(arc, hubLoad); + P_SerializeSounds(arc); + if (arc.IsLoading()) + { + for (i = 0; i < numsectors; i++) + { + P_Recalculate3DFloors(§ors[i]); + } + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].mo != NULL) + { + players[i].mo->SetupWeaponSlots(); } } } + Renderer->EndSerialize(arc); + unsigned tt = I_MSTime(); + Printf("Serialization took %d ms\n", tt - tm); } + diff --git a/src/p_saveg.h b/src/p_saveg.h index eb3e4de2b..a62e5c5b8 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -52,4 +52,6 @@ void P_SerializeSounds (FArchive &arc); void P_ReadACSDefereds (PNGHandle *png); void P_WriteACSDefereds (FILE *file); +void G_SerializeLevel(FArchive &arc, bool hubLoad); + #endif // __P_SAVEG_H__ diff --git a/src/p_states.cpp b/src/p_states.cpp index 2e61a7844..a56229e05 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -47,71 +47,6 @@ // actor. States are archived by recording the actor they belong // to and the index into that actor's list of states. -// For NULL states, which aren't owned by any actor, the owner -// is recorded as AActor with the following state. AActor should -// never actually have this many states of its own, so this -// is (relatively) safe. - -#define NULL_STATE_INDEX 127 - -//========================================================================== -// -// -//========================================================================== - -FArchive &operator<< (FArchive &arc, FState *&state) -{ - PClassActor *info; - - if (arc.IsStoring ()) - { - if (state == NULL) - { - arc.UserWriteClass (RUNTIME_CLASS(AActor)); - arc.WriteCount (NULL_STATE_INDEX); - return arc; - } - - info = FState::StaticFindStateOwner (state); - - if (info != NULL) - { - arc.UserWriteClass (info); - arc.WriteCount ((DWORD)(state - info->OwnedStates)); - } - else - { - /* this was never working as intended. - I_Error ("Cannot find owner for state %p:\n" - "%s %c%c %3d [%p] -> %p", state, - sprites[state->sprite].name, - state->GetFrame() + 'A', - state->GetFullbright() ? '*' : ' ', - state->GetTics(), - state->GetAction(), - state->GetNextState()); - */ - } - } - else - { - PClassActor *info; - DWORD ofs; - - arc.UserReadClass(info); - ofs = arc.ReadCount (); - if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor)) - { - state = NULL; - } - else - { - state = info->OwnedStates + ofs; - } - } - return arc; -} - //========================================================================== // // Find the actor that a state belongs to. diff --git a/src/serializer.cpp b/src/serializer.cpp index db467c21a..06625b054 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -504,7 +504,9 @@ void FSerializer::WriteObjects() BeginObject(nullptr); WriteKey("classtype"); w->mWriter.String(w->mDObjects[i]->GetClass()->TypeName.GetChars()); + w->mDObjects[i]->SerializeUserVars(*this); w->mDObjects[i]->Serialize(*this); + w->mDObjects[i]->CheckIfSerialized(); EndObject(); } EndArray(); @@ -1144,8 +1146,18 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState } else if (val->IsArray()) { - //rapidjson::Value cls = (*val)[0]; - //rapidjson::Value ndx = (*val)[1]; + const rapidjson::Value &cls = (*val)[0]; + const rapidjson::Value &ndx = (*val)[1]; + + state = nullptr; + if (cls.IsString() && ndx.IsInt()) + { + PClassActor *clas = PClass::FindActor(cls.GetString()); + if (clas) + { + state = clas->OwnedStates + ndx.GetInt(); + } + } } else { diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp new file mode 100644 index 000000000..a4d6df95d --- /dev/null +++ b/src/zzz_old.cpp @@ -0,0 +1,697 @@ +// For NULL states, which aren't owned by any actor, the owner +// is recorded as AActor with the following state. AActor should +// never actually have this many states of its own, so this +// is (relatively) safe. + +#define NULL_STATE_INDEX 127 + +// These are special tokens found in the data stream of an archive. +// Whenever a new object is encountered, it gets created using new and +// is then asked to serialize itself before processing of the previous +// object continues. This can result in some very deep recursion if +// you aren't careful about how you organize your data. + +#define NEW_OBJ ((BYTE)1) // Data for a new object follows +#define NEW_CLS_OBJ ((BYTE)2) // Data for a new class and object follows +#define OLD_OBJ ((BYTE)3) // Reference to an old object follows +#define NULL_OBJ ((BYTE)4) // Load as NULL +#define M1_OBJ ((BYTE)44) // Load as (DObject*)-1 + +#define NEW_PLYR_OBJ ((BYTE)5) // Data for a new player follows +#define NEW_PLYR_CLS_OBJ ((BYTE)6) // Data for a new class and player follows + +#define NEW_NAME ((BYTE)27) // A new name follows +#define OLD_NAME ((BYTE)28) // Reference to an old name follows +#define NIL_NAME ((BYTE)33) // Load as NULL + +#define NEW_SPRITE ((BYTE)11) // A new sprite name follows +#define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows + +inline FArchive &operator<< (FArchive &arc, FLinkedSector &link) +{ + arc << link.Sector << link.Type; + return arc; +} + +// +// P_ArchiveWorld +// +void P_SerializeWorld (FArchive &arc) +{ + int i, j; + sector_t *sec; + line_t *li; + zone_t *zn; + + // do sectors + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + arc << sec->floorplane + << sec->ceilingplane; + arc << sec->lightlevel; + arc << sec->special; + arc << sec->soundtraversed + << sec->seqType + << sec->friction + << sec->movefactor + << sec->stairlock + << sec->prevsec + << sec->nextsec + << sec->planes[sector_t::floor] + << sec->planes[sector_t::ceiling] + << sec->heightsec + << sec->bottommap << sec->midmap << sec->topmap + << sec->gravity; + P_SerializeTerrain(arc, sec->terrainnum[0]); + P_SerializeTerrain(arc, sec->terrainnum[1]); + arc << sec->damageamount; + arc << sec->damageinterval + << sec->leakydamage + << sec->damagetype + << sec->sky + << sec->MoreFlags + << sec->Flags + << sec->Portals[sector_t::floor] << sec->Portals[sector_t::ceiling] + << sec->ZoneNumber; + arc << sec->interpolations[0] + << sec->interpolations[1] + << sec->interpolations[2] + << sec->interpolations[3] + << sec->SeqName; + + sec->e->Serialize(arc); + if (arc.IsStoring ()) + { + arc << sec->ColorMap->Color + << sec->ColorMap->Fade; + BYTE sat = sec->ColorMap->Desaturate; + arc << sat; + } + else + { + PalEntry color, fade; + BYTE desaturate; + arc << color << fade + << desaturate; + sec->ColorMap = GetSpecialLights (color, fade, desaturate); + } + } + + // do lines + for (i = 0, li = lines; i < numlines; i++, li++) + { + arc << li->flags + << li->activation + << li->special + << li->alpha; + + if (P_IsACSSpecial(li->special)) + { + P_SerializeACSScriptNumber(arc, li->args[0], false); + } + else + { + arc << li->args[0]; + } + arc << li->args[1] << li->args[2] << li->args[3] << li->args[4]; + + arc << li->portalindex; + for (j = 0; j < 2; j++) + { + if (li->sidedef[j] == NULL) + continue; + + side_t *si = li->sidedef[j]; + arc << si->textures[side_t::top] + << si->textures[side_t::mid] + << si->textures[side_t::bottom] + << si->Light + << si->Flags + << si->LeftSide + << si->RightSide + << si->Index; + } + } + + // do zones + unsigned numzones = Zones.Size(); + arc << numzones; + + if (arc.IsLoading()) + { + Zones.Resize(numzones); + } + + for (i = 0, zn = &Zones[0]; i < (int)numzones; ++i, ++zn) + { + arc << zn->Environment; + } + + arc << linePortals << sectorPortals; + P_CollectLinkedPortals(); +} + +void P_SerializeWorldActors(FArchive &arc) +{ + int i; + sector_t *sec; + line_t *line; + + for (i = 0, sec = sectors; i < numsectors; i++, sec++) + { + arc << sec->SoundTarget + << sec->SecActTarget + << sec->floordata + << sec->ceilingdata + << sec->lightingdata; + } + for (auto &s : sectorPortals) + { + arc << s.mSkybox; + } + for (i = 0, line = lines; i < numlines; i++, line++) + { + for (int s = 0; s < 2; s++) + { + if (line->sidedef[s] != NULL) + { + DBaseDecal::SerializeChain(arc, &line->sidedef[s]->AttachedDecals); + } + } + } +} + +void extsector_t::Serialize(FArchive &arc) +{ + arc << FakeFloor.Sectors + << Midtex.Floor.AttachedLines + << Midtex.Floor.AttachedSectors + << Midtex.Ceiling.AttachedLines + << Midtex.Ceiling.AttachedSectors + << Linked.Floor.Sectors + << Linked.Ceiling.Sectors; +} + +FArchive &operator<< (FArchive &arc, side_t::part &p) +{ + arc << p.xOffset << p.yOffset << p.interpolation << p.texture + << p.xScale << p.yScale;// << p.Light; + return arc; +} + +FArchive &operator<< (FArchive &arc, sector_t::splane &p) +{ + arc << p.xform.xOffs << p.xform.yOffs << p.xform.xScale << p.xform.yScale + << p.xform.Angle << p.xform.baseyOffs << p.xform.baseAngle + << p.Flags << p.Light << p.Texture << p.TexZ << p.alpha; + return arc; +} + +//========================================================================== +// +// ArchiveSubsectors +// +//========================================================================== +void RecalculateDrawnSubsectors(); + +void P_SerializeSubsectors(FArchive &arc) +{ + int num_verts, num_subs, num_nodes; + BYTE by; + + if (arc.IsStoring()) + { + if (hasglnodes) + { + arc << numvertexes << numsubsectors << numnodes; // These are only for verification + for(int i=0;iObjectFlags & OF_EuthanizeMe) + { + // Objects that want to die are not saved to the archive, but + // we leave the pointers to them alone. + id[0] = NULL_OBJ; + Write (id, 1); + } + else + { + PClass *type = obj->GetClass(); + DWORD *classarcid; + + if (type == RUNTIME_CLASS(DObject)) + { + //I_Error ("Tried to save an instance of DObject.\n" + // "This should not happen.\n"); + id[0] = NULL_OBJ; + Write (id, 1); + } + else if (NULL == (classarcid = ClassToArchive.CheckKey(type))) + { + // No instances of this class have been written out yet. + // Write out the class, then write out the object. If this + // is an actor controlled by a player, make note of that + // so that it can be overridden when moving around in a hub. + if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && + (player = static_cast(obj)->player) && + player->mo == obj) + { + id[0] = NEW_PLYR_CLS_OBJ; + id[1] = (BYTE)(player - players); + Write (id, 2); + } + else + { + id[0] = NEW_CLS_OBJ; + Write (id, 1); + } + WriteClass (type); +// Printf ("Make class %s (%u)\n", type->Name, m_File->Tell()); + MapObject (obj); + obj->SerializeUserVars (*this); + obj->Serialize (*this); + obj->CheckIfSerialized (); + } + else + { + // An instance of this class has already been saved. If + // this object has already been written, save a reference + // to the saved object. Otherwise, save a reference to the + // class, then save the object. Again, if this is a player- + // controlled actor, remember that. + DWORD *objarcid = ObjectToArchive.CheckKey(obj); + + if (objarcid == NULL) + { + + if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && + (player = static_cast(obj)->player) && + player->mo == obj) + { + id[0] = NEW_PLYR_OBJ; + id[1] = (BYTE)(player - players); + Write (id, 2); + } + else + { + id[0] = NEW_OBJ; + Write (id, 1); + } + WriteCount (*classarcid); +// Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell()); + MapObject (obj); + obj->SerializeUserVars (*this); + obj->Serialize (*this); + obj->CheckIfSerialized (); + } + else + { + id[0] = OLD_OBJ; + Write (id, 1); + WriteCount (*objarcid); + } + } + } + return *this; +} + +void AActor::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + + if (arc.IsStoring()) + { + arc.WriteSprite(sprite); + } + else + { + sprite = arc.ReadSprite(); + } + + arc << __Pos + << Angles.Yaw + << Angles.Pitch + << Angles.Roll + << frame + << Scale + << RenderStyle + << renderflags + << picnum + << floorpic + << ceilingpic + << TIDtoHate + << LastLookPlayerNumber + << LastLookActor + << effects + << Alpha + << fillcolor + << Sector + << floorz + << ceilingz + << dropoffz + << floorsector + << ceilingsector + << radius + << Height + << projectilepassheight + << Vel + << tics + << state + << DamageVal; + if (DamageVal == 0x40000000 || DamageVal == -1) + { + DamageVal = -1; + DamageFunc = GetDefault()->DamageFunc; + } + else + { + DamageFunc = nullptr; + } + P_SerializeTerrain(arc, floorterrain); + arc << projectileKickback + << flags + << flags2 + << flags3 + << flags4 + << flags5 + << flags6 + << flags7 + << weaponspecial + << special1 + << special2 + << specialf1 + << specialf2 + << health + << movedir + << visdir + << movecount + << strafecount + << target + << lastenemy + << LastHeard + << reactiontime + << threshold + << player + << SpawnPoint + << SpawnAngle + << StartHealth + << skillrespawncount + << tracer + << Floorclip + << tid + << special; + if (P_IsACSSpecial(special)) + { + P_SerializeACSScriptNumber(arc, args[0], false); + } + else + { + arc << args[0]; + } + arc << args[1] << args[2] << args[3] << args[4]; + arc << accuracy << stamina; + arc << goal + << waterlevel + << MinMissileChance + << SpawnFlags + << Inventory + << InventoryID; + arc << FloatBobPhase + << Translation + << SeeSound + << AttackSound + << PainSound + << DeathSound + << ActiveSound + << UseSound + << BounceSound + << WallBounceSound + << CrushPainSound + << Speed + << FloatSpeed + << Mass + << PainChance + << SpawnState + << SeeState + << MeleeState + << MissileState + << MaxDropOffHeight + << MaxStepHeight + << BounceFlags + << bouncefactor + << wallbouncefactor + << bouncecount + << maxtargetrange + << meleethreshold + << meleerange + << DamageType; + arc << DamageTypeReceived; + arc << PainType + << DeathType; + arc << Gravity + << FastChaseStrafeCount + << master + << smokecounter + << BlockingMobj + << BlockingLine + << VisibleToTeam // [BB] + << pushfactor + << Species + << Score; + arc << DesignatedTeam; + arc << lastpush << lastbump + << PainThreshold + << DamageFactor; + arc << DamageMultiply; + arc << WeaveIndexXY << WeaveIndexZ + << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner + << PoisonDamage << PoisonDuration << PoisonPeriod; + arc << PoisonDamageType << PoisonDamageTypeReceived; + arc << ConversationRoot << Conversation; + arc << FriendPlayer; + arc << TeleFogSourceType + << TeleFogDestType; + arc << RipperLevel + << RipLevelMin + << RipLevelMax; + arc << DefThreshold; + if (SaveVersion >= 4549) + { + arc << SpriteAngle; + arc << SpriteRotation; + } + + if (SaveVersion >= 4550) + { + arc << alternative; + } + + { + FString tagstr; + if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; + arc << tagstr; + if (arc.IsLoading()) + { + if (tagstr.Len() == 0) Tag = NULL; + else Tag = mStringPropertyData.Alloc(tagstr); + } + } + + if (arc.IsLoading ()) + { + touching_sectorlist = NULL; + LinkToWorld(false, Sector); + + AddToHash (); + SetShade (fillcolor); + if (player) + { + if (playeringame[player - players] && + player->cls != NULL && + !(flags4 & MF4_NOSKIN) && + state->sprite == GetDefaultByType (player->cls)->SpawnState->sprite) + { // Give player back the skin + sprite = skins[player->userinfo.GetSkin()].sprite; + } + if (Speed == 0) + { + Speed = GetDefault()->Speed; + } + } + ClearInterpolation(); + UpdateWaterLevel(false); + } +} + +FArchive &operator<< (FArchive &arc, sector_t *&sec) +{ + return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); +} + +FArchive &operator<< (FArchive &arc, const sector_t *&sec) +{ + return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); +} + +FArchive &operator<< (FArchive &arc, line_t *&line) +{ + return arc.SerializePointer (lines, (BYTE **)&line, sizeof(*lines)); +} + +FArchive &operator<< (FArchive &arc, vertex_t *&vert) +{ + return arc.SerializePointer (vertexes, (BYTE **)&vert, sizeof(*vertexes)); +} + +FArchive &operator<< (FArchive &arc, side_t *&side) +{ + return arc.SerializePointer (sides, (BYTE **)&side, sizeof(*sides)); +} + +FArchive &operator<<(FArchive &arc, DAngle &ang) +{ + arc << ang.Degrees; + return arc; +} + +FArchive &operator<<(FArchive &arc, DVector3 &vec) +{ + arc << vec.X << vec.Y << vec.Z; + return arc; +} + +FArchive &operator<<(FArchive &arc, DVector2 &vec) +{ + arc << vec.X << vec.Y; + return arc; +} + +//========================================================================== +// +// +//========================================================================== + +FArchive &operator<< (FArchive &arc, FState *&state) +{ + PClassActor *info; + + if (arc.IsStoring ()) + { + if (state == NULL) + { + arc.UserWriteClass (RUNTIME_CLASS(AActor)); + arc.WriteCount (NULL_STATE_INDEX); + return arc; + } + + info = FState::StaticFindStateOwner (state); + + if (info != NULL) + { + arc.UserWriteClass (info); + arc.WriteCount ((DWORD)(state - info->OwnedStates)); + } + else + { + /* this was never working as intended. + I_Error ("Cannot find owner for state %p:\n" + "%s %c%c %3d [%p] -> %p", state, + sprites[state->sprite].name, + state->GetFrame() + 'A', + state->GetFullbright() ? '*' : ' ', + state->GetTics(), + state->GetAction(), + state->GetNextState()); + */ + } + } + else + { + PClassActor *info; + DWORD ofs; + + arc.UserReadClass(info); + ofs = arc.ReadCount (); + if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor)) + { + state = NULL; + } + else + { + state = info->OwnedStates + ofs; + } + } + return arc; +} + +void DObject::Serialize (FArchive &arc) +{ + ObjectFlags |= OF_SerialSuccess; +} From e754fae0a83be77d1baad3af4742ca47b2d8cec1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 10:41:21 +0200 Subject: [PATCH 06/59] - removed FS HUD pics. No mod in existence ever used them and a quickly thrown together test showed that the code did not even work. And since there's no reason to fix it they are gone now. --- src/CMakeLists.txt | 1 - src/fragglescript/t_fspic.cpp | 204 --------------------------------- src/fragglescript/t_func.cpp | 42 +------ src/serializer.h | 209 ++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 243 deletions(-) delete mode 100644 src/fragglescript/t_fspic.cpp create mode 100644 src/serializer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc8034a43..94f7c9ab3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1220,7 +1220,6 @@ set (PCH_SOURCES thingdef/thingdef_properties.cpp thingdef/thingdef_states.cpp xlat/parse_xlat.cpp - fragglescript/t_fspic.cpp fragglescript/t_func.cpp fragglescript/t_load.cpp fragglescript/t_oper.cpp diff --git a/src/fragglescript/t_fspic.cpp b/src/fragglescript/t_fspic.cpp deleted file mode 100644 index 0e7c80346..000000000 --- a/src/fragglescript/t_fspic.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* -** t_fspic.cpp -** Fragglescript HUD pics (incomplete and untested!) -** -**--------------------------------------------------------------------------- -** Copyright 2005 Christoph Oelckers -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#include "t_script.h" -#include "doomtype.h" -#include "p_local.h" -#include "farchive.h" -#include "sbar.h" -#include "v_video.h" - - - -struct FHudPic -{ - FTextureID texturenum; - int xpos; - int ypos; - bool draw; - - void Serialize(FArchive & arc) - { - arc << xpos << ypos << draw << texturenum; - } - -}; - -//====================================================================== -// -//====================================================================== -class DHUDPicManager : public DHUDMessage -{ - // This is no real hudmessage but this way I don't need any external code to handle this - // because the hudmessage and thinker code handles everything automatically - DECLARE_CLASS(DHUDPicManager, DHUDMessage) - float basetrans; - -public: - - TArray piclist; - - DHUDPicManager(); - ~DHUDPicManager() {} - void Serialize(FArchive & ar); - virtual void DoDraw (int linenum, int x, int y, int hudheight, float translucent); - void DoDraw (int, int, int, bool, int) { assert(false); } -} ; - -IMPLEMENT_CLASS(DHUDPicManager) - -//====================================================================== -// -//====================================================================== -DHUDPicManager::DHUDPicManager() -{ - HUDWidth=HUDHeight=0; - basetrans=0.8f; - //SetID(0xffffffff); - NumLines=1; - HoldTics=0; // stay forever! - //logtoconsole=false; -} - - -//====================================================================== -// -//====================================================================== -void DHUDPicManager::Serialize(FArchive & ar) -{ - Super::Serialize(ar); - - short count=piclist.Size(); - ar << count << basetrans; - if (ar.IsLoading()) piclist.Resize(count); - for(int i=0;iDrawTexture(tex, piclist[i].xpos, piclist[i].ypos, DTA_320x200, true, - DTA_AlphaF, translucent*basetrans, TAG_DONE); - } -} - - -//====================================================================== -// -//====================================================================== -static TArray & GetPicList() -{ - //TThinkerIterator it; - DHUDPicManager * pm=NULL;//it.Next(); - - if (!pm) pm=new DHUDPicManager; - return pm->piclist; -} - -//====================================================================== -// -// External interface -// -//====================================================================== - -//====================================================================== -// -//====================================================================== -int HU_GetFSPic(FTextureID texturenum, int xpos, int ypos) -{ - TArray &piclist=GetPicList(); - unsigned int i; - - for(i=0;i &piclist=GetPicList(); - - if(handle >= piclist.Size()) return -1; - piclist[handle].texturenum.SetInvalid(); - return 0; -} - - -//====================================================================== -// -//====================================================================== -int HU_ModifyFSPic(unsigned handle, FTextureID texturenum, int xpos, int ypos) -{ - TArray &piclist=GetPicList(); - - if(handle >= piclist.Size()) return -1; - if(!piclist[handle].texturenum.isValid()) return -1; - - piclist[handle].texturenum = texturenum; - piclist[handle].xpos = xpos; - piclist[handle].ypos = ypos; - return 0; -} - -//====================================================================== -// -//====================================================================== -int HU_FSDisplay(unsigned handle, bool newval) -{ - TArray &piclist=GetPicList(); - - if(handle >= piclist.Size()) return -1; - if(!piclist[handle].texturenum.isValid()) return -1; - - piclist[handle].draw = newval; - return 0; -} - diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index deb4907a3..6c7196f62 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -3647,58 +3647,24 @@ void FParser::SF_Pow() //========================================================================== -int HU_GetFSPic(FTextureID lumpnum, int xpos, int ypos); -int HU_DeleteFSPic(unsigned int handle); -int HU_ModifyFSPic(unsigned int handle, FTextureID lumpnum, int xpos, int ypos); -int HU_FSDisplay(unsigned int handle, bool newval); - void FParser::SF_NewHUPic() { - if (CheckArgs(3)) - { - t_return.type = svt_int; - t_return.value.i = HU_GetFSPic( - TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny), - intvalue(t_argv[1]), intvalue(t_argv[2])); - } + // disabled because it was never used and never tested } void FParser::SF_DeleteHUPic() { - if (CheckArgs(1)) - { - if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1) - script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0])); - } + // disabled because it was never used and never tested } void FParser::SF_ModifyHUPic() { - if (t_argc != 4) - { - script_error("modifyhupic: invalid number of arguments\n"); - return; - } - - if (HU_ModifyFSPic(intvalue(t_argv[0]), - TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny), - intvalue(t_argv[2]), intvalue(t_argv[3])) == -1) - { - script_error("modifyhypic: invalid sfpic handle %i\n", intvalue(t_argv[0])); - } - return; + // disabled because it was never used and never tested } void FParser::SF_SetHUPicDisplay() { - if (t_argc != 2) - { - script_error("sethupicdisplay: invalud number of arguments\n"); - return; - } - - if (HU_FSDisplay(intvalue(t_argv[0]), intvalue(t_argv[1]) > 0 ? 1 : 0) == -1) - script_error("sethupicdisplay: invalid pic handle %i\n", intvalue(t_argv[0])); + // disabled because it was never used and never tested } diff --git a/src/serializer.h b/src/serializer.h new file mode 100644 index 000000000..f56f9c87a --- /dev/null +++ b/src/serializer.h @@ -0,0 +1,209 @@ +#ifndef __SERIALIZER_H +#define __SERIALIZER_H + +#include +#include "tarray.h" +#include "r_defs.h" + +struct FWriter; +struct FReader; + +extern TArray loadsectors; +extern TArray loadlines; +extern TArray loadsides; +extern char nulspace[]; + + + +class FSerializer +{ + +public: + FWriter *w = nullptr; + FReader *r = nullptr; + + int ArraySize(); + +public: + + bool OpenWriter(); + bool OpenReader(const char *buffer, size_t length); + void Close(); + void WriteKey(const char *key); + bool BeginObject(const char *name); + void EndObject(); + bool BeginArray(const char *name); + void EndArray(); + void WriteObjects(); + const char *GetOutput(unsigned *len = nullptr); + 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, uint16_t &spritenum, uint16_t *def); + FSerializer &StringPtr(const char *key, const char *&charptr); // This only retrieves the address but creates no permanent copy of the string. + bool isReading() const + { + return r != nullptr; + } + + bool isWriting() const + { + return w != nullptr; + } + + bool canSkip() const; + + template + FSerializer &operator()(const char *key, T &obj) + { + return Serialize(*this, key, obj, (T*)nullptr); + } + + template + FSerializer &operator()(const char *key, T &obj, T &def) + { + return Serialize(*this, key, obj, &def); + } + + template + FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false) + { + if (fullcompare && isWriting() && !memcmp(obj, nulspace, count * sizeof(T))) + { + return *this; + } + + if (BeginArray(key)) + { + for (int i = 0; i < count; i++) + { + Serialize(*this, nullptr, obj[i], (T*)nullptr); + } + EndArray(); + } + return *this; + } + + template + FSerializer &Array(const char *key, T *obj, T *def, int count, bool fullcompare = false) + { + if (fullcompare && isWriting() && def != nullptr && !memcmp(obj, def, count * sizeof(T))) + { + return *this; + } + if (BeginArray(key)) + { + for (int i = 0; i < count; i++) + { + Serialize(*this, nullptr, obj[i], def ? &def[i] : nullptr); + } + EndArray(); + } + return *this; + } +}; + +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); +FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint32_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, int8_t &value, int8_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, uint8_t &value, uint8_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value, int16_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint16_t *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval); +FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); +FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval); +FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval); +FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/); +FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval); +FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def); +FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def); +FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); +FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def); +FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def); +FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def); + + +template +FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) +{ + DObject *v = static_cast(value); + Serialize(arc, key, v, nullptr); + value = static_cast(v); + return arc; +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value, TObjPtr *) +{ + Serialize(arc, key, value.o, nullptr); + return arc; +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value, TArray *) +{ + if (arc.isWriting()) + { + if (value.Size() == 0) return arc; // do not save empty arrays + } + bool res = arc.BeginArray(key); + if (arc.isReading()) + { + if (!res) + { + value.Clear(); + return arc; + } + value.Resize(arc.ArraySize()); + } + for (unsigned i = 0; i < value.Size(); i++) + { + Serialize(arc, nullptr, value[i], (T*)nullptr); + } + arc.EndArray(); + return arc; +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def) +{ + return arc.Array(key, &p[0], def? &(*def)[0] : nullptr, 3, true); +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, DRotator &p, DRotator *def) +{ + return arc.Array(key, &p[0], def? &(*def)[0] : nullptr, 3, true); +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector2 &p, DVector2 *def) +{ + return arc.Array(key, &p[0], def? &(*def)[0] : nullptr, 2, true); +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, DAngle &p, DAngle *def) +{ + return Serialize(arc, key, p.Degrees, def? &def->Degrees : nullptr); +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe, PalEntry *def) +{ + return Serialize(arc, key, pe.d, def? &def->d : nullptr); +} + +inline FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def) +{ + return Serialize(arc, key, style.AsDWORD, def ? &def->AsDWORD : nullptr); +} + +template +FSerializer &Serialize(FSerializer &arc, const char *key, TFlags &flags, TFlags *def) +{ + return Serialize(arc, key, flags.Value, def? &def->Value : nullptr); +} + + +#endif \ No newline at end of file From d24aa5dec99303560e9796f823534e0b00404601 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 10:47:59 +0200 Subject: [PATCH 07/59] - reformatting for easier search. --- src/actor.h | 2 +- src/b_bot.cpp | 2 +- src/b_bot.h | 2 +- src/c_dispatch.cpp | 4 ++-- src/d_player.h | 4 ++-- src/decallib.cpp | 20 ++++++++++---------- src/dobject.h | 2 +- src/dsectoreffect.cpp | 4 ++-- src/dsectoreffect.h | 4 ++-- src/farchive.cpp | 2 +- src/farchive.h | 2 +- src/fragglescript/t_func.cpp | 5 +++-- src/g_doom/a_doomglobal.h | 2 +- src/g_doom/a_scriptedmarine.cpp | 2 +- src/g_heretic/a_hereticweaps.cpp | 6 +++--- src/g_hexen/a_clericholy.cpp | 2 +- src/g_hexen/a_heresiarch.cpp | 6 +++--- src/g_hexen/a_magestaff.cpp | 2 +- src/g_hexen/a_spike.cpp | 4 ++-- src/g_raven/a_minotaur.cpp | 2 +- src/g_raven/ravenshared.h | 2 +- src/g_shared/a_action.cpp | 4 ++-- src/g_shared/a_armor.cpp | 8 ++++---- src/g_shared/a_artifacts.cpp | 10 +++++----- src/g_shared/a_artifacts.h | 10 +++++----- src/g_shared/a_camera.cpp | 8 ++++---- src/g_shared/a_decals.cpp | 4 ++-- src/g_shared/a_flashfader.cpp | 2 +- src/g_shared/a_lightning.cpp | 2 +- src/g_shared/a_lightning.h | 2 +- src/g_shared/a_morph.cpp | 4 ++-- src/g_shared/a_movingcamera.cpp | 12 ++++++------ src/g_shared/a_pickups.cpp | 8 ++++---- src/g_shared/a_pickups.h | 20 ++++++++++---------- src/g_shared/a_puzzleitems.cpp | 2 +- src/g_shared/a_quake.cpp | 2 +- src/g_shared/a_sharedglobal.h | 12 ++++++------ src/g_shared/a_soundsequence.cpp | 4 ++-- src/g_shared/a_weaponpiece.cpp | 4 ++-- src/g_shared/a_weaponpiece.h | 4 ++-- src/g_shared/a_weapons.cpp | 2 +- src/g_shared/hudmessages.cpp | 6 +++--- src/g_shared/sbar.h | 10 +++++----- src/g_shared/shared_sbar.cpp | 2 +- src/g_strife/a_strifeglobal.h | 2 +- src/g_strife/a_strifeweapons.cpp | 2 +- src/p_acs.cpp | 8 ++++---- src/p_acs.h | 4 ++-- src/p_ceiling.cpp | 2 +- src/p_doors.cpp | 4 ++-- src/p_floor.cpp | 6 +++--- src/p_lights.cpp | 14 +++++++------- src/p_pillar.cpp | 2 +- src/p_plats.cpp | 2 +- src/p_pusher.cpp | 4 ++-- src/p_saveg.cpp | 1 + src/p_scroll.cpp | 4 ++-- src/p_spec.cpp | 8 ++++---- src/p_spec.h | 16 ++++++++-------- src/p_switch.cpp | 4 ++-- src/p_user.cpp | 4 ++-- src/po_man.cpp | 10 +++++----- src/r_utility.cpp | 2 +- src/r_utility.h | 2 +- src/s_advsound.cpp | 4 ++-- src/s_sndseq.cpp | 8 ++++---- src/s_sndseq.h | 2 +- src/tflags.h | 2 +- src/zzz_old.cpp | 32 +++++++++++++++++++++++++++++++- 69 files changed, 202 insertions(+), 170 deletions(-) diff --git a/src/actor.h b/src/actor.h index fcfeb4fce..50cd2704c 100644 --- a/src/actor.h +++ b/src/actor.h @@ -583,7 +583,7 @@ public: void Destroy (); ~AActor (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Serialize(FSerializer &arc); void PostSerialize(); diff --git a/src/b_bot.cpp b/src/b_bot.cpp index a4160b92c..a18aa14af 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -55,7 +55,7 @@ void DBot::Clear () old = { 0, 0 }; } -void DBot::Serialize (FArchive &arc) +void DBot::Serialize(FArchive &arc) { Super::Serialize (arc); diff --git a/src/b_bot.h b/src/b_bot.h index f9085d6fb..e7a6c730e 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -142,7 +142,7 @@ public: DBot (); void Clear (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); //(b_think.cpp) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 811052e1c..a751b686f 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -65,7 +65,7 @@ class DWaitingCommand : public DThinker public: DWaitingCommand (const char *cmd, int tics); ~DWaitingCommand (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); private: @@ -189,7 +189,7 @@ static const char *KeyConfCommands[] = IMPLEMENT_CLASS (DWaitingCommand) -void DWaitingCommand::Serialize (FArchive &arc) +void DWaitingCommand::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Command << TicsLeft; diff --git a/src/d_player.h b/src/d_player.h index e932223aa..deec3f61d 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -102,7 +102,7 @@ class APlayerPawn : public AActor DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn) HAS_OBJECT_POINTERS public: - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void PostBeginPlay(); virtual void Tick(); @@ -383,7 +383,7 @@ public: ~player_t(); player_t &operator= (const player_t &p); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); size_t FixPointers (const DObject *obj, DObject *replacement); size_t PropagateMark(); diff --git a/src/decallib.cpp b/src/decallib.cpp index fe8de4d0a..c2bd0f4f3 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -113,7 +113,7 @@ struct DDecalThinker : public DThinker HAS_OBJECT_POINTERS public: DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {} - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); TObjPtr TheDecal; protected: DDecalThinker () : DThinker (STAT_DECALTHINKER) {} @@ -123,7 +123,7 @@ IMPLEMENT_POINTY_CLASS (DDecalThinker) DECLARE_POINTER (TheDecal) END_POINTERS -void DDecalThinker::Serialize (FArchive &arc) +void DDecalThinker::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TheDecal; @@ -143,7 +143,7 @@ class DDecalFader : public DDecalThinker DECLARE_CLASS (DDecalFader, DDecalThinker) public: DDecalFader (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); int TimeToStartDecay; @@ -168,7 +168,7 @@ class DDecalColorer : public DDecalThinker DECLARE_CLASS (DDecalColorer, DDecalThinker) public: DDecalColorer (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); int TimeToStartDecay; @@ -194,7 +194,7 @@ class DDecalStretcher : public DDecalThinker DECLARE_CLASS (DDecalStretcher, DDecalThinker) public: DDecalStretcher (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); int TimeToStart; @@ -225,7 +225,7 @@ class DDecalSlider : public DDecalThinker DECLARE_CLASS (DDecalSlider, DDecalThinker) public: DDecalSlider (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); int TimeToStart; @@ -1153,7 +1153,7 @@ FDecalAnimator::~FDecalAnimator () IMPLEMENT_CLASS (DDecalFader) -void DDecalFader::Serialize (FArchive &arc) +void DDecalFader::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TimeToStartDecay << TimeToEndDecay << StartTrans; @@ -1200,7 +1200,7 @@ DThinker *FDecalFaderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const IMPLEMENT_CLASS (DDecalStretcher) -void DDecalStretcher::Serialize (FArchive &arc) +void DDecalStretcher::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TimeToStart @@ -1288,7 +1288,7 @@ void DDecalStretcher::Tick () IMPLEMENT_CLASS (DDecalSlider) -void DDecalSlider::Serialize (FArchive &arc) +void DDecalSlider::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TimeToStart @@ -1371,7 +1371,7 @@ FDecalAnimator *FDecalLib::FindAnimator (const char *name) IMPLEMENT_CLASS (DDecalColorer) -void DDecalColorer::Serialize (FArchive &arc) +void DDecalColorer::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TimeToStartDecay << TimeToEndDecay << StartColor << GoalColor; diff --git a/src/dobject.h b/src/dobject.h index 7f177c8d4..d42228172 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -467,7 +467,7 @@ public: inline bool IsA (const PClass *type) const; void SerializeUserVars(FArchive &arc); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); void SerializeUserVars(FSerializer &arc); virtual void Serialize(FSerializer &arc); diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index d33eeb061..ed8eb3327 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -64,7 +64,7 @@ DSectorEffect::DSectorEffect (sector_t *sector) m_Sector = sector; } -void DSectorEffect::Serialize (FArchive &arc) +void DSectorEffect::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Sector; @@ -90,7 +90,7 @@ void DMover::Destroy() Super::Destroy(); } -void DMover::Serialize (FArchive &arc) +void DMover::Serialize(FArchive &arc) { Super::Serialize (arc); arc << interpolation; diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index 792771fb6..775bca9ac 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -10,7 +10,7 @@ class DSectorEffect : public DThinker public: DSectorEffect (sector_t *sector); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Destroy(); sector_t *GetSector() const { return m_Sector; } @@ -32,7 +32,7 @@ protected: private: protected: DMover (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Destroy(); }; diff --git a/src/farchive.cpp b/src/farchive.cpp index 8bb39b35f..f454d3b7b 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -526,7 +526,7 @@ void FCompressedMemFile::Close () } } -void FCompressedMemFile::Serialize (FArchive &arc) +void FCompressedMemFile::Serialize(FArchive &arc) { if (arc.IsStoring ()) { diff --git a/src/farchive.h b/src/farchive.h index 44ae8c001..e874ae3dc 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -127,7 +127,7 @@ public: bool IsOpen () const; void GetSizes(unsigned int &one, unsigned int &two) const; - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: bool FreeOnExplode () { return !m_SourceFromMem; } diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 6c7196f62..8e2c31f2d 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1780,7 +1780,8 @@ class DLightLevel : public DLighting public: DLightLevel(sector_t * s,int destlevel,int speed); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); + void Serialize(FArchive &arc); void Tick (); void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; } }; @@ -1789,7 +1790,7 @@ public: IMPLEMENT_CLASS (DLightLevel) -void DLightLevel::Serialize (FArchive &arc) +void DLightLevel::Serialize(FArchive &arc) { Super::Serialize (arc); arc << destlevel << speed; diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h index 23e181cc8..bcaaff887 100644 --- a/src/g_doom/a_doomglobal.h +++ b/src/g_doom/a_doomglobal.h @@ -29,7 +29,7 @@ public: void Tick (); void SetWeapon (EMarineWeapon); void SetSprite (PClassActor *source); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); int CurrentWeapon; diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp index a3d33f555..a014891ea 100644 --- a/src/g_doom/a_scriptedmarine.cpp +++ b/src/g_doom/a_scriptedmarine.cpp @@ -21,7 +21,7 @@ static FRandom pr_m_fireshotgun2 ("SMarineFireSSG"); IMPLEMENT_CLASS (AScriptedMarine) -void AScriptedMarine::Serialize (FArchive &arc) +void AScriptedMarine::Serialize(FArchive &arc) { Super::Serialize (arc); diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index d20146339..fa35bf1f9 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -866,13 +866,13 @@ class ARainTracker : public AInventory { DECLARE_CLASS (ARainTracker, AInventory) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); AActor *Rain1, *Rain2; }; IMPLEMENT_CLASS (ARainTracker) -void ARainTracker::Serialize (FArchive &arc) +void ARainTracker::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Rain1 << Rain2; @@ -1140,7 +1140,7 @@ class APhoenixRod : public AWeapon { DECLARE_CLASS (APhoenixRod, AWeapon) public: - void Serialize (FArchive &arc) + void Serialize(FArchive &arc) { Super::Serialize (arc); arc << FlameCount; diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index 749ad3dad..7c8e8f8e7 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -31,7 +31,7 @@ class ACWeapWraithverge : public AClericWeapon { DECLARE_CLASS (ACWeapWraithverge, AClericWeapon) public: - void Serialize (FArchive &arc) + void Serialize(FArchive &arc) { Super::Serialize (arc); arc << CHolyCount; diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index 7784f0833..acddc588b 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -67,13 +67,13 @@ public: const PClass *StopBall; DAngle BallAngle; - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); }; IMPLEMENT_CLASS (AHeresiarch) -void AHeresiarch::Serialize (FArchive &arc) +void AHeresiarch::Serialize(FArchive &arc) { Super::Serialize (arc); arc << StopBall << BallAngle; @@ -105,7 +105,7 @@ public: DAngle AngleOffset; DAngle OldAngle; - void Serialize (FArchive &arc) + void Serialize(FArchive &arc) { Super::Serialize (arc); arc << AngleOffset << OldAngle; diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp index 1bd20715d..9ad303179 100644 --- a/src/g_hexen/a_magestaff.cpp +++ b/src/g_hexen/a_magestaff.cpp @@ -29,7 +29,7 @@ class AMWeapBloodscourge : public AMageWeapon { DECLARE_CLASS (AMWeapBloodscourge, AMageWeapon) public: - void Serialize (FArchive &arc) + void Serialize(FArchive &arc) { Super::Serialize (arc); arc << MStaffCount; diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index 4770f5d3a..ced6c43a6 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -22,7 +22,7 @@ class AThrustFloor : public AActor DECLARE_CLASS (AThrustFloor, AActor) HAS_OBJECT_POINTERS public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Activate (AActor *activator); void Deactivate (AActor *activator); @@ -34,7 +34,7 @@ IMPLEMENT_POINTY_CLASS (AThrustFloor) DECLARE_POINTER (DirtClump) END_POINTERS -void AThrustFloor::Serialize (FArchive &arc) +void AThrustFloor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << DirtClump; diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp index 34ecfd161..281e1db49 100644 --- a/src/g_raven/a_minotaur.cpp +++ b/src/g_raven/a_minotaur.cpp @@ -79,7 +79,7 @@ void AMinotaurFriend::BeginPlay () StartTime = -1; } -void AMinotaurFriend::Serialize (FArchive &arc) +void AMinotaurFriend::Serialize(FArchive &arc) { Super::Serialize (arc); arc << StartTime; diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h index 0010905cc..824bc7c86 100644 --- a/src/g_raven/ravenshared.h +++ b/src/g_raven/ravenshared.h @@ -23,7 +23,7 @@ public: void Die (AActor *source, AActor *inflictor, int dmgflags); bool OkayToSwitchTarget (AActor *other); void BeginPlay (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); }; #endif //__RAVENSHARED_H__ diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 0e333a46e..3e3231c15 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -361,7 +361,7 @@ class DCorpsePointer : public DThinker public: DCorpsePointer (AActor *ptr); void Destroy (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); TObjPtr Corpse; DWORD Count; // Only the first corpse pointer's count is valid. private: @@ -435,7 +435,7 @@ void DCorpsePointer::Destroy () Super::Destroy (); } -void DCorpsePointer::Serialize (FArchive &arc) +void DCorpsePointer::Serialize(FArchive &arc) { Super::Serialize(arc); arc << Corpse << Count; diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index 2b783e380..b221c07b9 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -21,7 +21,7 @@ IMPLEMENT_CLASS (AHexenArmor) // //=========================================================================== -void ABasicArmor::Serialize (FArchive &arc) +void ABasicArmor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType << ActualSaveAmount; @@ -192,7 +192,7 @@ void ABasicArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) // //=========================================================================== -void ABasicArmorPickup::Serialize (FArchive &arc) +void ABasicArmorPickup::Serialize(FArchive &arc) { Super::Serialize (arc); arc << SavePercent << SaveAmount << MaxAbsorb << MaxFullAbsorb; @@ -274,7 +274,7 @@ bool ABasicArmorPickup::Use (bool pickup) // //=========================================================================== -void ABasicArmorBonus::Serialize (FArchive &arc) +void ABasicArmorBonus::Serialize(FArchive &arc) { Super::Serialize (arc); arc << SavePercent << SaveAmount << MaxSaveAmount << BonusCount << BonusMax @@ -371,7 +371,7 @@ bool ABasicArmorBonus::Use (bool pickup) // //=========================================================================== -void AHexenArmor::Serialize (FArchive &arc) +void AHexenArmor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Slots[0] << Slots[1] << Slots[2] << Slots[3] diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index b5ac970ca..76426f554 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -101,7 +101,7 @@ bool APowerupGiver::Use (bool pickup) // //=========================================================================== -void APowerupGiver::Serialize (FArchive &arc) +void APowerupGiver::Serialize(FArchive &arc) { Super::Serialize (arc); arc << PowerupType; @@ -136,7 +136,7 @@ void APowerup::Tick () // //=========================================================================== -void APowerup::Serialize (FArchive &arc) +void APowerup::Serialize(FArchive &arc) { Super::Serialize (arc); arc << EffectTics << BlendColor << Mode; @@ -901,7 +901,7 @@ IMPLEMENT_CLASS (APowerTorch) // //=========================================================================== -void APowerTorch::Serialize (FArchive &arc) +void APowerTorch::Serialize(FArchive &arc) { Super::Serialize (arc); arc << NewTorch << NewTorchDelta; @@ -963,7 +963,7 @@ IMPLEMENT_CLASS (APowerFlight) // //=========================================================================== -void APowerFlight::Serialize (FArchive &arc) +void APowerFlight::Serialize(FArchive &arc) { Super::Serialize (arc); arc << HitCenterFrame; @@ -1899,7 +1899,7 @@ IMPLEMENT_CLASS(APowerMorph) // //=========================================================================== -void APowerMorph::Serialize (FArchive &arc) +void APowerMorph::Serialize(FArchive &arc) { Super::Serialize (arc); arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash; diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 2a52756ab..d742c2df4 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -16,7 +16,7 @@ public: virtual bool HandlePickup (AInventory *item); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void OwnerDied (); virtual bool GetNoTeleportFreeze(); virtual PalEntry GetBlend (); @@ -51,7 +51,7 @@ class APowerupGiver : public AInventory DECLARE_CLASS_WITH_META (APowerupGiver, AInventory, PClassPowerupGiver) public: virtual bool Use (bool pickup); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); PClassActor *PowerupType; @@ -121,7 +121,7 @@ class APowerTorch : public APowerLightAmp { DECLARE_CLASS (APowerTorch, APowerLightAmp) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: void DoEffect (); int NewTorch, NewTorchDelta; @@ -132,7 +132,7 @@ class APowerFlight : public APowerup DECLARE_CLASS (APowerFlight, APowerup) public: bool DrawPowerup (int x, int y); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: void InitEffect (); @@ -272,7 +272,7 @@ class APowerMorph : public APowerup { DECLARE_CLASS( APowerMorph, APowerup ) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void SetNoCallUndoMorph() { bNoCallUndoMorph = true; } FNameNoInit PlayerClass, MorphFlash, UnMorphFlash; diff --git a/src/g_shared/a_camera.cpp b/src/g_shared/a_camera.cpp index 64868231b..58eb3e114 100644 --- a/src/g_shared/a_camera.cpp +++ b/src/g_shared/a_camera.cpp @@ -55,7 +55,7 @@ public: void PostBeginPlay (); void Tick (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: DAngle Center; DAngle Acc; @@ -65,7 +65,7 @@ protected: IMPLEMENT_CLASS (ASecurityCamera) -void ASecurityCamera::Serialize (FArchive &arc) +void ASecurityCamera::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Center << Acc << Delta << Range; @@ -114,14 +114,14 @@ public: void PostBeginPlay (); void Tick (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: DAngle MaxPitchChange; }; IMPLEMENT_CLASS (AAimingCamera) -void AAimingCamera::Serialize (FArchive &arc) +void AAimingCamera::Serialize(FArchive &arc) { Super::Serialize (arc); arc << MaxPitchChange; diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 94e92e725..91ac40096 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -122,7 +122,7 @@ void DBaseDecal::Remove () WallNext = NULL; } -void DBaseDecal::Serialize (FArchive &arc) +void DBaseDecal::Serialize(FArchive &arc) { Super::Serialize (arc); arc << LeftDistance @@ -609,7 +609,7 @@ void DImpactDecal::SerializeTime (FArchive &arc) } } -void DImpactDecal::Serialize (FArchive &arc) +void DImpactDecal::Serialize(FArchive &arc) { Super::Serialize (arc); } diff --git a/src/g_shared/a_flashfader.cpp b/src/g_shared/a_flashfader.cpp index b5b4a0549..736f9e0da 100644 --- a/src/g_shared/a_flashfader.cpp +++ b/src/g_shared/a_flashfader.cpp @@ -26,7 +26,7 @@ void DFlashFader::Destroy () Super::Destroy(); } -void DFlashFader::Serialize (FArchive &arc) +void DFlashFader::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TotalTics << StartTic << ForWho; diff --git a/src/g_shared/a_lightning.cpp b/src/g_shared/a_lightning.cpp index 2f2c4e235..72bde0078 100644 --- a/src/g_shared/a_lightning.cpp +++ b/src/g_shared/a_lightning.cpp @@ -35,7 +35,7 @@ DLightningThinker::~DLightningThinker () } } -void DLightningThinker::Serialize (FArchive &arc) +void DLightningThinker::Serialize(FArchive &arc) { int i; short *lights; diff --git a/src/g_shared/a_lightning.h b/src/g_shared/a_lightning.h index 1c4ca2a65..cca8d1fcc 100644 --- a/src/g_shared/a_lightning.h +++ b/src/g_shared/a_lightning.h @@ -13,7 +13,7 @@ class DLightningThinker : public DThinker public: DLightningThinker (); ~DLightningThinker (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); void ForceLightning (int mode); void TerminateLightning(); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 57271f768..b8c5096c0 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -634,7 +634,7 @@ int AMorphProjectile::DoSpecialDamage (AActor *target, int damage, FName damaget return -1; } -void AMorphProjectile::Serialize (FArchive &arc) +void AMorphProjectile::Serialize(FArchive &arc) { Super::Serialize (arc); arc << PlayerClass << MonsterClass << Duration << MorphStyle << MorphFlash << UnMorphFlash; @@ -647,7 +647,7 @@ IMPLEMENT_POINTY_CLASS (AMorphedMonster) DECLARE_POINTER (UnmorphedMe) END_POINTERS -void AMorphedMonster::Serialize (FArchive &arc) +void AMorphedMonster::Serialize(FArchive &arc) { Super::Serialize (arc); arc << UnmorphedMe << UnmorphTime << MorphStyle << MorphExitFlash << FlagsSave; diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index 202e1823f..c18e6c483 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -60,7 +60,7 @@ public: AInterpolationPoint *ScanForLoop (); void FormChain (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); TObjPtr Next; }; @@ -69,7 +69,7 @@ IMPLEMENT_POINTY_CLASS (AInterpolationPoint) DECLARE_POINTER (Next) END_POINTERS -void AInterpolationPoint::Serialize (FArchive &arc) +void AInterpolationPoint::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Next; @@ -166,7 +166,7 @@ protected: virtual bool Interpolate (); virtual void NewNode (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); bool bActive, bJustStepped; TObjPtr PrevNode, CurrNode; @@ -179,7 +179,7 @@ IMPLEMENT_POINTY_CLASS (APathFollower) DECLARE_POINTER (CurrNode) END_POINTERS -void APathFollower::Serialize (FArchive &arc) +void APathFollower::Serialize(FArchive &arc) { Super::Serialize (arc); arc << bActive << bJustStepped << PrevNode << CurrNode << Time << HoldTime; @@ -577,7 +577,7 @@ class AMovingCamera : public APathFollower public: void PostBeginPlay (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: bool Interpolate (); @@ -588,7 +588,7 @@ IMPLEMENT_POINTY_CLASS (AMovingCamera) DECLARE_POINTER (Activator) END_POINTERS -void AMovingCamera::Serialize (FArchive &arc) +void AMovingCamera::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Activator; diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index c72366201..1a8202916 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -89,7 +89,7 @@ IMPLEMENT_CLASS (AAmmo) // //=========================================================================== -void AAmmo::Serialize (FArchive &arc) +void AAmmo::Serialize(FArchive &arc) { Super::Serialize (arc); arc << BackpackAmount << BackpackMaxAmount; @@ -514,7 +514,7 @@ void AInventory::Tick () // //=========================================================================== -void AInventory::Serialize (FArchive &arc) +void AInventory::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Owner << Amount << MaxAmount << RespawnTics << ItemFlags << Icon << PickupSound << SpawnPointClass; @@ -1809,7 +1809,7 @@ bool AHealthPickup::Use (bool pickup) // //=========================================================================== -void AHealthPickup::Serialize (FArchive &arc) +void AHealthPickup::Serialize(FArchive &arc) { Super::Serialize(arc); arc << autousemode; @@ -1823,7 +1823,7 @@ void AHealthPickup::Serialize (FArchive &arc) // //=========================================================================== -void ABackpackItem::Serialize (FArchive &arc) +void ABackpackItem::Serialize(FArchive &arc) { Super::Serialize (arc); arc << bDepleted; diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 0023cd271..9122ffd12 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -153,7 +153,7 @@ class AInventory : public AActor HAS_OBJECT_POINTERS public: virtual void Touch (AActor *toucher); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void MarkPrecacheSounds() const; virtual void BeginPlay (); @@ -254,7 +254,7 @@ class AAmmo : public AInventory { DECLARE_CLASS_WITH_META(AAmmo, AInventory, PClassAmmo) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); AInventory *CreateCopy (AActor *other); bool HandlePickup (AInventory *item); PClassActor *GetParentAmmo () const; @@ -311,7 +311,7 @@ public: bool bAltFire; // Set when this weapon's alternate fire is used. virtual void MarkPrecacheSounds() const; - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual bool ShouldStay (); virtual void AttachToOwner (AActor *other); virtual bool HandlePickup (AInventory *item); @@ -431,7 +431,7 @@ class AHealthPickup : public AInventory public: int autousemode; - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); virtual bool HandlePickup (AInventory *item); @@ -451,7 +451,7 @@ class ABasicArmor : public AArmor { DECLARE_CLASS (ABasicArmor, AArmor) public: - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void Tick (); virtual AInventory *CreateCopy (AActor *other); virtual bool HandlePickup (AInventory *item); @@ -471,7 +471,7 @@ class ABasicArmorPickup : public AArmor { DECLARE_CLASS (ABasicArmorPickup, AArmor) public: - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -486,7 +486,7 @@ class ABasicArmorBonus : public AArmor { DECLARE_CLASS (ABasicArmorBonus, AArmor) public: - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -505,7 +505,7 @@ class AHexenArmor : public AArmor { DECLARE_CLASS (AHexenArmor, AArmor) public: - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); virtual bool HandlePickup (AInventory *item); @@ -533,7 +533,7 @@ class APuzzleItem : public AInventory { DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); bool ShouldStay (); bool Use (bool pickup); bool HandlePickup (AInventory *item); @@ -555,7 +555,7 @@ class ABackpackItem : public AInventory { DECLARE_CLASS (ABackpackItem, AInventory) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); AInventory *CreateTossable (); diff --git a/src/g_shared/a_puzzleitems.cpp b/src/g_shared/a_puzzleitems.cpp index dff532f17..774585ae9 100644 --- a/src/g_shared/a_puzzleitems.cpp +++ b/src/g_shared/a_puzzleitems.cpp @@ -20,7 +20,7 @@ void PClassPuzzleItem::DeriveData(PClass *newclass) IMPLEMENT_CLASS(APuzzleItem) -void APuzzleItem::Serialize (FArchive &arc) +void APuzzleItem::Serialize(FArchive &arc) { Super::Serialize (arc); arc << PuzzleItemNumber; diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 63394e02a..13194a06e 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -64,7 +64,7 @@ DEarthquake::DEarthquake(AActor *center, int intensityX, int intensityY, int int // //========================================================================== -void DEarthquake::Serialize (FArchive &arc) +void DEarthquake::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Spot << m_Intensity << m_Countdown diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 029653bb1..b4a5fbbf7 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -24,7 +24,7 @@ public: DBaseDecal (const AActor *actor); DBaseDecal (const DBaseDecal *basis); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Destroy (); FTextureID StickToWall(side_t *wall, double x, double y, F3DFloor * ffloor); double GetRealZ (const side_t *wall) const; @@ -70,7 +70,7 @@ public: void BeginPlay (); void Destroy (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); static void SerializeTime (FArchive &arc); protected: @@ -122,7 +122,7 @@ public: float r2, float g2, float b2, float a2, float time, AActor *who); void Destroy (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); AActor *WhoFor() { return ForWho; } void Cancel (); @@ -165,7 +165,7 @@ public: int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, double rollIntensity, double rollWave); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); TObjPtr m_Spot; double m_TremorRadius, m_DamageRadius; @@ -194,7 +194,7 @@ class AMorphProjectile : public AActor DECLARE_CLASS (AMorphProjectile, AActor) public: int DoSpecialDamage (AActor *target, int damage, FName damagetype); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash; int Duration, MorphStyle; @@ -206,7 +206,7 @@ class AMorphedMonster : public AActor HAS_OBJECT_POINTERS public: void Tick (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); void Destroy (); diff --git a/src/g_shared/a_soundsequence.cpp b/src/g_shared/a_soundsequence.cpp index b7ef2d25b..c1b9ced78 100644 --- a/src/g_shared/a_soundsequence.cpp +++ b/src/g_shared/a_soundsequence.cpp @@ -74,7 +74,7 @@ class ASoundSequenceSlot : public AActor DECLARE_CLASS (ASoundSequenceSlot, AActor) HAS_OBJECT_POINTERS public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); TObjPtr Sequence; }; @@ -89,7 +89,7 @@ END_POINTERS // //========================================================================== -void ASoundSequenceSlot::Serialize (FArchive &arc) +void ASoundSequenceSlot::Serialize(FArchive &arc) { Super::Serialize (arc); arc << Sequence; diff --git a/src/g_shared/a_weaponpiece.cpp b/src/g_shared/a_weaponpiece.cpp index 2d7549db7..7068fe275 100644 --- a/src/g_shared/a_weaponpiece.cpp +++ b/src/g_shared/a_weaponpiece.cpp @@ -17,7 +17,7 @@ void PClassWeaponPiece::ReplaceClassRef(PClass *oldclass, PClass *newclass) } -void AWeaponHolder::Serialize (FArchive &arc) +void AWeaponHolder::Serialize(FArchive &arc) { Super::Serialize(arc); arc << PieceMask << PieceWeapon; @@ -29,7 +29,7 @@ IMPLEMENT_POINTY_CLASS (AWeaponPiece) END_POINTERS -void AWeaponPiece::Serialize (FArchive &arc) +void AWeaponPiece::Serialize(FArchive &arc) { Super::Serialize (arc); arc << WeaponClass << FullWeapon << PieceValue; diff --git a/src/g_shared/a_weaponpiece.h b/src/g_shared/a_weaponpiece.h index 071ea6891..4f201fe19 100644 --- a/src/g_shared/a_weaponpiece.h +++ b/src/g_shared/a_weaponpiece.h @@ -15,7 +15,7 @@ class AWeaponPiece : public AInventory protected: bool PrivateShouldStay (); public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); bool TryPickup (AActor *&toucher); bool TryPickupRestricted (AActor *&toucher); bool ShouldStay (); @@ -37,5 +37,5 @@ public: int PieceMask; const PClass * PieceWeapon; - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); }; diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index 575914fe8..ed7c02531 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -73,7 +73,7 @@ void PClassWeapon::ReplaceClassRef(PClass *oldclass, PClass *newclass) // //=========================================================================== -void AWeapon::Serialize (FArchive &arc) +void AWeapon::Serialize(FArchive &arc) { Super::Serialize (arc); arc << WeaponFlags diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 32a8d2de5..7c86c98ac 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -504,7 +504,7 @@ DHUDMessageFadeOut::DHUDMessageFadeOut (FFont *font, const char *text, float x, // //============================================================================ -void DHUDMessageFadeOut::Serialize (FArchive &arc) +void DHUDMessageFadeOut::Serialize(FArchive &arc) { Super::Serialize (arc); arc << FadeOutTics; @@ -609,7 +609,7 @@ DHUDMessageFadeInOut::DHUDMessageFadeInOut (FFont *font, const char *text, float // //============================================================================ -void DHUDMessageFadeInOut::Serialize (FArchive &arc) +void DHUDMessageFadeInOut::Serialize(FArchive &arc) { Super::Serialize (arc); arc << FadeInTics; @@ -718,7 +718,7 @@ DHUDMessageTypeOnFadeOut::DHUDMessageTypeOnFadeOut (FFont *font, const char *tex // //============================================================================ -void DHUDMessageTypeOnFadeOut::Serialize (FArchive &arc) +void DHUDMessageTypeOnFadeOut::Serialize(FArchive &arc) { Super::Serialize (arc); arc << TypeOnTime << CurrLine << LineVisible << LineLen; diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 03901f66f..87242988e 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -71,7 +71,7 @@ public: EColorRange textColor, float holdTime); virtual ~DHUDMessage (); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); void Draw (int bottom, int visibility); virtual void ResetText (const char *text); @@ -157,7 +157,7 @@ public: DHUDMessageFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeOutTime); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); @@ -176,7 +176,7 @@ public: DHUDMessageFadeInOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeInTime, float fadeOutTime); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); @@ -195,7 +195,7 @@ public: DHUDMessageTypeOnFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float typeTime, float holdTime, float fadeOutTime); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); virtual void ScreenSizeChanged (); @@ -355,7 +355,7 @@ public: static void AddBlend (float r, float g, float b, float a, float v_blend[4]); - virtual void Serialize (FArchive &arc); + virtual void Serialize(FArchive &arc); virtual void Tick (); virtual void Draw (EHudState state); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index f66ec187d..b0a1f6a5d 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1654,7 +1654,7 @@ void DBaseStatusBar::ReceivedWeapon (AWeapon *weapon) { } -void DBaseStatusBar::Serialize (FArchive &arc) +void DBaseStatusBar::Serialize(FArchive &arc) { for (size_t i = 0; i < countof(Messages); ++i) { diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h index a91052b52..58974e41e 100644 --- a/src/g_strife/a_strifeglobal.h +++ b/src/g_strife/a_strifeglobal.h @@ -55,7 +55,7 @@ class ASigil : public AWeapon public: bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); bool SpecialDropAction (AActor *dropper); static int GiveSigilPiece (AActor *daPlayer); void BeginPlay(); diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index 24e7fb5c7..de03e6049 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -753,7 +753,7 @@ void ASigil::BeginPlay() // //============================================================================ -void ASigil::Serialize (FArchive &arc) +void ASigil::Serialize(FArchive &arc) { Super::Serialize (arc); arc << NumPieces << DownPieces; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index ffac7a78f..9a7137eb2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1367,7 +1367,7 @@ public: int tag, int height, int special, int arg0, int arg1, int arg2, int arg3, int arg4); void Tick (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); private: sector_t *Sector; double WatchD, LastD; @@ -1417,7 +1417,7 @@ DPlaneWatcher::DPlaneWatcher (AActor *it, line_t *line, int lineSide, bool ceili } } -void DPlaneWatcher::Serialize (FArchive &arc) +void DPlaneWatcher::Serialize(FArchive &arc) { Super::Serialize (arc); @@ -2927,7 +2927,7 @@ DACSThinker::~DACSThinker () ActiveThinker = NULL; } -void DACSThinker::Serialize (FArchive &arc) +void DACSThinker::Serialize(FArchive &arc) { int scriptnum; int scriptcount = 0; @@ -3056,7 +3056,7 @@ inline FArchive &operator<< (FArchive &arc, DLevelScript::EScriptState &state) return arc; } -void DLevelScript::Serialize (FArchive &arc) +void DLevelScript::Serialize(FArchive &arc) { DWORD i; diff --git a/src/p_acs.h b/src/p_acs.h index 243e2ccf3..9e2428cbf 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -859,7 +859,7 @@ public: const int *args, int argcount, int flags); ~DLevelScript (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); int RunScript (); inline void SetState (EScriptState newstate) { state = newstate; } @@ -944,7 +944,7 @@ public: DACSThinker (); ~DACSThinker (); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); typedef TMap ScriptMap; diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 888b67b6c..7590234fc 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -72,7 +72,7 @@ DCeiling::DCeiling () // //============================================================================ -void DCeiling::Serialize (FArchive &arc) +void DCeiling::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type diff --git a/src/p_doors.cpp b/src/p_doors.cpp index ab551de1b..a614db6a5 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -59,7 +59,7 @@ DDoor::DDoor () { } -void DDoor::Serialize (FArchive &arc) +void DDoor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type @@ -530,7 +530,7 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec) { } -void DAnimatedDoor::Serialize (FArchive &arc) +void DAnimatedDoor::Serialize(FArchive &arc) { Super::Serialize (arc); diff --git a/src/p_floor.cpp b/src/p_floor.cpp index ccb1c82e9..67cc6efa7 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -84,7 +84,7 @@ DFloor::DFloor () { } -void DFloor::Serialize (FArchive &arc) +void DFloor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type @@ -855,7 +855,7 @@ DElevator::DElevator (sector_t *sec) m_Interp_Ceiling = sec->SetInterpolation(sector_t::CeilingMove, true); } -void DElevator::Serialize (FArchive &arc) +void DElevator::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type @@ -1136,7 +1136,7 @@ DWaggleBase::DWaggleBase () { } -void DWaggleBase::Serialize (FArchive &arc) +void DWaggleBase::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_OriginalDist diff --git a/src/p_lights.cpp b/src/p_lights.cpp index 90d1da869..59e21fce6 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -197,7 +197,7 @@ DFireFlicker::DFireFlicker () { } -void DFireFlicker::Serialize (FArchive &arc) +void DFireFlicker::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Count << m_MaxLight << m_MinLight; @@ -262,7 +262,7 @@ DFlicker::DFlicker () { } -void DFlicker::Serialize (FArchive &arc) +void DFlicker::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Count << m_MaxLight << m_MinLight; @@ -336,7 +336,7 @@ DLightFlash::DLightFlash () { } -void DLightFlash::Serialize (FArchive &arc) +void DLightFlash::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Count << m_MaxLight << m_MaxTime << m_MinLight << m_MinTime; @@ -407,7 +407,7 @@ DStrobe::DStrobe () { } -void DStrobe::Serialize (FArchive &arc) +void DStrobe::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Count << m_MaxLight << m_MinLight << m_DarkTime << m_BrightTime; @@ -661,7 +661,7 @@ DGlow::DGlow () { } -void DGlow::Serialize (FArchive &arc) +void DGlow::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Direction << m_MaxLight << m_MinLight; @@ -728,7 +728,7 @@ DGlow2::DGlow2 () { } -void DGlow2::Serialize (FArchive &arc) +void DGlow2::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_End << m_MaxTics << m_OneShot << m_Start << m_Tics; @@ -857,7 +857,7 @@ DPhased::DPhased () { } -void DPhased::Serialize (FArchive &arc) +void DPhased::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_BaseLevel << m_Phase; diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index 2a134cc50..e68187b4c 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -72,7 +72,7 @@ void DPillar::Destroy() Super::Destroy(); } -void DPillar::Serialize (FArchive &arc) +void DPillar::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type diff --git a/src/p_plats.cpp b/src/p_plats.cpp index 96fec71c1..dec3894b0 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -56,7 +56,7 @@ DPlat::DPlat () { } -void DPlat::Serialize (FArchive &arc) +void DPlat::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Speed diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp index 4fb3b8f1f..511e6bbb4 100644 --- a/src/p_pusher.cpp +++ b/src/p_pusher.cpp @@ -52,7 +52,7 @@ public: DPusher (); DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); int CheckForSectorMatch (EPusher type, int tag); void ChangeValues (int magnitude, int angle) { @@ -91,7 +91,7 @@ inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type) return arc; } -void DPusher::Serialize (FArchive &arc) +void DPusher::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 550e9455d..0735eb128 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -65,6 +65,7 @@ #include "serializer.h" // just the stuff that already got converted to FSerializer so that it can be seen as 'done' when searching. +#define COMMON_STUFF #include "zzz_old.cpp" void CopyPlayer (player_t *dst, player_t *src, const char *name); diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 52be13d56..9dba9e4ca 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -46,7 +46,7 @@ public: DScroller (double dx, double dy, const line_t *l, int control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); void Destroy(); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); bool AffectsWall (int wallnum) const { return m_Type == EScroll::sc_side && m_Affectee == wallnum; } @@ -114,7 +114,7 @@ EScrollPos operator &(EScrollPos one, EScrollPos two) // //----------------------------------------------------------------------------- -void DScroller::Serialize (FArchive &arc) +void DScroller::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Type diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 4c3bdb488..c10d5dd2a 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -648,7 +648,7 @@ class DLightTransfer : public DThinker DLightTransfer() {} public: DLightTransfer (sector_t *srcSec, int target, bool copyFloor); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: @@ -662,7 +662,7 @@ protected: IMPLEMENT_CLASS (DLightTransfer) -void DLightTransfer::Serialize (FArchive &arc) +void DLightTransfer::Serialize(FArchive &arc) { Super::Serialize (arc); arc << LastLight; @@ -736,7 +736,7 @@ class DWallLightTransfer : public DThinker DWallLightTransfer() {} public: DWallLightTransfer (sector_t *srcSec, int target, BYTE flags); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: @@ -750,7 +750,7 @@ protected: IMPLEMENT_CLASS (DWallLightTransfer) -void DWallLightTransfer::Serialize (FArchive &arc) +void DWallLightTransfer::Serialize(FArchive &arc) { Super::Serialize (arc); arc << LastLight; diff --git a/src/p_spec.h b/src/p_spec.h index d7abd4b42..0e1b9a142 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -184,7 +184,7 @@ public: platRaiseAndStayLockout, }; - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); bool IsLift() const { return m_Type == platDownWaitUpStay || m_Type == platDownWaitUpStayStone; } @@ -241,7 +241,7 @@ public: DPillar (sector_t *sector, EPillar type, double speed, double height, double height2, int crush, bool hexencrush); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); void Destroy(); @@ -283,7 +283,7 @@ public: DDoor (sector_t *sector); DDoor (sector_t *sec, EVlDoor type, double speed, int delay, int lightTag, int topcountdown); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: EVlDoor m_Type; @@ -325,7 +325,7 @@ public: DAnimatedDoor (sector_t *sector); DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); bool StartClosing (); @@ -405,7 +405,7 @@ public: DCeiling (sector_t *sec); DCeiling (sector_t *sec, double speed1, double speed2, int silent); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: @@ -508,7 +508,7 @@ public: DFloor (sector_t *sec); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); //protected: @@ -573,7 +573,7 @@ public: DElevator (sector_t *sec); void Destroy(); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: @@ -601,7 +601,7 @@ class DWaggleBase : public DMover public: DWaggleBase (sector_t *sec); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); protected: double m_OriginalDist; diff --git a/src/p_switch.cpp b/src/p_switch.cpp index fdea7747b..0388176e2 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -61,7 +61,7 @@ public: DActiveButton (); DActiveButton (side_t *, int, FSwitchDef *, const DVector2 &pos, bool flippable); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); side_t *m_Side; @@ -354,7 +354,7 @@ DActiveButton::DActiveButton (side_t *side, int Where, FSwitchDef *Switch, // //========================================================================== -void DActiveButton::Serialize (FArchive &arc) +void DActiveButton::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Side << m_Part << m_SwitchDef << m_Frame << m_Timer << bFlippable << m_Pos << bReturning; diff --git a/src/p_user.cpp b/src/p_user.cpp index b602432b3..25bdd70aa 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -631,7 +631,7 @@ END_POINTERS IMPLEMENT_CLASS (APlayerChunk) -void APlayerPawn::Serialize (FArchive &arc) +void APlayerPawn::Serialize(FArchive &arc) { Super::Serialize (arc); @@ -3001,7 +3001,7 @@ void P_UnPredictPlayer () } } -void player_t::Serialize (FArchive &arc) +void player_t::Serialize(FArchive &arc) { int i; FString skinname; diff --git a/src/po_man.cpp b/src/po_man.cpp index e449e0d67..f6b1ec2b6 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -87,7 +87,7 @@ class DMovePoly : public DPolyAction DECLARE_CLASS (DMovePoly, DPolyAction) public: DMovePoly (int polyNum); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: DMovePoly (); @@ -118,7 +118,7 @@ class DPolyDoor : public DMovePoly DECLARE_CLASS (DPolyDoor, DMovePoly) public: DPolyDoor (int polyNum, podoortype_t type); - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void Tick (); protected: DAngle m_Direction; @@ -199,7 +199,7 @@ DPolyAction::DPolyAction () { } -void DPolyAction::Serialize (FArchive &arc) +void DPolyAction::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_PolyObj << m_Speed << m_Dist << m_Interpolation; @@ -277,7 +277,7 @@ DMovePoly::DMovePoly () { } -void DMovePoly::Serialize (FArchive &arc) +void DMovePoly::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Angle << m_Speed; @@ -331,7 +331,7 @@ DPolyDoor::DPolyDoor () { } -void DPolyDoor::Serialize (FArchive &arc) +void DPolyDoor::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Direction << m_TotalDist << m_Tics << m_WaitTics << m_Type << m_Close; diff --git a/src/r_utility.cpp b/src/r_utility.cpp index ebe36dc9d..d5ab09b4c 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1041,7 +1041,7 @@ void FCanvasTextureInfo::EmptyList () // //========================================================================== -void FCanvasTextureInfo::Serialize (FArchive &arc) +void FCanvasTextureInfo::Serialize(FArchive &arc) { if (arc.IsStoring ()) { diff --git a/src/r_utility.h b/src/r_utility.h index 09a8b5667..4d004e835 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -113,7 +113,7 @@ struct FCanvasTextureInfo static void Add (AActor *viewpoint, FTextureID picnum, int fov); static void UpdateAll (); static void EmptyList (); - static void Serialize (FArchive &arc); + static void Serialize(FArchive &arc); static void Mark(); private: diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 3d0fe1bac..0c57288f9 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2102,7 +2102,7 @@ class AAmbientSound : public AActor { DECLARE_CLASS (AAmbientSound, AActor) public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void MarkPrecacheSounds () const; void BeginPlay (); @@ -2125,7 +2125,7 @@ IMPLEMENT_CLASS (AAmbientSound) // //========================================================================== -void AAmbientSound::Serialize (FArchive &arc) +void AAmbientSound::Serialize(FArchive &arc) { Super::Serialize (arc); arc << bActive << NextCheck; diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 06d5883b6..cb700621c 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -298,7 +298,7 @@ DSeqNode::DSeqNode () m_Next = m_Prev = m_ChildSeqNode = m_ParentSeqNode = NULL; } -void DSeqNode::Serialize (FArchive &arc) +void DSeqNode::Serialize(FArchive &arc) { int seqOffset; unsigned int i; @@ -425,7 +425,7 @@ IMPLEMENT_POINTY_CLASS (DSeqActorNode) DECLARE_POINTER (m_Actor) END_POINTERS -void DSeqActorNode::Serialize (FArchive &arc) +void DSeqActorNode::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Actor; @@ -433,7 +433,7 @@ void DSeqActorNode::Serialize (FArchive &arc) IMPLEMENT_CLASS (DSeqPolyNode) -void DSeqPolyNode::Serialize (FArchive &arc) +void DSeqPolyNode::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Poly; @@ -441,7 +441,7 @@ void DSeqPolyNode::Serialize (FArchive &arc) IMPLEMENT_CLASS (DSeqSectorNode) -void DSeqSectorNode::Serialize (FArchive &arc) +void DSeqSectorNode::Serialize(FArchive &arc) { Super::Serialize (arc); arc << m_Sector << Channel; diff --git a/src/s_sndseq.h b/src/s_sndseq.h index d27dc86da..89a95505a 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -20,7 +20,7 @@ class DSeqNode : public DObject DECLARE_CLASS (DSeqNode, DObject) HAS_OBJECT_POINTERS public: - void Serialize (FArchive &arc); + void Serialize(FArchive &arc); void StopAndDestroy (); void Destroy (); void Tick (); diff --git a/src/tflags.h b/src/tflags.h index 5f44c8391..1affb6cbc 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -83,7 +83,7 @@ public: operator TT() const { return Value; } // Serialize to FArchive - FArchive& Serialize (FArchive& arc) + FArchive& Serialize(FArchive& arc) { arc << Value; return arc; diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index a4d6df95d..1a6064856 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -1,3 +1,5 @@ +#ifdef COMMON_STUFF + // For NULL states, which aren't owned by any actor, the owner // is recorded as AActor with the following state. AActor should // never actually have this many states of its own, so this @@ -691,7 +693,35 @@ FArchive &operator<< (FArchive &arc, FState *&state) return arc; } -void DObject::Serialize (FArchive &arc) +void DObject::Serialize(FArchive &arc) { ObjectFlags |= OF_SerialSuccess; } + + +class DLightLevel : public DLighting +{ + DECLARE_CLASS(DLightLevel, DLighting) + + unsigned char destlevel; + unsigned char speed; + + DLightLevel() {} + +public: + + DLightLevel(sector_t * s, int destlevel, int speed); + void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); + void Tick(); + void Destroy() { Super::Destroy(); m_Sector->lightingdata = NULL; } +}; + +void DLightLevel::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << destlevel << speed; + if (arc.IsLoading()) m_Sector->lightingdata = this; +} + +#endif \ No newline at end of file From 7edf4c1afc88086b4aa35e8a4a841a0eb9126135 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 12:53:42 +0200 Subject: [PATCH 08/59] - added new serializers to several classes and moved the old ones to the dump file. --- src/actor.h | 4 +- src/dobject.h | 4 +- src/fragglescript/t_func.cpp | 2 +- src/g_doom/a_doomglobal.h | 3 +- src/g_doom/a_doommisc.cpp | 1 + src/g_doom/a_scriptedmarine.cpp | 15 +-- src/g_heretic/a_hereticmisc.cpp | 1 + src/g_heretic/a_hereticweaps.cpp | 10 +- src/g_hexen/a_heresiarch.cpp | 17 ++- src/g_hexen/a_hexenmisc.cpp | 1 + src/g_hexen/a_spike.cpp | 7 +- src/g_raven/a_minotaur.cpp | 5 +- src/g_raven/ravenshared.h | 3 +- src/g_shared/a_armor.cpp | 43 ++++--- src/g_shared/a_pickups.cpp | 32 +++-- src/g_shared/a_pickups.h | 34 ++++-- src/g_shared/a_puzzleitems.cpp | 6 - src/g_shared/a_weapons.cpp | 63 ++++++---- src/serializer.cpp | 2 +- src/serializer.h | 2 +- src/zzz_old.cpp | 194 +++++++++++++++++++++++++++---- 21 files changed, 334 insertions(+), 115 deletions(-) diff --git a/src/actor.h b/src/actor.h index 50cd2704c..b46917a4a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -583,7 +583,7 @@ public: void Destroy (); ~AActor (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL void Serialize(FSerializer &arc); void PostSerialize(); @@ -990,7 +990,7 @@ public: double Speed; double FloatSpeed; - WORD sprite; // used to find patch_t and flip value + int sprite; // used to find patch_t and flip value BYTE frame; // sprite frame to draw DVector2 Scale; // Scaling values; 1 is normal size FRenderStyle RenderStyle; // Style to draw this actor with diff --git a/src/dobject.h b/src/dobject.h index d42228172..7e0c49db6 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -438,6 +438,8 @@ template inline void GC::Mark(TObjPtr &obj) GC::Mark(&obj.o); } +#define DECLARE_OLD_SERIAL virtual void Serialize(FArchive &arc); + class DObject { public: @@ -467,7 +469,7 @@ public: inline bool IsA (const PClass *type) const; void SerializeUserVars(FArchive &arc); - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL void SerializeUserVars(FSerializer &arc); virtual void Serialize(FSerializer &arc); diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 8e2c31f2d..7e4b933f1 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -1781,7 +1781,7 @@ public: DLightLevel(sector_t * s,int destlevel,int speed); void Serialize(FArchive &arc); - void Serialize(FArchive &arc); + //void Serialize(FArchive &arc); void Tick (); void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; } }; diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h index bcaaff887..e18917f01 100644 --- a/src/g_doom/a_doomglobal.h +++ b/src/g_doom/a_doomglobal.h @@ -29,7 +29,8 @@ public: void Tick (); void SetWeapon (EMarineWeapon); void SetSprite (PClassActor *source); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); int CurrentWeapon; diff --git a/src/g_doom/a_doommisc.cpp b/src/g_doom/a_doommisc.cpp index 93e7abdaa..c281173af 100644 --- a/src/g_doom/a_doommisc.cpp +++ b/src/g_doom/a_doommisc.cpp @@ -18,6 +18,7 @@ #include "portal.h" #include "d_player.h" #include "p_maputl.h" +#include "serializer.h" #include "g_shared/a_pickups.h" // Include all the other Doom stuff here to reduce compile time diff --git a/src/g_doom/a_scriptedmarine.cpp b/src/g_doom/a_scriptedmarine.cpp index a014891ea..2c293a2de 100644 --- a/src/g_doom/a_scriptedmarine.cpp +++ b/src/g_doom/a_scriptedmarine.cpp @@ -21,19 +21,14 @@ static FRandom pr_m_fireshotgun2 ("SMarineFireSSG"); IMPLEMENT_CLASS (AScriptedMarine) -void AScriptedMarine::Serialize(FArchive &arc) +void AScriptedMarine::Serialize(FSerializer &arc) { Super::Serialize (arc); - if (arc.IsStoring ()) - { - arc.WriteSprite (SpriteOverride); - } - else - { - SpriteOverride = arc.ReadSprite (); - } - arc << CurrentWeapon; + auto def = (AScriptedMarine*)GetDefault(); + + arc.Sprite("spriteoverride", SpriteOverride, &def->SpriteOverride) + ("currentweapon", CurrentWeapon, def->CurrentWeapon); } void AScriptedMarine::Activate (AActor *activator) diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp index 95c2ae6d0..199b3f1c4 100644 --- a/src/g_heretic/a_hereticmisc.cpp +++ b/src/g_heretic/a_hereticmisc.cpp @@ -18,6 +18,7 @@ #include "d_player.h" #include "a_morph.h" #include "p_spec.h" +#include "serializer.h" // Include all the other Heretic stuff here to reduce compile time #include "a_chicken.cpp" diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index fa35bf1f9..dd6231288 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -866,16 +866,18 @@ class ARainTracker : public AInventory { DECLARE_CLASS (ARainTracker, AInventory) public: - void Serialize(FArchive &arc); - AActor *Rain1, *Rain2; + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); + TObjPtr Rain1, Rain2; }; IMPLEMENT_CLASS (ARainTracker) -void ARainTracker::Serialize(FArchive &arc) +void ARainTracker::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Rain1 << Rain2; + arc("rain1", Rain1) + ("rain2", Rain2); } //---------------------------------------------------------------------------- diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index acddc588b..10f6168a9 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -64,19 +64,21 @@ class AHeresiarch : public AActor { DECLARE_CLASS (AHeresiarch, AActor) public: - const PClass *StopBall; + PClassActor *StopBall; DAngle BallAngle; - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); }; IMPLEMENT_CLASS (AHeresiarch) -void AHeresiarch::Serialize(FArchive &arc) +void AHeresiarch::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << StopBall << BallAngle; + arc("stopball", StopBall) + ("ballangle", BallAngle); } void AHeresiarch::Die (AActor *source, AActor *inflictor, int dmgflags) @@ -105,10 +107,13 @@ public: DAngle AngleOffset; DAngle OldAngle; - void Serialize(FArchive &arc) + DECLARE_OLD_SERIAL + + void Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << AngleOffset << OldAngle; + arc("angleoffset", AngleOffset) + ("oldangle", OldAngle); } bool SpecialBlastHandling (AActor *source, double strength) diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index c1d41e762..122129ede 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -24,6 +24,7 @@ #include "r_utility.h" #include "p_maputl.h" #include "p_spec.h" +#include "serializer.h" // Include all the Hexen stuff here to reduce compile time #include "a_bats.cpp" diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index ced6c43a6..3be6a3101 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -22,7 +22,8 @@ class AThrustFloor : public AActor DECLARE_CLASS (AThrustFloor, AActor) HAS_OBJECT_POINTERS public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Activate (AActor *activator); void Deactivate (AActor *activator); @@ -34,10 +35,10 @@ IMPLEMENT_POINTY_CLASS (AThrustFloor) DECLARE_POINTER (DirtClump) END_POINTERS -void AThrustFloor::Serialize(FArchive &arc) +void AThrustFloor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << DirtClump; + arc("dirtclump", DirtClump); } void AThrustFloor::Activate (AActor *activator) diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp index 281e1db49..909b317e7 100644 --- a/src/g_raven/a_minotaur.cpp +++ b/src/g_raven/a_minotaur.cpp @@ -14,6 +14,7 @@ #include "farchive.h" #include "a_pickups.h" #include "d_player.h" +#include "serializer.h" #define MAULATORTICS (25*35) @@ -79,10 +80,10 @@ void AMinotaurFriend::BeginPlay () StartTime = -1; } -void AMinotaurFriend::Serialize(FArchive &arc) +void AMinotaurFriend::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << StartTime; + arc("starttime", StartTime); } void AMinotaurFriend::Die (AActor *source, AActor *inflictor, int dmgflags) diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h index 824bc7c86..dd5b74781 100644 --- a/src/g_raven/ravenshared.h +++ b/src/g_raven/ravenshared.h @@ -23,7 +23,8 @@ public: void Die (AActor *source, AActor *inflictor, int dmgflags); bool OkayToSwitchTarget (AActor *other); void BeginPlay (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); }; #endif //__RAVENSHARED_H__ diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index b221c07b9..e05b564f3 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -7,6 +7,7 @@ #include "g_level.h" #include "d_player.h" #include "farchive.h" +#include "serializer.h" IMPLEMENT_CLASS (AArmor) @@ -21,10 +22,17 @@ IMPLEMENT_CLASS (AHexenArmor) // //=========================================================================== -void ABasicArmor::Serialize(FArchive &arc) +void ABasicArmor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType << ActualSaveAmount; + auto def = (ABasicArmor *)GetDefault(); + arc("savepercent", SavePercent, def->SavePercent) + ("bonuscount", BonusCount, def->BonusCount) + ("maxabsorb", MaxAbsorb, def->MaxAbsorb) + ("maxfullabsorb", MaxFullAbsorb, def->MaxFullAbsorb) + ("absorbcount", AbsorbCount, def->AbsorbCount) + ("armortype", ArmorType, def->ArmorType) + ("actualsaveamount", ActualSaveAmount, def->ActualSaveAmount); } //=========================================================================== @@ -192,11 +200,15 @@ void ABasicArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) // //=========================================================================== -void ABasicArmorPickup::Serialize(FArchive &arc) +void ABasicArmorPickup::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << SavePercent << SaveAmount << MaxAbsorb << MaxFullAbsorb; - arc << DropTime; + + auto def = (ABasicArmorPickup *)GetDefault(); + arc("savepercent", SavePercent, def->SavePercent) + ("saveamount", SaveAmount, def->SaveAmount) + ("maxabsorb", MaxAbsorb, def->MaxAbsorb) + ("maxfullabsorb", MaxFullAbsorb, def->MaxFullAbsorb); } //=========================================================================== @@ -274,11 +286,17 @@ bool ABasicArmorPickup::Use (bool pickup) // //=========================================================================== -void ABasicArmorBonus::Serialize(FArchive &arc) +void ABasicArmorBonus::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << SavePercent << SaveAmount << MaxSaveAmount << BonusCount << BonusMax - << MaxAbsorb << MaxFullAbsorb; + auto def = (ABasicArmorBonus *)GetDefault(); + arc("savepercent", SavePercent, def->SavePercent) + ("saveamount", SaveAmount, def->SaveAmount) + ("maxsaveamount", MaxSaveAmount, def->MaxSaveAmount) + ("bonuscount", BonusCount, def->BonusCount) + ("bonusmax", BonusMax, def->BonusMax) + ("maxabsorb", MaxAbsorb, def->MaxAbsorb) + ("maxfullabsorb", MaxFullAbsorb, def->MaxFullAbsorb); } //=========================================================================== @@ -371,13 +389,12 @@ bool ABasicArmorBonus::Use (bool pickup) // //=========================================================================== -void AHexenArmor::Serialize(FArchive &arc) +void AHexenArmor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Slots[0] << Slots[1] << Slots[2] << Slots[3] - << Slots[4] - << SlotsIncrement[0] << SlotsIncrement[1] << SlotsIncrement[2] - << SlotsIncrement[3]; + auto def = (AHexenArmor *)GetDefault(); + arc.Array("slots", Slots, def->Slots, 5, true) + .Array("slotsincrement", SlotsIncrement, def->SlotsIncrement, 4); } //=========================================================================== diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 1a8202916..1ce531eea 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -21,6 +21,7 @@ #include "farchive.h" #include "d_player.h" #include "p_spec.h" +#include "serializer.h" static FRandom pr_restore ("RestorePos"); @@ -89,10 +90,12 @@ IMPLEMENT_CLASS (AAmmo) // //=========================================================================== -void AAmmo::Serialize(FArchive &arc) +void AAmmo::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << BackpackAmount << BackpackMaxAmount; + auto def = (AAmmo*)GetDefault(); + arc("backpackamount", BackpackAmount, def->BackpackAmount) + ("backpackmaxamount", BackpackMaxAmount, def->BackpackMaxAmount); } //=========================================================================== @@ -514,10 +517,21 @@ void AInventory::Tick () // //=========================================================================== -void AInventory::Serialize(FArchive &arc) +void AInventory::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Owner << Amount << MaxAmount << RespawnTics << ItemFlags << Icon << PickupSound << SpawnPointClass; + + auto def = (AInventory*)GetDefault(); + arc("owner", Owner) + ("amount", Amount, def->Amount) + ("maxamount", MaxAmount, def->MaxAmount) + ("interhubamount", InterHubAmount, def->InterHubAmount) + ("respawntics", RespawnTics, def->RespawnTics) + ("itemflags", ItemFlags, def->ItemFlags) + ("icon", Icon, def->Icon) + ("pickupsound", PickupSound, def->PickupSound) + ("spawnpointclass", SpawnPointClass, def->SpawnPointClass) + ("droptime", DropTime, def->DropTime); } //=========================================================================== @@ -1809,10 +1823,11 @@ bool AHealthPickup::Use (bool pickup) // //=========================================================================== -void AHealthPickup::Serialize(FArchive &arc) +void AHealthPickup::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << autousemode; + auto def = (AHealthPickup*)GetDefault(); + arc("autousemode", autousemode, def->autousemode); } // Backpack ----------------------------------------------------------------- @@ -1823,10 +1838,11 @@ void AHealthPickup::Serialize(FArchive &arc) // //=========================================================================== -void ABackpackItem::Serialize(FArchive &arc) +void ABackpackItem::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << bDepleted; + auto def = (ABackpackItem*)GetDefault(); + arc("bdepleted", bDepleted, def->bDepleted); } //=========================================================================== diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 9122ffd12..303cd3431 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -153,7 +153,8 @@ class AInventory : public AActor HAS_OBJECT_POINTERS public: virtual void Touch (AActor *toucher); - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void MarkPrecacheSounds() const; virtual void BeginPlay (); @@ -185,7 +186,7 @@ public: int RespawnTics; // Tics from pickup time to respawn time FTextureID Icon; // Icon to show on status bar or HUD int DropTime; // Countdown after dropping - const PClass *SpawnPointClass; // For respawning like Heretic's mace + PClassActor *SpawnPointClass; // For respawning like Heretic's mace DWORD ItemFlags; PClassActor *PickupFlash; // actor to spawn as pickup flash @@ -254,7 +255,8 @@ class AAmmo : public AInventory { DECLARE_CLASS_WITH_META(AAmmo, AInventory, PClassAmmo) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); AInventory *CreateCopy (AActor *other); bool HandlePickup (AInventory *item); PClassActor *GetParentAmmo () const; @@ -311,7 +313,8 @@ public: bool bAltFire; // Set when this weapon's alternate fire is used. virtual void MarkPrecacheSounds() const; - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual bool ShouldStay (); virtual void AttachToOwner (AActor *other); virtual bool HandlePickup (AInventory *item); @@ -395,7 +398,8 @@ class AWeaponGiver : public AWeapon public: bool TryPickup(AActor *&toucher); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); double DropAmmoFactor; }; @@ -431,7 +435,8 @@ class AHealthPickup : public AInventory public: int autousemode; - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); virtual bool HandlePickup (AInventory *item); @@ -451,7 +456,8 @@ class ABasicArmor : public AArmor { DECLARE_CLASS (ABasicArmor, AArmor) public: - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void Tick (); virtual AInventory *CreateCopy (AActor *other); virtual bool HandlePickup (AInventory *item); @@ -471,7 +477,8 @@ class ABasicArmorPickup : public AArmor { DECLARE_CLASS (ABasicArmorPickup, AArmor) public: - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -486,7 +493,8 @@ class ABasicArmorBonus : public AArmor { DECLARE_CLASS (ABasicArmorBonus, AArmor) public: - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -505,7 +513,8 @@ class AHexenArmor : public AArmor { DECLARE_CLASS (AHexenArmor, AArmor) public: - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); virtual bool HandlePickup (AInventory *item); @@ -533,7 +542,7 @@ class APuzzleItem : public AInventory { DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL bool ShouldStay (); bool Use (bool pickup); bool HandlePickup (AInventory *item); @@ -555,7 +564,8 @@ class ABackpackItem : public AInventory { DECLARE_CLASS (ABackpackItem, AInventory) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); AInventory *CreateTossable (); diff --git a/src/g_shared/a_puzzleitems.cpp b/src/g_shared/a_puzzleitems.cpp index 774585ae9..512d83b49 100644 --- a/src/g_shared/a_puzzleitems.cpp +++ b/src/g_shared/a_puzzleitems.cpp @@ -20,12 +20,6 @@ void PClassPuzzleItem::DeriveData(PClass *newclass) IMPLEMENT_CLASS(APuzzleItem) -void APuzzleItem::Serialize(FArchive &arc) -{ - Super::Serialize (arc); - arc << PuzzleItemNumber; -} - bool APuzzleItem::HandlePickup (AInventory *item) { // Can't carry more than 1 of each puzzle item in coop netplay diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index ed7c02531..eb73b22f2 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -18,6 +18,7 @@ #include "g_level.h" #include "d_net.h" #include "farchive.h" +#include "serializer.h" #define BONUSADD 6 @@ -73,28 +74,45 @@ void PClassWeapon::ReplaceClassRef(PClass *oldclass, PClass *newclass) // //=========================================================================== -void AWeapon::Serialize(FArchive &arc) +void AWeapon::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << WeaponFlags - << AmmoType1 << AmmoType2 - << AmmoGive1 << AmmoGive2 - << MinAmmo1 << MinAmmo2 - << AmmoUse1 << AmmoUse2 - << Kickback - << YAdjust - << UpSound << ReadySound - << SisterWeaponType - << ProjectileType << AltProjectileType - << SelectionOrder - << MoveCombatDist - << Ammo1 << Ammo2 << SisterWeapon << GivenAsMorphWeapon - << bAltFire - << ReloadCounter - << BobStyle << BobSpeed << BobRangeX << BobRangeY - << FOVScale - << Crosshair - << MinSelAmmo1 << MinSelAmmo2; + auto def = (AWeapon*)GetDefault(); + arc("weaponflags", WeaponFlags, def->WeaponFlags) + ("ammogive1", AmmoGive1, def->AmmoGive1) + ("ammogive2", AmmoGive2, def->AmmoGive2) + ("minammo1", MinAmmo1, def->MinAmmo1) + ("minammo2", MinAmmo2, def->MinAmmo2) + ("ammouse1", AmmoUse1, def->AmmoUse1) + ("ammouse2", AmmoUse2, def->AmmoUse2) + ("kickback", Kickback, Kickback) + ("yadjust", YAdjust, def->YAdjust) + ("upsound", UpSound, def->UpSound) + ("readysound", ReadySound, def->ReadySound) + ("selectionorder", SelectionOrder, def->SelectionOrder) + ("ammo1", Ammo1) + ("ammo2", Ammo2) + ("sisterweapon", SisterWeapon) + ("givenasmorphweapon", GivenAsMorphWeapon, def->GivenAsMorphWeapon) + ("altfire", bAltFire, def->bAltFire) + ("reloadcounter", ReloadCounter, def->ReloadCounter) + ("bobstyle", BobStyle, def->BobStyle) + ("bobspeed", BobSpeed, def->BobSpeed) + ("bobrangex", BobRangeX, def->BobRangeX) + ("bobrangey", BobRangeY, def->BobRangeY) + ("fovscale", FOVScale, def->FOVScale) + ("crosshair", Crosshair, def->Crosshair) + ("minselammo1", MinSelAmmo1, def->MinSelAmmo1) + ("minselammo2", MinSelAmmo2, def->MinSelAmmo2); + /* these can never change + ("ammotype1", AmmoType1, def->AmmoType1) + ("ammotype2", AmmoType2, def->AmmoType2) + ("sisterweapontype", SisterWeaponType, def->SisterWeaponType) + ("projectiletype", ProjectileType, def->ProjectileType) + ("altprojectiletype", AltProjectileType, def->AltProjectileType) + ("movecombatdist", MoveCombatDist, def->MoveCombatDist) + */ + } //=========================================================================== @@ -737,10 +755,11 @@ FState *AWeapon::GetStateForButtonName (FName button) IMPLEMENT_CLASS(AWeaponGiver) -void AWeaponGiver::Serialize(FArchive &arc) +void AWeaponGiver::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << DropAmmoFactor; + auto def = (AWeaponGiver *)GetDefault(); + arc("dropammofactor", DropAmmoFactor, def->DropAmmoFactor); } bool AWeaponGiver::TryPickup(AActor *&toucher) diff --git a/src/serializer.cpp b/src/serializer.cpp index 06625b054..fe4a76ba5 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -431,7 +431,7 @@ FSerializer &FSerializer::Terrain(const char *key, int &terrain, int *def) // //========================================================================== -FSerializer &FSerializer::Sprite(const char *key, uint16_t &spritenum, uint16_t *def) +FSerializer &FSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *def) { if (isWriting()) { diff --git a/src/serializer.h b/src/serializer.h index f56f9c87a..b85405a1f 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -38,7 +38,7 @@ public: const char *GetOutput(unsigned *len = nullptr); 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, uint16_t &spritenum, uint16_t *def); + 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. bool isReading() const { diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 1a6064856..952afcdb8 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -1,5 +1,8 @@ #ifdef COMMON_STUFF +#include "a_doomglobal.h" +#include "ravenshared.h" + // For NULL states, which aren't owned by any actor, the owner // is recorded as AActor with the following state. AActor should // never actually have this many states of its own, so this @@ -699,29 +702,178 @@ void DObject::Serialize(FArchive &arc) } -class DLightLevel : public DLighting -{ - DECLARE_CLASS(DLightLevel, DLighting) - - unsigned char destlevel; - unsigned char speed; - - DLightLevel() {} - -public: - - DLightLevel(sector_t * s, int destlevel, int speed); - void Serialize(FArchive &arc); - void Serialize(FSerializer &arc); - void Tick(); - void Destroy() { Super::Destroy(); m_Sector->lightingdata = NULL; } -}; - -void DLightLevel::Serialize(FArchive &arc) +void AScriptedMarine::Serialize(FArchive &arc) { Super::Serialize(arc); - arc << destlevel << speed; - if (arc.IsLoading()) m_Sector->lightingdata = this; + + if (arc.IsStoring()) + { + arc.WriteSprite(SpriteOverride); + } + else + { + SpriteOverride = arc.ReadSprite(); + } + arc << CurrentWeapon; } + +class AHeresiarch : public AActor +{ + DECLARE_CLASS(AHeresiarch, AActor) +public: + PClassActor *StopBall; + DAngle BallAngle; + + DECLARE_OLD_SERIAL +}; + +void AHeresiarch::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << StopBall << BallAngle; +} + +class ASorcBall : public AActor +{ + DECLARE_CLASS(ASorcBall, AActor) +public: + DAngle AngleOffset; + DAngle OldAngle; + + DECLARE_OLD_SERIAL +}; + +void ASorcBall::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << AngleOffset << OldAngle; +} + +class AThrustFloor : public AActor +{ + DECLARE_CLASS(AThrustFloor, AActor) +public: + DECLARE_OLD_SERIAL + + TObjPtr DirtClump; +}; + +void AThrustFloor::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << DirtClump; +} + +void AMinotaurFriend::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << StartTime; +} + +class ARainTracker : public AInventory +{ + DECLARE_CLASS(ARainTracker, AInventory) +public: + DECLARE_OLD_SERIAL + TObjPtr Rain1, Rain2; +}; + +void ARainTracker::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Rain1 << Rain2; +} + +void AInventory::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Owner << Amount << MaxAmount << RespawnTics << ItemFlags << Icon << PickupSound << SpawnPointClass; +} + +void AHealthPickup::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << autousemode; +} + +void AAmmo::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << BackpackAmount << BackpackMaxAmount; +} + +void AWeapon::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << WeaponFlags + << AmmoType1 << AmmoType2 + << AmmoGive1 << AmmoGive2 + << MinAmmo1 << MinAmmo2 + << AmmoUse1 << AmmoUse2 + << Kickback + << YAdjust + << UpSound << ReadySound + << SisterWeaponType + << ProjectileType << AltProjectileType + << SelectionOrder + << MoveCombatDist + << Ammo1 << Ammo2 << SisterWeapon << GivenAsMorphWeapon + << bAltFire + << ReloadCounter + << BobStyle << BobSpeed << BobRangeX << BobRangeY + << FOVScale + << Crosshair + << MinSelAmmo1 << MinSelAmmo2; +} + +void ABackpackItem::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << bDepleted; +} + +void AWeaponGiver::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << DropAmmoFactor; +} + +void ABasicArmor::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType << ActualSaveAmount; +} + +void ABasicArmorPickup::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << SavePercent << SaveAmount << MaxAbsorb << MaxFullAbsorb; + arc << DropTime;// in inventory! +} + +void ABasicArmorBonus::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << SavePercent << SaveAmount << MaxSaveAmount << BonusCount << BonusMax + << MaxAbsorb << MaxFullAbsorb; +} + +void AHexenArmor::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Slots[0] << Slots[1] << Slots[2] << Slots[3] + << Slots[4] + << SlotsIncrement[0] << SlotsIncrement[1] << SlotsIncrement[2] + << SlotsIncrement[3]; +} + +void APuzzleItem::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << PuzzleItemNumber; +} + + + #endif \ No newline at end of file From a542e9914360d16456310a77d2bedea2807accc0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 13:36:58 +0200 Subject: [PATCH 09/59] - a few more --- src/g_shared/a_weaponpiece.cpp | 14 ++- src/g_shared/a_weaponpiece.h | 10 +- src/r_data/r_interpolate.cpp | 55 ++++++----- src/r_data/r_interpolate.h | 3 +- src/serializer.cpp | 10 +- src/serializer.h | 5 + src/zzz_old.cpp | 163 +++++++++++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 32 deletions(-) diff --git a/src/g_shared/a_weaponpiece.cpp b/src/g_shared/a_weaponpiece.cpp index 7068fe275..aefce18c1 100644 --- a/src/g_shared/a_weaponpiece.cpp +++ b/src/g_shared/a_weaponpiece.cpp @@ -1,7 +1,7 @@ #include "a_pickups.h" #include "a_weaponpiece.h" #include "doomstat.h" -#include "farchive.h" +#include "serializer.h" IMPLEMENT_CLASS(PClassWeaponPiece) IMPLEMENT_CLASS (AWeaponHolder) @@ -17,10 +17,11 @@ void PClassWeaponPiece::ReplaceClassRef(PClass *oldclass, PClass *newclass) } -void AWeaponHolder::Serialize(FArchive &arc) +void AWeaponHolder::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << PieceMask << PieceWeapon; + arc("piecemask", PieceMask) + ("pieceweapon", PieceWeapon); } @@ -29,10 +30,13 @@ IMPLEMENT_POINTY_CLASS (AWeaponPiece) END_POINTERS -void AWeaponPiece::Serialize(FArchive &arc) +void AWeaponPiece::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << WeaponClass << FullWeapon << PieceValue; + auto def = (AWeaponPiece*)GetDefault(); + arc("weaponclass", WeaponClass, def->WeaponClass) + ("fullweapon", FullWeapon) + ("piecevalue", PieceValue, def->PieceValue); } //========================================================================== diff --git a/src/g_shared/a_weaponpiece.h b/src/g_shared/a_weaponpiece.h index 4f201fe19..0ab11a80f 100644 --- a/src/g_shared/a_weaponpiece.h +++ b/src/g_shared/a_weaponpiece.h @@ -15,7 +15,8 @@ class AWeaponPiece : public AInventory protected: bool PrivateShouldStay (); public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool TryPickup (AActor *&toucher); bool TryPickupRestricted (AActor *&toucher); bool ShouldStay (); @@ -23,7 +24,7 @@ public: virtual void PlayPickupSound (AActor *toucher); int PieceValue; - PClassWeapon *WeaponClass; + PClassActor *WeaponClass; TObjPtr FullWeapon; }; @@ -35,7 +36,8 @@ class AWeaponHolder : public AInventory public: int PieceMask; - const PClass * PieceWeapon; + PClassActor * PieceWeapon; - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); }; diff --git a/src/r_data/r_interpolate.cpp b/src/r_data/r_interpolate.cpp index 4b826c56b..38cb7a7a5 100644 --- a/src/r_data/r_interpolate.cpp +++ b/src/r_data/r_interpolate.cpp @@ -39,7 +39,7 @@ #include "p_local.h" #include "i_system.h" #include "po_man.h" -#include "farchive.h" +#include "serializer.h" //========================================================================== // @@ -66,7 +66,8 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); size_t PointerSubstitution (DObject *old, DObject *notOld); size_t PropagateMark(); }; @@ -94,7 +95,8 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; @@ -121,7 +123,8 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; //========================================================================== @@ -147,7 +150,8 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; @@ -370,11 +374,11 @@ void DInterpolation::Destroy() // //========================================================================== -void DInterpolation::Serialize(FArchive &arc) +void DInterpolation::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << refcount; - if (arc.IsLoading()) + arc("refcount", refcount); + if (arc.isReading()) { interpolator.AddInterpolation(this); } @@ -516,10 +520,14 @@ void DSectorPlaneInterpolation::Interpolate(double smoothratio) // //========================================================================== -void DSectorPlaneInterpolation::Serialize(FArchive &arc) +void DSectorPlaneInterpolation::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << sector << ceiling << oldheight << oldtexz << attached; + arc("sector", sector) + ("ceiling", ceiling) + ("oldheight", oldheight) + ("oldtexz", oldtexz) + ("attached", attached); } //========================================================================== @@ -648,10 +656,13 @@ void DSectorScrollInterpolation::Interpolate(double smoothratio) // //========================================================================== -void DSectorScrollInterpolation::Serialize(FArchive &arc) +void DSectorScrollInterpolation::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << sector << ceiling << oldx << oldy; + arc("sector", sector) + ("ceiling", ceiling) + ("oldx", oldx) + ("oldy", oldy); } @@ -739,10 +750,13 @@ void DWallScrollInterpolation::Interpolate(double smoothratio) // //========================================================================== -void DWallScrollInterpolation::Serialize(FArchive &arc) +void DWallScrollInterpolation::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << side << part << oldx << oldy; + arc("side", side) + ("part", part) + ("oldx", oldx) + ("oldy", oldy); } //========================================================================== @@ -855,15 +869,14 @@ void DPolyobjInterpolation::Interpolate(double smoothratio) // //========================================================================== -void DPolyobjInterpolation::Serialize(FArchive &arc) +void DPolyobjInterpolation::Serialize(FSerializer &arc) { Super::Serialize(arc); - int po = int(poly - polyobjs); - arc << po << oldverts; - poly = polyobjs + po; - - arc << oldcx << oldcy; - if (arc.IsLoading()) bakverts.Resize(oldverts.Size()); + arc("poly", poly) + ("oldverts", oldverts) + ("oldcx", oldcx) + ("oldcy", oldcy); + if (arc.isReading()) bakverts.Resize(oldverts.Size()); } diff --git a/src/r_data/r_interpolate.h b/src/r_data/r_interpolate.h index e49500e57..8fc98c44c 100644 --- a/src/r_data/r_interpolate.h +++ b/src/r_data/r_interpolate.h @@ -31,7 +31,8 @@ public: virtual void UpdateInterpolation() = 0; virtual void Restore() = 0; virtual void Interpolate(double smoothratio) = 0; - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; //========================================================================== diff --git a/src/serializer.cpp b/src/serializer.cpp index fe4a76ba5..d675a3cf9 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -3,7 +3,9 @@ #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag -//#define PRETTY +#ifdef _DEBUG +#define PRETTY +#endif #include "rapidjson/rapidjson.h" #include "rapidjson/writer.h" @@ -26,6 +28,7 @@ #include "d_player.h" #include "r_data/r_interpolate.h" #include "g_shared/a_sharedglobal.h" +#include "po_man.h" char nulspace[1024 * 1024 * 4]; @@ -802,6 +805,11 @@ FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T ** return arc; } +FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval) +{ + return SerializePointer(arc, key, value, defval, polyobjs); +} + FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) { return SerializePointer(arc, key, value, defval, sides); diff --git a/src/serializer.h b/src/serializer.h index b85405a1f..5a87c63fc 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -26,6 +26,10 @@ public: public: + ~FSerializer() + { + Close(); + } bool OpenWriter(); bool OpenReader(const char *buffer, size_t length); void Close(); @@ -114,6 +118,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint1 FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval); FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval); FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval); +FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval); FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval); FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval); diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 952afcdb8..37486164f 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -2,6 +2,7 @@ #include "a_doomglobal.h" #include "ravenshared.h" +#include "a_weaponpiece.h" // For NULL states, which aren't owned by any actor, the owner // is recorded as AActor with the following state. AActor should @@ -874,6 +875,168 @@ void APuzzleItem::Serialize(FArchive &arc) arc << PuzzleItemNumber; } +class DSectorPlaneInterpolation : public DInterpolation +{ + DECLARE_CLASS(DSectorPlaneInterpolation, DInterpolation) + + sector_t *sector; + double oldheight, oldtexz; + double bakheight, baktexz; + bool ceiling; + TArray attached; + + +public: + + DSectorPlaneInterpolation() {} + DSectorPlaneInterpolation(sector_t *sector, bool plane, bool attach); + void Destroy(); + void UpdateInterpolation(); + void Restore(); + void Interpolate(double smoothratio); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); + size_t PointerSubstitution(DObject *old, DObject *notOld); + size_t PropagateMark(); +}; + +//========================================================================== +// +// +// +//========================================================================== + +class DSectorScrollInterpolation : public DInterpolation +{ + DECLARE_CLASS(DSectorScrollInterpolation, DInterpolation) + + sector_t *sector; + double oldx, oldy; + double bakx, baky; + bool ceiling; + +public: + + DSectorScrollInterpolation() {} + DSectorScrollInterpolation(sector_t *sector, bool plane); + void Destroy(); + void UpdateInterpolation(); + void Restore(); + void Interpolate(double smoothratio); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); +}; + + +//========================================================================== +// +// +// +//========================================================================== + +class DWallScrollInterpolation : public DInterpolation +{ + DECLARE_CLASS(DWallScrollInterpolation, DInterpolation) + + side_t *side; + int part; + double oldx, oldy; + double bakx, baky; + +public: + + DWallScrollInterpolation() {} + DWallScrollInterpolation(side_t *side, int part); + void Destroy(); + void UpdateInterpolation(); + void Restore(); + void Interpolate(double smoothratio); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); +}; + +//========================================================================== +// +// +// +//========================================================================== + +class DPolyobjInterpolation : public DInterpolation +{ + DECLARE_CLASS(DPolyobjInterpolation, DInterpolation) + + FPolyObj *poly; + TArray oldverts, bakverts; + double oldcx, oldcy; + double bakcx, bakcy; + +public: + + DPolyobjInterpolation() {} + DPolyobjInterpolation(FPolyObj *poly); + void Destroy(); + void UpdateInterpolation(); + void Restore(); + void Interpolate(double smoothratio); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); +}; + +void DInterpolation::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << refcount; + if (arc.IsLoading()) + { + interpolator.AddInterpolation(this); + } +} +//========================================================================== +// +// +// +//========================================================================== + +void DSectorPlaneInterpolation::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << sector << ceiling << oldheight << oldtexz << attached; +} + +void DSectorScrollInterpolation::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << sector << ceiling << oldx << oldy; +} +void DWallScrollInterpolation::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << side << part << oldx << oldy; +} + +void DPolyobjInterpolation::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + int po = int(poly - polyobjs); + arc << po << oldverts; + poly = polyobjs + po; + + arc << oldcx << oldcy; + if (arc.IsLoading()) bakverts.Resize(oldverts.Size()); +} + +void AWeaponHolder::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << PieceMask << PieceWeapon; +} + +void AWeaponPiece::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << WeaponClass << FullWeapon << PieceValue; +} + #endif \ No newline at end of file From 88eab9d1f94c2b2b149030002d4df61bb1a9ea14 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 15:07:53 +0200 Subject: [PATCH 10/59] - And another batch of serializers. --- src/d_dehacked.cpp | 6 +- src/d_dehacked.h | 3 +- src/d_player.h | 3 +- src/dsectoreffect.cpp | 10 +- src/dsectoreffect.h | 6 +- src/g_heretic/a_hereticmisc.cpp | 1 - src/g_heretic/a_hereticweaps.cpp | 5 +- src/g_hexen/a_clericholy.cpp | 5 +- src/g_hexen/a_magestaff.cpp | 5 +- src/g_shared/a_artifacts.cpp | 44 +++-- src/g_shared/a_artifacts.h | 18 +- src/g_shared/a_camera.cpp | 19 +- src/g_shared/a_morph.cpp | 21 ++- src/g_shared/a_movingcamera.cpp | 28 ++- src/g_shared/a_sharedglobal.h | 6 +- src/g_shared/a_soundsequence.cpp | 9 +- src/g_strife/a_strifeglobal.h | 3 +- src/g_strife/a_strifestuff.cpp | 2 +- src/g_strife/a_strifeweapons.cpp | 5 +- src/p_user.cpp | 44 +++-- src/s_advsound.cpp | 10 +- src/zzz_old.cpp | 301 +++++++++++++++++++++++++++++++ 22 files changed, 456 insertions(+), 98 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index dfa4f56f0..9e068b596 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -69,7 +69,7 @@ #include "i_system.h" #include "doomerrors.h" #include "p_effect.h" -#include "farchive.h" +#include "serializer.h" #include "vmbuilder.h" // [SO] Just the way Randy said to do it :) @@ -3198,8 +3198,8 @@ PClassActor *ADehackedPickup::DetermineType () return NULL; } -void ADehackedPickup::Serialize(FArchive &arc) +void ADehackedPickup::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << droppedbymonster; + arc("droppedbymonster", droppedbymonster); } diff --git a/src/d_dehacked.h b/src/d_dehacked.h index 4418f4f09..e256718b6 100644 --- a/src/d_dehacked.h +++ b/src/d_dehacked.h @@ -48,7 +48,8 @@ public: bool TryPickup (AActor *&toucher); void PlayPickupSound (AActor *toucher); void DoPickupSpecial (AActor *toucher); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); private: PClassActor *DetermineType (); AInventory *RealPickup; diff --git a/src/d_player.h b/src/d_player.h index deec3f61d..989b28691 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -102,7 +102,8 @@ class APlayerPawn : public AActor DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn) HAS_OBJECT_POINTERS public: - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void PostBeginPlay(); virtual void Tick(); diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index ed8eb3327..78e00e6b0 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -28,7 +28,7 @@ #include "p_3dmidtex.h" #include "r_data/r_interpolate.h" #include "statnums.h" -#include "farchive.h" +#include "serializer.h" #include "doomstat.h" IMPLEMENT_CLASS (DSectorEffect) @@ -64,10 +64,10 @@ DSectorEffect::DSectorEffect (sector_t *sector) m_Sector = sector; } -void DSectorEffect::Serialize(FArchive &arc) +void DSectorEffect::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Sector; + arc("sector", m_Sector); } IMPLEMENT_POINTY_CLASS (DMover) @@ -90,10 +90,10 @@ void DMover::Destroy() Super::Destroy(); } -void DMover::Serialize(FArchive &arc) +void DMover::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << interpolation; + arc("interpolation", interpolation); } void DMover::StopInterpolation(bool force) diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index 775bca9ac..6008cb63f 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -10,7 +10,8 @@ class DSectorEffect : public DThinker public: DSectorEffect (sector_t *sector); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Destroy(); sector_t *GetSector() const { return m_Sector; } @@ -32,7 +33,8 @@ protected: private: protected: DMover (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Destroy(); }; diff --git a/src/g_heretic/a_hereticmisc.cpp b/src/g_heretic/a_hereticmisc.cpp index 199b3f1c4..36969f6e7 100644 --- a/src/g_heretic/a_hereticmisc.cpp +++ b/src/g_heretic/a_hereticmisc.cpp @@ -14,7 +14,6 @@ #include "templates.h" #include "r_data/r_translate.h" #include "doomstat.h" -#include "farchive.h" #include "d_player.h" #include "a_morph.h" #include "p_spec.h" diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index dd6231288..34c77d316 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -1142,10 +1142,11 @@ class APhoenixRod : public AWeapon { DECLARE_CLASS (APhoenixRod, AWeapon) public: - void Serialize(FArchive &arc) + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << FlameCount; + arc("flamecount", FlameCount); } int FlameCount; // for flamethrower duration }; diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index 7c8e8f8e7..6a840edb5 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -31,10 +31,11 @@ class ACWeapWraithverge : public AClericWeapon { DECLARE_CLASS (ACWeapWraithverge, AClericWeapon) public: - void Serialize(FArchive &arc) + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << CHolyCount; + arc("cholycount", CHolyCount); } PalEntry GetBlend () { diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp index 9ad303179..c05206d32 100644 --- a/src/g_hexen/a_magestaff.cpp +++ b/src/g_hexen/a_magestaff.cpp @@ -29,10 +29,11 @@ class AMWeapBloodscourge : public AMageWeapon { DECLARE_CLASS (AMWeapBloodscourge, AMageWeapon) public: - void Serialize(FArchive &arc) + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << MStaffCount; + arc("mstaffcount", MStaffCount); } PalEntry GetBlend () { diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 76426f554..77e5cede6 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -19,7 +19,7 @@ #include "g_level.h" #include "doomstat.h" #include "v_palette.h" -#include "farchive.h" +#include "serializer.h" #include "r_utility.h" #include "r_data/colormaps.h" @@ -101,12 +101,15 @@ bool APowerupGiver::Use (bool pickup) // //=========================================================================== -void APowerupGiver::Serialize(FArchive &arc) +void APowerupGiver::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << PowerupType; - arc << EffectTics << BlendColor << Mode; - arc << Strength; + auto def = (APowerupGiver*)GetDefault(); + arc("poweruptype", PowerupType, def->PowerupType) + ("effecttics", EffectTics, def->EffectTics) + ("blendcolor", BlendColor, def->BlendColor) + ("mode", Mode, def->Mode) + ("strength", Strength, def->Strength); } // Powerup ------------------------------------------------------------------- @@ -136,11 +139,14 @@ void APowerup::Tick () // //=========================================================================== -void APowerup::Serialize(FArchive &arc) +void APowerup::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << EffectTics << BlendColor << Mode; - arc << Strength; + auto def = (APowerup*)GetDefault(); + arc("effecttics", EffectTics, def->EffectTics) + ("blendcolor", BlendColor, def->BlendColor) + ("mode", Mode, def->Mode) + ("strength", Strength, def->Strength); } //=========================================================================== @@ -901,10 +907,11 @@ IMPLEMENT_CLASS (APowerTorch) // //=========================================================================== -void APowerTorch::Serialize(FArchive &arc) +void APowerTorch::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << NewTorch << NewTorchDelta; + arc("newtorch", NewTorch) + ("newtorchdelta", NewTorchDelta); } //=========================================================================== @@ -963,10 +970,10 @@ IMPLEMENT_CLASS (APowerFlight) // //=========================================================================== -void APowerFlight::Serialize(FArchive &arc) +void APowerFlight::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << HitCenterFrame; + arc("hitcenterframe", HitCenterFrame); } //=========================================================================== @@ -1217,10 +1224,10 @@ IMPLEMENT_CLASS (APowerSpeed) // //=========================================================================== -void APowerSpeed::Serialize(FArchive &arc) +void APowerSpeed::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << SpeedFlags; + arc("speedflags", SpeedFlags); } //=========================================================================== @@ -1899,11 +1906,14 @@ IMPLEMENT_CLASS(APowerMorph) // //=========================================================================== -void APowerMorph::Serialize(FArchive &arc) +void APowerMorph::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash; - arc << Player; + arc("playerclass", PlayerClass) + ("morphstyle", MorphStyle) + ("morphflash", MorphFlash) + ("unmorphflash", UnMorphFlash) + ("player", Player); } //=========================================================================== diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index d742c2df4..97aaad0d3 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -16,7 +16,8 @@ public: virtual bool HandlePickup (AInventory *item); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void OwnerDied (); virtual bool GetNoTeleportFreeze(); virtual PalEntry GetBlend (); @@ -51,7 +52,8 @@ class APowerupGiver : public AInventory DECLARE_CLASS_WITH_META (APowerupGiver, AInventory, PClassPowerupGiver) public: virtual bool Use (bool pickup); - virtual void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); PClassActor *PowerupType; @@ -121,7 +123,8 @@ class APowerTorch : public APowerLightAmp { DECLARE_CLASS (APowerTorch, APowerLightAmp) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); protected: void DoEffect (); int NewTorch, NewTorchDelta; @@ -132,7 +135,8 @@ class APowerFlight : public APowerup DECLARE_CLASS (APowerFlight, APowerup) public: bool DrawPowerup (int x, int y); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); protected: void InitEffect (); @@ -155,7 +159,8 @@ class APowerSpeed : public APowerup DECLARE_CLASS (APowerSpeed, APowerup) protected: void DoEffect (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); double GetSpeedFactor(); public: int SpeedFlags; @@ -272,7 +277,8 @@ class APowerMorph : public APowerup { DECLARE_CLASS( APowerMorph, APowerup ) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); void SetNoCallUndoMorph() { bNoCallUndoMorph = true; } FNameNoInit PlayerClass, MorphFlash, UnMorphFlash; diff --git a/src/g_shared/a_camera.cpp b/src/g_shared/a_camera.cpp index 58eb3e114..b8f4730ad 100644 --- a/src/g_shared/a_camera.cpp +++ b/src/g_shared/a_camera.cpp @@ -36,7 +36,7 @@ #include "info.h" #include "a_sharedglobal.h" #include "p_local.h" -#include "farchive.h" +#include "serializer.h" #include "math/cmath.h" /* @@ -55,7 +55,8 @@ public: void PostBeginPlay (); void Tick (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: DAngle Center; DAngle Acc; @@ -65,10 +66,13 @@ protected: IMPLEMENT_CLASS (ASecurityCamera) -void ASecurityCamera::Serialize(FArchive &arc) +void ASecurityCamera::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Center << Acc << Delta << Range; + arc("center", Center) + ("acc", Acc) + ("delta", Delta) + ("range", Range); } void ASecurityCamera::PostBeginPlay () @@ -114,17 +118,18 @@ public: void PostBeginPlay (); void Tick (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: DAngle MaxPitchChange; }; IMPLEMENT_CLASS (AAimingCamera) -void AAimingCamera::Serialize(FArchive &arc) +void AAimingCamera::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << MaxPitchChange; + arc("maxpitchchange", MaxPitchChange); } void AAimingCamera::PostBeginPlay () diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index b8c5096c0..46ae5cac7 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -11,9 +11,10 @@ #include "a_morph.h" #include "doomstat.h" #include "g_level.h" -#include "farchive.h" +#include "serializer.h" #include "p_enemy.h" #include "d_player.h" +#include "r_data/sprites.h" static FRandom pr_morphmonst ("MorphMonster"); @@ -634,10 +635,16 @@ int AMorphProjectile::DoSpecialDamage (AActor *target, int damage, FName damaget return -1; } -void AMorphProjectile::Serialize(FArchive &arc) +void AMorphProjectile::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << PlayerClass << MonsterClass << Duration << MorphStyle << MorphFlash << UnMorphFlash; + auto def = (AMorphProjectile*)GetDefault(); + arc("playerclass", PlayerClass, def->PlayerClass) + ("monsterclass", MonsterClass, def->MonsterClass) + ("duration", Duration, def->Duration) + ("morphstyle", MorphStyle, def->MorphStyle) + ("morphflash", MorphFlash, def->MorphFlash) + ("unmorphflash", UnMorphFlash, def->UnMorphFlash); } @@ -647,10 +654,14 @@ IMPLEMENT_POINTY_CLASS (AMorphedMonster) DECLARE_POINTER (UnmorphedMe) END_POINTERS -void AMorphedMonster::Serialize(FArchive &arc) +void AMorphedMonster::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << UnmorphedMe << UnmorphTime << MorphStyle << MorphExitFlash << FlagsSave; + arc("unmorphedme", UnmorphedMe) + ("unmorphtime", UnmorphTime) + ("morphstyle", MorphStyle) + ("morphexitflash", MorphExitFlash) + ("flagsave", FlagsSave); } void AMorphedMonster::Destroy () diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index c18e6c483..72ceef268 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -37,7 +37,7 @@ #include "p_local.h" #include "p_lnspec.h" #include "doomstat.h" -#include "farchive.h" +#include "serializer.h" /* == InterpolationPoint: node along a camera's path @@ -60,7 +60,8 @@ public: AInterpolationPoint *ScanForLoop (); void FormChain (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); TObjPtr Next; }; @@ -69,10 +70,10 @@ IMPLEMENT_POINTY_CLASS (AInterpolationPoint) DECLARE_POINTER (Next) END_POINTERS -void AInterpolationPoint::Serialize(FArchive &arc) +void AInterpolationPoint::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Next; + arc("next", Next); } void AInterpolationPoint::BeginPlay () @@ -166,7 +167,8 @@ protected: virtual bool Interpolate (); virtual void NewNode (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool bActive, bJustStepped; TObjPtr PrevNode, CurrNode; @@ -179,10 +181,15 @@ IMPLEMENT_POINTY_CLASS (APathFollower) DECLARE_POINTER (CurrNode) END_POINTERS -void APathFollower::Serialize(FArchive &arc) +void APathFollower::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << bActive << bJustStepped << PrevNode << CurrNode << Time << HoldTime; + arc("active", bActive) + ("juststepped", bJustStepped) + ("prevnode", PrevNode) + ("currnode", CurrNode) + ("time", Time) + ("holdtime", HoldTime); } // Interpolate between p2 and p3 along a Catmull-Rom spline @@ -577,7 +584,8 @@ class AMovingCamera : public APathFollower public: void PostBeginPlay (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: bool Interpolate (); @@ -588,10 +596,10 @@ IMPLEMENT_POINTY_CLASS (AMovingCamera) DECLARE_POINTER (Activator) END_POINTERS -void AMovingCamera::Serialize(FArchive &arc) +void AMovingCamera::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Activator; + arc("activator", Activator); } void AMovingCamera::PostBeginPlay () diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index b4a5fbbf7..1a03c6767 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -194,7 +194,8 @@ class AMorphProjectile : public AActor DECLARE_CLASS (AMorphProjectile, AActor) public: int DoSpecialDamage (AActor *target, int damage, FName damagetype); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash; int Duration, MorphStyle; @@ -206,7 +207,8 @@ class AMorphedMonster : public AActor HAS_OBJECT_POINTERS public: void Tick (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); void Destroy (); diff --git a/src/g_shared/a_soundsequence.cpp b/src/g_shared/a_soundsequence.cpp index c1b9ced78..d824b9678 100644 --- a/src/g_shared/a_soundsequence.cpp +++ b/src/g_shared/a_soundsequence.cpp @@ -65,7 +65,7 @@ #include "s_sound.h" #include "m_random.h" #include "s_sndseq.h" -#include "farchive.h" +#include "serializer.h" // SoundSequenceSlot -------------------------------------------------------- @@ -74,7 +74,8 @@ class ASoundSequenceSlot : public AActor DECLARE_CLASS (ASoundSequenceSlot, AActor) HAS_OBJECT_POINTERS public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); TObjPtr Sequence; }; @@ -89,10 +90,10 @@ END_POINTERS // //========================================================================== -void ASoundSequenceSlot::Serialize(FArchive &arc) +void ASoundSequenceSlot::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Sequence; + arc("sequence", Sequence); } // SoundSequence ------------------------------------------------------------ diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h index 58974e41e..30a14e91b 100644 --- a/src/g_strife/a_strifeglobal.h +++ b/src/g_strife/a_strifeglobal.h @@ -55,7 +55,8 @@ class ASigil : public AWeapon public: bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool SpecialDropAction (AActor *dropper); static int GiveSigilPiece (AActor *daPlayer); void BeginPlay(); diff --git a/src/g_strife/a_strifestuff.cpp b/src/g_strife/a_strifestuff.cpp index 4533016ec..ad6bbe133 100644 --- a/src/g_strife/a_strifestuff.cpp +++ b/src/g_strife/a_strifestuff.cpp @@ -18,7 +18,7 @@ #include "templates.h" #include "d_event.h" #include "v_font.h" -#include "farchive.h" +#include "serializer.h" #include "p_spec.h" #include "portal.h" diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index de03e6049..be3c3bf2b 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -753,10 +753,11 @@ void ASigil::BeginPlay() // //============================================================================ -void ASigil::Serialize(FArchive &arc) +void ASigil::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << NumPieces << DownPieces; + arc("numpieces", NumPieces) + ("downpieces", DownPieces); } //============================================================================ diff --git a/src/p_user.cpp b/src/p_user.cpp index 25bdd70aa..bfefca466 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -53,6 +53,7 @@ #include "d_net.h" #include "gstrings.h" #include "farchive.h" +#include "serializer.h" #include "r_renderer.h" #include "d_player.h" #include "r_utility.h" @@ -631,29 +632,32 @@ END_POINTERS IMPLEMENT_CLASS (APlayerChunk) -void APlayerPawn::Serialize(FArchive &arc) +void APlayerPawn::Serialize(FSerializer &arc) { Super::Serialize (arc); + auto def = (APlayerPawn*)GetDefault(); - arc << JumpZ - << MaxHealth - << RunHealth - << SpawnMask - << ForwardMove1 - << ForwardMove2 - << SideMove1 - << SideMove2 - << ScoreIcon - << InvFirst - << InvSel - << MorphWeapon - << DamageFade - << PlayerFlags - << FlechetteType; - arc << GruntSpeed << FallingScreamMinSpeed << FallingScreamMaxSpeed; - arc << UseRange; - arc << AirCapacity; - arc << ViewHeight; + arc("jumpz", JumpZ, def->JumpZ) + ("maxhealth", MaxHealth, def->MaxHealth) + ("runhealth", RunHealth, def->RunHealth) + ("spawnmask", SpawnMask, def->SpawnMask) + ("forwardmove1", ForwardMove1, def->ForwardMove1) + ("forwardmove2", ForwardMove2, def->ForwardMove2) + ("sidemove1", SideMove1, def->SideMove1) + ("sidemove2", SideMove2, def->SideMove2) + ("scoreicon", ScoreIcon, def->ScoreIcon) + ("invfirst", InvFirst) + ("invsel", InvSel) + ("morphweapon", MorphWeapon, def->MorphWeapon) + ("damagefade", DamageFade, def->DamageFade) + ("playerflags", PlayerFlags, def->PlayerFlags) + ("flechettetype", FlechetteType, def->FlechetteType) + ("gruntspeed", GruntSpeed, def->GruntSpeed) + ("fallingscreammin", FallingScreamMinSpeed, def->FallingScreamMinSpeed) + ("fallingscreammaxn", FallingScreamMaxSpeed, def->FallingScreamMaxSpeed) + ("userange", UseRange, def->UseRange) + ("aircapacity", AirCapacity, def->AirCapacity) + ("viewheight", ViewHeight, def->ViewHeight); } //=========================================================================== diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 0c57288f9..c390e6797 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -50,7 +50,7 @@ #include "d_netinf.h" #include "i_system.h" #include "d_player.h" -#include "farchive.h" +#include "serializer.h" // MACROS ------------------------------------------------------------------ @@ -2102,7 +2102,8 @@ class AAmbientSound : public AActor { DECLARE_CLASS (AAmbientSound, AActor) public: - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void MarkPrecacheSounds () const; void BeginPlay (); @@ -2125,10 +2126,11 @@ IMPLEMENT_CLASS (AAmbientSound) // //========================================================================== -void AAmbientSound::Serialize(FArchive &arc) +void AAmbientSound::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << bActive << NextCheck; + arc("active", bActive) + ("nextcheck", NextCheck); } //========================================================================== diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 37486164f..82e59cbb8 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -1,8 +1,11 @@ #ifdef COMMON_STUFF #include "a_doomglobal.h" +#include "a_hexenglobal.h" +#include "a_strifeglobal.h" #include "ravenshared.h" #include "a_weaponpiece.h" +#include "d_dehacked.h" // For NULL states, which aren't owned by any actor, the owner // is recorded as AActor with the following state. AActor should @@ -1037,6 +1040,304 @@ void AWeaponPiece::Serialize(FArchive &arc) arc << WeaponClass << FullWeapon << PieceValue; } +void DSectorEffect::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << m_Sector; +} + +void DMover::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << interpolation; +} + +class AAmbientSound : public AActor +{ + DECLARE_CLASS(AAmbientSound, AActor) +public: + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); + + void MarkPrecacheSounds() const; + void BeginPlay(); + void Tick(); + void Activate(AActor *activator); + void Deactivate(AActor *activator); + +protected: + bool bActive; +private: + void SetTicker(struct FAmbientSound *ambient); + int NextCheck; +}; + +void AAmbientSound::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << bActive << NextCheck; +} + +class AInterpolationPoint : public AActor +{ + DECLARE_CLASS(AInterpolationPoint, AActor) + HAS_OBJECT_POINTERS +public: + void BeginPlay(); + void HandleSpawnFlags(); + void Tick() {} // Nodes do no thinking + AInterpolationPoint *ScanForLoop(); + void FormChain(); + + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); + + TObjPtr Next; +}; + +class APathFollower : public AActor +{ + DECLARE_CLASS(APathFollower, AActor) + HAS_OBJECT_POINTERS +public: + void BeginPlay(); + void PostBeginPlay(); + void Tick(); + void Activate(AActor *activator); + void Deactivate(AActor *activator); +protected: + double Splerp(double p1, double p2, double p3, double p4); + double Lerp(double p1, double p2); + virtual bool Interpolate(); + virtual void NewNode(); + + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); + + bool bActive, bJustStepped; + TObjPtr PrevNode, CurrNode; + float Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode->Next + int HoldTime; +}; + +void AInterpolationPoint::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Next; +} + +void APathFollower::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << bActive << bJustStepped << PrevNode << CurrNode << Time << HoldTime; +} + +class AMovingCamera : public APathFollower +{ + DECLARE_CLASS(AMovingCamera, APathFollower) + HAS_OBJECT_POINTERS +public: + void PostBeginPlay(); + + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); +protected: + bool Interpolate(); + + TObjPtr Activator; +}; + +void AMovingCamera::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Activator; +} + +void AMorphProjectile::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << PlayerClass << MonsterClass << Duration << MorphStyle << MorphFlash << UnMorphFlash; +} + +void AMorphedMonster::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << UnmorphedMe << UnmorphTime << MorphStyle << MorphExitFlash << FlagsSave; +} + +void ADehackedPickup::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << droppedbymonster; +} + +class ASoundSequenceSlot : public AActor +{ + DECLARE_CLASS(ASoundSequenceSlot, AActor) + HAS_OBJECT_POINTERS +public: + DECLARE_OLD_SERIAL + + TObjPtr Sequence; +}; + +void ASoundSequenceSlot::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Sequence; +} + +void APlayerPawn::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + + arc << JumpZ + << MaxHealth + << RunHealth + << SpawnMask + << ForwardMove1 + << ForwardMove2 + << SideMove1 + << SideMove2 + << ScoreIcon + << InvFirst + << InvSel + << MorphWeapon + << DamageFade + << PlayerFlags + << FlechetteType; + arc << GruntSpeed << FallingScreamMinSpeed << FallingScreamMaxSpeed; + arc << UseRange; + arc << AirCapacity; + arc << ViewHeight; +} + +class APhoenixRod : public AWeapon +{ + DECLARE_CLASS(APhoenixRod, AWeapon) +public: + DECLARE_OLD_SERIAL + int FlameCount; // for flamethrower duration +}; + +void APhoenixRod::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << FlameCount; +} + +class ACWeapWraithverge : public AClericWeapon +{ + DECLARE_CLASS(ACWeapWraithverge, AClericWeapon) +public: + void Serialize(FArchive &arc); + BYTE CHolyCount; +}; + +void ACWeapWraithverge::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << CHolyCount; +} + +class AMWeapBloodscourge : public AMageWeapon +{ + DECLARE_CLASS(AMWeapBloodscourge, AMageWeapon) +public: + void Serialize(FArchive &arc); + BYTE MStaffCount; +}; + +void AMWeapBloodscourge::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << MStaffCount; +} + +void ASigil::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << NumPieces << DownPieces; +} + +class ASecurityCamera : public AActor +{ + DECLARE_CLASS(ASecurityCamera, AActor) +public: + void PostBeginPlay(); + void Tick(); + + void Serialize(FArchive &arc); +protected: + DAngle Center; + DAngle Acc; + DAngle Delta; + DAngle Range; +}; + +void ASecurityCamera::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << Center << Acc << Delta << Range; +} + + +class AAimingCamera : public ASecurityCamera +{ + DECLARE_CLASS(AAimingCamera, ASecurityCamera) +public: + void PostBeginPlay(); + void Tick(); + + void Serialize(FArchive &arc); +protected: + DAngle MaxPitchChange; +}; + +void AAimingCamera::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << MaxPitchChange; +} + +void APowerupGiver::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << PowerupType; + arc << EffectTics << BlendColor << Mode; + arc << Strength; +} +void APowerup::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << EffectTics << BlendColor << Mode; + arc << Strength; +} + +void APowerTorch::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << NewTorch << NewTorchDelta; +} + +void APowerFlight::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << HitCenterFrame; +} + +void APowerSpeed::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << SpeedFlags; +} + +void APowerMorph::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash; + arc << Player; +} #endif \ No newline at end of file From 340c7795f37858a30555b1ab6aa704745da0ea6a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 16:10:25 +0200 Subject: [PATCH 11/59] - clean out the dump. It's not like this branch is ever going zo be used for saving with the old code anyway. Only the stuff needed to not make it crash or fail on compilation is kept. --- src/zzz_old.cpp | 636 ------------------------------------------------ 1 file changed, 636 deletions(-) diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 82e59cbb8..5c6349b5c 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -1,4 +1,3 @@ -#ifdef COMMON_STUFF #include "a_doomglobal.h" #include "a_hexenglobal.h" @@ -706,638 +705,3 @@ void DObject::Serialize(FArchive &arc) } -void AScriptedMarine::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - - if (arc.IsStoring()) - { - arc.WriteSprite(SpriteOverride); - } - else - { - SpriteOverride = arc.ReadSprite(); - } - arc << CurrentWeapon; -} - - -class AHeresiarch : public AActor -{ - DECLARE_CLASS(AHeresiarch, AActor) -public: - PClassActor *StopBall; - DAngle BallAngle; - - DECLARE_OLD_SERIAL -}; - -void AHeresiarch::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << StopBall << BallAngle; -} - -class ASorcBall : public AActor -{ - DECLARE_CLASS(ASorcBall, AActor) -public: - DAngle AngleOffset; - DAngle OldAngle; - - DECLARE_OLD_SERIAL -}; - -void ASorcBall::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << AngleOffset << OldAngle; -} - -class AThrustFloor : public AActor -{ - DECLARE_CLASS(AThrustFloor, AActor) -public: - DECLARE_OLD_SERIAL - - TObjPtr DirtClump; -}; - -void AThrustFloor::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << DirtClump; -} - -void AMinotaurFriend::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << StartTime; -} - -class ARainTracker : public AInventory -{ - DECLARE_CLASS(ARainTracker, AInventory) -public: - DECLARE_OLD_SERIAL - TObjPtr Rain1, Rain2; -}; - -void ARainTracker::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Rain1 << Rain2; -} - -void AInventory::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Owner << Amount << MaxAmount << RespawnTics << ItemFlags << Icon << PickupSound << SpawnPointClass; -} - -void AHealthPickup::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << autousemode; -} - -void AAmmo::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << BackpackAmount << BackpackMaxAmount; -} - -void AWeapon::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << WeaponFlags - << AmmoType1 << AmmoType2 - << AmmoGive1 << AmmoGive2 - << MinAmmo1 << MinAmmo2 - << AmmoUse1 << AmmoUse2 - << Kickback - << YAdjust - << UpSound << ReadySound - << SisterWeaponType - << ProjectileType << AltProjectileType - << SelectionOrder - << MoveCombatDist - << Ammo1 << Ammo2 << SisterWeapon << GivenAsMorphWeapon - << bAltFire - << ReloadCounter - << BobStyle << BobSpeed << BobRangeX << BobRangeY - << FOVScale - << Crosshair - << MinSelAmmo1 << MinSelAmmo2; -} - -void ABackpackItem::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << bDepleted; -} - -void AWeaponGiver::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << DropAmmoFactor; -} - -void ABasicArmor::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << SavePercent << BonusCount << MaxAbsorb << MaxFullAbsorb << AbsorbCount << ArmorType << ActualSaveAmount; -} - -void ABasicArmorPickup::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << SavePercent << SaveAmount << MaxAbsorb << MaxFullAbsorb; - arc << DropTime;// in inventory! -} - -void ABasicArmorBonus::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << SavePercent << SaveAmount << MaxSaveAmount << BonusCount << BonusMax - << MaxAbsorb << MaxFullAbsorb; -} - -void AHexenArmor::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Slots[0] << Slots[1] << Slots[2] << Slots[3] - << Slots[4] - << SlotsIncrement[0] << SlotsIncrement[1] << SlotsIncrement[2] - << SlotsIncrement[3]; -} - -void APuzzleItem::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << PuzzleItemNumber; -} - -class DSectorPlaneInterpolation : public DInterpolation -{ - DECLARE_CLASS(DSectorPlaneInterpolation, DInterpolation) - - sector_t *sector; - double oldheight, oldtexz; - double bakheight, baktexz; - bool ceiling; - TArray attached; - - -public: - - DSectorPlaneInterpolation() {} - DSectorPlaneInterpolation(sector_t *sector, bool plane, bool attach); - void Destroy(); - void UpdateInterpolation(); - void Restore(); - void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL - virtual void Serialize(FSerializer &arc); - size_t PointerSubstitution(DObject *old, DObject *notOld); - size_t PropagateMark(); -}; - -//========================================================================== -// -// -// -//========================================================================== - -class DSectorScrollInterpolation : public DInterpolation -{ - DECLARE_CLASS(DSectorScrollInterpolation, DInterpolation) - - sector_t *sector; - double oldx, oldy; - double bakx, baky; - bool ceiling; - -public: - - DSectorScrollInterpolation() {} - DSectorScrollInterpolation(sector_t *sector, bool plane); - void Destroy(); - void UpdateInterpolation(); - void Restore(); - void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL - virtual void Serialize(FSerializer &arc); -}; - - -//========================================================================== -// -// -// -//========================================================================== - -class DWallScrollInterpolation : public DInterpolation -{ - DECLARE_CLASS(DWallScrollInterpolation, DInterpolation) - - side_t *side; - int part; - double oldx, oldy; - double bakx, baky; - -public: - - DWallScrollInterpolation() {} - DWallScrollInterpolation(side_t *side, int part); - void Destroy(); - void UpdateInterpolation(); - void Restore(); - void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL - virtual void Serialize(FSerializer &arc); -}; - -//========================================================================== -// -// -// -//========================================================================== - -class DPolyobjInterpolation : public DInterpolation -{ - DECLARE_CLASS(DPolyobjInterpolation, DInterpolation) - - FPolyObj *poly; - TArray oldverts, bakverts; - double oldcx, oldcy; - double bakcx, bakcy; - -public: - - DPolyobjInterpolation() {} - DPolyobjInterpolation(FPolyObj *poly); - void Destroy(); - void UpdateInterpolation(); - void Restore(); - void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL - virtual void Serialize(FSerializer &arc); -}; - -void DInterpolation::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << refcount; - if (arc.IsLoading()) - { - interpolator.AddInterpolation(this); - } -} -//========================================================================== -// -// -// -//========================================================================== - -void DSectorPlaneInterpolation::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << sector << ceiling << oldheight << oldtexz << attached; -} - -void DSectorScrollInterpolation::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << sector << ceiling << oldx << oldy; -} -void DWallScrollInterpolation::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << side << part << oldx << oldy; -} - -void DPolyobjInterpolation::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - int po = int(poly - polyobjs); - arc << po << oldverts; - poly = polyobjs + po; - - arc << oldcx << oldcy; - if (arc.IsLoading()) bakverts.Resize(oldverts.Size()); -} - -void AWeaponHolder::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << PieceMask << PieceWeapon; -} - -void AWeaponPiece::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << WeaponClass << FullWeapon << PieceValue; -} - -void DSectorEffect::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << m_Sector; -} - -void DMover::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << interpolation; -} - -class AAmbientSound : public AActor -{ - DECLARE_CLASS(AAmbientSound, AActor) -public: - DECLARE_OLD_SERIAL - void Serialize(FSerializer &arc); - - void MarkPrecacheSounds() const; - void BeginPlay(); - void Tick(); - void Activate(AActor *activator); - void Deactivate(AActor *activator); - -protected: - bool bActive; -private: - void SetTicker(struct FAmbientSound *ambient); - int NextCheck; -}; - -void AAmbientSound::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << bActive << NextCheck; -} - -class AInterpolationPoint : public AActor -{ - DECLARE_CLASS(AInterpolationPoint, AActor) - HAS_OBJECT_POINTERS -public: - void BeginPlay(); - void HandleSpawnFlags(); - void Tick() {} // Nodes do no thinking - AInterpolationPoint *ScanForLoop(); - void FormChain(); - - DECLARE_OLD_SERIAL - void Serialize(FSerializer &arc); - - TObjPtr Next; -}; - -class APathFollower : public AActor -{ - DECLARE_CLASS(APathFollower, AActor) - HAS_OBJECT_POINTERS -public: - void BeginPlay(); - void PostBeginPlay(); - void Tick(); - void Activate(AActor *activator); - void Deactivate(AActor *activator); -protected: - double Splerp(double p1, double p2, double p3, double p4); - double Lerp(double p1, double p2); - virtual bool Interpolate(); - virtual void NewNode(); - - DECLARE_OLD_SERIAL - void Serialize(FSerializer &arc); - - bool bActive, bJustStepped; - TObjPtr PrevNode, CurrNode; - float Time; // Runs from 0.0 to 1.0 between CurrNode and CurrNode->Next - int HoldTime; -}; - -void AInterpolationPoint::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Next; -} - -void APathFollower::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << bActive << bJustStepped << PrevNode << CurrNode << Time << HoldTime; -} - -class AMovingCamera : public APathFollower -{ - DECLARE_CLASS(AMovingCamera, APathFollower) - HAS_OBJECT_POINTERS -public: - void PostBeginPlay(); - - DECLARE_OLD_SERIAL - void Serialize(FSerializer &arc); -protected: - bool Interpolate(); - - TObjPtr Activator; -}; - -void AMovingCamera::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Activator; -} - -void AMorphProjectile::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << PlayerClass << MonsterClass << Duration << MorphStyle << MorphFlash << UnMorphFlash; -} - -void AMorphedMonster::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << UnmorphedMe << UnmorphTime << MorphStyle << MorphExitFlash << FlagsSave; -} - -void ADehackedPickup::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << droppedbymonster; -} - -class ASoundSequenceSlot : public AActor -{ - DECLARE_CLASS(ASoundSequenceSlot, AActor) - HAS_OBJECT_POINTERS -public: - DECLARE_OLD_SERIAL - - TObjPtr Sequence; -}; - -void ASoundSequenceSlot::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Sequence; -} - -void APlayerPawn::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - - arc << JumpZ - << MaxHealth - << RunHealth - << SpawnMask - << ForwardMove1 - << ForwardMove2 - << SideMove1 - << SideMove2 - << ScoreIcon - << InvFirst - << InvSel - << MorphWeapon - << DamageFade - << PlayerFlags - << FlechetteType; - arc << GruntSpeed << FallingScreamMinSpeed << FallingScreamMaxSpeed; - arc << UseRange; - arc << AirCapacity; - arc << ViewHeight; -} - -class APhoenixRod : public AWeapon -{ - DECLARE_CLASS(APhoenixRod, AWeapon) -public: - DECLARE_OLD_SERIAL - int FlameCount; // for flamethrower duration -}; - -void APhoenixRod::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << FlameCount; -} - -class ACWeapWraithverge : public AClericWeapon -{ - DECLARE_CLASS(ACWeapWraithverge, AClericWeapon) -public: - void Serialize(FArchive &arc); - BYTE CHolyCount; -}; - -void ACWeapWraithverge::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << CHolyCount; -} - -class AMWeapBloodscourge : public AMageWeapon -{ - DECLARE_CLASS(AMWeapBloodscourge, AMageWeapon) -public: - void Serialize(FArchive &arc); - BYTE MStaffCount; -}; - -void AMWeapBloodscourge::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << MStaffCount; -} - -void ASigil::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << NumPieces << DownPieces; -} - -class ASecurityCamera : public AActor -{ - DECLARE_CLASS(ASecurityCamera, AActor) -public: - void PostBeginPlay(); - void Tick(); - - void Serialize(FArchive &arc); -protected: - DAngle Center; - DAngle Acc; - DAngle Delta; - DAngle Range; -}; - -void ASecurityCamera::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << Center << Acc << Delta << Range; -} - - -class AAimingCamera : public ASecurityCamera -{ - DECLARE_CLASS(AAimingCamera, ASecurityCamera) -public: - void PostBeginPlay(); - void Tick(); - - void Serialize(FArchive &arc); -protected: - DAngle MaxPitchChange; -}; - -void AAimingCamera::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << MaxPitchChange; -} - -void APowerupGiver::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << PowerupType; - arc << EffectTics << BlendColor << Mode; - arc << Strength; -} -void APowerup::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << EffectTics << BlendColor << Mode; - arc << Strength; -} - -void APowerTorch::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << NewTorch << NewTorchDelta; -} - -void APowerFlight::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << HitCenterFrame; -} - -void APowerSpeed::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << SpeedFlags; -} - -void APowerMorph::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - arc << PlayerClass << MorphStyle << MorphFlash << UnMorphFlash; - arc << Player; -} - - -#endif \ No newline at end of file From e89d072abc840ce96296bbe7a1b08ecd6ebed306 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 19:14:30 +0200 Subject: [PATCH 12/59] - most thinkers are done. Some stuff about polyobject pointers is temporarily disabled right now because some of the required functions have already been pulled out. --- src/d_dehacked.h | 2 +- src/d_player.h | 2 +- src/dsectoreffect.h | 4 +- src/fragglescript/t_func.cpp | 11 ++-- src/g_doom/a_doomglobal.h | 2 +- src/g_heretic/a_hereticweaps.cpp | 4 +- src/g_hexen/a_clericholy.cpp | 2 +- src/g_hexen/a_heresiarch.cpp | 4 +- src/g_hexen/a_magestaff.cpp | 2 +- src/g_hexen/a_spike.cpp | 2 +- src/g_raven/ravenshared.h | 2 +- src/g_shared/a_artifacts.h | 12 ++--- src/g_shared/a_camera.cpp | 4 +- src/g_shared/a_movingcamera.cpp | 6 +-- src/g_shared/a_pickups.h | 22 ++++---- src/g_shared/a_sharedglobal.h | 4 +- src/g_shared/a_soundsequence.cpp | 2 +- src/g_shared/a_weaponpiece.h | 4 +- src/g_strife/a_strifeglobal.h | 2 +- src/p_ceiling.cpp | 54 ++++++------------- src/p_doors.cpp | 52 +++++++++--------- src/p_floor.cpp | 90 ++++++++++++-------------------- src/p_lights.cpp | 63 ++++++++++++++-------- src/p_pillar.cpp | 30 ++++------- src/p_plats.cpp | 39 +++++--------- src/p_sectors.cpp | 29 ++++------ src/p_spec.h | 16 +++--- src/po_man.cpp | 58 ++++++++------------ src/po_man.h | 4 +- src/r_data/r_interpolate.cpp | 8 +-- src/r_data/r_interpolate.h | 2 +- src/r_defs.h | 2 +- src/s_advsound.cpp | 2 +- src/s_sndseq.cpp | 2 +- src/s_sound.cpp | 2 +- src/serializer.cpp | 25 +++++---- src/serializer.h | 35 +++++++++---- src/textures/animations.cpp | 15 +++--- src/zzz_old.cpp | 11 ++++ 39 files changed, 290 insertions(+), 342 deletions(-) diff --git a/src/d_dehacked.h b/src/d_dehacked.h index e256718b6..564a6d499 100644 --- a/src/d_dehacked.h +++ b/src/d_dehacked.h @@ -48,7 +48,7 @@ public: bool TryPickup (AActor *&toucher); void PlayPickupSound (AActor *toucher); void DoPickupSpecial (AActor *toucher); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); private: PClassActor *DetermineType (); diff --git a/src/d_player.h b/src/d_player.h index 989b28691..f288f64f9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -102,7 +102,7 @@ class APlayerPawn : public AActor DECLARE_CLASS_WITH_META(APlayerPawn, AActor, PClassPlayerPawn) HAS_OBJECT_POINTERS public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void PostBeginPlay(); diff --git a/src/dsectoreffect.h b/src/dsectoreffect.h index 6008cb63f..0a5e9bd56 100644 --- a/src/dsectoreffect.h +++ b/src/dsectoreffect.h @@ -10,7 +10,7 @@ class DSectorEffect : public DThinker public: DSectorEffect (sector_t *sector); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Destroy(); @@ -33,7 +33,7 @@ protected: private: protected: DMover (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Destroy(); }; diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 7e4b933f1..a02ec947f 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -65,7 +65,7 @@ #include "v_palette.h" #include "v_font.h" #include "r_data/colormaps.h" -#include "farchive.h" +#include "serializer.h" #include "p_setup.h" #include "p_spec.h" #include "r_utility.h" @@ -1780,8 +1780,7 @@ class DLightLevel : public DLighting public: DLightLevel(sector_t * s,int destlevel,int speed); - void Serialize(FArchive &arc); - //void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; } }; @@ -1790,11 +1789,11 @@ public: IMPLEMENT_CLASS (DLightLevel) -void DLightLevel::Serialize(FArchive &arc) +void DLightLevel::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << destlevel << speed; - if (arc.IsLoading()) m_Sector->lightingdata=this; + arc("destlevel", destlevel) + ("speed", speed); } diff --git a/src/g_doom/a_doomglobal.h b/src/g_doom/a_doomglobal.h index e18917f01..31d41f6c0 100644 --- a/src/g_doom/a_doomglobal.h +++ b/src/g_doom/a_doomglobal.h @@ -29,7 +29,7 @@ public: void Tick (); void SetWeapon (EMarineWeapon); void SetSprite (PClassActor *source); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); int CurrentWeapon; diff --git a/src/g_heretic/a_hereticweaps.cpp b/src/g_heretic/a_hereticweaps.cpp index 34c77d316..eb7d49983 100644 --- a/src/g_heretic/a_hereticweaps.cpp +++ b/src/g_heretic/a_hereticweaps.cpp @@ -866,7 +866,7 @@ class ARainTracker : public AInventory { DECLARE_CLASS (ARainTracker, AInventory) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); TObjPtr Rain1, Rain2; }; @@ -1142,7 +1142,7 @@ class APhoenixRod : public AWeapon { DECLARE_CLASS (APhoenixRod, AWeapon) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); diff --git a/src/g_hexen/a_clericholy.cpp b/src/g_hexen/a_clericholy.cpp index 6a840edb5..b08ab3e74 100644 --- a/src/g_hexen/a_clericholy.cpp +++ b/src/g_hexen/a_clericholy.cpp @@ -31,7 +31,7 @@ class ACWeapWraithverge : public AClericWeapon { DECLARE_CLASS (ACWeapWraithverge, AClericWeapon) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); diff --git a/src/g_hexen/a_heresiarch.cpp b/src/g_hexen/a_heresiarch.cpp index 10f6168a9..c96c4cc1f 100644 --- a/src/g_hexen/a_heresiarch.cpp +++ b/src/g_hexen/a_heresiarch.cpp @@ -67,7 +67,7 @@ public: PClassActor *StopBall; DAngle BallAngle; - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); }; @@ -107,7 +107,7 @@ public: DAngle AngleOffset; DAngle OldAngle; - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { diff --git a/src/g_hexen/a_magestaff.cpp b/src/g_hexen/a_magestaff.cpp index c05206d32..d7b06d009 100644 --- a/src/g_hexen/a_magestaff.cpp +++ b/src/g_hexen/a_magestaff.cpp @@ -29,7 +29,7 @@ class AMWeapBloodscourge : public AMageWeapon { DECLARE_CLASS (AMWeapBloodscourge, AMageWeapon) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc) { Super::Serialize (arc); diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index 3be6a3101..9f7396399 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -22,7 +22,7 @@ class AThrustFloor : public AActor DECLARE_CLASS (AThrustFloor, AActor) HAS_OBJECT_POINTERS public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Activate (AActor *activator); diff --git a/src/g_raven/ravenshared.h b/src/g_raven/ravenshared.h index dd5b74781..81824a957 100644 --- a/src/g_raven/ravenshared.h +++ b/src/g_raven/ravenshared.h @@ -23,7 +23,7 @@ public: void Die (AActor *source, AActor *inflictor, int dmgflags); bool OkayToSwitchTarget (AActor *other); void BeginPlay (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); }; diff --git a/src/g_shared/a_artifacts.h b/src/g_shared/a_artifacts.h index 97aaad0d3..85ebc2e81 100644 --- a/src/g_shared/a_artifacts.h +++ b/src/g_shared/a_artifacts.h @@ -16,7 +16,7 @@ public: virtual bool HandlePickup (AInventory *item); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void OwnerDied (); virtual bool GetNoTeleportFreeze(); @@ -52,7 +52,7 @@ class APowerupGiver : public AInventory DECLARE_CLASS_WITH_META (APowerupGiver, AInventory, PClassPowerupGiver) public: virtual bool Use (bool pickup); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); @@ -123,7 +123,7 @@ class APowerTorch : public APowerLightAmp { DECLARE_CLASS (APowerTorch, APowerLightAmp) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); protected: void DoEffect (); @@ -135,7 +135,7 @@ class APowerFlight : public APowerup DECLARE_CLASS (APowerFlight, APowerup) public: bool DrawPowerup (int x, int y); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); protected: @@ -159,7 +159,7 @@ class APowerSpeed : public APowerup DECLARE_CLASS (APowerSpeed, APowerup) protected: void DoEffect (); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); double GetSpeedFactor(); public: @@ -277,7 +277,7 @@ class APowerMorph : public APowerup { DECLARE_CLASS( APowerMorph, APowerup ) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); void SetNoCallUndoMorph() { bNoCallUndoMorph = true; } diff --git a/src/g_shared/a_camera.cpp b/src/g_shared/a_camera.cpp index b8f4730ad..fb3d81533 100644 --- a/src/g_shared/a_camera.cpp +++ b/src/g_shared/a_camera.cpp @@ -55,7 +55,7 @@ public: void PostBeginPlay (); void Tick (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: DAngle Center; @@ -118,7 +118,7 @@ public: void PostBeginPlay (); void Tick (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: DAngle MaxPitchChange; diff --git a/src/g_shared/a_movingcamera.cpp b/src/g_shared/a_movingcamera.cpp index 72ceef268..76f128b15 100644 --- a/src/g_shared/a_movingcamera.cpp +++ b/src/g_shared/a_movingcamera.cpp @@ -60,7 +60,7 @@ public: AInterpolationPoint *ScanForLoop (); void FormChain (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); TObjPtr Next; @@ -167,7 +167,7 @@ protected: virtual bool Interpolate (); virtual void NewNode (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool bActive, bJustStepped; @@ -584,7 +584,7 @@ class AMovingCamera : public APathFollower public: void PostBeginPlay (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); protected: bool Interpolate (); diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index 303cd3431..9dba52639 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -153,7 +153,7 @@ class AInventory : public AActor HAS_OBJECT_POINTERS public: virtual void Touch (AActor *toucher); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void MarkPrecacheSounds() const; @@ -255,7 +255,7 @@ class AAmmo : public AInventory { DECLARE_CLASS_WITH_META(AAmmo, AInventory, PClassAmmo) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); AInventory *CreateCopy (AActor *other); bool HandlePickup (AInventory *item); @@ -313,7 +313,7 @@ public: bool bAltFire; // Set when this weapon's alternate fire is used. virtual void MarkPrecacheSounds() const; - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual bool ShouldStay (); virtual void AttachToOwner (AActor *other); @@ -398,7 +398,7 @@ class AWeaponGiver : public AWeapon public: bool TryPickup(AActor *&toucher); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); double DropAmmoFactor; @@ -435,7 +435,7 @@ class AHealthPickup : public AInventory public: int autousemode; - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); @@ -456,7 +456,7 @@ class ABasicArmor : public AArmor { DECLARE_CLASS (ABasicArmor, AArmor) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual void Tick (); virtual AInventory *CreateCopy (AActor *other); @@ -477,7 +477,7 @@ class ABasicArmorPickup : public AArmor { DECLARE_CLASS (ABasicArmorPickup, AArmor) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -493,7 +493,7 @@ class ABasicArmorBonus : public AArmor { DECLARE_CLASS (ABasicArmorBonus, AArmor) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual bool Use (bool pickup); @@ -513,7 +513,7 @@ class AHexenArmor : public AArmor { DECLARE_CLASS (AHexenArmor, AArmor) public: - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); virtual AInventory *CreateCopy (AActor *other); virtual AInventory *CreateTossable (); @@ -542,7 +542,7 @@ class APuzzleItem : public AInventory { DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem) public: - DECLARE_OLD_SERIAL + bool ShouldStay (); bool Use (bool pickup); bool HandlePickup (AInventory *item); @@ -564,7 +564,7 @@ class ABackpackItem : public AInventory { DECLARE_CLASS (ABackpackItem, AInventory) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 1a03c6767..dd332fae1 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -194,7 +194,7 @@ class AMorphProjectile : public AActor DECLARE_CLASS (AMorphProjectile, AActor) public: int DoSpecialDamage (AActor *target, int damage, FName damagetype); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); FNameNoInit PlayerClass, MonsterClass, MorphFlash, UnMorphFlash; @@ -207,7 +207,7 @@ class AMorphedMonster : public AActor HAS_OBJECT_POINTERS public: void Tick (); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Die (AActor *source, AActor *inflictor, int dmgflags); void Destroy (); diff --git a/src/g_shared/a_soundsequence.cpp b/src/g_shared/a_soundsequence.cpp index d824b9678..f09b5b7ad 100644 --- a/src/g_shared/a_soundsequence.cpp +++ b/src/g_shared/a_soundsequence.cpp @@ -74,7 +74,7 @@ class ASoundSequenceSlot : public AActor DECLARE_CLASS (ASoundSequenceSlot, AActor) HAS_OBJECT_POINTERS public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); TObjPtr Sequence; diff --git a/src/g_shared/a_weaponpiece.h b/src/g_shared/a_weaponpiece.h index 0ab11a80f..88f3da02c 100644 --- a/src/g_shared/a_weaponpiece.h +++ b/src/g_shared/a_weaponpiece.h @@ -15,7 +15,7 @@ class AWeaponPiece : public AInventory protected: bool PrivateShouldStay (); public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool TryPickup (AActor *&toucher); bool TryPickupRestricted (AActor *&toucher); @@ -38,6 +38,6 @@ public: int PieceMask; PClassActor * PieceWeapon; - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); }; diff --git a/src/g_strife/a_strifeglobal.h b/src/g_strife/a_strifeglobal.h index 30a14e91b..9e7f9148e 100644 --- a/src/g_strife/a_strifeglobal.h +++ b/src/g_strife/a_strifeglobal.h @@ -55,7 +55,7 @@ class ASigil : public AWeapon public: bool HandlePickup (AInventory *item); AInventory *CreateCopy (AActor *other); - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); bool SpecialDropAction (AActor *dropper); static int GiveSigilPiece (AActor *daPlayer); diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 7590234fc..32e5d62be 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -29,31 +29,9 @@ #include "doomstat.h" #include "r_state.h" #include "gi.h" -#include "farchive.h" +#include "serializer.h" #include "p_spec.h" -//============================================================================ -// -// -// -//============================================================================ - -inline FArchive &operator<< (FArchive &arc, DCeiling::ECeiling &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DCeiling::ECeiling)val; - return arc; -} - -inline FArchive &operator<< (FArchive &arc, DCeiling::ECrushMode &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DCeiling::ECrushMode)val; - return arc; -} - //============================================================================ // // CEILINGS @@ -72,23 +50,23 @@ DCeiling::DCeiling () // //============================================================================ -void DCeiling::Serialize(FArchive &arc) +void DCeiling::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_BottomHeight - << m_TopHeight - << m_Speed - << m_Speed1 - << m_Speed2 - << m_Crush - << m_Silent - << m_Direction - << m_Texture - << m_NewSpecial - << m_Tag - << m_OldDirection - << m_CrushMode; + arc.Enum("type", m_Type) + ("bottomheight", m_BottomHeight) + ("topheight", m_TopHeight) + ("speed", m_Speed) + ("speed1", m_Speed1) + ("speed2", m_Speed2) + ("crush", m_Crush) + ("silent", m_Silent) + ("direction", m_Direction) + ("texture", m_Texture) + ("newspecial", m_NewSpecial) + ("tag", m_Tag) + ("olddirecton", m_OldDirection) + .Enum("crushmode", m_CrushMode); } //============================================================================ diff --git a/src/p_doors.cpp b/src/p_doors.cpp index a614db6a5..33e37218d 100644 --- a/src/p_doors.cpp +++ b/src/p_doors.cpp @@ -35,7 +35,7 @@ #include "i_system.h" #include "sc_man.h" #include "cmdlib.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" #include "p_spec.h" @@ -47,29 +47,23 @@ IMPLEMENT_CLASS (DDoor) -inline FArchive &operator<< (FArchive &arc, DDoor::EVlDoor &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DDoor::EVlDoor)val; - return arc; -} - DDoor::DDoor () { } -void DDoor::Serialize(FArchive &arc) +void DDoor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_TopDist - << m_BotSpot << m_BotDist << m_OldFloorDist - << m_Speed - << m_Direction - << m_TopWait - << m_TopCountdown - << m_LightTag; + arc.Enum("type", m_Type) + ("topdist", m_TopDist) + ("botspot", m_BotSpot) + ("botdist", m_BotDist) + ("oldfloordist", m_OldFloorDist) + ("speed", m_Speed) + ("direction", m_Direction) + ("topwait", m_TopWait) + ("topcountdown", m_TopCountdown) + ("lighttag", m_LightTag); } //============================================================================ @@ -530,19 +524,21 @@ DAnimatedDoor::DAnimatedDoor (sector_t *sec) { } -void DAnimatedDoor::Serialize(FArchive &arc) +void DAnimatedDoor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Line1 << m_Line2 - << m_Frame - << m_Timer - << m_BotDist - << m_Status - << m_Speed - << m_Delay - << m_DoorAnim - << m_SetBlocking1 << m_SetBlocking2; + arc("line1", m_Line1) + ("line2", m_Line2) + ("frame", m_Frame) + ("timer", m_Timer) + ("botdist", m_BotDist) + ("status", m_Status) + ("speed", m_Speed) + ("delay", m_Delay) + ("dooranim", m_DoorAnim) + ("setblock1", m_SetBlocking1) + ("setblock2", m_SetBlocking2); } //============================================================================ diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 67cc6efa7..db4887ab8 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -28,7 +28,7 @@ #include "s_sndseq.h" #include "doomstat.h" #include "r_state.h" -#include "farchive.h" +#include "serializer.h" #include "p_3dmidtex.h" #include "p_spec.h" #include "r_data/r_interpolate.h" @@ -39,20 +39,6 @@ // //========================================================================== -inline FArchive &operator<< (FArchive &arc, DFloor::EFloor &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DFloor::EFloor)val; - return arc; -} - -//========================================================================== -// -// -// -//========================================================================== - static void StartFloorSound (sector_t *sec) { if (sec->Flags & SECF_SILENTMOVE) return; @@ -84,23 +70,23 @@ DFloor::DFloor () { } -void DFloor::Serialize(FArchive &arc) +void DFloor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_Crush - << m_Direction - << m_NewSpecial - << m_Texture - << m_FloorDestDist - << m_Speed - << m_ResetCount - << m_OrgDist - << m_Delay - << m_PauseTime - << m_StepTime - << m_PerStepTime - << m_Hexencrush; + arc.Enum("type", m_Type) + ("crush", m_Crush) + ("direction", m_Direction) + ("newspecial", m_NewSpecial) + ("texture", m_Texture) + ("floordestdist", m_FloorDestDist) + ("speed", m_Speed) + ("resetcount", m_ResetCount) + ("orgdist", m_OrgDist) + ("delay", m_Delay) + ("pausetime", m_PauseTime) + ("steptime", m_StepTime) + ("persteptime", m_PerStepTime) + ("crushmode", m_Hexencrush); } //========================================================================== @@ -834,14 +820,6 @@ IMPLEMENT_POINTY_CLASS (DElevator) DECLARE_POINTER(m_Interp_Ceiling) END_POINTERS -inline FArchive &operator<< (FArchive &arc, DElevator::EElevator &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DElevator::EElevator)val; - return arc; -} - DElevator::DElevator () { } @@ -855,16 +833,16 @@ DElevator::DElevator (sector_t *sec) m_Interp_Ceiling = sec->SetInterpolation(sector_t::CeilingMove, true); } -void DElevator::Serialize(FArchive &arc) +void DElevator::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_Direction - << m_FloorDestDist - << m_CeilingDestDist - << m_Speed - << m_Interp_Floor - << m_Interp_Ceiling; + arc.Enum("type", m_Type) + ("direction", m_Direction) + ("floordestdist", m_FloorDestDist) + ("ceilingdestdist", m_CeilingDestDist) + ("speed", m_Speed) + ("interp_floor", m_Interp_Floor) + ("interp_ceiling", m_Interp_Ceiling); } //========================================================================== @@ -1136,18 +1114,18 @@ DWaggleBase::DWaggleBase () { } -void DWaggleBase::Serialize(FArchive &arc) +void DWaggleBase::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_OriginalDist - << m_Accumulator - << m_AccDelta - << m_TargetScale - << m_Scale - << m_ScaleDelta - << m_Ticker - << m_State - << m_Interpolation; + arc("originaldist", m_OriginalDist) + ("accumulator", m_Accumulator) + ("accdelta", m_AccDelta) + ("targetscale", m_TargetScale) + ("scale", m_Scale) + ("scaledelta", m_ScaleDelta) + ("ticker", m_Ticker) + ("state", m_State) + ("interpolation", m_Interpolation); } //========================================================================== diff --git a/src/p_lights.cpp b/src/p_lights.cpp index 59e21fce6..fb3ab339e 100644 --- a/src/p_lights.cpp +++ b/src/p_lights.cpp @@ -35,7 +35,7 @@ // State. #include "r_state.h" #include "statnums.h" -#include "farchive.h" +#include "serializer.h" static FRandom pr_flicker ("Flicker"); static FRandom pr_lightflash ("LightFlash"); @@ -49,7 +49,7 @@ class DFireFlicker : public DLighting public: DFireFlicker(sector_t *sector); DFireFlicker(sector_t *sector, int upper, int lower); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_Count; @@ -64,7 +64,7 @@ class DFlicker : public DLighting DECLARE_CLASS(DFlicker, DLighting) public: DFlicker(sector_t *sector, int upper, int lower); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_Count; @@ -80,7 +80,7 @@ class DLightFlash : public DLighting public: DLightFlash(sector_t *sector); DLightFlash(sector_t *sector, int min, int max); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_Count; @@ -98,7 +98,7 @@ class DStrobe : public DLighting public: DStrobe(sector_t *sector, int utics, int ltics, bool inSync); DStrobe(sector_t *sector, int upper, int lower, int utics, int ltics); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_Count; @@ -115,7 +115,7 @@ class DGlow : public DLighting DECLARE_CLASS(DGlow, DLighting) public: DGlow(sector_t *sector); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_MinLight; @@ -131,7 +131,7 @@ class DGlow2 : public DLighting DECLARE_CLASS(DGlow2, DLighting) public: DGlow2(sector_t *sector, int start, int end, int tics, bool oneshot); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: int m_Start; @@ -150,7 +150,7 @@ class DPhased : public DLighting public: DPhased(sector_t *sector); DPhased(sector_t *sector, int baselevel, int phase); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: BYTE m_BaseLevel; @@ -197,10 +197,12 @@ DFireFlicker::DFireFlicker () { } -void DFireFlicker::Serialize(FArchive &arc) +void DFireFlicker::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Count << m_MaxLight << m_MinLight; + arc("count", m_Count) + ("maxlight", m_MaxLight) + ("minlight", m_MinLight); } @@ -262,10 +264,12 @@ DFlicker::DFlicker () { } -void DFlicker::Serialize(FArchive &arc) +void DFlicker::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Count << m_MaxLight << m_MinLight; + arc("count", m_Count) + ("maxlight", m_MaxLight) + ("minlight", m_MinLight); } //----------------------------------------------------------------------------- @@ -336,10 +340,14 @@ DLightFlash::DLightFlash () { } -void DLightFlash::Serialize(FArchive &arc) +void DLightFlash::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Count << m_MaxLight << m_MaxTime << m_MinLight << m_MinTime; + arc("count", m_Count) + ("maxlight", m_MaxLight) + ("minlight", m_MinLight) + ("maxtime", m_MaxTime) + ("mintime", m_MinTime); } //----------------------------------------------------------------------------- @@ -407,10 +415,14 @@ DStrobe::DStrobe () { } -void DStrobe::Serialize(FArchive &arc) +void DStrobe::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Count << m_MaxLight << m_MinLight << m_DarkTime << m_BrightTime; + arc("count", m_Count) + ("maxlight", m_MaxLight) + ("minlight", m_MinLight) + ("darktime", m_DarkTime) + ("brighttime", m_BrightTime); } //----------------------------------------------------------------------------- @@ -661,10 +673,12 @@ DGlow::DGlow () { } -void DGlow::Serialize(FArchive &arc) +void DGlow::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Direction << m_MaxLight << m_MinLight; + arc("direction", m_Direction) + ("maxlight", m_MaxLight) + ("minlight", m_MinLight); } //----------------------------------------------------------------------------- @@ -728,10 +742,14 @@ DGlow2::DGlow2 () { } -void DGlow2::Serialize(FArchive &arc) +void DGlow2::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_End << m_MaxTics << m_OneShot << m_Start << m_Tics; + arc("end", m_End) + ("maxtics", m_MaxTics) + ("oneshot", m_OneShot) + ("start", m_Start) + ("tics", m_Tics); } //----------------------------------------------------------------------------- @@ -857,10 +875,11 @@ DPhased::DPhased () { } -void DPhased::Serialize(FArchive &arc) +void DPhased::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_BaseLevel << m_Phase; + arc("baselevel", m_BaseLevel) + ("phase", m_Phase); } //----------------------------------------------------------------------------- diff --git a/src/p_pillar.cpp b/src/p_pillar.cpp index e68187b4c..a55bd5cdb 100644 --- a/src/p_pillar.cpp +++ b/src/p_pillar.cpp @@ -37,7 +37,7 @@ #include "p_spec.h" #include "g_level.h" #include "s_sndseq.h" -#include "farchive.h" +#include "serializer.h" #include "r_data/r_interpolate.h" IMPLEMENT_POINTY_CLASS (DPillar) @@ -45,14 +45,6 @@ IMPLEMENT_POINTY_CLASS (DPillar) DECLARE_POINTER(m_Interp_Ceiling) END_POINTERS -inline FArchive &operator<< (FArchive &arc, DPillar::EPillar &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DPillar::EPillar)val; - return arc; -} - DPillar::DPillar () { } @@ -72,18 +64,18 @@ void DPillar::Destroy() Super::Destroy(); } -void DPillar::Serialize(FArchive &arc) +void DPillar::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_FloorSpeed - << m_CeilingSpeed - << m_FloorTarget - << m_CeilingTarget - << m_Crush - << m_Hexencrush - << m_Interp_Floor - << m_Interp_Ceiling; + arc.Enum("type", m_Type) + ("floorspeed", m_FloorSpeed) + ("ceilingspeed", m_CeilingSpeed) + ("floortarget", m_FloorTarget) + ("ceilingtarget", m_CeilingTarget) + ("crush", m_Crush) + ("hexencrush", m_Hexencrush) + ("interp_floor", m_Interp_Floor) + ("interp_ceiling", m_Interp_Ceiling); } void DPillar::Tick () diff --git a/src/p_plats.cpp b/src/p_plats.cpp index dec3894b0..868f47138 100644 --- a/src/p_plats.cpp +++ b/src/p_plats.cpp @@ -30,45 +30,30 @@ #include "doomstat.h" #include "r_state.h" #include "gi.h" -#include "farchive.h" +#include "serializer.h" #include "p_spec.h" static FRandom pr_doplat ("DoPlat"); IMPLEMENT_CLASS (DPlat) -inline FArchive &operator<< (FArchive &arc, DPlat::EPlatType &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DPlat::EPlatType)val; - return arc; -} -inline FArchive &operator<< (FArchive &arc, DPlat::EPlatState &state) -{ - BYTE val = (BYTE)state; - arc << val; - state = (DPlat::EPlatState)val; - return arc; -} - DPlat::DPlat () { } -void DPlat::Serialize(FArchive &arc) +void DPlat::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Speed - << m_Low - << m_High - << m_Wait - << m_Count - << m_Status - << m_OldStatus - << m_Crush - << m_Tag - << m_Type; + arc.Enum("type", m_Type) + ("speed", m_Speed) + ("low", m_Low) + ("high", m_High) + ("wait", m_Wait) + ("count", m_Count) + .Enum("status", m_Status) + .Enum("oldstatus", m_OldStatus) + ("crush", m_Crush) + ("tag", m_Tag); } void DPlat::PlayPlatSound (const char *sound) diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index ed8087543..eb1742aa8 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -28,7 +28,7 @@ #include "nodebuild.h" #include "p_terrain.h" #include "po_man.h" -#include "farchive.h" +#include "serializer.h" #include "r_utility.h" #include "a_sharedglobal.h" #include "p_local.h" @@ -1053,14 +1053,14 @@ double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, doub // //=========================================================================== -FArchive &operator<< (FArchive &arc, secspecial_t &p) -{ - arc << p.special - << p.damageamount - << p.damagetype - << p.damageinterval - << p.leakydamage - << p.Flags; + FSerializer &Serialize(FSerializer &arc, const char *key, secspecial_t &spec, secspecial_t *def) + { + arc("special", spec.special) + ("damageamount", spec.damageamount) + ("damagetype", spec.damagetype) + ("damageinterval", spec.damageinterval) + ("leakydamage", spec.leakydamage) + ("flags", spec.Flags); return arc; } @@ -1101,17 +1101,6 @@ bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) cons return copy; } -FArchive &operator<< (FArchive &arc, secplane_t &plane) -{ - arc << plane.normal << plane.D; - if (plane.normal.Z != 0) - { // plane.c should always be non-0. Otherwise, the plane - // would be perfectly vertical. (But then, don't let this crash on a broken savegame...) - plane.negiC = -1 / plane.normal.Z; - } - return arc; -} - //========================================================================== // // P_AlignFlat diff --git a/src/p_spec.h b/src/p_spec.h index 0e1b9a142..23237bb15 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -184,7 +184,7 @@ public: platRaiseAndStayLockout, }; - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); bool IsLift() const { return m_Type == platDownWaitUpStay || m_Type == platDownWaitUpStayStone; } @@ -241,7 +241,7 @@ public: DPillar (sector_t *sector, EPillar type, double speed, double height, double height2, int crush, bool hexencrush); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); void Destroy(); @@ -283,7 +283,7 @@ public: DDoor (sector_t *sector); DDoor (sector_t *sec, EVlDoor type, double speed, int delay, int lightTag, int topcountdown); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: EVlDoor m_Type; @@ -325,7 +325,7 @@ public: DAnimatedDoor (sector_t *sector); DAnimatedDoor (sector_t *sec, line_t *line, int speed, int delay, FDoorAnimation *anim); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); bool StartClosing (); @@ -405,7 +405,7 @@ public: DCeiling (sector_t *sec); DCeiling (sector_t *sec, double speed1, double speed2, int silent); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: @@ -508,7 +508,7 @@ public: DFloor (sector_t *sec); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); //protected: @@ -573,7 +573,7 @@ public: DElevator (sector_t *sec); void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: @@ -601,7 +601,7 @@ class DWaggleBase : public DMover public: DWaggleBase (sector_t *sec); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); protected: double m_OriginalDist; diff --git a/src/po_man.cpp b/src/po_man.cpp index f6b1ec2b6..2eed96596 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -27,7 +27,7 @@ #include "po_man.h" #include "p_setup.h" #include "vectors.h" -#include "farchive.h" +#include "serializer.h" #include "p_blockmap.h" #include "p_maputl.h" #include "r_utility.h" @@ -50,25 +50,6 @@ inline vertex_t *side_t::V2() const } -FArchive &operator<< (FArchive &arc, FPolyObj *&poly) -{ - return arc.SerializePointer (polyobjs, (BYTE **)&poly, sizeof(FPolyObj)); -} - -FArchive &operator<< (FArchive &arc, const FPolyObj *&poly) -{ - return arc.SerializePointer (polyobjs, (BYTE **)&poly, sizeof(FPolyObj)); -} - -inline FArchive &operator<< (FArchive &arc, podoortype_t &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (podoortype_t)val; - return arc; -} - - class DRotatePoly : public DPolyAction { DECLARE_CLASS (DRotatePoly, DPolyAction) @@ -87,7 +68,7 @@ class DMovePoly : public DPolyAction DECLARE_CLASS (DMovePoly, DPolyAction) public: DMovePoly (int polyNum); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: DMovePoly (); @@ -102,7 +83,7 @@ class DMovePolyTo : public DPolyAction DECLARE_CLASS(DMovePolyTo, DPolyAction) public: DMovePolyTo(int polyNum); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); protected: DMovePolyTo(); @@ -118,7 +99,7 @@ class DPolyDoor : public DMovePoly DECLARE_CLASS (DPolyDoor, DMovePoly) public: DPolyDoor (int polyNum, podoortype_t type); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: DAngle m_Direction; @@ -199,10 +180,13 @@ DPolyAction::DPolyAction () { } -void DPolyAction::Serialize(FArchive &arc) +void DPolyAction::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_PolyObj << m_Speed << m_Dist << m_Interpolation; + arc("polyobj", m_PolyObj) + ("speed", m_Speed) + ("dist", m_Dist) + ("interpolation", m_Interpolation); } DPolyAction::DPolyAction (int polyNum) @@ -277,14 +261,12 @@ DMovePoly::DMovePoly () { } -void DMovePoly::Serialize(FArchive &arc) +void DMovePoly::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Angle << m_Speed; - if (SaveVersion >= 4548) - { - arc << m_Speedv; - } + arc("angle", m_Angle) + ("speed", m_Speed); + ("speedv", m_Speedv); } DMovePoly::DMovePoly (int polyNum) @@ -307,10 +289,11 @@ DMovePolyTo::DMovePolyTo() { } -void DMovePolyTo::Serialize(FArchive &arc) +void DMovePolyTo::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << m_Speedv << m_Target; + arc("speedv", m_Speedv) + ("target", m_Target); } DMovePolyTo::DMovePolyTo(int polyNum) @@ -331,10 +314,15 @@ DPolyDoor::DPolyDoor () { } -void DPolyDoor::Serialize(FArchive &arc) +void DPolyDoor::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Direction << m_TotalDist << m_Tics << m_WaitTics << m_Type << m_Close; + arc.Enum("type", m_Type) + ("direction", m_Direction) + ("totaldist", m_TotalDist) + ("tics", m_Tics) + ("waittics", m_WaitTics) + ("close", m_Close); } DPolyDoor::DPolyDoor (int polyNum, podoortype_t type) diff --git a/src/po_man.h b/src/po_man.h index 18b01d9cc..ec6a3901b 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -11,7 +11,7 @@ class DPolyAction : public DThinker HAS_OBJECT_POINTERS public: DPolyAction(int polyNum); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Destroy(); void Stop(); double GetSpeed() const { return m_Speed; } @@ -126,8 +126,6 @@ struct polyblock_t void PO_LinkToSubsectors(); -FArchive &operator<< (FArchive &arc, FPolyObj *&poly); -FArchive &operator<< (FArchive &arc, const FPolyObj *&poly); // ===== PO_MAN ===== diff --git a/src/r_data/r_interpolate.cpp b/src/r_data/r_interpolate.cpp index 38cb7a7a5..df8773879 100644 --- a/src/r_data/r_interpolate.cpp +++ b/src/r_data/r_interpolate.cpp @@ -66,7 +66,7 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); size_t PointerSubstitution (DObject *old, DObject *notOld); size_t PropagateMark(); @@ -95,7 +95,7 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; @@ -123,7 +123,7 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; @@ -150,7 +150,7 @@ public: void UpdateInterpolation(); void Restore(); void Interpolate(double smoothratio); - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; diff --git a/src/r_data/r_interpolate.h b/src/r_data/r_interpolate.h index 8fc98c44c..092cbe5ee 100644 --- a/src/r_data/r_interpolate.h +++ b/src/r_data/r_interpolate.h @@ -31,7 +31,7 @@ public: virtual void UpdateInterpolation() = 0; virtual void Restore() = 0; virtual void Interpolate(double smoothratio) = 0; - DECLARE_OLD_SERIAL + virtual void Serialize(FSerializer &arc); }; diff --git a/src/r_defs.h b/src/r_defs.h index de8bd2c95..4a0f038de 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -591,7 +591,7 @@ struct secspecial_t } }; -FArchive &operator<< (FArchive &arc, secspecial_t &p); +FSerializer &Serialize(FSerializer &arc, const char *key, secspecial_t &spec, secspecial_t *def); enum class EMoveResult { ok, crushed, pastdest }; diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c390e6797..b073070fd 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -2102,7 +2102,7 @@ class AAmbientSound : public AActor { DECLARE_CLASS (AAmbientSound, AActor) public: - DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void MarkPrecacheSounds () const; diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index cb700621c..ebfbf056b 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -436,7 +436,7 @@ IMPLEMENT_CLASS (DSeqPolyNode) void DSeqPolyNode::Serialize(FArchive &arc) { Super::Serialize (arc); - arc << m_Poly; + //arc << m_Poly; } IMPLEMENT_CLASS (DSeqSectorNode) diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 949dee7f8..3b01b519e 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2239,7 +2239,7 @@ static FArchive &operator<<(FArchive &arc, FSoundChan &chan) case SOURCE_None: break; case SOURCE_Actor: arc << chan.Actor; break; case SOURCE_Sector: arc << chan.Sector; break; - case SOURCE_Polyobj: arc << chan.Poly; break; + case SOURCE_Polyobj: /*arc << chan.Poly;*/ break; case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break; default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; } diff --git a/src/serializer.cpp b/src/serializer.cpp index d675a3cf9..9cf156056 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -805,31 +805,36 @@ FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T ** return arc; } -FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval) { return SerializePointer(arc, key, value, defval, polyobjs); } -FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) { return SerializePointer(arc, key, value, defval, sides); } -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval) { return SerializePointer(arc, key, value, defval, sectors); } -FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) { return SerializePointer(arc, key, value, defval, players); } -FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval) { return SerializePointer(arc, key, value, defval, lines); } +template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval) +{ + return SerializePointer(arc, key, value, defval, vertexes); +} + //========================================================================== // // @@ -988,7 +993,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def) { if (arc.isWriting()) { @@ -1079,7 +1084,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def) { if (arc.isWriting()) { @@ -1114,7 +1119,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PC // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def) { if (arc.isWriting()) { @@ -1183,7 +1188,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def) { if (arc.isWriting()) { @@ -1236,7 +1241,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *& // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def) { if (arc.isWriting()) { diff --git a/src/serializer.h b/src/serializer.h index 5a87c63fc..73b830b2d 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -2,6 +2,7 @@ #define __SERIALIZER_H #include +#include #include "tarray.h" #include "r_defs.h" @@ -104,6 +105,15 @@ public: } return *this; } + + template + FSerializer &Enum(const char *key, T &obj) + { + auto val = (std::underlying_type::type)obj; + Serialize(*this, key, val, nullptr); + obj = (T)val; + return *this; + } }; FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval); @@ -117,21 +127,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int16_t &value, int16_ FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint16_t *defval); FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval); FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval); -FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval); -FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval); -FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval); -FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval); -FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval); FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval); FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/); FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval); -FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def); FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def); -FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); -FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def); -FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDialogueNode *&node, FStrifeDialogueNode **def); -FSerializer &Serialize(FSerializer &arc, const char *key, FString *&pstr, FString **def); - template FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) @@ -174,6 +173,20 @@ 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, 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, FDynamicColormap *&cm, FDynamicColormap **def); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **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); + + inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def) { return arc.Array(key, &p[0], def? &(*def)[0] : nullptr, 3, true); diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index 76ad48443..a2b1c1319 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -44,6 +44,7 @@ #include "w_wad.h" #include "g_level.h" #include "farchive.h" +#include "serializer.h" // MACROS ------------------------------------------------------------------ @@ -990,17 +991,13 @@ void FTextureManager::UpdateAnimations (DWORD mstime) // //========================================================================== -template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &Doorani) +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimation *&p, FDoorAnimation **def) { - if (arc.IsStoring()) + FTextureID tex = p->BaseTexture; + Serialize(arc, key, tex, def ? &(*def)->BaseTexture : nullptr); + if (arc.isReading()) { - arc << Doorani->BaseTexture; - } - else - { - FTextureID tex; - arc << tex; - Doorani = TexMan.FindAnimatedDoor(tex); + p = TexMan.FindAnimatedDoor(tex); } return arc; } diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 5c6349b5c..91e994beb 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -705,3 +705,14 @@ void DObject::Serialize(FArchive &arc) } +FArchive &operator<< (FArchive &arc, secplane_t &plane) +{ + arc << plane.normal << plane.D; + if (plane.normal.Z != 0) + { // plane.c should always be non-0. Otherwise, the plane + // would be perfectly vertical. (But then, don't let this crash on a broken savegame...) + plane.negiC = -1 / plane.normal.Z; + } + return arc; +} + From a5000ead4c27d88074beeae76d7afb318f8accda Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 19 Sep 2016 19:58:04 +0200 Subject: [PATCH 13/59] - another batch. --- src/g_doom/a_bossbrain.cpp | 2 +- src/g_heretic/a_dsparil.cpp | 2 +- src/g_shared/a_action.cpp | 9 +++-- src/g_shared/a_flashfader.cpp | 12 +++--- src/g_shared/a_lightning.cpp | 38 +++++------------ src/g_shared/a_lightning.h | 4 +- src/g_shared/a_quake.cpp | 24 +++++++---- src/g_shared/a_sharedglobal.h | 4 +- src/g_shared/a_specialspot.cpp | 74 ++++++++++++++-------------------- src/g_shared/a_specialspot.h | 12 +++--- src/p_sectors.cpp | 16 +++++--- 11 files changed, 89 insertions(+), 108 deletions(-) diff --git a/src/g_doom/a_bossbrain.cpp b/src/g_doom/a_bossbrain.cpp index e6fd5f16d..b0e725910 100644 --- a/src/g_doom/a_bossbrain.cpp +++ b/src/g_doom/a_bossbrain.cpp @@ -120,7 +120,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_BrainSpit) bool isdefault = false; // shoot a cube at current target - targ = state->GetNextInList(PClass::FindClass("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain)); + targ = state->GetNextInList(PClass::FindActor("BossTarget"), G_SkillProperty(SKILLP_EasyBossBrain)); if (targ != NULL) { diff --git a/src/g_heretic/a_dsparil.cpp b/src/g_heretic/a_dsparil.cpp index 124cb1403..f5870f09e 100644 --- a/src/g_heretic/a_dsparil.cpp +++ b/src/g_heretic/a_dsparil.cpp @@ -148,7 +148,7 @@ void P_DSparilTeleport (AActor *actor) DSpotState *state = DSpotState::GetSpotState(); if (state == NULL) return; - spot = state->GetSpotWithMinMaxDistance(PClass::FindClass("BossSpot"), actor->X(), actor->Y(), 128, 0); + spot = state->GetSpotWithMinMaxDistance(PClass::FindActor("BossSpot"), actor->X(), actor->Y(), 128, 0); if (spot == NULL) return; prev = actor->Pos(); diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 3e3231c15..c0ec37d09 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -11,7 +11,7 @@ #include "p_enemy.h" #include "statnums.h" #include "templates.h" -#include "farchive.h" +#include "serializer.h" #include "r_data/r_translate.h" static FRandom pr_freezedeath ("FreezeDeath"); @@ -361,7 +361,7 @@ class DCorpsePointer : public DThinker public: DCorpsePointer (AActor *ptr); void Destroy (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); TObjPtr Corpse; DWORD Count; // Only the first corpse pointer's count is valid. private: @@ -435,10 +435,11 @@ void DCorpsePointer::Destroy () Super::Destroy (); } -void DCorpsePointer::Serialize(FArchive &arc) +void DCorpsePointer::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << Corpse << Count; + arc("corpse", Corpse) + ("count", Count); } diff --git a/src/g_shared/a_flashfader.cpp b/src/g_shared/a_flashfader.cpp index 736f9e0da..71673e5c7 100644 --- a/src/g_shared/a_flashfader.cpp +++ b/src/g_shared/a_flashfader.cpp @@ -1,7 +1,7 @@ #include "a_sharedglobal.h" #include "g_level.h" #include "d_player.h" -#include "farchive.h" +#include "serializer.h" IMPLEMENT_POINTY_CLASS (DFlashFader) DECLARE_POINTER (ForWho) @@ -26,13 +26,13 @@ void DFlashFader::Destroy () Super::Destroy(); } -void DFlashFader::Serialize(FArchive &arc) +void DFlashFader::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TotalTics << StartTic << ForWho; - for (int i = 1; i >= 0; --i) - for (int j = 3; j >= 0; --j) - arc << Blends[i][j]; + arc("totaltics", TotalTics) + ("starttic", StartTic) + ("forwho", ForWho) + .Array("blends", Blends[0], 8); } void DFlashFader::Tick () diff --git a/src/g_shared/a_lightning.cpp b/src/g_shared/a_lightning.cpp index 72bde0078..3eaedffc9 100644 --- a/src/g_shared/a_lightning.cpp +++ b/src/g_shared/a_lightning.cpp @@ -9,7 +9,7 @@ #include "r_sky.h" #include "g_level.h" #include "r_state.h" -#include "farchive.h" +#include "serializer.h" static FRandom pr_lightning ("Lightning"); @@ -19,44 +19,24 @@ DLightningThinker::DLightningThinker () : DThinker (STAT_LIGHTNING) { Stopped = false; - LightningLightLevels = NULL; LightningFlashCount = 0; NextLightningFlash = ((pr_lightning()&15)+5)*35; // don't flash at level start - LightningLightLevels = new short[numsectors]; - clearbufshort(LightningLightLevels, numsectors, SHRT_MAX); + LightningLightLevels.Resize(numsectors); + clearbufshort(&LightningLightLevels[0], numsectors, SHRT_MAX); } DLightningThinker::~DLightningThinker () { - if (LightningLightLevels != NULL) - { - delete[] LightningLightLevels; - } } -void DLightningThinker::Serialize(FArchive &arc) +void DLightningThinker::Serialize(FSerializer &arc) { - int i; - short *lights; - Super::Serialize (arc); - - arc << Stopped << NextLightningFlash << LightningFlashCount; - - if (arc.IsLoading ()) - { - if (LightningLightLevels != NULL) - { - delete[] LightningLightLevels; - } - LightningLightLevels = new short[numsectors]; - } - lights = LightningLightLevels; - for (i = numsectors; i > 0; ++lights, --i) - { - arc << *lights; - } + arc("stopped", Stopped) + ("next", NextLightningFlash) + ("count", LightningFlashCount) + ("levels", LightningLightLevels); } void DLightningThinker::Tick () @@ -107,7 +87,7 @@ void DLightningThinker::LightningFlash () tempSec->SetLightLevel(LightningLightLevels[j]); } } - clearbufshort(LightningLightLevels, numsectors, SHRT_MAX); + clearbufshort(&LightningLightLevels[0], numsectors, SHRT_MAX); level.flags &= ~LEVEL_SWAPSKIES; } return; diff --git a/src/g_shared/a_lightning.h b/src/g_shared/a_lightning.h index cca8d1fcc..62a260474 100644 --- a/src/g_shared/a_lightning.h +++ b/src/g_shared/a_lightning.h @@ -13,7 +13,7 @@ class DLightningThinker : public DThinker public: DLightningThinker (); ~DLightningThinker (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); void ForceLightning (int mode); void TerminateLightning(); @@ -24,7 +24,7 @@ protected: int NextLightningFlash; int LightningFlashCount; bool Stopped; - short *LightningLightLevels; + TArray LightningLightLevels; }; void P_StartLightning (); diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 13194a06e..98e505b2b 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -8,7 +8,7 @@ #include "s_sound.h" #include "a_sharedglobal.h" #include "statnums.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" #include "r_utility.h" @@ -64,15 +64,23 @@ DEarthquake::DEarthquake(AActor *center, int intensityX, int intensityY, int int // //========================================================================== -void DEarthquake::Serialize(FArchive &arc) +void DEarthquake::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Spot << m_Intensity << m_Countdown - << m_TremorRadius << m_DamageRadius - << m_QuakeSFX << m_Flags << m_CountdownStart - << m_WaveSpeed - << m_Falloff << m_Highpoint << m_MiniCount - << m_RollIntensity << m_RollWave; + arc("spot", m_Spot) + ("intensity", m_Intensity) + ("countdown", m_Countdown) + ("tremorradius", m_TremorRadius) + ("damageradius", m_DamageRadius) + ("quakesfx", m_QuakeSFX) + ("quakeflags", m_Flags) + ("countdownstart", m_CountdownStart) + ("wavespeed", m_WaveSpeed) + ("falloff", m_Falloff) + ("highpoint", m_Highpoint) + ("minicount", m_MiniCount) + ("rollintensity", m_RollIntensity) + ("rollwave", m_RollWave); } //========================================================================== diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index dd332fae1..7f4168563 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -122,7 +122,7 @@ public: float r2, float g2, float b2, float a2, float time, AActor *who); void Destroy (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); AActor *WhoFor() { return ForWho; } void Cancel (); @@ -165,7 +165,7 @@ public: int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, double rollIntensity, double rollWave); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); TObjPtr m_Spot; double m_TremorRadius, m_DamageRadius; diff --git a/src/g_shared/a_specialspot.cpp b/src/g_shared/a_specialspot.cpp index a0469fb40..1d8d1f72d 100644 --- a/src/g_shared/a_specialspot.cpp +++ b/src/g_shared/a_specialspot.cpp @@ -39,7 +39,7 @@ #include "i_system.h" #include "thingdef/thingdef.h" #include "doomstat.h" -#include "farchive.h" +#include "serializer.h" #include "a_pickups.h" static FRandom pr_spot ("SpecialSpot"); @@ -58,7 +58,7 @@ TObjPtr DSpotState::SpotState; struct FSpotList { - const PClass *Type; + PClassActor *Type; TArray Spots; unsigned Index; int SkipCount; @@ -68,7 +68,7 @@ struct FSpotList { } - FSpotList(const PClass *type) + FSpotList(PClassActor *type) { Type = type; Index = 0; @@ -82,17 +82,6 @@ struct FSpotList // //---------------------------------------------------------------------------- - void Serialize(FArchive &arc) - { - arc << Type << Spots << Index << SkipCount << numcalls; - } - - //---------------------------------------------------------------------------- - // - // - // - //---------------------------------------------------------------------------- - bool Add(ASpecialSpot *newspot) { for(unsigned i = 0; i < Spots.Size(); i++) @@ -194,6 +183,26 @@ struct FSpotList // //---------------------------------------------------------------------------- +FSerializer &Serialize(FSerializer &arc, const char *key, FSpotList &list, FSpotList *def) +{ + if (arc.BeginObject(key)) + { + arc("type", list.Type) + ("spots", list.Spots) + ("index", list.Index) + ("skipcount", list.SkipCount) + ("numcalls", list.numcalls) + .EndObject(); + } + return arc; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + DSpotState::DSpotState () : DThinker (STAT_INFO) { @@ -215,10 +224,6 @@ DSpotState::DSpotState () void DSpotState::Destroy () { - for(unsigned i = 0; i < SpotLists.Size(); i++) - { - delete SpotLists[i]; - } SpotLists.Clear(); SpotLists.ShrinkToFit(); @@ -254,13 +259,13 @@ DSpotState *DSpotState::GetSpotState(bool create) // //---------------------------------------------------------------------------- -FSpotList *DSpotState::FindSpotList(const PClass *type) +FSpotList *DSpotState::FindSpotList(PClassActor *type) { for(unsigned i = 0; i < SpotLists.Size(); i++) { - if (SpotLists[i]->Type == type) return SpotLists[i]; + if (SpotLists[i].Type == type) return &SpotLists[i]; } - return SpotLists[SpotLists.Push(new FSpotList(type))]; + return &SpotLists[SpotLists.Push(FSpotList(type))]; } //---------------------------------------------------------------------------- @@ -295,27 +300,10 @@ bool DSpotState::RemoveSpot(ASpecialSpot *spot) // //---------------------------------------------------------------------------- -void DSpotState::Serialize(FArchive &arc) +void DSpotState::Serialize(FSerializer &arc) { Super::Serialize(arc); - if (arc.IsStoring()) - { - arc.WriteCount(SpotLists.Size()); - for(unsigned i = 0; i < SpotLists.Size(); i++) - { - SpotLists[i]->Serialize(arc); - } - } - else - { - unsigned c = arc.ReadCount(); - SpotLists.Resize(c); - for(unsigned i = 0; i < SpotLists.Size(); i++) - { - SpotLists[i] = new FSpotList; - SpotLists[i]->Serialize(arc); - } - } + arc("spots", SpotLists); } //---------------------------------------------------------------------------- @@ -324,7 +312,7 @@ void DSpotState::Serialize(FArchive &arc) // //---------------------------------------------------------------------------- -ASpecialSpot *DSpotState::GetNextInList(const PClass *type, int skipcounter) +ASpecialSpot *DSpotState::GetNextInList(PClassActor *type, int skipcounter) { FSpotList *list = FindSpotList(type); if (list != NULL) return list->GetNextInList(skipcounter); @@ -337,7 +325,7 @@ ASpecialSpot *DSpotState::GetNextInList(const PClass *type, int skipcounter) // //---------------------------------------------------------------------------- -ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(const PClass *type, double x, double y, double mindist, double maxdist) +ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(PClassActor *type, double x, double y, double mindist, double maxdist) { FSpotList *list = FindSpotList(type); if (list != NULL) return list->GetSpotWithMinMaxDistance(x, y, mindist, maxdist); @@ -350,7 +338,7 @@ ASpecialSpot *DSpotState::GetSpotWithMinMaxDistance(const PClass *type, double x // //---------------------------------------------------------------------------- -ASpecialSpot *DSpotState::GetRandomSpot(const PClass *type, bool onlyonce) +ASpecialSpot *DSpotState::GetRandomSpot(PClassActor *type, bool onlyonce) { FSpotList *list = FindSpotList(type); if (list != NULL) return list->GetRandomSpot(onlyonce); diff --git a/src/g_shared/a_specialspot.h b/src/g_shared/a_specialspot.h index db148b19d..8fe38608a 100644 --- a/src/g_shared/a_specialspot.h +++ b/src/g_shared/a_specialspot.h @@ -22,7 +22,7 @@ class DSpotState : public DThinker { DECLARE_CLASS(DSpotState, DThinker) static TObjPtr SpotState; - TArray SpotLists; + TArray SpotLists; public: @@ -31,13 +31,13 @@ public: void Destroy (); void Tick (); static DSpotState *GetSpotState(bool create = true); - FSpotList *FindSpotList(const PClass *type); + FSpotList *FindSpotList(PClassActor *type); bool AddSpot(ASpecialSpot *spot); bool RemoveSpot(ASpecialSpot *spot); - void Serialize(FArchive &arc); - ASpecialSpot *GetNextInList(const PClass *type, int skipcounter); - ASpecialSpot *GetSpotWithMinMaxDistance(const PClass *type, double x, double y, double mindist, double maxdist); - ASpecialSpot *GetRandomSpot(const PClass *type, bool onlyonce = false); + void Serialize(FSerializer &arc); + ASpecialSpot *GetNextInList(PClassActor *type, int skipcounter); + ASpecialSpot *GetSpotWithMinMaxDistance(PClassActor *type, double x, double y, double mindist, double maxdist); + ASpecialSpot *GetRandomSpot(PClassActor *type, bool onlyonce = false); }; diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index eb1742aa8..5ed4cd144 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -1055,12 +1055,16 @@ double sector_t::NextLowestFloorAt(double x, double y, double z, int flags, doub FSerializer &Serialize(FSerializer &arc, const char *key, secspecial_t &spec, secspecial_t *def) { - arc("special", spec.special) - ("damageamount", spec.damageamount) - ("damagetype", spec.damagetype) - ("damageinterval", spec.damageinterval) - ("leakydamage", spec.leakydamage) - ("flags", spec.Flags); + if (arc.BeginObject(key)) + { + arc("special", spec.special) + ("damageamount", spec.damageamount) + ("damagetype", spec.damagetype) + ("damageinterval", spec.damageinterval) + ("leakydamage", spec.leakydamage) + ("flags", spec.Flags) + .EndObject(); + } return arc; } From ab43e0c8cbbb310f8668727cf26b23b41151dfeb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 00:41:22 +0200 Subject: [PATCH 14/59] - all thinker serializers done. --- src/b_bot.cpp | 64 +++---- src/b_bot.h | 3 +- src/c_dispatch.cpp | 9 +- src/decallib.cpp | 63 +++---- src/farchive.h | 7 - src/fragglescript/t_prepro.cpp | 12 +- src/fragglescript/t_script.cpp | 41 +++-- src/fragglescript/t_script.h | 10 +- src/fragglescript/t_variable.cpp | 11 +- src/g_shared/a_decals.cpp | 138 +++++---------- src/g_shared/a_sharedglobal.h | 12 +- src/g_shared/hudmessages.cpp | 2 +- src/m_cheat.cpp | 6 +- src/p_acs.cpp | 280 ++++++++++++++----------------- src/p_acs.h | 13 +- src/p_pusher.cpp | 26 +-- src/p_saveg.cpp | 2 +- src/p_scroll.cpp | 44 ++--- src/p_spec.cpp | 22 ++- src/p_switch.cpp | 37 +++- src/serializer.cpp | 136 ++++++++++++++- src/serializer.h | 5 +- src/textures/anim_switches.cpp | 21 --- src/tflags.h | 7 - src/v_font.cpp | 27 --- src/v_font.h | 3 +- src/zzz_old.cpp | 219 ++++++------------------ 27 files changed, 564 insertions(+), 656 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index a18aa14af..c4d702c8e 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -12,7 +12,7 @@ #include "cmdlib.h" #include "teaminfo.h" #include "d_net.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" IMPLEMENT_POINTY_CLASS(DBot) @@ -55,31 +55,44 @@ void DBot::Clear () old = { 0, 0 }; } -void DBot::Serialize(FArchive &arc) +FSerializer &Serialize(FSerializer &arc, const char *key, botskill_t &skill, botskill_t *def) +{ + if (arc.BeginObject(key)) + { + arc("aiming", skill.aiming) + ("perfection", skill.perfection) + ("reaction", skill.reaction) + ("isp", skill.isp) + .EndObject(); + } + return arc; +} + +void DBot::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << player - << Angle - << dest - << prev - << enemy - << missile - << mate - << last_mate - << skill - << t_active - << t_respawn - << t_strafe - << t_react - << t_fight - << t_roam - << t_rocket - << first_shot - << sleft - << allround - << increase - << old; + arc("player", player) + ("angle", Angle) + ("dest", dest) + ("prev", prev) + ("enemy", enemy) + ("missile", missile) + ("mate", mate) + ("lastmate", last_mate) + ("skill", skill) + ("active", t_active) + ("respawn", t_respawn) + ("strafe", t_strafe) + ("react", t_react) + ("fight", t_fight) + ("roam", t_roam) + ("rocket", t_rocket) + ("firstshot", first_shot) + ("sleft", sleft) + ("allround", allround) + ("increase", increase) + ("old", old); } void DBot::Tick () @@ -193,11 +206,6 @@ CCMD (listbots) Printf ("> %d bots\n", count); } -FArchive &operator<< (FArchive &arc, botskill_t &skill) -{ - return arc << skill.aiming << skill.perfection << skill.reaction << skill.isp; -} - // set the bot specific weapon information // This is intentionally not in the weapon definition anymore. void InitBotStuff() diff --git a/src/b_bot.h b/src/b_bot.h index e7a6c730e..9ba5e6869 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -142,7 +142,8 @@ public: DBot (); void Clear (); - void Serialize(FArchive &arc); + DECLARE_OLD_SERIAL + void Serialize(FSerializer &arc); void Tick (); //(b_think.cpp) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index a751b686f..c3fc9bae1 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -53,7 +53,7 @@ #include "v_text.h" #include "d_net.h" #include "d_main.h" -#include "farchive.h" +#include "serializer.h" // MACROS ------------------------------------------------------------------ @@ -65,7 +65,7 @@ class DWaitingCommand : public DThinker public: DWaitingCommand (const char *cmd, int tics); ~DWaitingCommand (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); private: @@ -189,10 +189,11 @@ static const char *KeyConfCommands[] = IMPLEMENT_CLASS (DWaitingCommand) -void DWaitingCommand::Serialize(FArchive &arc) +void DWaitingCommand::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << Command << TicsLeft; + arc("command", Command) + ("ticsleft", TicsLeft); } DWaitingCommand::DWaitingCommand () diff --git a/src/decallib.cpp b/src/decallib.cpp index c2bd0f4f3..fc7e465dc 100644 --- a/src/decallib.cpp +++ b/src/decallib.cpp @@ -48,7 +48,7 @@ #include "g_level.h" #include "colormatcher.h" #include "b_bot.h" -#include "farchive.h" +#include "serializer.h" FDecalLib DecalLibrary; @@ -113,7 +113,7 @@ struct DDecalThinker : public DThinker HAS_OBJECT_POINTERS public: DDecalThinker (DBaseDecal *decal) : DThinker (STAT_DECALTHINKER), TheDecal (decal) {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); TObjPtr TheDecal; protected: DDecalThinker () : DThinker (STAT_DECALTHINKER) {} @@ -123,10 +123,10 @@ IMPLEMENT_POINTY_CLASS (DDecalThinker) DECLARE_POINTER (TheDecal) END_POINTERS -void DDecalThinker::Serialize(FArchive &arc) +void DDecalThinker::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TheDecal; + arc("thedecal", TheDecal); } struct FDecalFaderAnim : public FDecalAnimator @@ -143,7 +143,7 @@ class DDecalFader : public DDecalThinker DECLARE_CLASS (DDecalFader, DDecalThinker) public: DDecalFader (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); int TimeToStartDecay; @@ -168,7 +168,7 @@ class DDecalColorer : public DDecalThinker DECLARE_CLASS (DDecalColorer, DDecalThinker) public: DDecalColorer (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); int TimeToStartDecay; @@ -194,7 +194,7 @@ class DDecalStretcher : public DDecalThinker DECLARE_CLASS (DDecalStretcher, DDecalThinker) public: DDecalStretcher (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); int TimeToStart; @@ -225,7 +225,7 @@ class DDecalSlider : public DDecalThinker DECLARE_CLASS (DDecalSlider, DDecalThinker) public: DDecalSlider (DBaseDecal *decal) : DDecalThinker (decal) {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); int TimeToStart; @@ -1153,10 +1153,12 @@ FDecalAnimator::~FDecalAnimator () IMPLEMENT_CLASS (DDecalFader) -void DDecalFader::Serialize(FArchive &arc) +void DDecalFader::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TimeToStartDecay << TimeToEndDecay << StartTrans; + arc("starttime", TimeToStartDecay) + ("endtime", TimeToEndDecay) + ("starttrans", StartTrans); } void DDecalFader::Tick () @@ -1200,18 +1202,18 @@ DThinker *FDecalFaderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const IMPLEMENT_CLASS (DDecalStretcher) -void DDecalStretcher::Serialize(FArchive &arc) +void DDecalStretcher::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TimeToStart - << TimeToStop - << GoalX - << StartX - << bStretchX - << GoalY - << StartY - << bStretchY - << bStarted; + arc("starttime", TimeToStart) + ("endtime", TimeToStop) + ("goalx", GoalX) + ("startx", StartX) + ("stretchx", bStretchX) + ("goaly", GoalY) + ("starty", StartY) + ("stretchy", bStretchY) + ("started", bStarted); } DThinker *FDecalStretcherAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const @@ -1288,16 +1290,14 @@ void DDecalStretcher::Tick () IMPLEMENT_CLASS (DDecalSlider) -void DDecalSlider::Serialize(FArchive &arc) +void DDecalSlider::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TimeToStart - << TimeToStop - /*<< DistX*/ - << DistY - /*<< StartX*/ - << StartY - << bStarted; + arc("starttime", TimeToStart) + ("endtime", TimeToStop) + ("disty", DistY) + ("starty", StartY) + ("started", bStarted); } DThinker *FDecalSliderAnim::CreateThinker (DBaseDecal *actor, side_t *wall) const @@ -1371,10 +1371,13 @@ FDecalAnimator *FDecalLib::FindAnimator (const char *name) IMPLEMENT_CLASS (DDecalColorer) -void DDecalColorer::Serialize(FArchive &arc) +void DDecalColorer::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TimeToStartDecay << TimeToEndDecay << StartColor << GoalColor; + arc("starttime", TimeToStartDecay) + ("endtime", TimeToEndDecay) + ("startcolor", StartColor) + ("goalcolor", GoalColor); } void DDecalColorer::Tick () diff --git a/src/farchive.h b/src/farchive.h index e874ae3dc..e6cd9f2da 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -294,13 +294,6 @@ inline FArchive &operator<< (FArchive &arc, T* &object) return arc.SerializeObject ((DObject*&)object, RUNTIME_TEMPLATE_CLASS(T)); } -class FFont; -FArchive &SerializeFFontPtr (FArchive &arc, FFont* &font); -template<> inline FArchive &operator<< (FArchive &arc, FFont* &font) -{ - return SerializeFFontPtr (arc, font); -} - struct FStrifeDialogueNode; struct FSwitchDef; struct FDoorAnimation; diff --git a/src/fragglescript/t_prepro.cpp b/src/fragglescript/t_prepro.cpp index 1826d2fe9..619b6d570 100644 --- a/src/fragglescript/t_prepro.cpp +++ b/src/fragglescript/t_prepro.cpp @@ -56,7 +56,7 @@ #include "t_script.h" #include "i_system.h" #include "w_wad.h" -#include "farchive.h" +#include "serializer.h" //========================================================================== @@ -81,10 +81,14 @@ END_POINTERS // //========================================================================== -void DFsSection::Serialize(FArchive &ar) +void DFsSection::Serialize(FSerializer &arc) { - Super::Serialize(ar); - ar << type << start_index << end_index << loop_index << next; + Super::Serialize(arc); + arc("type", type) + ("start_index", start_index) + ("end_index", end_index) + ("loop_index", loop_index) + ("next", next); } //========================================================================== diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp index e433ca5ce..bbf333acf 100644 --- a/src/fragglescript/t_script.cpp +++ b/src/fragglescript/t_script.cpp @@ -54,7 +54,7 @@ #include "i_system.h" #include "doomerrors.h" #include "doomstat.h" -#include "farchive.h" +#include "serializer.h" //========================================================================== // @@ -187,18 +187,23 @@ void DFsScript::Destroy() // //========================================================================== -void DFsScript::Serialize(FArchive &arc) +void DFsScript::Serialize(FSerializer &arc) { Super::Serialize(arc); // don't save a reference to the global script - if (parent == global_script) parent = NULL; + if (parent == global_script) parent = nullptr; - arc << data << scriptnum << len << parent << trigger << lastiftrue; - for(int i=0; i< SECTIONSLOTS; i++) arc << sections[i]; - for(int i=0; i< VARIABLESLOTS; i++) arc << variables[i]; - for(int i=0; i< MAXSCRIPTS; i++) arc << children[i]; + arc("data", data) + ("scriptnum", scriptnum) + ("len", len) + ("parent", parent) + ("trigger", trigger) + ("lastiftrue", lastiftrue) + .Array("sections", sections, SECTIONSLOTS) + .Array("variables", variables, VARIABLESLOTS) + .Array("children", children, MAXSCRIPTS); - if (parent == NULL) parent = global_script; + if (parent == nullptr) parent = global_script; } //========================================================================== @@ -338,12 +343,17 @@ void DRunningScript::Destroy() // //========================================================================== -void DRunningScript::Serialize(FArchive &arc) +void DRunningScript::Serialize(FSerializer &arc) { Super::Serialize(arc); - - arc << script << save_point << wait_type << wait_data << prev << next << trigger; - for(int i=0; i< VARIABLESLOTS; i++) arc << variables[i]; + arc("script", script) + ("save_point", save_point) + ("wait_type", wait_type) + ("wait_data", wait_data) + ("prev", prev) + ("next", next) + ("trigger", trigger) + .Array("variables", variables, VARIABLESLOTS); } @@ -416,10 +426,13 @@ void DFraggleThinker::Destroy() // //========================================================================== -void DFraggleThinker::Serialize(FArchive &arc) +void DFraggleThinker::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << LevelScript << RunningScripts << SpawnedThings << nocheckposition; + arc("levelscript", LevelScript) + ("runningscripts", RunningScripts) + ("spawnedthings", SpawnedThings) + ("nocheckposition", nocheckposition); } //========================================================================== diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h index 02ea444a2..7f89652e6 100644 --- a/src/fragglescript/t_script.h +++ b/src/fragglescript/t_script.h @@ -189,7 +189,7 @@ public: void GetValue(svalue_t &result); void SetValue(const svalue_t &newvalue); - void Serialize(FArchive &ar); + void Serialize(FSerializer &ar); }; //========================================================================== @@ -238,7 +238,7 @@ public: next = NULL; } - void Serialize(FArchive &ar); + void Serialize(FSerializer &ar); }; @@ -337,7 +337,7 @@ public: DFsScript(); void Destroy(); - void Serialize(FArchive &ar); + void Serialize(FSerializer &ar); DFsVariable *NewVariable(const char *name, int vtype); void NewFunction(const char *name, void (FParser::*handler)()); @@ -652,7 +652,7 @@ class DRunningScript : public DObject public: DRunningScript(AActor *trigger=NULL, DFsScript *owner = NULL, int index = 0) ; void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); TObjPtr script; @@ -689,7 +689,7 @@ public: void Destroy(); - void Serialize(FArchive & arc); + void Serialize(FSerializer & arc); void Tick(); size_t PropagateMark(); size_t PointerSubstitution (DObject *old, DObject *notOld); diff --git a/src/fragglescript/t_variable.cpp b/src/fragglescript/t_variable.cpp index b41b05490..125f32367 100644 --- a/src/fragglescript/t_variable.cpp +++ b/src/fragglescript/t_variable.cpp @@ -54,7 +54,7 @@ #include "t_script.h" #include "a_pickups.h" -#include "farchive.h" +#include "serializer.h" //========================================================================== @@ -306,10 +306,15 @@ void DFsVariable::SetValue(const svalue_t &newvalue) // //========================================================================== -void DFsVariable::Serialize(FArchive & ar) +void DFsVariable::Serialize(FSerializer & ar) { Super::Serialize(ar); - ar << Name << type << string << actor << value.i << next; + ar("name", Name) + ("type", type) + ("string", string) + ("actor", actor) + ("value", value.i) + ("next", next); } diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 91ac40096..ebbaaf197 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -44,8 +44,9 @@ #include "d_net.h" #include "colormatcher.h" #include "v_palette.h" -#include "farchive.h" +#include "serializer.h" #include "doomdata.h" +#include "r_state.h" static double DecalWidth, DecalLeft, DecalRight; static double SpreadZ; @@ -58,7 +59,8 @@ static int ImpactCount; CVAR (Bool, cl_spreaddecals, true, CVAR_ARCHIVE) IMPLEMENT_POINTY_CLASS (DBaseDecal) - DECLARE_POINTER(WallNext) + DECLARE_POINTER(WallPrev) + DECLARE_POINTER(WallNext) END_POINTERS IMPLEMENT_CLASS (DImpactDecal) @@ -75,7 +77,7 @@ DBaseDecal::DBaseDecal () DBaseDecal::DBaseDecal (double z) : DThinker(STAT_DECAL), WallNext(0), WallPrev(0), LeftDistance(0), Z(z), ScaleX(1.), ScaleY(1.), Alpha(1.), - AlphaColor(0), Translation(0), RenderFlags(0) + AlphaColor(0), Translation(0), RenderFlags(0), Side(nullptr), Sector(nullptr) { RenderStyle = STYLE_None; PicNum.SetInvalid(); @@ -83,8 +85,8 @@ DBaseDecal::DBaseDecal (double z) DBaseDecal::DBaseDecal (int statnum, double z) : DThinker(statnum), - WallNext(0), WallPrev(0), LeftDistance(0), Z(z), ScaleX(1.), ScaleY(1.), Alpha(1.), - AlphaColor(0), Translation(0), RenderFlags(0) + WallNext(nullptr), WallPrev(nullptr), LeftDistance(0), Z(z), ScaleX(1.), ScaleY(1.), Alpha(1.), + AlphaColor(0), Translation(0), RenderFlags(0), Side(nullptr), Sector(nullptr) { RenderStyle = STYLE_None; PicNum.SetInvalid(); @@ -92,17 +94,17 @@ DBaseDecal::DBaseDecal (int statnum, double z) DBaseDecal::DBaseDecal (const AActor *basis) : DThinker(STAT_DECAL), - WallNext(0), WallPrev(0), LeftDistance(0), Z(basis->Z()), ScaleX(basis->Scale.X), ScaleY(basis->Scale.Y), + WallNext(nullptr), WallPrev(nullptr), LeftDistance(0), Z(basis->Z()), ScaleX(basis->Scale.X), ScaleY(basis->Scale.Y), Alpha(basis->Alpha), AlphaColor(basis->fillcolor), Translation(basis->Translation), PicNum(basis->picnum), - RenderFlags(basis->renderflags), RenderStyle(basis->RenderStyle) + RenderFlags(basis->renderflags), RenderStyle(basis->RenderStyle), Side(nullptr), Sector(nullptr) { } DBaseDecal::DBaseDecal (const DBaseDecal *basis) : DThinker(STAT_DECAL), - WallNext(0), WallPrev(0), LeftDistance(basis->LeftDistance), Z(basis->Z), ScaleX(basis->ScaleX), + WallNext(nullptr), WallPrev(nullptr), LeftDistance(basis->LeftDistance), Z(basis->Z), ScaleX(basis->ScaleX), ScaleY(basis->ScaleY), Alpha(basis->Alpha), AlphaColor(basis->AlphaColor), Translation(basis->Translation), - PicNum(basis->PicNum), RenderFlags(basis->RenderFlags), RenderStyle(basis->RenderStyle) + PicNum(basis->PicNum), RenderFlags(basis->RenderFlags), RenderStyle(basis->RenderStyle), Side(nullptr), Sector(nullptr) { } @@ -114,64 +116,32 @@ void DBaseDecal::Destroy () void DBaseDecal::Remove () { - DBaseDecal **prev = WallPrev; - DBaseDecal *next = WallNext; - if (prev && (*prev = next)) - next->WallPrev = prev; + if (WallPrev == nullptr) Side->AttachedDecals = WallNext; + else WallPrev->WallNext = WallNext; + + if (WallNext != nullptr) WallNext->WallPrev = WallPrev; + WallPrev = NULL; WallNext = NULL; } -void DBaseDecal::Serialize(FArchive &arc) +void DBaseDecal::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << LeftDistance - << Z - << ScaleX << ScaleY - << Alpha - << AlphaColor - << Translation - << PicNum - << RenderFlags - << RenderStyle - << Sector; -} - -void DBaseDecal::SerializeChain (FArchive &arc, DBaseDecal **first) -{ - DWORD numInChain; - DBaseDecal *fresh; - DBaseDecal **firstptr = first; - - if (arc.IsLoading ()) - { - numInChain = arc.ReadCount (); - - while (numInChain--) - { - arc << fresh; - *firstptr = fresh; - fresh->WallPrev = firstptr; - firstptr = &fresh->WallNext; - } - } - else - { - numInChain = 0; - fresh = *firstptr; - while (fresh != NULL) - { - fresh = fresh->WallNext; - ++numInChain; - } - arc.WriteCount (numInChain); - fresh = *firstptr; - while (numInChain--) - { - arc << fresh; - fresh = fresh->WallNext; - } - } + arc("wallprev", WallPrev) + ("wallnext", WallNext) + ("leftdistance", LeftDistance) + ("z", Z) + ("scalex", ScaleX) + ("scaley", ScaleY) + ("alpha", Alpha) + ("alphacolor", AlphaColor) + ("translation", Translation) + ("picnum", PicNum) + ("renderflags", RenderFlags) + ("renderstyle", RenderStyle) + ("side", Side) + ("sectpr", Sector); } void DBaseDecal::GetXY (side_t *wall, double &ox, double &oy) const @@ -211,26 +181,18 @@ void DBaseDecal::SetShade (int r, int g, int b) // Returns the texture the decal stuck to. FTextureID DBaseDecal::StickToWall (side_t *wall, double x, double y, F3DFloor *ffloor) { - // Stick the decal at the end of the chain so it appears on top - DBaseDecal *next, **prev; + Side = wall; + WallPrev = wall->AttachedDecals; - prev = &wall->AttachedDecals; - while (*prev != NULL) + while (WallPrev != nullptr && WallPrev->WallNext != nullptr) { - next = *prev; - prev = &next->WallNext; + WallPrev = WallPrev->WallNext; } + if (WallPrev != nullptr) WallPrev->WallNext = this; + else wall->AttachedDecals = this; + WallNext = nullptr; + - *prev = this; - WallNext = NULL; - WallPrev = prev; -/* - WallNext = wall->AttachedDecals; - WallPrev = &wall->AttachedDecals; - if (WallNext) - WallNext->WallPrev = &WallNext; - wall->AttachedDecals = this; -*/ sector_t *front, *back; line_t *line; FTextureID tex; @@ -592,28 +554,6 @@ CUSTOM_CVAR (Int, cl_maxdecals, 1024, CVAR_ARCHIVE) } } -// Uses: target points to previous impact decal -// tracer points to next impact decal -// -// Note that this means we can't simply serialize an impact decal as-is -// because doing so when many are present in a level could result in -// a lot of recursion and we would run out of stack. Not nice. So instead, -// the save game code calls DImpactDecal::SerializeAll to serialize a -// list of impact decals. - -void DImpactDecal::SerializeTime (FArchive &arc) -{ - if (arc.IsLoading ()) - { - ImpactCount = 0; - } -} - -void DImpactDecal::Serialize(FArchive &arc) -{ - Super::Serialize (arc); -} - DImpactDecal::DImpactDecal () : DBaseDecal (STAT_AUTODECAL, 0.) { diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index 7f4168563..c00cd8cf0 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -24,7 +24,7 @@ public: DBaseDecal (const AActor *actor); DBaseDecal (const DBaseDecal *basis); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Destroy (); FTextureID StickToWall(side_t *wall, double x, double y, F3DFloor * ffloor); double GetRealZ (const side_t *wall) const; @@ -33,9 +33,7 @@ public: void Spread (const FDecalTemplate *tpl, side_t *wall, double x, double y, double z, F3DFloor * ffloor); void GetXY (side_t *side, double &x, double &y) const; - static void SerializeChain (FArchive &arc, DBaseDecal **firstptr); - - DBaseDecal *WallNext, **WallPrev; + DBaseDecal *WallNext, *WallPrev; double LeftDistance; double Z; @@ -46,7 +44,8 @@ public: FTextureID PicNum; DWORD RenderFlags; FRenderStyle RenderStyle; - sector_t * Sector; // required for 3D floors + side_t *Side; + sector_t *Sector; protected: virtual DBaseDecal *CloneSelf(const FDecalTemplate *tpl, double x, double y, double z, side_t *wall, F3DFloor * ffloor) const; @@ -70,9 +69,6 @@ public: void BeginPlay (); void Destroy (); - void Serialize(FArchive &arc); - static void SerializeTime (FArchive &arc); - protected: DBaseDecal *CloneSelf(const FDecalTemplate *tpl, double x, double y, double z, side_t *wall, F3DFloor * ffloor) const; static void CheckMax (); diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 7c86c98ac..a5e97066f 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -185,7 +185,7 @@ void DHUDMessage::Serialize(FArchive &arc) Super::Serialize(arc); arc << Left << Top << CenterX << HoldTics << Tics << State << TextColor - << SBarID << SourceText << Font << Next + //<< SBarID << SourceText << Font << Next << HUDWidth << HUDHeight << NoWrap << ClipX << ClipY << ClipWidth << ClipHeight diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index e78174a55..7c110a52c 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -45,7 +45,7 @@ #include "d_net.h" #include "d_dehacked.h" #include "gi.h" -#include "farchive.h" +#include "serializer.h" #include "r_utility.h" #include "a_morph.h" @@ -1026,10 +1026,10 @@ public: } // You'll probably never be able to catch this in a save game, but // just in case, add a proper serializer. - void Serialize(FArchive &arc) + void Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << Pawn; + arc("pawn", Pawn); } }; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 9a7137eb2..b49f820f4 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -75,6 +75,7 @@ #include "po_man.h" #include "actorptrselect.h" #include "farchive.h" +#include "serializer.h" #include "decallib.h" #include "p_terrain.h" #include "version.h" @@ -82,6 +83,7 @@ #include "r_utility.h" #include "a_morph.h" #include "i_music.h" +#include "serializer.h" #include "g_shared/a_pickups.h" @@ -1367,11 +1369,12 @@ public: int tag, int height, int special, int arg0, int arg1, int arg2, int arg3, int arg4); void Tick (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); private: sector_t *Sector; double WatchD, LastD; - int Special, Arg0, Arg1, Arg2, Arg3, Arg4; + int Special; + int Args[5]; TObjPtr Activator; line_t *Line; bool LineSide; @@ -1387,11 +1390,16 @@ END_POINTERS DPlaneWatcher::DPlaneWatcher (AActor *it, line_t *line, int lineSide, bool ceiling, int tag, int height, int special, int arg0, int arg1, int arg2, int arg3, int arg4) - : Special (special), Arg0 (arg0), Arg1 (arg1), Arg2 (arg2), Arg3 (arg3), Arg4 (arg4), + : Special (special), Activator (it), Line (line), LineSide (!!lineSide), bCeiling (ceiling) { int secnum; + Args[0] = arg0; + Args[1] = arg1; + Args[2] = arg2; + Args[3] = arg3; + Args[4] = arg4; secnum = P_FindFirstSectorFromTag (tag); if (secnum >= 0) { @@ -1417,13 +1425,19 @@ DPlaneWatcher::DPlaneWatcher (AActor *it, line_t *line, int lineSide, bool ceili } } -void DPlaneWatcher::Serialize(FArchive &arc) +void DPlaneWatcher::Serialize(FSerializer &arc) { Super::Serialize (arc); + arc("special", Special) + .Args("args", Args, nullptr, Special) + ("sector", Sector) + ("ceiling", bCeiling) + ("watchd", WatchD) + ("lastd", LastD) + ("activator", Activator) + ("line", Line) + ("lineside", LineSide); - arc << Special << Arg0 << Arg1 << Arg2 << Arg3 << Arg4 - << Sector << bCeiling << WatchD << LastD << Activator - << Line << LineSide << bCeiling; } void DPlaneWatcher::Tick () @@ -1448,7 +1462,7 @@ void DPlaneWatcher::Tick () if ((LastD < WatchD && newd >= WatchD) || (LastD > WatchD && newd <= WatchD)) { - P_ExecuteSpecial(Special, Line, Activator, LineSide, Arg0, Arg1, Arg2, Arg3, Arg4); + P_ExecuteSpecial(Special, Line, Activator, LineSide, Args[0], Args[1], Args[2], Args[3], Args[4]); Destroy (); } @@ -2868,33 +2882,6 @@ void FBehavior::StaticStopMyScripts (AActor *actor) } } -//========================================================================== -// -// P_SerializeACSScriptNumber -// -// Serializes a script number. If it's negative, it's really a name, so -// that will get serialized after it. -// -//========================================================================== - -void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) -{ - arc << scriptnum; - // If the script number is negative, then it's really a name. - // So read/store the name after it. - if (scriptnum < 0) - { - if (arc.IsStoring()) - { - arc.WriteName(FName(ENamedName(-scriptnum)).GetChars()); - } - else - { - const char *nam = arc.ReadName(); - scriptnum = -FName(nam); - } - } -} //---- The ACS Interpreter ----// @@ -2927,81 +2914,63 @@ DACSThinker::~DACSThinker () ActiveThinker = NULL; } -void DACSThinker::Serialize(FArchive &arc) +//========================================================================== +// +// helper class for the runningscripts serializer +// +//========================================================================== + +struct SavingRunningscript { int scriptnum; - int scriptcount = 0; + DLevelScript *lscript; +}; - Super::Serialize (arc); - if (arc.IsStoring()) +FSerializer &Serialize(FSerializer &arc, const char *key, SavingRunningscript &rs, SavingRunningscript *def) +{ + if (arc.BeginObject(key)) { - DLevelScript *script; - script = Scripts; - while (script) - { - scriptcount++; - - // We want to store this list backwards, so we can't loose the last pointer - if (script->next == NULL) - break; - script = script->next; - } - arc << scriptcount; - - while (script) - { - arc << script; - script = script->prev; - } + arc.ScriptNum("num", rs.scriptnum) + ("script", rs.lscript) + .EndObject(); } - else + return arc; +} + +void DACSThinker::Serialize(FSerializer &arc) +{ + arc("scripts", Scripts); + + if (arc.isWriting()) { - // We are running through this list backwards, so the next entry is the last processed - DLevelScript *next = NULL; - arc << scriptcount; - Scripts = NULL; - LastScript = NULL; - for (int i = 0; i < scriptcount; i++) + if (RunningScripts.CountUsed()) { - arc << Scripts; + ScriptMap::Iterator it(RunningScripts); + ScriptMap::Pair *pair; - Scripts->next = next; - Scripts->prev = NULL; - if (next != NULL) - next->prev = Scripts; - - next = Scripts; - - if (i == 0) - LastScript = Scripts; + arc.BeginArray("runningscripts"); + while (it.NextPair(pair)) + { + assert(pair->Value != nullptr); + SavingRunningscript srs = { pair->Key, pair->Value }; + arc(nullptr, srs); + } + arc.EndArray(); } } - if (arc.IsStoring ()) - { - ScriptMap::Iterator it(RunningScripts); - ScriptMap::Pair *pair; - - while (it.NextPair(pair)) - { - assert(pair->Value != NULL); - arc << pair->Value; - scriptnum = pair->Key; - P_SerializeACSScriptNumber(arc, scriptnum, true); - } - DLevelScript *nilptr = NULL; - arc << nilptr; - } else // Loading { - DLevelScript *script = NULL; + DLevelScript *script = nullptr; RunningScripts.Clear(); - - arc << script; - while (script) + if (arc.BeginArray("runniongscripts")) { - P_SerializeACSScriptNumber(arc, scriptnum, true); - RunningScripts[scriptnum] = script; - arc << script; + auto cnt = arc.ArraySize(); + for (int i = 0; i < cnt; i++) + { + SavingRunningscript srs; + arc(nullptr, srs); + RunningScripts[srs.scriptnum] = srs.lscript; + } } } } @@ -3048,58 +3017,74 @@ IMPLEMENT_POINTY_CLASS (DLevelScript) DECLARE_POINTER(activator) END_POINTERS -inline FArchive &operator<< (FArchive &arc, DLevelScript::EScriptState &state) +//========================================================================== +// +// SerializeFFontPtr +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def) { - BYTE val = (BYTE)state; - arc << val; - state = (DLevelScript::EScriptState)val; - return arc; -} - -void DLevelScript::Serialize(FArchive &arc) -{ - DWORD i; - - Super::Serialize (arc); - - P_SerializeACSScriptNumber(arc, script, false); - - arc << state - << statedata - << activator - << activationline - << backSide - << numlocalvars; - - if (arc.IsLoading()) + if (arc.isWriting()) { - localvars = new SDWORD[numlocalvars]; - } - for (i = 0; i < (DWORD)numlocalvars; i++) - { - arc << localvars[i]; - } - - if (arc.IsStoring ()) - { - WORD lib = activeBehavior->GetLibraryID() >> LIBRARYID_SHIFT; - arc << lib; - i = activeBehavior->PC2Ofs (pc); - arc << i; + const char *n = font->GetName(); + return arc.StringPtr(key, n); } else { - WORD lib; - arc << lib << i; - activeBehavior = FBehavior::StaticGetModule (lib); - pc = activeBehavior->Ofs2PC (i); + const char *n; + arc.StringPtr(key, n); + font = V_GetFont(n); + if (font == NULL) + { + Printf("Could not load font %s\n", n); + font = SmallFont; + } + return arc; } - arc << activefont - << hudwidth << hudheight; - arc << ClipRectLeft << ClipRectTop << ClipRectWidth << ClipRectHeight - << WrapWidth; - arc << InModuleScriptNumber; +} + + +void DLevelScript::Serialize(FSerializer &arc) +{ + Super::Serialize(arc); + + uint32_t pcofs; + uint16_t lib; + + if (arc.isWriting()) + { + lib = activeBehavior->GetLibraryID() >> LIBRARYID_SHIFT; + pcofs = activeBehavior->PC2Ofs(pc); + } + + arc.ScriptNum("scriptnum", script) + ("next", next) + ("prev", prev) + .Enum("state", state) + ("statedata", statedata) + ("activator", activator) + ("activationline", activationline) + ("backside", backSide) + ("localvars", Localvars) + ("lib", lib) + ("pc", pcofs) + ("activefont", activefont) + ("hudwidth", hudwidth) + ("hudheight", hudheight) + ("cliprectleft", ClipRectLeft) + ("cliprectop", ClipRectTop) + ("cliprectwidth", ClipRectWidth) + ("cliprectheight", ClipRectHeight) + ("wrapwidth", WrapWidth) + ("inmodulescriptnum", InModuleScriptNumber); + + if (arc.isReading()) + { + activeBehavior = FBehavior::StaticGetModule(lib); + pc = activeBehavior->Ofs2PC(pcofs); + } } DLevelScript::DLevelScript () @@ -3108,14 +3093,10 @@ DLevelScript::DLevelScript () if (DACSThinker::ActiveThinker == NULL) new DACSThinker; activefont = SmallFont; - localvars = NULL; } DLevelScript::~DLevelScript () { - if (localvars != NULL) - delete[] localvars; - localvars = NULL; } void DLevelScript::Unlink () @@ -6140,7 +6121,7 @@ static bool CharArrayParms(int &capacity, int &offset, int &a, int *Stack, int & int DLevelScript::RunScript () { DACSThinker *controller = DACSThinker::ActiveThinker; - SDWORD *locals = localvars; + SDWORD *locals = &Localvars[0]; ACSLocalArrays noarrays; ACSLocalArrays *localarrays = &noarrays; ScriptFunction *activeFunction = NULL; @@ -9676,12 +9657,11 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr script = num; assert(code->VarCount >= code->ArgCount); - numlocalvars = code->VarCount; - localvars = new SDWORD[code->VarCount]; - memset(localvars, 0, code->VarCount * sizeof(SDWORD)); + Localvars.Resize(code->VarCount); + memset(&Localvars[0], 0, code->VarCount * sizeof(SDWORD)); for (int i = 0; i < MIN(argcount, code->ArgCount); ++i) { - localvars[i] = args[i]; + Localvars[i] = args[i]; } pc = module->GetScriptAddress(code); InModuleScriptNumber = module->GetScriptIndex(code); diff --git a/src/p_acs.h b/src/p_acs.h index 9e2428cbf..b31996788 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -859,7 +859,7 @@ public: const int *args, int argcount, int flags); ~DLevelScript (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); int RunScript (); inline void SetState (EScriptState newstate) { state = newstate; } @@ -869,22 +869,21 @@ public: void MarkLocalVarStrings() const { - GlobalACSStrings.MarkStringArray(localvars, numlocalvars); + GlobalACSStrings.MarkStringArray(&Localvars[0], Localvars.Size()); } void LockLocalVarStrings() const { - GlobalACSStrings.LockStringArray(localvars, numlocalvars); + GlobalACSStrings.LockStringArray(&Localvars[0], Localvars.Size()); } void UnlockLocalVarStrings() const { - GlobalACSStrings.UnlockStringArray(localvars, numlocalvars); + GlobalACSStrings.UnlockStringArray(&Localvars[0], Localvars.Size()); } protected: DLevelScript *next, *prev; int script; - SDWORD *localvars; - int numlocalvars; + TArray Localvars; int *pc; EScriptState state; int statedata; @@ -944,7 +943,7 @@ public: DACSThinker (); ~DACSThinker (); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); typedef TMap ScriptMap; diff --git a/src/p_pusher.cpp b/src/p_pusher.cpp index 511e6bbb4..38c9c9720 100644 --- a/src/p_pusher.cpp +++ b/src/p_pusher.cpp @@ -26,7 +26,7 @@ #include #include "actor.h" #include "p_spec.h" -#include "farchive.h" +#include "serializer.h" #include "p_lnspec.h" #include "c_cvars.h" #include "p_maputl.h" @@ -52,7 +52,7 @@ public: DPusher (); DPusher (EPusher type, line_t *l, int magnitude, int angle, AActor *source, int affectee); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); int CheckForSectorMatch (EPusher type, int tag); void ChangeValues (int magnitude, int angle) { @@ -83,23 +83,15 @@ DPusher::DPusher () { } -inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (DPusher::EPusher)val; - return arc; -} - -void DPusher::Serialize(FArchive &arc) +void DPusher::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_Source - << m_PushVec - << m_Magnitude - << m_Radius - << m_Affectee; + arc.Enum("type", m_Type) + ("source", m_Source) + ("pushvec", m_PushVec) + ("magnitude", m_Magnitude) + ("radius", m_Radius) + ("affectee", m_Affectee); } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 0735eb128..99c372b4d 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -358,7 +358,7 @@ static void SpawnExtraPlayers () void P_SerializeThinkers (FArchive &arc, bool hubLoad) { arc.EnableThinkers(); - DImpactDecal::SerializeTime (arc); + //DImpactDecal::Im ::SerializeTime (arc); DThinker::SerializeAll (arc, hubLoad); } diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 9dba9e4ca..761ce00ec 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -26,7 +26,7 @@ #include #include "actor.h" #include "p_spec.h" -#include "farchive.h" +#include "serializer.h" #include "p_lnspec.h" #include "r_data/r_interpolate.h" @@ -46,7 +46,7 @@ public: DScroller (double dx, double dy, const line_t *l, int control, int accel, EScrollPos scrollpos = EScrollPos::scw_all); void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); bool AffectsWall (int wallnum) const { return m_Type == EScroll::sc_side && m_Affectee == wallnum; } @@ -87,22 +87,6 @@ END_POINTERS // //----------------------------------------------------------------------------- -inline FArchive &operator<< (FArchive &arc, EScroll &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (EScroll)val; - return arc; -} - -inline FArchive &operator<< (FArchive &arc, EScrollPos &type) -{ - int val = (int)type; - arc << val; - type = (EScrollPos)val; - return arc; -} - EScrollPos operator &(EScrollPos one, EScrollPos two) { return EScrollPos(int(one) & int(two)); @@ -114,20 +98,20 @@ EScrollPos operator &(EScrollPos one, EScrollPos two) // //----------------------------------------------------------------------------- -void DScroller::Serialize(FArchive &arc) +void DScroller::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Type - << m_dx << m_dy - << m_Affectee - << m_Control - << m_LastHeight - << m_vdx << m_vdy - << m_Accel - << m_Parts - << m_Interpolations[0] - << m_Interpolations[1] - << m_Interpolations[2]; + arc.Enum("type", m_Type) + ("dx", m_dx) + ("dy", m_dy) + ("affectee", m_Affectee) + ("control", m_Control) + ("lastheight", m_LastHeight) + ("vdx", m_vdx) + ("vdy", m_vdy) + ("accel", m_Accel) + .Enum("parts", m_Parts) + .Array("interpolations", m_Interpolations, 3); } //----------------------------------------------------------------------------- diff --git a/src/p_spec.cpp b/src/p_spec.cpp index c10d5dd2a..dc878bc33 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -62,7 +62,7 @@ #include "g_level.h" #include "v_font.h" #include "a_sharedglobal.h" -#include "farchive.h" +#include "serializer.h" #include "a_keys.h" #include "c_dispatch.h" #include "r_sky.h" @@ -648,7 +648,7 @@ class DLightTransfer : public DThinker DLightTransfer() {} public: DLightTransfer (sector_t *srcSec, int target, bool copyFloor); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: @@ -662,11 +662,13 @@ protected: IMPLEMENT_CLASS (DLightTransfer) -void DLightTransfer::Serialize(FArchive &arc) +void DLightTransfer::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << LastLight; - arc << Source << TargetTag << CopyFloor; + arc("lastlight", LastLight) + ("source", Source) + ("targettag", TargetTag) + ("copyfloor", CopyFloor); } DLightTransfer::DLightTransfer (sector_t *srcSec, int target, bool copyFloor) @@ -736,7 +738,7 @@ class DWallLightTransfer : public DThinker DWallLightTransfer() {} public: DWallLightTransfer (sector_t *srcSec, int target, BYTE flags); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); protected: @@ -750,11 +752,13 @@ protected: IMPLEMENT_CLASS (DWallLightTransfer) -void DWallLightTransfer::Serialize(FArchive &arc) +void DWallLightTransfer::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << LastLight; - arc << Source << TargetID << Flags; + arc("lastlight", LastLight) + ("source", Source) + ("targetid", TargetID) + ("flags", Flags); } DWallLightTransfer::DWallLightTransfer (sector_t *srcSec, int target, BYTE flags) diff --git a/src/p_switch.cpp b/src/p_switch.cpp index 0388176e2..c60b0fdcd 100644 --- a/src/p_switch.cpp +++ b/src/p_switch.cpp @@ -46,7 +46,7 @@ #include "w_wad.h" #include "tarray.h" #include "cmdlib.h" -#include "farchive.h" +#include "serializer.h" #include "p_maputl.h" #include "p_spec.h" @@ -61,7 +61,7 @@ public: DActiveButton (); DActiveButton (side_t *, int, FSwitchDef *, const DVector2 &pos, bool flippable); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick (); side_t *m_Side; @@ -348,16 +348,45 @@ DActiveButton::DActiveButton (side_t *side, int Where, FSwitchDef *Switch, AdvanceFrame (); } +//========================================================================== +// +// operator<< +// +//========================================================================== + +template<> FSerializer &Serialize (FSerializer &arc, const char *key, FSwitchDef* &Switch, FSwitchDef **def) +{ + if (arc.isWriting()) + { + Serialize(arc, key, Switch->PreTexture, nullptr); + } + else + { + FTextureID tex; + tex.SetInvalid(); + Serialize(arc, key, tex, nullptr); + Switch = TexMan.FindSwitch(tex); + } + return arc; +} + //========================================================================== // // // //========================================================================== -void DActiveButton::Serialize(FArchive &arc) +void DActiveButton::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Side << m_Part << m_SwitchDef << m_Frame << m_Timer << bFlippable << m_Pos << bReturning; + arc("side", m_Side) + ("part", m_Part) + ("switchdef", m_SwitchDef) + ("frame", m_Frame) + ("timer", m_Timer) + ("fippable", bFlippable) + ("pos", m_Pos) + ("returning", bReturning); } //========================================================================== diff --git a/src/serializer.cpp b/src/serializer.cpp index 9cf156056..44d95c3dd 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -407,6 +407,49 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe return *this; } +//========================================================================== +// +// Special handler for script numbers +// +//========================================================================== + +FSerializer &FSerializer::ScriptNum(const char *key, int &num) +{ + if (isWriting()) + { + WriteKey(key); + if (num < 0) + { + w->mWriter.String(FName(ENamedName(-num)).GetChars()); + } + else + { + w->mWriter.Int(num); + } + w->mWriter.EndArray(); + } + else + { + auto val = r->FindKey(key); + if (val != nullptr) + { + if (val->IsInt()) + { + num = val->GetInt(); + } + else if (val->IsString()) + { + num = -FName(val->GetString()); + } + else + { + I_Error("Integer expected for '%s'", key); + } + } + } + return *this; +} + //========================================================================== // // @@ -1047,7 +1090,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI { if (arc.isWriting()) { - if (arc.w->inObject() && def != nullptr && sid != *def) + if (!arc.w->inObject() || def == nullptr || sid != *def) { arc.WriteKey(key); const char *sn = (const char*)sid; @@ -1088,7 +1131,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor { if (arc.isWriting()) { - if (arc.w->inObject() && def != nullptr && clst != *def) + if (!arc.w->inObject() || def == nullptr || clst != *def) { arc.WriteKey(key); arc.w->mWriter.String(clst->TypeName.GetChars()); @@ -1123,7 +1166,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&st { if (arc.isWriting()) { - if (arc.w->inObject() && def != nullptr && state != *def) + if (!arc.w->inObject() || def == nullptr || state != *def) { arc.WriteKey(key); if (state == nullptr) @@ -1192,7 +1235,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial { if (arc.isWriting()) { - if (arc.w->inObject() && def != nullptr && node != *def) + if (!arc.w->inObject() || def == nullptr || node != *def) { arc.WriteKey(key); if (node == nullptr) @@ -1245,7 +1288,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p { if (arc.isWriting()) { - if (arc.w->inObject() && def != nullptr && pstr != *def) + if (!arc.w->inObject() || def == nullptr || pstr != *def) { arc.WriteKey(key); if (pstr == nullptr) @@ -1281,3 +1324,86 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p } +//========================================================================== +// +// +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString *def) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || pstr.Compare(*def) != 0) + { + arc.WriteKey(key); + arc.w->mWriter.String(pstr.GetChars()); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + pstr = ""; + } + else if (val->IsString()) + { + pstr = val->GetString(); + } + else + { + I_Error("string expected for '%s'", key); + } + } + } + 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->mWriter.Null(); + } + else + { + arc.w->mWriter.String(pstr); + } + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsNull()) + { + pstr = nullptr; + } + else if (val->IsString()) + { + pstr = copystring(val->GetString()); + } + else + { + I_Error("string expected for '%s'", key); + } + } + } + return arc; +} \ No newline at end of file diff --git a/src/serializer.h b/src/serializer.h index 73b830b2d..15b969692 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -44,7 +44,8 @@ public: 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); - FSerializer &StringPtr(const char *key, const char *&charptr); // This only retrieves the address but creates no permanent copy of the string. + 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 &ScriptNum(const char *key, int &num); bool isReading() const { return r != nullptr; @@ -131,6 +132,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/); FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval); FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def); +FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def); template FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) @@ -185,6 +187,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&st 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); inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def) diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp index 2cde1d0c8..9a2aa2441 100644 --- a/src/textures/anim_switches.cpp +++ b/src/textures/anim_switches.cpp @@ -398,24 +398,3 @@ FSwitchDef *FTextureManager::FindSwitch (FTextureID texture) return NULL; } -//========================================================================== -// -// operator<< -// -//========================================================================== - -template<> FArchive &operator<< (FArchive &arc, FSwitchDef* &Switch) -{ - if (arc.IsStoring()) - { - arc << Switch->PreTexture; - } - else - { - FTextureID tex; - arc << tex; - Switch = TexMan.FindSwitch(tex); - } - return arc; -} - diff --git a/src/tflags.h b/src/tflags.h index 1affb6cbc..a7536ab01 100644 --- a/src/tflags.h +++ b/src/tflags.h @@ -82,13 +82,6 @@ public: TT GetValue() const { return Value; } operator TT() const { return Value; } - // Serialize to FArchive - FArchive& Serialize(FArchive& arc) - { - arc << Value; - return arc; - } - // Set the value of the flagset manually with an integer. // Please think twice before using this. static Self FromInt (TT value) { return Self (static_cast (value)); } diff --git a/src/v_font.cpp b/src/v_font.cpp index 052074d11..dd2bf7068 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -340,33 +340,6 @@ FFont *V_GetFont(const char *name) } return font; } -//========================================================================== -// -// SerializeFFontPtr -// -//========================================================================== - -FArchive &SerializeFFontPtr (FArchive &arc, FFont* &font) -{ - if (arc.IsStoring ()) - { - arc << font->Name; - } - else - { - char *name = NULL; - - arc << name; - font = V_GetFont(name); - if (font == NULL) - { - Printf ("Could not load font %s\n", name); - font = SmallFont; - } - delete[] name; - } - return arc; -} //========================================================================== // diff --git a/src/v_font.h b/src/v_font.h index f6397a0d0..762ba4cd7 100644 --- a/src/v_font.h +++ b/src/v_font.h @@ -88,6 +88,7 @@ public: int GetDefaultKerning () const { return GlobalKerning; } virtual void LoadTranslations(); void Preload() const; + const char *GetName() const { return Name; } static FFont *FindFont (const char *fontname); static void StaticPreloadFonts(); @@ -134,8 +135,6 @@ protected: friend void V_ClearFonts(); friend void V_RetranslateFonts(); - - friend FArchive &SerializeFFontPtr (FArchive &arc, FFont* &font); }; diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 91e994beb..8b9d0277f 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -183,7 +183,7 @@ void P_SerializeWorldActors(FArchive &arc) { if (line->sidedef[s] != NULL) { - DBaseDecal::SerializeChain(arc, &line->sidedef[s]->AttachedDecals); + //DBaseDecal::SerializeChain(arc, &line->sidedef[s]->AttachedDecals); } } } @@ -394,173 +394,6 @@ void AActor::Serialize(FArchive &arc) { Super::Serialize(arc); - if (arc.IsStoring()) - { - arc.WriteSprite(sprite); - } - else - { - sprite = arc.ReadSprite(); - } - - arc << __Pos - << Angles.Yaw - << Angles.Pitch - << Angles.Roll - << frame - << Scale - << RenderStyle - << renderflags - << picnum - << floorpic - << ceilingpic - << TIDtoHate - << LastLookPlayerNumber - << LastLookActor - << effects - << Alpha - << fillcolor - << Sector - << floorz - << ceilingz - << dropoffz - << floorsector - << ceilingsector - << radius - << Height - << projectilepassheight - << Vel - << tics - << state - << DamageVal; - if (DamageVal == 0x40000000 || DamageVal == -1) - { - DamageVal = -1; - DamageFunc = GetDefault()->DamageFunc; - } - else - { - DamageFunc = nullptr; - } - P_SerializeTerrain(arc, floorterrain); - arc << projectileKickback - << flags - << flags2 - << flags3 - << flags4 - << flags5 - << flags6 - << flags7 - << weaponspecial - << special1 - << special2 - << specialf1 - << specialf2 - << health - << movedir - << visdir - << movecount - << strafecount - << target - << lastenemy - << LastHeard - << reactiontime - << threshold - << player - << SpawnPoint - << SpawnAngle - << StartHealth - << skillrespawncount - << tracer - << Floorclip - << tid - << special; - if (P_IsACSSpecial(special)) - { - P_SerializeACSScriptNumber(arc, args[0], false); - } - else - { - arc << args[0]; - } - arc << args[1] << args[2] << args[3] << args[4]; - arc << accuracy << stamina; - arc << goal - << waterlevel - << MinMissileChance - << SpawnFlags - << Inventory - << InventoryID; - arc << FloatBobPhase - << Translation - << SeeSound - << AttackSound - << PainSound - << DeathSound - << ActiveSound - << UseSound - << BounceSound - << WallBounceSound - << CrushPainSound - << Speed - << FloatSpeed - << Mass - << PainChance - << SpawnState - << SeeState - << MeleeState - << MissileState - << MaxDropOffHeight - << MaxStepHeight - << BounceFlags - << bouncefactor - << wallbouncefactor - << bouncecount - << maxtargetrange - << meleethreshold - << meleerange - << DamageType; - arc << DamageTypeReceived; - arc << PainType - << DeathType; - arc << Gravity - << FastChaseStrafeCount - << master - << smokecounter - << BlockingMobj - << BlockingLine - << VisibleToTeam // [BB] - << pushfactor - << Species - << Score; - arc << DesignatedTeam; - arc << lastpush << lastbump - << PainThreshold - << DamageFactor; - arc << DamageMultiply; - arc << WeaveIndexXY << WeaveIndexZ - << PoisonDamageReceived << PoisonDurationReceived << PoisonPeriodReceived << Poisoner - << PoisonDamage << PoisonDuration << PoisonPeriod; - arc << PoisonDamageType << PoisonDamageTypeReceived; - arc << ConversationRoot << Conversation; - arc << FriendPlayer; - arc << TeleFogSourceType - << TeleFogDestType; - arc << RipperLevel - << RipLevelMin - << RipLevelMax; - arc << DefThreshold; - if (SaveVersion >= 4549) - { - arc << SpriteAngle; - arc << SpriteRotation; - } - - if (SaveVersion >= 4550) - { - arc << alternative; - } - { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; @@ -716,3 +549,53 @@ FArchive &operator<< (FArchive &arc, secplane_t &plane) return arc; } +FArchive &operator<< (FArchive &arc, botskill_t &skill) +{ + return arc << skill.aiming << skill.perfection << skill.reaction << skill.isp; +} + +void DBot::Serialize(FArchive &arc) +{ + Super::Serialize(arc); + + arc << player + << Angle + << dest + << prev + << enemy + << missile + << mate + << last_mate + << skill + << t_active + << t_respawn + << t_strafe + << t_react + << t_fight + << t_roam + << t_rocket + << first_shot + << sleft + << allround + << increase + << old; +} +void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) +{ + arc << scriptnum; + // If the script number is negative, then it's really a name. + // So read/store the name after it. + if (scriptnum < 0) + { + if (arc.IsStoring()) + { + arc.WriteName(FName(ENamedName(-scriptnum)).GetChars()); + } + else + { + const char *nam = arc.ReadName(); + scriptnum = -FName(nam); + } + } +} + From daf43f9d35e81af1c4d0975cec6ca7910d254ae0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 09:11:13 +0200 Subject: [PATCH 15/59] - added polyobject serializer. - added sanity checks to prevent a savegame from being loaded with an incompatible map - refactored a few things to simplify serialization. - started work on main level serializer function. --- src/am_map.cpp | 15 ++-- src/am_map.h | 4 +- src/g_level.cpp | 10 +-- src/g_level.h | 9 +- src/json.cpp | 48 +++++----- src/p_mobj.cpp | 7 +- src/p_saveg.cpp | 216 +++++++++++++++++++++++---------------------- src/p_scroll.cpp | 4 +- src/p_setup.cpp | 8 +- src/portal.cpp | 42 --------- src/serializer.cpp | 15 ++++ src/serializer.h | 1 + src/zzz_old.cpp | 42 +++++++++ 13 files changed, 221 insertions(+), 200 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 7e346843b..9292e81d9 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -38,7 +38,7 @@ #include "gi.h" #include "p_setup.h" #include "c_bind.h" -#include "farchive.h" +#include "serializer.h" #include "r_renderer.h" #include "r_sky.h" #include "sbar.h" @@ -3106,13 +3106,14 @@ void AM_Drawer () // //============================================================================= -void AM_SerializeMarkers(FArchive &arc) +void AM_SerializeMarkers(FSerializer &arc) { - arc << markpointnum; - for (int i=0; i 0) { - memset (Scrolls, 0, sizeof(*Scrolls)*numsectors); + memset (&Scrolls[0], 0, sizeof(Scrolls[0])*Scrolls.Size()); } } @@ -1832,10 +1832,10 @@ void FLevelLocals::AddScroller (int secnum) { return; } - if (Scrolls == NULL) + if (Scrolls.Size() == 0) { - Scrolls = new FSectorScrollValues[numsectors]; - memset (Scrolls, 0, sizeof(*Scrolls)*numsectors); + Scrolls.Resize(numsectors); + memset (&Scrolls[0], 0, sizeof(Scrolls[0])*numsectors); } } diff --git a/src/g_level.h b/src/g_level.h index 8360bd45e..5b994a3d2 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -375,17 +375,12 @@ struct level_info_t } }; -// [RH] These get zeroed every tic and are updated by thinkers. -struct FSectorScrollValues -{ - DVector2 Scroll; -}; - struct FLevelLocals { void Tick (); void AddScroller (int secnum); + BYTE md5[16]; // for savegame validation. If the MD5 does not match the savegame won't be loaded. int time; // time in the hub int maptime; // time in the map int totaltime; // time in the game @@ -436,7 +431,7 @@ struct FLevelLocals int airsupply; int DefaultEnvironment; // Default sound environment. - FSectorScrollValues *Scrolls; // NULL if no DScrollers in this level + TArray Scrolls; // NULL if no DScrollers in this level SBYTE WallVertLight; // Light diffs for vert/horiz walls SBYTE WallHorizLight; diff --git a/src/json.cpp b/src/json.cpp index c3fb88247..0fca84d4a 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -5,6 +5,28 @@ #include "i_system.h" #include "a_sharedglobal.h" + + +void DObject::SerializeUserVars(FSerializer &arc) +{ + PSymbolTable *symt; + FName varname; + //DWORD count, j; + int *varloc = NULL; + + symt = &GetClass()->Symbols; + + if (arc.isWriting()) + { + // Write all fields that aren't serialized by native code. + //GetClass()->WriteValue(arc, this); + } + else + { + //GetClass()->ReadValue(arc, this); + } +} + //========================================================================== // // @@ -18,12 +40,15 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node) while (!(node->ObjectFlags & OF_Sentinel)) { assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); + //node->SerializeUserVars(arc); ::Serialize(arc, nullptr, node, nullptr); + node->CheckIfSerialized(); node = node->NextThinker; } } } + void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) { //DThinker *thinker; @@ -50,29 +75,10 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) -void DObject::SerializeUserVars(FSerializer &arc) -{ - PSymbolTable *symt; - FName varname; - //DWORD count, j; - int *varloc = NULL; - - symt = &GetClass()->Symbols; - - if (arc.isWriting()) - { - // Write all fields that aren't serialized by native code. - //GetClass()->WriteValue(arc, this); - } - else - { - //GetClass()->ReadValue(arc, this); - } -} -void SerializeWorld(FSerializer &arc); +void G_SerializeLevel(FSerializer &arc, bool); CCMD(writejson) { @@ -81,7 +87,7 @@ CCMD(writejson) arc.OpenWriter(); arc.BeginObject(nullptr); DThinker::SerializeThinkers(arc, false); - SerializeWorld(arc); + G_SerializeLevel(arc, false); arc.WriteObjects(); arc.EndObject(); DWORD tt = I_MSTime(); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 2459f79af..70fff197a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -3432,7 +3432,7 @@ void AActor::Tick () // [RH] Consider carrying sectors here DVector2 cumm(0, 0); - if ((level.Scrolls != NULL || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR)) + if ((level.Scrolls.Size() != 0 || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR)) { double height, waterheight; // killough 4/4/98: add waterheight const msecnode_t *node; @@ -3453,10 +3453,9 @@ void AActor::Tick () sector_t *sec = node->m_sector; DVector2 scrollv; - if (level.Scrolls != NULL) + if (level.Scrolls.Size() > (sec-sectors)) { - const FSectorScrollValues *scroll = &level.Scrolls[sec - sectors]; - scrollv = scroll->Scroll; + scrollv = level.Scrolls[sec - sectors]; } else { diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 99c372b4d..13fac2e7e 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -732,6 +732,36 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, F return arc; } +//============================================================================ +// +// one polyobject. +// +//============================================================================ + +FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj &poly, FPolyObj *def) +{ + if (arc.BeginObject(key)) + { + DAngle angle = poly.Angle; + DVector2 delta = poly.StartSpot.pos; + arc("angle", angle) + ("pos", delta) + ("interpolation", poly.interpolation) + ("blocked", poly.bBlocked) + ("hasportals", poly.bHasPortals) + ("specialdata", poly.specialdata) + .EndObject(); + + if (arc.isReading()) + { + poly.RotatePolyobj(angle, true); + delta -= poly.StartSpot.pos; + poly.MovePolyobj(delta, true); + } + } + return arc; +} + //========================================================================== // // @@ -760,21 +790,89 @@ FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def // //============================================================================ -void SerializeWorld(FSerializer &arc) +void G_SerializeLevel(FSerializer &arc, bool hubload) { + int i = level.totaltime; + + if (arc.isWriting()) + { + arc.Array("checksum", level.md5, 16); + } + else + { + // prevent bad things from happening by doing a check on the size of level arrays and the map's entire checksum. + // The old code happily tried to load savegames with any mismatch here, often causing meaningless errors + // deep down in the deserializer or just a crash if the few insufficient safeguards were not triggered. + BYTE chk[16] = { 0 }; + arc.Array("checksum", chk, 16); + if (arc.GetSize("linedefs") != numlines || + arc.GetSize("sidedefs") != numsides || + arc.GetSize("sectors") != numsectors || + arc.GetSize("polyobjs") != po_NumPolyobjs || + memcmp(chk, level.md5, 16)) + { + I_Error("Savegame is from a different level"); + } + } + + //Renderer->StartSerialize(arc); + if (arc.isReading()) + { + P_DestroyThinkers(hubload); + // ReadObjects + } + + arc("level.flags", level.flags) + ("level.flags2", level.flags2) + ("level.fadeto", level.fadeto) + ("level.found_secrets", level.found_secrets) + ("level.found_items", level.found_items) + ("level.killed_monsters", level.killed_monsters) + ("level.total_secrets", level.total_secrets) + ("level.total_items", level.total_items) + ("level.total_monsters", level.total_monsters) + ("level.gravity", level.gravity) + ("level.aircontrol", level.aircontrol) + ("level.teamdamage", level.teamdamage) + ("level.maptime", level.maptime) + ("level.totaltime", i) + ("level.skytexture1", level.skytexture1) + ("level.skytexture2", level.skytexture2) + ("level.scrolls", level.Scrolls); + + // Hub transitions must keep the current total time + if (!hubload) + level.totaltime = i; + + if (arc.isReading()) + { + sky1texture = level.skytexture1; + sky2texture = level.skytexture2; + R_InitSkyMap(); + interpolator.ClearInterpolations(); + } + + G_AirControlChanged(); + // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON - arc.Array("linedefs", lines, &loadlines[0], numlines) - .Array("sidedefs", sides, &loadsides[0], numsides) - .Array("sectors", sectors, &loadsectors[0], numsectors) - ("subsectors", subsectors) - ("zones", Zones) - ("lineportals", linePortals) - ("sectorportals", sectorPortals); + AM_SerializeMarkers(arc); + + //FBehavior::StaticSerializeModuleStates(arc); + arc.Array("linedefs", lines, &loadlines[0], numlines); + arc.Array("sidedefs", sides, &loadsides[0], numsides); + arc.Array("sectors", sectors, &loadsectors[0], numsectors); + arc.Array("polyobjs", polyobjs, po_NumPolyobjs); + arc("subsectors", subsectors); + //StatusBar->Serialize(arc); + arc("zones", Zones); + arc("lineportals", linePortals); + arc("sectorportals", sectorPortals); if (arc.isReading()) P_CollectLinkedPortals(); } + //========================================================================== // // ArchiveSounds @@ -830,33 +928,8 @@ void P_SerializePolyobjs (FArchive &arc) DAngle angle; DVector2 delta; - arc << data; - if (data != ASEG_POLYOBJS) - I_Error ("Polyobject marker missing"); - - arc << data; - if (data != po_NumPolyobjs) - { - I_Error ("UnarchivePolyobjs: Bad polyobj count"); - } for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++) { - arc << data; - if (data != po->tag) - { - I_Error ("UnarchivePolyobjs: Invalid polyobj tag"); - } - arc << angle << delta << po->interpolation; - arc << po->bBlocked; - arc << po->bHasPortals; - if (SaveVersion >= 4548) - { - arc << po->specialdata; - } - - po->RotatePolyobj (angle, true); - delta -= po->StartSpot.pos; - po->MovePolyobj (delta, true); } } } @@ -868,73 +941,7 @@ void P_SerializePolyobjs (FArchive &arc) void G_SerializeLevel(FArchive &arc, bool hubLoad) { - int i = level.totaltime; - - unsigned tm = I_MSTime(); - - Renderer->StartSerialize(arc); - if (arc.IsLoading()) P_DestroyThinkers(hubLoad); - - arc << level.flags - << level.flags2 - << level.fadeto - << level.found_secrets - << level.found_items - << level.killed_monsters - << level.gravity - << level.aircontrol - << level.teamdamage - << level.maptime - << i; - - // Hub transitions must keep the current total time - if (!hubLoad) - level.totaltime = i; - - arc << level.skytexture1 << level.skytexture2; - if (arc.IsLoading()) - { - sky1texture = level.skytexture1; - sky2texture = level.skytexture2; - R_InitSkyMap(); - } - - G_AirControlChanged(); - - BYTE t; - - // Does this level have scrollers? - if (arc.IsStoring()) - { - t = level.Scrolls ? 1 : 0; - arc << t; - } - else - { - arc << t; - if (level.Scrolls) - { - delete[] level.Scrolls; - level.Scrolls = NULL; - } - if (t) - { - level.Scrolls = new FSectorScrollValues[numsectors]; - memset(level.Scrolls, 0, sizeof(level.Scrolls)*numsectors); - } - } - - FBehavior::StaticSerializeModuleStates(arc); - if (arc.IsLoading()) interpolator.ClearInterpolations(); - P_SerializeWorld(arc); - P_SerializeThinkers(arc, hubLoad); - P_SerializeWorldActors(arc); // serializing actor pointers in the world data must be done after SerializeWorld has restored the entire sector state, otherwise LinkToWorld may fail. - P_SerializePolyobjs(arc); - P_SerializeSubsectors(arc); - StatusBar->Serialize(arc); - - arc << level.total_monsters << level.total_items << level.total_secrets; - +#if 0 // Does this level have custom translations? FRemapTable *trans; WORD w; @@ -969,17 +976,17 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad) // This must be saved, too, of course! FCanvasTextureInfo::Serialize(arc); - AM_SerializeMarkers(arc); + //AM_SerializeMarkers(arc); P_SerializePlayers(arc, hubLoad); P_SerializeSounds(arc); if (arc.IsLoading()) { - for (i = 0; i < numsectors; i++) + for (int i = 0; i < numsectors; i++) { P_Recalculate3DFloors(§ors[i]); } - for (i = 0; i < MAXPLAYERS; ++i) + for (int i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i] && players[i].mo != NULL) { @@ -988,7 +995,6 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad) } } Renderer->EndSerialize(arc); - unsigned tt = I_MSTime(); - Printf("Serialization took %d ms\n", tt - tm); +#endif } diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 761ce00ec..2c7333a21 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -220,8 +220,8 @@ void DScroller::Tick () // [RH] Don't actually carry anything here. That happens later. case EScroll::sc_carry: - level.Scrolls[m_Affectee].Scroll.X += dx; - level.Scrolls[m_Affectee].Scroll.Y += dy; + level.Scrolls[m_Affectee].X += dx; + level.Scrolls[m_Affectee].Y += dy; break; case EScroll::sc_carry_ceiling: // to be added later diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 99bb01330..aafed854b 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3552,11 +3552,7 @@ void P_FreeLevelData () po_NumPolyobjs = 0; Zones.Clear(); P_FreeStrifeConversations (); - if (level.Scrolls != NULL) - { - delete[] level.Scrolls; - level.Scrolls = NULL; - } + level.Scrolls.Clear(); P_ClearUDMFKeys(); } @@ -3668,6 +3664,8 @@ void P_SetupLevel (const char *lumpname, int position) I_Error("Unable to open map '%s'\n", lumpname); } + // generate a checksum for the level, to be included and checked with savegames. + map->GetChecksum(level.md5); // find map num level.lumpnum = map->lumpnum; hasglnodes = false; diff --git a/src/portal.cpp b/src/portal.cpp index d7dad220e..90cff83db 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -197,48 +197,6 @@ void FLinePortalTraverse::AddLineIntercepts(int bx, int by) } } -//============================================================================ -// -// Save a line portal for savegames. -// -//============================================================================ - -FArchive &operator<< (FArchive &arc, FLinePortal &port) -{ - arc << port.mOrigin - << port.mDestination - << port.mDisplacement - << port.mType - << port.mFlags - << port.mDefFlags - << port.mAlign; - return arc; -} - -//============================================================================ -// -// Save a sector portal for savegames. -// -//============================================================================ - -FArchive &operator<< (FArchive &arc, FSectorPortal &port) -{ - arc << port.mType - << port.mFlags - << port.mPartner - << port.mPlane - << port.mOrigin - << port.mDestination - << port.mDisplacement - << port.mPlaneZ; - if (arc.IsLoading()) - { - port.mSkybox = nullptr; - } - return arc; -} - - //============================================================================ // // finds the destination for a line portal for spawning diff --git a/src/serializer.cpp b/src/serializer.cpp index 44d95c3dd..35ce9d4e3 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -534,6 +534,21 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) return *this; } +//========================================================================== +// +// +// +//========================================================================== + +unsigned FSerializer::GetSize(const char *group) +{ + if (isWriting()) return -1; // we do not know this when writing. + + const rapidjson::Value &val = r->mDocObj[group]; + if (!val.IsArray()) return -1; + return val.Size(); +} + //========================================================================== // // Writes out all collected objects diff --git a/src/serializer.h b/src/serializer.h index 15b969692..efa92a550 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -40,6 +40,7 @@ public: bool BeginArray(const char *name); void EndArray(); void WriteObjects(); + unsigned GetSize(const char *group); const char *GetOutput(unsigned *len = nullptr); FSerializer &Args(const char *key, int *args, int *defargs, int special); FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr); diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 8b9d0277f..939767c87 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -599,3 +599,45 @@ void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) } } +//============================================================================ +// +// Save a line portal for savegames. +// +//============================================================================ + +FArchive &operator<< (FArchive &arc, FLinePortal &port) +{ + arc << port.mOrigin + << port.mDestination + << port.mDisplacement + << port.mType + << port.mFlags + << port.mDefFlags + << port.mAlign; + return arc; +} + +//============================================================================ +// +// Save a sector portal for savegames. +// +//============================================================================ + +FArchive &operator<< (FArchive &arc, FSectorPortal &port) +{ + arc << port.mType + << port.mFlags + << port.mPartner + << port.mPlane + << port.mOrigin + << port.mDestination + << port.mDisplacement + << port.mPlaneZ; + if (arc.IsLoading()) + { + port.mSkybox = nullptr; + } + return arc; +} + + From af6404f763288acce5848449a291780e6e57d053 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 10:27:53 +0200 Subject: [PATCH 16/59] - all DObjects converted. - cleaned out some old cruft that's no longer needed. --- src/actor.h | 1 - src/b_bot.h | 1 - src/dobject.h | 3 - src/dthinker.cpp | 7 +- src/farchive.cpp | 2 + src/farchive.h | 9 - src/g_shared/hudmessages.cpp | 59 +++--- src/g_shared/sbar.h | 11 +- src/g_shared/shared_sbar.cpp | 10 +- src/json.cpp | 15 -- src/p_conversation.cpp | 23 --- src/p_pspr.cpp | 19 +- src/p_pspr.h | 2 +- src/p_saveg.cpp | 39 +--- src/p_user.cpp | 50 +---- src/r_utility.cpp | 2 + src/s_sndseq.cpp | 113 ++++++----- src/s_sndseq.h | 4 +- src/s_sound.cpp | 4 +- src/zzz_old.cpp | 373 +---------------------------------- 20 files changed, 137 insertions(+), 610 deletions(-) diff --git a/src/actor.h b/src/actor.h index b46917a4a..1e939d563 100644 --- a/src/actor.h +++ b/src/actor.h @@ -583,7 +583,6 @@ public: void Destroy (); ~AActor (); - DECLARE_OLD_SERIAL void Serialize(FSerializer &arc); void PostSerialize(); diff --git a/src/b_bot.h b/src/b_bot.h index 9ba5e6869..5ad77707e 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -142,7 +142,6 @@ public: DBot (); void Clear (); - DECLARE_OLD_SERIAL void Serialize(FSerializer &arc); void Tick (); diff --git a/src/dobject.h b/src/dobject.h index 7e0c49db6..0419e8c59 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -438,8 +438,6 @@ template inline void GC::Mark(TObjPtr &obj) GC::Mark(&obj.o); } -#define DECLARE_OLD_SERIAL virtual void Serialize(FArchive &arc); - class DObject { public: @@ -469,7 +467,6 @@ public: inline bool IsA (const PClass *type) const; void SerializeUserVars(FArchive &arc); - DECLARE_OLD_SERIAL void SerializeUserVars(FSerializer &arc); virtual void Serialize(FSerializer &arc); diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 0efcecbdd..06295a4b3 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -39,6 +39,7 @@ #include "i_system.h" #include "doomerrors.h" #include "farchive.h" +#include "serializer.h" #include "d_player.h" @@ -103,14 +104,14 @@ bool FThinkerList::IsEmpty() const return Sentinel == NULL || Sentinel->NextThinker == NULL; } -void DThinker::SaveList(FArchive &arc, DThinker *node) +void DThinker::SaveList(FSerializer &arc, DThinker *node) { if (node != NULL) { while (!(node->ObjectFlags & OF_Sentinel)) { assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); - arc << node; + ::Serialize(arc, nullptr, node, nullptr); node = node->NextThinker; } } @@ -118,6 +119,7 @@ void DThinker::SaveList(FArchive &arc, DThinker *node) void DThinker::SerializeAll(FArchive &arc, bool hubLoad) { +#if 0 DThinker *thinker; BYTE stat; int statcount; @@ -197,6 +199,7 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad) } bSerialOverride = false; } +#endif } DThinker::DThinker (int statnum) throw() diff --git a/src/farchive.cpp b/src/farchive.cpp index f454d3b7b..1151c6198 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -1087,6 +1087,7 @@ FArchive &FArchive::SerializeObject (DObject *&object, PClass *type) FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) { +#if 0 BYTE objHead; const PClass *type; BYTE playerNum; @@ -1203,6 +1204,7 @@ FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) default: I_Error ("Unknown object code (%d) in archive\n", objHead); } +#endif return *this; } diff --git a/src/farchive.h b/src/farchive.h index e6cd9f2da..6353bda7d 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -288,19 +288,10 @@ inline FArchive &operator<< (FArchive &arc, PalEntry &p) return arc << p.a << p.r << p.g << p.b; } -template -inline FArchive &operator<< (FArchive &arc, T* &object) -{ - return arc.SerializeObject ((DObject*&)object, RUNTIME_TEMPLATE_CLASS(T)); -} - struct FStrifeDialogueNode; struct FSwitchDef; struct FDoorAnimation; struct FLinePortal; -template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node); -template<> FArchive &operator<< (FArchive &arc, FSwitchDef* &sw); -template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &da); FArchive &operator<< (FArchive &arc, FLinePortal &da); FArchive &operator<< (FArchive &arc, FSectorPortal &da); diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index a5e97066f..531870571 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -39,7 +39,7 @@ #include "v_video.h" #include "cmdlib.h" #include "doomstat.h" -#include "farchive.h" +#include "serializer.h" EXTERN_CVAR(Int, con_scaletext) int active_con_scaletext(); @@ -56,14 +56,6 @@ IMPLEMENT_CLASS (DHUDMessageTypeOnFadeOut) * Basic HUD message. Appears and disappears without any special effects * *************************************************************************/ -inline FArchive &operator<< (FArchive &arc, EColorRange &i) -{ - BYTE val = (BYTE)i; - arc << val; - i = (EColorRange)val; - return arc; -} - //============================================================================ // // DHUDMessage Constructor @@ -180,20 +172,30 @@ DHUDMessage::~DHUDMessage () // //============================================================================ -void DHUDMessage::Serialize(FArchive &arc) +void DHUDMessage::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << Left << Top << CenterX << HoldTics - << Tics << State << TextColor - //<< SBarID << SourceText << Font << Next - << HUDWidth << HUDHeight - << NoWrap - << ClipX << ClipY << ClipWidth << ClipHeight - << WrapWidth - << HandleAspect - << VisibilityFlags - << Style << Alpha; - if (arc.IsLoading()) + arc("left", Left) + ("top", Top) + ("centerx", CenterX) + ("holdtics", HoldTics) + ("tics", Tics) + ("state", State) + .Enum("textcolor", TextColor) + ("hudwidth", HUDWidth) + ("hudheight", HUDHeight) + ("nowrap", NoWrap) + ("clipx", ClipX) + ("clipy", ClipY) + ("clipwidth", ClipWidth) + ("clipheight", ClipHeight) + ("wrapwidth", WrapWidth) + ("handleaspect", HandleAspect) + ("visibilityflags", VisibilityFlags) + ("style", Style) + ("alpha", Alpha); + + if (arc.isReading()) { Lines = NULL; ResetText(SourceText); @@ -504,10 +506,10 @@ DHUDMessageFadeOut::DHUDMessageFadeOut (FFont *font, const char *text, float x, // //============================================================================ -void DHUDMessageFadeOut::Serialize(FArchive &arc) +void DHUDMessageFadeOut::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << FadeOutTics; + arc("fadeouttics", FadeOutTics); } //============================================================================ @@ -609,10 +611,10 @@ DHUDMessageFadeInOut::DHUDMessageFadeInOut (FFont *font, const char *text, float // //============================================================================ -void DHUDMessageFadeInOut::Serialize(FArchive &arc) +void DHUDMessageFadeInOut::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << FadeInTics; + arc("fadeintics", FadeInTics); } //============================================================================ @@ -718,10 +720,13 @@ DHUDMessageTypeOnFadeOut::DHUDMessageTypeOnFadeOut (FFont *font, const char *tex // //============================================================================ -void DHUDMessageTypeOnFadeOut::Serialize(FArchive &arc) +void DHUDMessageTypeOnFadeOut::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << TypeOnTime << CurrLine << LineVisible << LineLen; + arc("typeontime", TypeOnTime) + ("currline", CurrLine) + ("linevisible", LineVisible) + ("linelen", LineLen); } //============================================================================ diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 87242988e..6d5b10739 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -71,7 +71,7 @@ public: EColorRange textColor, float holdTime); virtual ~DHUDMessage (); - virtual void Serialize(FArchive &arc); + virtual void Serialize(FSerializer &arc); void Draw (int bottom, int visibility); virtual void ResetText (const char *text); @@ -157,7 +157,7 @@ public: DHUDMessageFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeOutTime); - virtual void Serialize(FArchive &arc); + virtual void Serialize(FSerializer &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); @@ -176,7 +176,7 @@ public: DHUDMessageFadeInOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float holdTime, float fadeInTime, float fadeOutTime); - virtual void Serialize(FArchive &arc); + virtual void Serialize(FSerializer &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); @@ -195,7 +195,7 @@ public: DHUDMessageTypeOnFadeOut (FFont *font, const char *text, float x, float y, int hudwidth, int hudheight, EColorRange textColor, float typeTime, float holdTime, float fadeOutTime); - virtual void Serialize(FArchive &arc); + virtual void Serialize(FSerializer &arc); virtual void DoDraw (int linenum, int x, int y, bool clean, int hudheight); virtual bool Tick (); virtual void ScreenSizeChanged (); @@ -355,7 +355,8 @@ public: static void AddBlend (float r, float g, float b, float a, float v_blend[4]); - virtual void Serialize(FArchive &arc); + // do not make this a DObject Serialize function because it's not used like one! + void SerializeMessages(FSerializer &arc); virtual void Tick (); virtual void Draw (EHudState state); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index b0a1f6a5d..a3bb76b7f 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -51,8 +51,9 @@ #include "colormatcher.h" #include "v_palette.h" #include "d_player.h" -#include "farchive.h" +#include "serializer.h" #include "gstrings.h" +#include "r_utility.h" #include "../version.h" @@ -1654,12 +1655,9 @@ void DBaseStatusBar::ReceivedWeapon (AWeapon *weapon) { } -void DBaseStatusBar::Serialize(FArchive &arc) +void DBaseStatusBar::SerializeMessages(FSerializer &arc) { - for (size_t i = 0; i < countof(Messages); ++i) - { - arc << Messages[i]; - } + arc.Array("hudmessages", Messages, 3, true); } void DBaseStatusBar::ScreenSizeChanged () diff --git a/src/json.cpp b/src/json.cpp index 0fca84d4a..2d8c84d33 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -33,21 +33,6 @@ void DObject::SerializeUserVars(FSerializer &arc) // //========================================================================== -void DThinker::SaveList(FSerializer &arc, DThinker *node) -{ - if (node != NULL) - { - while (!(node->ObjectFlags & OF_Sentinel)) - { - assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); - //node->SerializeUserVars(arc); - ::Serialize(arc, nullptr, node, nullptr); - node->CheckIfSerialized(); - node = node->NextThinker; - } - } -} - void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) { diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 545603368..a697223e7 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -57,7 +57,6 @@ #include "doomstat.h" #include "c_console.h" #include "sbar.h" -#include "farchive.h" #include "p_lnspec.h" #include "r_utility.h" #include "p_local.h" @@ -1470,25 +1469,3 @@ static void TerminalResponse (const char *str) } } - -template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node) -{ - DWORD convnum; - if (arc.IsStoring()) - { - arc.WriteCount (node == NULL? ~0u : node->ThisNodeNum); - } - else - { - convnum = arc.ReadCount(); - if (convnum >= StrifeDialogues.Size()) - { - node = NULL; - } - else - { - node = StrifeDialogues[convnum]; - } - } - return arc; -} diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 9f1233609..93b4506e3 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -29,6 +29,7 @@ #include "g_level.h" #include "farchive.h" #include "d_player.h" +#include "serializer.h" // MACROS ------------------------------------------------------------------ @@ -1446,13 +1447,23 @@ void DPSprite::Tick() // //------------------------------------------------------------------------ -void DPSprite::Serialize(FArchive &arc) +void DPSprite::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc << Next << Caller << Owner << Flags - << State << Tics << Sprite << Frame - << ID << x << y << oldx << oldy; + arc("next", Next) + ("caller", Caller) + ("owner", Owner) + ("flags", Flags) + ("state", State) + ("tics", Tics) + .Sprite("sprite", Sprite, nullptr) + ("frame", Frame) + ("id", ID) + ("x", x) + ("y", y) + ("oldx", oldx) + ("oldy", oldy); } //------------------------------------------------------------------------ diff --git a/src/p_pspr.h b/src/p_pspr.h index 41d258df8..7ef5e86ec 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -87,7 +87,7 @@ public: private: DPSprite () {} - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void Tick(); void Destroy(); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 13fac2e7e..c0f39be40 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -863,7 +863,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) arc.Array("sectors", sectors, &loadsectors[0], numsectors); arc.Array("polyobjs", polyobjs, po_NumPolyobjs); arc("subsectors", subsectors); - //StatusBar->Serialize(arc); + StatusBar->SerializeMessages(arc); arc("zones", Zones); arc("lineportals", linePortals); arc("sectorportals", sectorPortals); @@ -882,7 +882,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) void P_SerializeSounds (FArchive &arc) { S_SerializeSounds (arc); - DSeqNode::SerializeSequences (arc); + //DSeqNode::SerializeSequences (arc); char *name = NULL; BYTE order; @@ -900,40 +900,6 @@ void P_SerializeSounds (FArchive &arc) delete[] name; } -//========================================================================== -// -// ArchivePolyobjs -// -//========================================================================== -#define ASEG_POLYOBJS 104 - -void P_SerializePolyobjs (FArchive &arc) -{ - int i; - FPolyObj *po; - - if (arc.IsStoring ()) - { - int seg = ASEG_POLYOBJS; - arc << seg << po_NumPolyobjs; - for(i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++) - { - arc << po->tag << po->Angle << po->StartSpot.pos << po->interpolation << po->bBlocked << po->bHasPortals; - arc << po->specialdata; - } - } - else - { - int data; - DAngle angle; - DVector2 delta; - - for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++) - { - } - } -} - //========================================================================== // // @@ -976,7 +942,6 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad) // This must be saved, too, of course! FCanvasTextureInfo::Serialize(arc); - //AM_SerializeMarkers(arc); P_SerializePlayers(arc, hubLoad); P_SerializeSounds(arc); diff --git a/src/p_user.cpp b/src/p_user.cpp index bfefca466..56dc8ffc9 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3007,6 +3007,7 @@ void P_UnPredictPlayer () void player_t::Serialize(FArchive &arc) { +#if 0 int i; FString skinname; @@ -3079,54 +3080,6 @@ void player_t::Serialize(FArchive &arc) for (i = 0; i < MAXPLAYERS; i++) arc << frags[i]; - if (SaveVersion < 4547) - { - int layer = PSP_WEAPON; - for (i = 0; i < 5; i++) - { - FState *state; - int tics; - double sx, sy; - int sprite; - int frame; - - arc << state << tics - << sx << sy - << sprite << frame; - - if (state != nullptr && - ((layer < PSP_TARGETCENTER && ReadyWeapon != nullptr) || - (layer >= PSP_TARGETCENTER && mo->FindInventory(RUNTIME_CLASS(APowerTargeter), true)))) - { - DPSprite *pspr; - pspr = GetPSprite(PSPLayers(layer)); - pspr->State = state; - pspr->Tics = tics; - pspr->Sprite = sprite; - pspr->Frame = frame; - pspr->Owner = this; - - if (layer == PSP_FLASH) - { - pspr->x = 0; - pspr->y = 0; - } - else - { - pspr->x = sx; - pspr->y = sy; - } - } - - if (layer == PSP_WEAPON) - layer = PSP_FLASH; - else if (layer == PSP_FLASH) - layer = PSP_TARGETCENTER; - else - layer++; - } - } - else arc << psprites; arc << CurrentPlayerClass; @@ -3154,6 +3107,7 @@ void player_t::Serialize(FArchive &arc) userinfo.SkinChanged(skinname, CurrentPlayerClass); } arc << MUSINFOactor << MUSINFOtics; +#endif } bool P_IsPlayerTotallyFrozen(const player_t *player) diff --git a/src/r_utility.cpp b/src/r_utility.cpp index d5ab09b4c..985081a4b 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1043,6 +1043,7 @@ void FCanvasTextureInfo::EmptyList () void FCanvasTextureInfo::Serialize(FArchive &arc) { +#if 0 if (arc.IsStoring ()) { FCanvasTextureInfo *probe; @@ -1070,6 +1071,7 @@ void FCanvasTextureInfo::Serialize(FArchive &arc) Add (viewpoint, picnum, fov); } } +#endif } //========================================================================== diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index ebfbf056b..23837c28b 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -27,7 +27,7 @@ #include "templates.h" #include "c_dispatch.h" #include "g_level.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" // MACROS ------------------------------------------------------------------ @@ -106,7 +106,7 @@ class DSeqActorNode : public DSeqNode public: DSeqActorNode(AActor *actor, int sequence, int modenum); void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void MakeSound(int loop, FSoundID id) { S_Sound(m_Actor, CHAN_BODY|loop, id, clamp(m_Volume, 0.f, 1.f), m_Atten); @@ -134,7 +134,7 @@ class DSeqPolyNode : public DSeqNode public: DSeqPolyNode(FPolyObj *poly, int sequence, int modenum); void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void MakeSound(int loop, FSoundID id) { S_Sound (m_Poly, CHAN_BODY|loop, id, clamp(m_Volume, 0.f, 1.f), m_Atten); @@ -162,7 +162,7 @@ class DSeqSectorNode : public DSeqNode public: DSeqSectorNode(sector_t *sec, int chan, int sequence, int modenum); void Destroy(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void MakeSound(int loop, FSoundID id) { Channel = (Channel & 7) | CHAN_AREA | loop; @@ -280,9 +280,9 @@ static FRandom pr_sndseq ("SndSeq"); // CODE -------------------------------------------------------------------- -void DSeqNode::SerializeSequences (FArchive &arc) +void DSeqNode::SerializeSequences (FSerializer &arc) { - arc << SequenceListHead; + arc("sndseqlisthead", SequenceListHead); } IMPLEMENT_POINTY_CLASS (DSeqNode) @@ -298,55 +298,58 @@ DSeqNode::DSeqNode () m_Next = m_Prev = m_ChildSeqNode = m_ParentSeqNode = NULL; } -void DSeqNode::Serialize(FArchive &arc) +void DSeqNode::Serialize(FSerializer &arc) { int seqOffset; unsigned int i; + FName seqName; + int delayTics = 0; + FSoundID id; + float volume; + float atten = ATTN_NORM; + int seqnum; + unsigned int numchoices; - Super::Serialize (arc); - if (arc.IsStoring ()) + // copy these to local variables so that the actual serialization code does not need to be duplicated for saving and loading. + if (arc.isWriting()) { - seqOffset = (int)SN_GetSequenceOffset (m_Sequence, m_SequencePtr); - arc << seqOffset - << m_DelayUntilTic - << m_Volume - << m_Atten - << m_ModeNum - << m_Next - << m_Prev - << m_ChildSeqNode - << m_ParentSeqNode - << m_CurrentSoundID - << Sequences[m_Sequence]->SeqName; + seqOffset = (int)SN_GetSequenceOffset(m_Sequence, m_SequencePtr); + delayTics = m_DelayUntilTic; + volume = m_Volume; + atten = m_Atten; + id = m_CurrentSoundID; + seqName = Sequences[m_Sequence]->SeqName; + numchoices = m_SequenceChoices.Size(); + } + Super::Serialize(arc); - arc.WriteCount (m_SequenceChoices.Size()); - for (i = 0; i < m_SequenceChoices.Size(); ++i) + arc("seqoffset", seqOffset) + ("delaytics", delayTics) + ("volume", volume) + ("atten", atten) + ("modelnum", m_ModeNum) + ("next", m_Next) + ("prev", m_Prev) + ("childseqnode", m_ChildSeqNode) + ("parentseqnode", m_ParentSeqNode) + ("id", id) + ("seqname", seqName) + ("numchoices", numchoices); + + // The way this is saved makes it hard to encapsulate so just do it the hard way... + if (arc.isWriting()) + { + if (numchoices > 0 && arc.BeginArray("choices")) { - arc << Sequences[m_SequenceChoices[i]]->SeqName; + for (i = 0; i < m_SequenceChoices.Size(); ++i) + { + arc(nullptr, Sequences[m_SequenceChoices[i]]->SeqName); + } + arc.EndArray(); } } else { - FName seqName; - int delayTics = 0; - FSoundID id; - float volume; - float atten = ATTN_NORM; - int seqnum; - unsigned int numchoices; - - arc << seqOffset - << delayTics - << volume - << atten - << m_ModeNum - << m_Next - << m_Prev - << m_ChildSeqNode - << m_ParentSeqNode - << id - << seqName; - seqnum = FindSequence (seqName); if (seqnum >= 0) { @@ -360,12 +363,15 @@ void DSeqNode::Serialize(FArchive &arc) ChangeData (seqOffset, delayTics - TIME_REFERENCE, volume, id); - numchoices = arc.ReadCount(); m_SequenceChoices.Resize(numchoices); - for (i = 0; i < numchoices; ++i) + if (numchoices > 0 && arc.BeginArray("choices")) { - arc << seqName; - m_SequenceChoices[i] = FindSequence (seqName); + for (i = 0; i < numchoices; ++i) + { + arc(nullptr, seqName); + m_SequenceChoices[i] = FindSequence(seqName); + } + arc.EndArray(); } } } @@ -425,15 +431,15 @@ IMPLEMENT_POINTY_CLASS (DSeqActorNode) DECLARE_POINTER (m_Actor) END_POINTERS -void DSeqActorNode::Serialize(FArchive &arc) +void DSeqActorNode::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Actor; + arc("actor", m_Actor); } IMPLEMENT_CLASS (DSeqPolyNode) -void DSeqPolyNode::Serialize(FArchive &arc) +void DSeqPolyNode::Serialize(FSerializer &arc) { Super::Serialize (arc); //arc << m_Poly; @@ -441,10 +447,11 @@ void DSeqPolyNode::Serialize(FArchive &arc) IMPLEMENT_CLASS (DSeqSectorNode) -void DSeqSectorNode::Serialize(FArchive &arc) +void DSeqSectorNode::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << m_Sector << Channel; + arc("sector",m_Sector) + ("channel", Channel); } //========================================================================== diff --git a/src/s_sndseq.h b/src/s_sndseq.h index 89a95505a..74ded5af4 100644 --- a/src/s_sndseq.h +++ b/src/s_sndseq.h @@ -20,7 +20,7 @@ class DSeqNode : public DObject DECLARE_CLASS (DSeqNode, DObject) HAS_OBJECT_POINTERS public: - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); void StopAndDestroy (); void Destroy (); void Tick (); @@ -38,7 +38,7 @@ public: inline static DSeqNode *FirstSequence() { return SequenceListHead; } inline DSeqNode *NextSequence() const { return m_Next; } - static void SerializeSequences (FArchive &arc); + static void SerializeSequences (FSerializer &arc); protected: DSeqNode (); diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 3b01b519e..93fbe735a 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2237,7 +2237,7 @@ static FArchive &operator<<(FArchive &arc, FSoundChan &chan) switch (chan.SourceType) { case SOURCE_None: break; - case SOURCE_Actor: arc << chan.Actor; break; + //case SOURCE_Actor: arc << chan.Actor; break; case SOURCE_Sector: arc << chan.Sector; break; case SOURCE_Polyobj: /*arc << chan.Poly;*/ break; case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break; @@ -2324,7 +2324,7 @@ void S_SerializeSounds(FArchive &arc) // sounds might be heard briefly before pausing for the wipe. RestartEvictionsAt = level.time + 2; } - DSeqNode::SerializeSequences(arc); + //DSeqNode::SerializeSequences(arc); GSnd->Sync(false); GSnd->UpdateSounds(); } diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 939767c87..60adcc580 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -35,260 +35,10 @@ #define NEW_SPRITE ((BYTE)11) // A new sprite name follows #define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows -inline FArchive &operator<< (FArchive &arc, FLinkedSector &link) -{ - arc << link.Sector << link.Type; - return arc; -} - -// -// P_ArchiveWorld -// -void P_SerializeWorld (FArchive &arc) -{ - int i, j; - sector_t *sec; - line_t *li; - zone_t *zn; - - // do sectors - for (i = 0, sec = sectors; i < numsectors; i++, sec++) - { - arc << sec->floorplane - << sec->ceilingplane; - arc << sec->lightlevel; - arc << sec->special; - arc << sec->soundtraversed - << sec->seqType - << sec->friction - << sec->movefactor - << sec->stairlock - << sec->prevsec - << sec->nextsec - << sec->planes[sector_t::floor] - << sec->planes[sector_t::ceiling] - << sec->heightsec - << sec->bottommap << sec->midmap << sec->topmap - << sec->gravity; - P_SerializeTerrain(arc, sec->terrainnum[0]); - P_SerializeTerrain(arc, sec->terrainnum[1]); - arc << sec->damageamount; - arc << sec->damageinterval - << sec->leakydamage - << sec->damagetype - << sec->sky - << sec->MoreFlags - << sec->Flags - << sec->Portals[sector_t::floor] << sec->Portals[sector_t::ceiling] - << sec->ZoneNumber; - arc << sec->interpolations[0] - << sec->interpolations[1] - << sec->interpolations[2] - << sec->interpolations[3] - << sec->SeqName; - - sec->e->Serialize(arc); - if (arc.IsStoring ()) - { - arc << sec->ColorMap->Color - << sec->ColorMap->Fade; - BYTE sat = sec->ColorMap->Desaturate; - arc << sat; - } - else - { - PalEntry color, fade; - BYTE desaturate; - arc << color << fade - << desaturate; - sec->ColorMap = GetSpecialLights (color, fade, desaturate); - } - } - - // do lines - for (i = 0, li = lines; i < numlines; i++, li++) - { - arc << li->flags - << li->activation - << li->special - << li->alpha; - - if (P_IsACSSpecial(li->special)) - { - P_SerializeACSScriptNumber(arc, li->args[0], false); - } - else - { - arc << li->args[0]; - } - arc << li->args[1] << li->args[2] << li->args[3] << li->args[4]; - - arc << li->portalindex; - for (j = 0; j < 2; j++) - { - if (li->sidedef[j] == NULL) - continue; - - side_t *si = li->sidedef[j]; - arc << si->textures[side_t::top] - << si->textures[side_t::mid] - << si->textures[side_t::bottom] - << si->Light - << si->Flags - << si->LeftSide - << si->RightSide - << si->Index; - } - } - - // do zones - unsigned numzones = Zones.Size(); - arc << numzones; - - if (arc.IsLoading()) - { - Zones.Resize(numzones); - } - - for (i = 0, zn = &Zones[0]; i < (int)numzones; ++i, ++zn) - { - arc << zn->Environment; - } - - arc << linePortals << sectorPortals; - P_CollectLinkedPortals(); -} - -void P_SerializeWorldActors(FArchive &arc) -{ - int i; - sector_t *sec; - line_t *line; - - for (i = 0, sec = sectors; i < numsectors; i++, sec++) - { - arc << sec->SoundTarget - << sec->SecActTarget - << sec->floordata - << sec->ceilingdata - << sec->lightingdata; - } - for (auto &s : sectorPortals) - { - arc << s.mSkybox; - } - for (i = 0, line = lines; i < numlines; i++, line++) - { - for (int s = 0; s < 2; s++) - { - if (line->sidedef[s] != NULL) - { - //DBaseDecal::SerializeChain(arc, &line->sidedef[s]->AttachedDecals); - } - } - } -} - -void extsector_t::Serialize(FArchive &arc) -{ - arc << FakeFloor.Sectors - << Midtex.Floor.AttachedLines - << Midtex.Floor.AttachedSectors - << Midtex.Ceiling.AttachedLines - << Midtex.Ceiling.AttachedSectors - << Linked.Floor.Sectors - << Linked.Ceiling.Sectors; -} - -FArchive &operator<< (FArchive &arc, side_t::part &p) -{ - arc << p.xOffset << p.yOffset << p.interpolation << p.texture - << p.xScale << p.yScale;// << p.Light; - return arc; -} - -FArchive &operator<< (FArchive &arc, sector_t::splane &p) -{ - arc << p.xform.xOffs << p.xform.yOffs << p.xform.xScale << p.xform.yScale - << p.xform.Angle << p.xform.baseyOffs << p.xform.baseAngle - << p.Flags << p.Light << p.Texture << p.TexZ << p.alpha; - return arc; -} - -//========================================================================== -// -// ArchiveSubsectors -// -//========================================================================== -void RecalculateDrawnSubsectors(); - -void P_SerializeSubsectors(FArchive &arc) -{ - int num_verts, num_subs, num_nodes; - BYTE by; - - if (arc.IsStoring()) - { - if (hasglnodes) - { - arc << numvertexes << numsubsectors << numnodes; // These are only for verification - for(int i=0;iLen() > 0) tagstr = *Tag; - arc << tagstr; - if (arc.IsLoading()) - { - if (tagstr.Len() == 0) Tag = NULL; - else Tag = mStringPropertyData.Alloc(tagstr); - } - } - - if (arc.IsLoading ()) - { - touching_sectorlist = NULL; - LinkToWorld(false, Sector); - - AddToHash (); - SetShade (fillcolor); - if (player) - { - if (playeringame[player - players] && - player->cls != NULL && - !(flags4 & MF4_NOSKIN) && - state->sprite == GetDefaultByType (player->cls)->SpawnState->sprite) - { // Give player back the skin - sprite = skins[player->userinfo.GetSkin()].sprite; - } - if (Speed == 0) - { - Speed = GetDefault()->Speed; - } - } - ClearInterpolation(); - UpdateWaterLevel(false); - } -} FArchive &operator<< (FArchive &arc, sector_t *&sec) { @@ -532,12 +243,6 @@ FArchive &operator<< (FArchive &arc, FState *&state) return arc; } -void DObject::Serialize(FArchive &arc) -{ - ObjectFlags |= OF_SerialSuccess; -} - - FArchive &operator<< (FArchive &arc, secplane_t &plane) { arc << plane.normal << plane.D; @@ -549,37 +254,6 @@ FArchive &operator<< (FArchive &arc, secplane_t &plane) return arc; } -FArchive &operator<< (FArchive &arc, botskill_t &skill) -{ - return arc << skill.aiming << skill.perfection << skill.reaction << skill.isp; -} - -void DBot::Serialize(FArchive &arc) -{ - Super::Serialize(arc); - - arc << player - << Angle - << dest - << prev - << enemy - << missile - << mate - << last_mate - << skill - << t_active - << t_respawn - << t_strafe - << t_react - << t_fight - << t_roam - << t_rocket - << first_shot - << sleft - << allround - << increase - << old; -} void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) { arc << scriptnum; @@ -598,46 +272,3 @@ void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) } } } - -//============================================================================ -// -// Save a line portal for savegames. -// -//============================================================================ - -FArchive &operator<< (FArchive &arc, FLinePortal &port) -{ - arc << port.mOrigin - << port.mDestination - << port.mDisplacement - << port.mType - << port.mFlags - << port.mDefFlags - << port.mAlign; - return arc; -} - -//============================================================================ -// -// Save a sector portal for savegames. -// -//============================================================================ - -FArchive &operator<< (FArchive &arc, FSectorPortal &port) -{ - arc << port.mType - << port.mFlags - << port.mPartner - << port.mPlane - << port.mOrigin - << port.mDestination - << port.mDisplacement - << port.mPlaneZ; - if (arc.IsLoading()) - { - port.mSkybox = nullptr; - } - return arc; -} - - From 42e38f6cc161519635ac13b5d7c221411a956fc5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 10:59:48 +0200 Subject: [PATCH 17/59] - more cleanup to reduce references to FArchive. --- src/dobjtype.cpp | 12 +- src/farchive.cpp | 268 ----------------------------- src/farchive.h | 63 ------- src/info.h | 4 - src/p_user.cpp | 5 - src/r_data/r_translate.cpp | 2 + src/s_sound.cpp | 4 +- src/serializer.cpp | 23 ++- src/zzz_old.cpp | 343 ++++++++++++++----------------------- 9 files changed, 159 insertions(+), 565 deletions(-) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 7a593c5fe..e7a31d8cf 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -453,14 +453,18 @@ void PType::SkipValue(FArchive &ar, int tag) case VAL_Object: { +#if 0 DObject *skipper; ar << skipper; +#endif break; } case VAL_State: { +#if 0 FState *skipper; ar << skipper; +#endif break; } case VAL_String: @@ -1739,7 +1743,7 @@ int PStatePointer::GetRegType() const void PStatePointer::WriteValue(FArchive &ar, const void *addr) const { ar.WriteByte(VAL_State); - ar << *(FState **)addr; + //ar << *(FState **)addr; } //========================================================================== @@ -1754,7 +1758,7 @@ bool PStatePointer::ReadValue(FArchive &ar, void *addr) const ar << tag; if (tag == VAL_State) { - ar << *(FState **)addr; + //ar << *(FState **)addr; return true; } SkipValue(ar, tag); @@ -1856,6 +1860,7 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const void PPointer::WriteValue(FArchive &ar, const void *addr) const { +#if 0 if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { ar.WriteByte(VAL_Object); @@ -1866,6 +1871,7 @@ void PPointer::WriteValue(FArchive &ar, const void *addr) const assert(0 && "Pointer points to a type we don't handle"); I_Error("Attempt to save pointer to unhandled type"); } +#endif } //========================================================================== @@ -1876,6 +1882,7 @@ void PPointer::WriteValue(FArchive &ar, const void *addr) const bool PPointer::ReadValue(FArchive &ar, void *addr) const { +#if 0 BYTE tag; ar << tag; if (tag == VAL_Object && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) @@ -1884,6 +1891,7 @@ bool PPointer::ReadValue(FArchive &ar, void *addr) const return true; } SkipValue(ar, tag); +#endif return false; } diff --git a/src/farchive.cpp b/src/farchive.cpp index 1151c6198..98063be87 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -1021,267 +1021,6 @@ FArchive &FArchive::operator<< (FName &n) return *this; } -FArchive &FArchive::SerializePointer (void *ptrbase, BYTE **ptr, DWORD elemSize) -{ - DWORD w; - - if (m_Storing) - { - if (*(void **)ptr) - { - w = DWORD(((size_t)*ptr - (size_t)ptrbase) / elemSize); - } - else - { - w = ~0u; - } - WriteCount (w); - } - else - { - w = ReadCount (); - if (w != ~0u) - { - *(void **)ptr = (BYTE *)ptrbase + w * elemSize; - } - else - { - *(void **)ptr = NULL; - } - } - return *this; -} - -FArchive &FArchive::SerializeObject (DObject *&object, PClass *type) -{ - if (!m_ThinkersAllowed && type->IsDescendantOf(RUNTIME_CLASS(DThinker))) - { - assert(true); - I_Error("Tried to serialize a thinker before P_SerializeThinkers"); - } - - if (!type->IsDescendantOf(RUNTIME_CLASS(PClass))) - { // a regular object - if (IsStoring()) - { - return WriteObject(object); - } - else - { - return ReadObject(object, type); - } - } - else - { // a class object - if (IsStoring()) - { - UserWriteClass((PClass *)object); - } - else - { - UserReadClass(object); - } - return *this; - } -} - -FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype) -{ -#if 0 - BYTE objHead; - const PClass *type; - BYTE playerNum; - DWORD index; - DObject *newobj; - - operator<< (objHead); - - switch (objHead) - { - case NULL_OBJ: - obj = NULL; - break; - - case M1_OBJ: - obj = (DObject *)~0; - break; - - case OLD_OBJ: - index = ReadCount(); - if (index >= ArchiveToObject.Size()) - { - I_Error ("Object reference too high (%u; max is %u)\n", index, ArchiveToObject.Size()); - } - obj = ArchiveToObject[index]; - break; - - case NEW_PLYR_CLS_OBJ: - operator<< (playerNum); - if (m_HubTravel) - { - // If travelling inside a hub, use the existing player actor - type = ReadClass (wanttype); -// Printf ("New player class: %s (%u)\n", type->Name, m_File->Tell()); - obj = players[playerNum].mo; - - // But also create a new one so that we can get past the one - // stored in the archive. - AActor *tempobj = static_cast(type->CreateNew ()); - MapObject (obj != NULL ? obj : tempobj); - tempobj->SerializeUserVars (*this); - tempobj->Serialize (*this); - tempobj->CheckIfSerialized (); - // If this player is not present anymore, keep the new body - // around just so that the load will succeed. - if (obj != NULL) - { - // When the temporary player's inventory items were loaded, - // they became owned by the real player. Undo that now. - for (AInventory *item = tempobj->Inventory; item != NULL; item = item->Inventory) - { - item->Owner = tempobj; - } - tempobj->Destroy (); - } - else - { - obj = tempobj; - players[playerNum].mo = static_cast(obj); - } - break; - } - /* fallthrough when not travelling to a previous level */ - case NEW_CLS_OBJ: - type = ReadClass (wanttype); -// Printf ("New class: %s (%u)\n", type->Name, m_File->Tell()); - newobj = obj = type->CreateNew (); - MapObject (obj); - newobj->SerializeUserVars (*this); - newobj->Serialize (*this); - newobj->CheckIfSerialized (); - break; - - case NEW_PLYR_OBJ: - operator<< (playerNum); - if (m_HubTravel) - { - type = ReadStoredClass (wanttype); -// Printf ("Use player class: %s (%u)\n", type->Name, m_File->Tell()); - obj = players[playerNum].mo; - - AActor *tempobj = static_cast(type->CreateNew ()); - MapObject (obj != NULL ? obj : tempobj); - tempobj->SerializeUserVars (*this); - tempobj->Serialize (*this); - tempobj->CheckIfSerialized (); - if (obj != NULL) - { - for (AInventory *item = tempobj->Inventory; - item != NULL; item = item->Inventory) - { - item->Owner = tempobj; - } - tempobj->Destroy (); - } - else - { - obj = tempobj; - players[playerNum].mo = static_cast(obj); - } - break; - } - /* fallthrough when not travelling to a previous level */ - case NEW_OBJ: - type = ReadStoredClass (wanttype); -// Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell()); - obj = type->CreateNew (); - MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); - break; - - default: - I_Error ("Unknown object code (%d) in archive\n", objHead); - } -#endif - return *this; -} - -void FArchive::WriteSprite (int spritenum) -{ - BYTE id; - - if ((unsigned)spritenum >= (unsigned)sprites.Size()) - { - spritenum = 0; - } - - if (m_SpriteMap[spritenum] < 0) - { - m_SpriteMap[spritenum] = (int)(m_NumSprites++); - id = NEW_SPRITE; - Write (&id, 1); - Write (sprites[spritenum].name, 4); - - // Write the current sprite number as a hint, because - // these will only change between different versions. - WriteCount (spritenum); - } - else - { - id = OLD_SPRITE; - Write (&id, 1); - WriteCount (m_SpriteMap[spritenum]); - } -} - -int FArchive::ReadSprite () -{ - BYTE id; - - Read (&id, 1); - if (id == OLD_SPRITE) - { - DWORD index = ReadCount (); - if (index >= m_NumSprites) - { - I_Error ("Sprite %u has not been read yet\n", index); - } - return m_SpriteMap[index]; - } - else if (id == NEW_SPRITE) - { - DWORD name; - DWORD hint; - - Read (&name, 4); - hint = ReadCount (); - - if (hint >= NumStdSprites || sprites[hint].dwName != name) - { - for (hint = NumStdSprites; hint-- != 0; ) - { - if (sprites[hint].dwName == name) - { - break; - } - } - if (hint >= sprites.Size()) - { // Don't know this sprite, so just use the first one - hint = 0; - } - } - m_SpriteMap[m_NumSprites++] = hint; - return hint; - } - else - { - I_Error ("Expected a sprite but got something else\n"); - return 0; - } -} - DWORD FArchive::AddName (const char *name) { DWORD index; @@ -1388,13 +1127,6 @@ PClass *FArchive::ReadStoredClass (const PClass *wanttype) return type; } -DWORD FArchive::MapObject (DObject *obj) -{ - DWORD i = ArchiveToObject.Push(obj); - ObjectToArchive[obj] = i; - return i; -} - void FArchive::UserWriteClass (PClass *type) { BYTE id; diff --git a/src/farchive.h b/src/farchive.h index 6353bda7d..f216fc78a 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -196,16 +196,10 @@ virtual void Read (void *mem, unsigned int len); FArchive& operator<< (char *&str); FArchive& operator<< (FName &n); FArchive& operator<< (FString &str); - FArchive& SerializePointer (void *ptrbase, BYTE **ptr, DWORD elemSize); - FArchive& SerializeObject (DObject *&object, PClass *type); - FArchive& WriteObject (DObject *obj); - FArchive& ReadObject (DObject *&obj, PClass *wanttype); void WriteName (const char *name); const char *ReadName (); // The returned name disappears with the archive, unlike strings - void WriteSprite (int spritenum); - int ReadSprite (); inline FArchive& operator<< (SBYTE &c) { return operator<< ((BYTE &)c); } inline FArchive& operator<< (SWORD &s) { return operator<< ((WORD &)s); } @@ -214,7 +208,6 @@ inline FArchive& operator<< (SQWORD &i) { return operator<< ((QWORD &)i); } inline FArchive& operator<< (unsigned char *&str) { return operator<< ((char *&)str); } inline FArchive& operator<< (signed char *&str) { return operator<< ((char *&)str); } inline FArchive& operator<< (bool &b) { return operator<< ((BYTE &)b); } -inline FArchive& operator<< (DObject* &object) { return ReadObject (object, RUNTIME_CLASS(DObject)); } void EnableThinkers() { @@ -229,7 +222,6 @@ inline FArchive& operator<< (DObject* &object) { return ReadObject (object, RUN protected: enum { EObjectHashSize = 137 }; - DWORD MapObject (DObject *obj); DWORD WriteClass (PClass *info); PClass *ReadClass (); PClass *ReadClass (const PClass *wanttype); @@ -283,60 +275,5 @@ public: FPNGChunkFile Chunk; }; -inline FArchive &operator<< (FArchive &arc, PalEntry &p) -{ - return arc << p.a << p.r << p.g << p.b; -} - -struct FStrifeDialogueNode; -struct FSwitchDef; -struct FDoorAnimation; -struct FLinePortal; -FArchive &operator<< (FArchive &arc, FLinePortal &da); -FArchive &operator<< (FArchive &arc, FSectorPortal &da); - - - -template -inline FArchive &operator<< (FArchive &arc, TArray &self) -{ - if (arc.IsStoring()) - { - arc.WriteCount(self.Count); - } - else - { - DWORD numStored = arc.ReadCount(); - self.Resize(numStored); - } - for (unsigned int i = 0; i < self.Count; ++i) - { - arc << self.Array[i]; - } - return arc; -} - -struct sector_t; -struct line_t; -struct vertex_t; -struct side_t; - -FArchive &operator<< (FArchive &arc, sector_t *&sec); -FArchive &operator<< (FArchive &arc, const sector_t *&sec); -FArchive &operator<< (FArchive &arc, line_t *&line); -FArchive &operator<< (FArchive &arc, vertex_t *&vert); -FArchive &operator<< (FArchive &arc, side_t *&side); - -FArchive &operator<<(FArchive &arc, DAngle &ang); -FArchive &operator<<(FArchive &arc, DVector3 &vec); -FArchive &operator<<(FArchive &arc, DVector2 &vec); - - - -template -FArchive& operator<< (FArchive& arc, TFlags& flag) -{ - return flag.Serialize (arc); -} #endif //__FARCHIVE_H__ diff --git a/src/info.h b/src/info.h index 6ced755a3..2c8365dd4 100644 --- a/src/info.h +++ b/src/info.h @@ -168,10 +168,6 @@ struct FStateLabels void Destroy(); // intentionally not a destructor! }; - - -FArchive &operator<< (FArchive &arc, FState *&state); - #include "gametype.h" struct DmgFactors : public TMap diff --git a/src/p_user.cpp b/src/p_user.cpp index 56dc8ffc9..57846dea4 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -238,11 +238,6 @@ CCMD (playerclasses) // 16 pixels of bob #define MAXBOB 16. -FArchive &operator<< (FArchive &arc, player_t *&p) -{ - return arc.SerializePointer (players, (BYTE **)&p, sizeof(*players)); -} - // The player_t constructor. Since LogText is not a POD, we cannot just // memset it all to 0. player_t::player_t() diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 37a3fb854..1772bc07f 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -194,6 +194,7 @@ bool FRemapTable::operator==(const FRemapTable &o) void FRemapTable::Serialize(FArchive &arc) { +#if 0 int n = NumEntries; arc << NumEntries; @@ -214,6 +215,7 @@ void FRemapTable::Serialize(FArchive &arc) { arc << Palette[j]; } +#endif } //---------------------------------------------------------------------------- diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 93fbe735a..0f7d690fa 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2234,15 +2234,17 @@ FArchive &operator<<(FArchive &arc, FSoundID &sid) static FArchive &operator<<(FArchive &arc, FSoundChan &chan) { arc << chan.SourceType; +#if 0 switch (chan.SourceType) { case SOURCE_None: break; - //case SOURCE_Actor: arc << chan.Actor; break; + case SOURCE_Actor: arc << chan.Actor; break; case SOURCE_Sector: arc << chan.Sector; break; case SOURCE_Polyobj: /*arc << chan.Poly;*/ break; case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break; default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; } +#endif arc << chan.SoundID << chan.OrgID << chan.Volume diff --git a/src/serializer.cpp b/src/serializer.cpp index 35ce9d4e3..c282c9090 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -560,14 +560,27 @@ void FSerializer::WriteObjects() if (isWriting()) { BeginArray("objects"); + // we cannot use the C++11 shorthand syntax here because the array may grow while being processed. for (unsigned i = 0; i < w->mDObjects.Size(); i++) { + auto obj = w->mDObjects[i]; + player_t *player; + BeginObject(nullptr); - WriteKey("classtype"); - w->mWriter.String(w->mDObjects[i]->GetClass()->TypeName.GetChars()); - w->mDObjects[i]->SerializeUserVars(*this); - w->mDObjects[i]->Serialize(*this); - w->mDObjects[i]->CheckIfSerialized(); + w->mWriter.Key("classtype"); + w->mWriter.String(obj->GetClass()->TypeName.GetChars()); + + if (obj->IsKindOf(RUNTIME_CLASS(AActor)) && + (player = static_cast(obj)->player) && + player->mo == obj) + { + w->mWriter.Key("playerindex"); + w->mWriter.Int(int(player - players)); + } + + obj->SerializeUserVars(*this); + obj->Serialize(*this); + obj->CheckIfSerialized(); EndObject(); } EndArray(); diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 60adcc580..92dd10877 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -36,223 +36,6 @@ #define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows -FArchive &FArchive::WriteObject (DObject *obj) -{ -#if 0 - player_t *player; - BYTE id[2]; - - if (obj == NULL) - { - id[0] = NULL_OBJ; - Write (id, 1); - } - else if (obj == (DObject*)~0) - { - id[0] = M1_OBJ; - Write (id, 1); - } - else if (obj->ObjectFlags & OF_EuthanizeMe) - { - // Objects that want to die are not saved to the archive, but - // we leave the pointers to them alone. - id[0] = NULL_OBJ; - Write (id, 1); - } - else - { - PClass *type = obj->GetClass(); - DWORD *classarcid; - - if (type == RUNTIME_CLASS(DObject)) - { - //I_Error ("Tried to save an instance of DObject.\n" - // "This should not happen.\n"); - id[0] = NULL_OBJ; - Write (id, 1); - } - else if (NULL == (classarcid = ClassToArchive.CheckKey(type))) - { - // No instances of this class have been written out yet. - // Write out the class, then write out the object. If this - // is an actor controlled by a player, make note of that - // so that it can be overridden when moving around in a hub. - if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && - (player = static_cast(obj)->player) && - player->mo == obj) - { - id[0] = NEW_PLYR_CLS_OBJ; - id[1] = (BYTE)(player - players); - Write (id, 2); - } - else - { - id[0] = NEW_CLS_OBJ; - Write (id, 1); - } - WriteClass (type); -// Printf ("Make class %s (%u)\n", type->Name, m_File->Tell()); - MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); - } - else - { - // An instance of this class has already been saved. If - // this object has already been written, save a reference - // to the saved object. Otherwise, save a reference to the - // class, then save the object. Again, if this is a player- - // controlled actor, remember that. - DWORD *objarcid = ObjectToArchive.CheckKey(obj); - - if (objarcid == NULL) - { - - if (obj->IsKindOf (RUNTIME_CLASS (AActor)) && - (player = static_cast(obj)->player) && - player->mo == obj) - { - id[0] = NEW_PLYR_OBJ; - id[1] = (BYTE)(player - players); - Write (id, 2); - } - else - { - id[0] = NEW_OBJ; - Write (id, 1); - } - WriteCount (*classarcid); -// Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell()); - MapObject (obj); - obj->SerializeUserVars (*this); - obj->Serialize (*this); - obj->CheckIfSerialized (); - } - else - { - id[0] = OLD_OBJ; - Write (id, 1); - WriteCount (*objarcid); - } - } - } -#endif - return *this; -} - - -FArchive &operator<< (FArchive &arc, sector_t *&sec) -{ - return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); -} - -FArchive &operator<< (FArchive &arc, const sector_t *&sec) -{ - return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors)); -} - -FArchive &operator<< (FArchive &arc, line_t *&line) -{ - return arc.SerializePointer (lines, (BYTE **)&line, sizeof(*lines)); -} - -FArchive &operator<< (FArchive &arc, vertex_t *&vert) -{ - return arc.SerializePointer (vertexes, (BYTE **)&vert, sizeof(*vertexes)); -} - -FArchive &operator<< (FArchive &arc, side_t *&side) -{ - return arc.SerializePointer (sides, (BYTE **)&side, sizeof(*sides)); -} - -FArchive &operator<<(FArchive &arc, DAngle &ang) -{ - arc << ang.Degrees; - return arc; -} - -FArchive &operator<<(FArchive &arc, DVector3 &vec) -{ - arc << vec.X << vec.Y << vec.Z; - return arc; -} - -FArchive &operator<<(FArchive &arc, DVector2 &vec) -{ - arc << vec.X << vec.Y; - return arc; -} - -//========================================================================== -// -// -//========================================================================== - -FArchive &operator<< (FArchive &arc, FState *&state) -{ - PClassActor *info; - - if (arc.IsStoring ()) - { - if (state == NULL) - { - arc.UserWriteClass (RUNTIME_CLASS(AActor)); - arc.WriteCount (NULL_STATE_INDEX); - return arc; - } - - info = FState::StaticFindStateOwner (state); - - if (info != NULL) - { - arc.UserWriteClass (info); - arc.WriteCount ((DWORD)(state - info->OwnedStates)); - } - else - { - /* this was never working as intended. - I_Error ("Cannot find owner for state %p:\n" - "%s %c%c %3d [%p] -> %p", state, - sprites[state->sprite].name, - state->GetFrame() + 'A', - state->GetFullbright() ? '*' : ' ', - state->GetTics(), - state->GetAction(), - state->GetNextState()); - */ - } - } - else - { - PClassActor *info; - DWORD ofs; - - arc.UserReadClass(info); - ofs = arc.ReadCount (); - if (ofs == NULL_STATE_INDEX && info == RUNTIME_CLASS(AActor)) - { - state = NULL; - } - else - { - state = info->OwnedStates + ofs; - } - } - return arc; -} - -FArchive &operator<< (FArchive &arc, secplane_t &plane) -{ - arc << plane.normal << plane.D; - if (plane.normal.Z != 0) - { // plane.c should always be non-0. Otherwise, the plane - // would be perfectly vertical. (But then, don't let this crash on a broken savegame...) - plane.negiC = -1 / plane.normal.Z; - } - return arc; -} void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) { @@ -272,3 +55,129 @@ void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) } } } + + +#if 0 +// still needed as reference. +FArchive &FArchive::ReadObject(DObject* &obj, PClass *wanttype) +{ + BYTE objHead; + const PClass *type; + BYTE playerNum; + DWORD index; + DObject *newobj; + + operator<< (objHead); + + switch (objHead) + { + case NULL_OBJ: + obj = NULL; + break; + + case M1_OBJ: + obj = (DObject *)~0; + break; + + case OLD_OBJ: + index = ReadCount(); + if (index >= ArchiveToObject.Size()) + { + I_Error("Object reference too high (%u; max is %u)\n", index, ArchiveToObject.Size()); + } + obj = ArchiveToObject[index]; + break; + + case NEW_PLYR_CLS_OBJ: + operator<< (playerNum); + if (m_HubTravel) + { + // If travelling inside a hub, use the existing player actor + type = ReadClass(wanttype); + // Printf ("New player class: %s (%u)\n", type->Name, m_File->Tell()); + obj = players[playerNum].mo; + + // But also create a new one so that we can get past the one + // stored in the archive. + AActor *tempobj = static_cast(type->CreateNew()); + MapObject(obj != NULL ? obj : tempobj); + tempobj->SerializeUserVars(*this); + tempobj->Serialize(*this); + tempobj->CheckIfSerialized(); + // If this player is not present anymore, keep the new body + // around just so that the load will succeed. + if (obj != NULL) + { + // When the temporary player's inventory items were loaded, + // they became owned by the real player. Undo that now. + for (AInventory *item = tempobj->Inventory; item != NULL; item = item->Inventory) + { + item->Owner = tempobj; + } + tempobj->Destroy(); + } + else + { + obj = tempobj; + players[playerNum].mo = static_cast(obj); + } + break; + } + /* fallthrough when not travelling to a previous level */ + case NEW_CLS_OBJ: + type = ReadClass(wanttype); + // Printf ("New class: %s (%u)\n", type->Name, m_File->Tell()); + newobj = obj = type->CreateNew(); + MapObject(obj); + newobj->SerializeUserVars(*this); + newobj->Serialize(*this); + newobj->CheckIfSerialized(); + break; + + case NEW_PLYR_OBJ: + operator<< (playerNum); + if (m_HubTravel) + { + type = ReadStoredClass(wanttype); + // Printf ("Use player class: %s (%u)\n", type->Name, m_File->Tell()); + obj = players[playerNum].mo; + + AActor *tempobj = static_cast(type->CreateNew()); + MapObject(obj != NULL ? obj : tempobj); + tempobj->SerializeUserVars(*this); + tempobj->Serialize(*this); + tempobj->CheckIfSerialized(); + if (obj != NULL) + { + for (AInventory *item = tempobj->Inventory; + item != NULL; item = item->Inventory) + { + item->Owner = tempobj; + } + tempobj->Destroy(); + } + else + { + obj = tempobj; + players[playerNum].mo = static_cast(obj); + } + break; + } + /* fallthrough when not travelling to a previous level */ + case NEW_OBJ: + type = ReadStoredClass(wanttype); + // Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell()); + obj = type->CreateNew(); + MapObject(obj); + obj->SerializeUserVars(*this); + obj->Serialize(*this); + obj->CheckIfSerialized(); + break; + + default: + I_Error("Unknown object code (%d) in archive\n", objHead); + } + return *this; +} +#endif + From cf1e6d527552030f07cc3832910dca5881095105 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 11:35:25 +0200 Subject: [PATCH 18/59] - converted FBehavior::StaticSerializeModuleStates. - removed some code which is no longer needed. --- src/dobject.h | 6 -- src/doomtype.h | 1 - src/p_acs.cpp | 130 ++++++++++++++------------------ src/p_acs.h | 6 +- src/p_pspr.h | 1 - src/p_saveg.cpp | 2 +- src/p_terrain.h | 2 - src/r_data/renderstyle.cpp | 6 -- src/r_data/renderstyle.h | 4 - src/textures/texturemanager.cpp | 19 ----- 10 files changed, 61 insertions(+), 116 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index 0419e8c59..0f81ae873 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -414,18 +414,12 @@ public: return GC::ReadBarrier(p) == u; } - template friend inline FArchive &operator<<(FArchive &arc, TObjPtr &o); template friend inline void GC::Mark(TObjPtr &obj); template friend FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr &value, TObjPtr *); friend class DObject; }; -template inline FArchive &operator<<(FArchive &arc, TObjPtr &o) -{ - return arc << o.p; -} - // Use barrier_cast instead of static_cast when you need to cast // the contents of a TObjPtr to a related type. template inline T barrier_cast(TObjPtr &o) diff --git a/src/doomtype.h b/src/doomtype.h index 34f750313..ccf8ccc3d 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -212,7 +212,6 @@ class PClassInventory; class FTextureID { friend class FTextureManager; - friend FArchive &operator<< (FArchive &arc, FTextureID &tex); friend FTextureID GetHUDIcon(PClassInventory *cls); friend void R_InitSpriteDefs(); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index b49f820f4..c366e4f42 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1628,64 +1628,72 @@ void FBehavior::UnlockMapVarStrings() const } } -void FBehavior::StaticSerializeModuleStates (FArchive &arc) +void FBehavior::StaticSerializeModuleStates (FSerializer &arc) { - DWORD modnum; + auto modnum = StaticModules.Size(); - modnum = StaticModules.Size(); - arc << modnum; - - if (modnum != StaticModules.Size()) + if (arc.BeginObject("acsmodules")) { - I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); - } + arc("count", modnum); - for (modnum = 0; modnum < StaticModules.Size(); ++modnum) - { - FBehavior *module = StaticModules[modnum]; - int ModSize = module->GetDataSize(); + if (modnum != StaticModules.Size()) + { + I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); + } - if (arc.IsStoring()) + for (modnum = 0; modnum < StaticModules.Size(); ++modnum) { - arc.WriteString (module->ModuleName); - arc << ModSize; - } - else - { - char *modname = NULL; - arc << modname; - arc << ModSize; - if (stricmp (modname, module->ModuleName) != 0) + if (arc.BeginArray("modules")) { - delete[] modname; - I_Error("Level was saved with a different set or order of ACS modules. (Have %s, save has %s)", module->ModuleName, modname); + FBehavior *module = StaticModules[modnum]; + const char *modname; + int ModSize = module->GetDataSize(); + + if (arc.BeginObject(nullptr)) + { + arc.StringPtr("modname", modname) + ("modsize", ModSize); + + if (arc.isReading()) + { + if (stricmp(modname, module->ModuleName) != 0) + { + I_Error("Level was saved with a different set or order of ACS modules. (Have %s, save has %s)", module->ModuleName, modname); + } + else if (ModSize != module->GetDataSize()) + { + I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); + } + } + module->SerializeVars(arc); + arc.EndObject(); + } + arc.EndArray(); } - else if (ModSize != module->GetDataSize()) - { - delete[] modname; - I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); - } - delete[] modname; } - module->SerializeVars (arc); + arc.EndObject(); } } -void FBehavior::SerializeVars (FArchive &arc) +void FBehavior::SerializeVars (FSerializer &arc) { - SerializeVarSet (arc, MapVarStore, NUM_MAPVARS); - for (int i = 0; i < NumArrays; ++i) + if (arc.BeginArray("variables")) { - SerializeVarSet (arc, ArrayStore[i].Elements, ArrayStore[i].ArraySize); + SerializeVarSet(arc, MapVarStore, NUM_MAPVARS); + for (int i = 0; i < NumArrays; ++i) + { + SerializeVarSet(arc, ArrayStore[i].Elements, ArrayStore[i].ArraySize); + } + arc.EndArray(); } } -void FBehavior::SerializeVarSet (FArchive &arc, SDWORD *vars, int max) +void FBehavior::SerializeVarSet (FSerializer &arc, SDWORD *vars, int max) { - SDWORD arcval; + SDWORD count; SDWORD first, last; - if (arc.IsStoring ()) + if (arc.isWriting ()) { // Find first non-zero variable for (first = 0; first < max; ++first) @@ -1707,52 +1715,28 @@ void FBehavior::SerializeVarSet (FArchive &arc, SDWORD *vars, int max) if (last < first) { // no non-zero variables - arcval = 0; - arc << arcval; + count = 0; + arc("count", count); return; } - arcval = last - first + 1; - arc << arcval; - arcval = first; - arc << arcval; - - while (first <= last) - { - arc << vars[first]; - ++first; - } + count = last - first + 1; + arc("count", count); + arc("first", first); + arc.Array("values", &vars[first], count); } else { - SDWORD truelast; - memset (vars, 0, max*sizeof(*vars)); + arc("count", count); - arc << last; - if (last == 0) + if (count == 0) { return; } - arc << first; - last += first; - truelast = last; - - if (last > max) - { - last = max; - } - - while (first < last) - { - arc << vars[first]; - ++first; - } - while (first < truelast) - { - arc << arcval; - ++first; - } + arc("first", first); + if (first + count > max) count = max - first; + arc.Array("values", &vars[first], count); } } diff --git a/src/p_acs.h b/src/p_acs.h index b31996788..512780f8c 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -324,7 +324,7 @@ public: static void StaticUnloadModules (); static bool StaticCheckAllGood (); static FBehavior *StaticGetModule (int lib); - static void StaticSerializeModuleStates (FArchive &arc); + static void StaticSerializeModuleStates (FSerializer &arc); static void StaticMarkLevelVarStrings(); static void StaticLockLevelVarStrings(); static void StaticUnlockLevelVarStrings(); @@ -368,8 +368,8 @@ private: void UnescapeStringTable(BYTE *chunkstart, BYTE *datastart, bool haspadding); int FindStringInChunk (DWORD *chunk, const char *varname) const; - void SerializeVars (FArchive &arc); - void SerializeVarSet (FArchive &arc, SDWORD *vars, int max); + void SerializeVars (FSerializer &arc); + void SerializeVarSet (FSerializer &arc, SDWORD *vars, int max); void MarkMapVarStrings() const; void LockMapVarStrings() const; diff --git a/src/p_pspr.h b/src/p_pspr.h index 7ef5e86ec..7e5109657 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -35,7 +35,6 @@ #define WEAPONTOP (32+6./16) class AInventory; -class FArchive; // // Overlay psprites are scaled shapes diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index c0f39be40..2e927e9fa 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -857,7 +857,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON AM_SerializeMarkers(arc); - //FBehavior::StaticSerializeModuleStates(arc); + FBehavior::StaticSerializeModuleStates(arc); arc.Array("linedefs", lines, &loadlines[0], numlines); arc.Array("sidedefs", sides, &loadsides[0], numsides); arc.Array("sectors", sectors, &loadsectors[0], numsectors); diff --git a/src/p_terrain.h b/src/p_terrain.h index 77d57da6c..694aa9f14 100644 --- a/src/p_terrain.h +++ b/src/p_terrain.h @@ -122,9 +122,7 @@ struct FTerrainDef extern TArray Splashes; extern TArray Terrains; -class FArchive; int P_FindTerrain(FName name); -void P_SerializeTerrain(FArchive &arc, int &terrainnum); FName P_GetTerrainName(int terrainnum); #endif //__P_TERRAIN_H__ diff --git a/src/r_data/renderstyle.cpp b/src/r_data/renderstyle.cpp index fdd20aa17..f84c941d9 100644 --- a/src/r_data/renderstyle.cpp +++ b/src/r_data/renderstyle.cpp @@ -99,12 +99,6 @@ static struct LegacyInit #endif -FArchive &operator<< (FArchive &arc, FRenderStyle &style) -{ - arc << style.BlendOp << style.SrcAlpha << style.DestAlpha << style.Flags; - return arc; -} - double GetAlpha(int type, double alpha) { switch (type) diff --git a/src/r_data/renderstyle.h b/src/r_data/renderstyle.h index c103610ef..78687cec0 100644 --- a/src/r_data/renderstyle.h +++ b/src/r_data/renderstyle.h @@ -162,8 +162,4 @@ inline FRenderStyle &FRenderStyle::operator= (ERenderStyle legacy) return *this; } -class FArchive; - -FArchive &operator<< (FArchive &arc, FRenderStyle &style); - #endif diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 95c976f8c..ba4db5e1d 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1227,25 +1227,6 @@ int FTextureManager::CountLumpTextures (int lumpnum) } -//========================================================================== -// -// operator<< -// -//========================================================================== - -FArchive &operator<< (FArchive &arc, FTextureID &tex) -{ - if (arc.IsStoring()) - { - TexMan.WriteTexture(arc, tex.texnum); - } - else - { - tex.texnum = TexMan.ReadTexture(arc); - } - return arc; -} - //========================================================================== // // FTextureID::operator+ From f3e8c7c241b6fb52060cb03d782f047086580d4f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 13:21:41 +0200 Subject: [PATCH 19/59] - fixed incomplete hudmessage serialization. - fixed a few errors in the ACS module serializer. - reordered a few things to how they were in the old code. - optimized serialization of the level.Scrolls array to happen within the sector. This is to allow skipping 0-entries which normally constitute the vast majority of them. --- src/g_shared/hudmessages.cpp | 4 ++ src/json.cpp | 1 - src/p_acs.cpp | 100 ++++++++++++++--------------------- src/p_saveg.cpp | 32 ++++++++--- src/serializer.cpp | 34 +++++++++++- src/serializer.h | 1 + 6 files changed, 102 insertions(+), 70 deletions(-) diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 531870571..b86cf6417 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -182,6 +182,10 @@ void DHUDMessage::Serialize(FSerializer &arc) ("tics", Tics) ("state", State) .Enum("textcolor", TextColor) + ("sbarid", SBarID) + ("sourcetext", SourceText) + ("font", Font) + ("next", Next) ("hudwidth", HUDWidth) ("hudheight", HUDHeight) ("nowrap", NoWrap) diff --git a/src/json.cpp b/src/json.cpp index 2d8c84d33..43f185301 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -71,7 +71,6 @@ CCMD(writejson) FSerializer arc; arc.OpenWriter(); arc.BeginObject(nullptr); - DThinker::SerializeThinkers(arc, false); G_SerializeLevel(arc, false); arc.WriteObjects(); arc.EndObject(); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index c366e4f42..22b44aa1c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1646,7 +1646,7 @@ void FBehavior::StaticSerializeModuleStates (FSerializer &arc) if (arc.BeginArray("modules")) { FBehavior *module = StaticModules[modnum]; - const char *modname; + const char *modname = module->ModuleName; int ModSize = module->GetDataSize(); if (arc.BeginObject(nullptr)) @@ -1693,50 +1693,54 @@ void FBehavior::SerializeVarSet (FSerializer &arc, SDWORD *vars, int max) SDWORD count; SDWORD first, last; - if (arc.isWriting ()) + if (arc.BeginObject(nullptr)) { - // Find first non-zero variable - for (first = 0; first < max; ++first) + if (arc.isWriting()) { - if (vars[first] != 0) + // Find first non-zero variable + for (first = 0; first < max; ++first) { - break; + if (vars[first] != 0) + { + break; + } + } + + // Find last non-zero variable + for (last = max - 1; last >= first; --last) + { + if (vars[last] != 0) + { + break; + } + } + + if (last < first) + { // no non-zero variables + count = 0; + arc("count", count); + } + else + { + count = last - first + 1; + arc("count", count); + arc("first", first); + arc.Array("values", &vars[first], count); } } - - // Find last non-zero variable - for (last = max - 1; last >= first; --last) + else { - if (vars[last] != 0) - { - break; - } - } - - if (last < first) - { // no non-zero variables - count = 0; + memset(vars, 0, max * sizeof(*vars)); arc("count", count); - return; - } - count = last - first + 1; - arc("count", count); - arc("first", first); - arc.Array("values", &vars[first], count); - } - else - { - memset (vars, 0, max*sizeof(*vars)); - arc("count", count); - - if (count == 0) - { - return; + if (count != 0) + { + arc("first", first); + if (first + count > max) count = max - first; + arc.Array("values", &vars[first], count); + } } - arc("first", first); - if (first + count > max) count = max - first; - arc.Array("values", &vars[first], count); + arc.EndObject(); } } @@ -2923,6 +2927,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, SavingRunningscript &r void DACSThinker::Serialize(FSerializer &arc) { + Super::Serialize(arc); arc("scripts", Scripts); if (arc.isWriting()) @@ -3007,29 +3012,6 @@ END_POINTERS // //========================================================================== -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def) -{ - if (arc.isWriting()) - { - const char *n = font->GetName(); - return arc.StringPtr(key, n); - } - else - { - const char *n; - arc.StringPtr(key, n); - font = V_GetFont(n); - if (font == NULL) - { - Printf("Could not load font %s\n", n); - font = SmallFont; - } - return arc; - } - -} - - void DLevelScript::Serialize(FSerializer &arc) { Super::Serialize(arc); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 2e927e9fa..1b9a0bfab 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -527,13 +527,17 @@ FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplan FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t *def) { + // save the Scroll data here because it's a lot easier to handle a default. + // Just writing out the full array can massively inflate the archive for no gain. + DVector2 scroll = { 0,0 }, nul = { 0,0 }; + if (arc.isWriting() && level.Scrolls.Size() > 0) scroll = level.Scrolls[p.sectornum]; + if (arc.BeginObject(key)) { arc("floorplane", p.floorplane, def->floorplane) ("ceilingplane", p.ceilingplane, def->ceilingplane) ("lightlevel", p.lightlevel, def->lightlevel) ("special", p.special, def->special) - ("soundtraversed", p.soundtraversed, def->soundtraversed) ("seqtype", p.seqType, def->seqType) ("seqname", p.SeqName, def->SeqName) ("friction", p.friction, def->friction) @@ -572,7 +576,17 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t ("colormap", p.ColorMap, def->ColorMap) .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) + ("scrolls", scroll, nul) .EndObject(); + + if (!scroll.isZero()) + { + if (level.Scrolls.Size() == 0) + { + level.Scrolls.Resize(numsectors); + memset(&level.Scrolls[0], 0, sizeof(level.Scrolls[0])*level.Scrolls.Size()); + } + } } return arc; } @@ -837,8 +851,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) ("level.maptime", level.maptime) ("level.totaltime", i) ("level.skytexture1", level.skytexture1) - ("level.skytexture2", level.skytexture2) - ("level.scrolls", level.Scrolls); + ("level.skytexture2", level.skytexture2); // Hub transitions must keep the current total time if (!hubload) @@ -852,23 +865,26 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) interpolator.ClearInterpolations(); } + G_AirControlChanged(); // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON - AM_SerializeMarkers(arc); FBehavior::StaticSerializeModuleStates(arc); + // The order here is important: First world state, then portal state, then thinkers, and last polyobjects. arc.Array("linedefs", lines, &loadlines[0], numlines); arc.Array("sidedefs", sides, &loadsides[0], numsides); arc.Array("sectors", sectors, &loadsectors[0], numsectors); - arc.Array("polyobjs", polyobjs, po_NumPolyobjs); - arc("subsectors", subsectors); - StatusBar->SerializeMessages(arc); arc("zones", Zones); arc("lineportals", linePortals); arc("sectorportals", sectorPortals); - if (arc.isReading()) P_CollectLinkedPortals(); + DThinker::SerializeThinkers(arc, !hubload); + arc.Array("polyobjs", polyobjs, po_NumPolyobjs); + arc("subsectors", subsectors); + StatusBar->SerializeMessages(arc); + AM_SerializeMarkers(arc); + } diff --git a/src/serializer.cpp b/src/serializer.cpp index c282c9090..1386319fb 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -29,6 +29,7 @@ #include "r_data/r_interpolate.h" #include "g_shared/a_sharedglobal.h" #include "po_man.h" +#include "v_font.h" char nulspace[1024 * 1024 * 4]; @@ -211,6 +212,7 @@ void FSerializer::WriteKey(const char *key) { if (isWriting() && w->inObject()) { + assert(key != nullptr); if (key == nullptr) { I_Error("missing element name"); @@ -426,7 +428,6 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num) { w->mWriter.Int(num); } - w->mWriter.EndArray(); } else { @@ -1434,4 +1435,33 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr } } return arc; -} \ No newline at end of file +} + +//========================================================================== +// +// +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def) +{ + if (arc.isWriting()) + { + const char *n = font->GetName(); + return arc.StringPtr(key, n); + } + else + { + const char *n; + arc.StringPtr(key, n); + font = V_GetFont(n); + if (font == NULL) + { + Printf("Could not load font %s\n", n); + font = SmallFont; + } + return arc; + } + +} + diff --git a/src/serializer.h b/src/serializer.h index efa92a550..abc62755a 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -189,6 +189,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial 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); inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def) From e41296a64da43bc21f8a4bb789fc933ea19e124c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 13:30:31 +0200 Subject: [PATCH 20/59] - added some separators to dobject.cpp - added restore code for subsector automap info. --- src/dobject.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ src/p_saveg.cpp | 9 +++++++ 2 files changed, 75 insertions(+) diff --git a/src/dobject.cpp b/src/dobject.cpp index c70a51e9a..ab4f25bf6 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -50,6 +50,12 @@ #include "farchive.h" #include "serializer.h" +//========================================================================== +// +// +// +//========================================================================== + ClassReg DObject::RegistrationInfo = { NULL, // MyClass @@ -62,6 +68,12 @@ ClassReg DObject::RegistrationInfo = }; _DECLARE_TI(DObject) +//========================================================================== +// +// +// +//========================================================================== + CCMD (dumpactors) { const char *const filters[32] = @@ -96,6 +108,12 @@ CCMD (dumpactors) } } +//========================================================================== +// +// +// +//========================================================================== + CCMD (dumpclasses) { // This is by no means speed-optimized. But it's an informational console @@ -235,6 +253,12 @@ CCMD (dumpclasses) Printf ("%d classes shown, %d omitted\n", shown, omitted); } +//========================================================================== +// +// +// +//========================================================================== + void DObject::InPlaceConstructor (void *mem) { new ((EInPlace *)mem) DObject; @@ -256,6 +280,12 @@ DObject::DObject (PClass *inClass) GC::Root = this; } +//========================================================================== +// +// +// +//========================================================================== + DObject::~DObject () { if (!PClass::bShutdown) @@ -305,11 +335,23 @@ DObject::~DObject () } } +//========================================================================== +// +// +// +//========================================================================== + void DObject::Destroy () { ObjectFlags = (ObjectFlags & ~OF_Fixed) | OF_EuthanizeMe; } +//========================================================================== +// +// +// +//========================================================================== + size_t DObject::PropagateMark() { const PClass *info = GetClass(); @@ -331,6 +373,12 @@ size_t DObject::PropagateMark() return 0; } +//========================================================================== +// +// +// +//========================================================================== + size_t DObject::PointerSubstitution (DObject *old, DObject *notOld) { const PClass *info = GetClass(); @@ -353,6 +401,12 @@ size_t DObject::PointerSubstitution (DObject *old, DObject *notOld) return changed; } +//========================================================================== +// +// +// +//========================================================================== + size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld) { DObject *probe; @@ -418,6 +472,12 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld) return changed; } +//========================================================================== +// +// +// +//========================================================================== + void DObject::SerializeUserVars(FArchive &arc) { PSymbolTable *symt; @@ -480,6 +540,12 @@ void DObject::SerializeUserVars(FArchive &arc) } } +//========================================================================== +// +// +// +//========================================================================== + void DObject::Serialize(FSerializer &arc) { ObjectFlags |= OF_SerialSuccess; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 1b9a0bfab..581d66fd6 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -675,6 +675,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subs if (num_verts == numvertexes && num_subs == numsubsectors && hasglnodes) { success = true; + int sub = 0; for (int i = 0; str[i] != 0; i++) { by = str[i]; @@ -688,6 +689,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, subsector_t *&ss, subs success = false; break; } + for (int s = 0; s < 6; s++) + { + if (sub + s < numsubsectors && (by & (1 << s))) + { + subsectors[sub + s].flags |= SSECF_DRAWN; + } + } + sub += 6; } } if (hasglnodes && !success) From e1010144324975426d1201b8e937d0fbb5934531 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 18:27:47 +0200 Subject: [PATCH 21/59] - converted the user variable serializer. --- src/b_bot.h | 2 - src/dobject.cpp | 60 +---- src/dobject.h | 2 - src/dobjtype.cpp | 651 ++++++++++++++------------------------------- src/dobjtype.h | 54 ++-- src/json.cpp | 20 -- src/serializer.cpp | 160 ++++++++++- src/serializer.h | 39 ++- 8 files changed, 419 insertions(+), 569 deletions(-) diff --git a/src/b_bot.h b/src/b_bot.h index 5ad77707e..5dfbb7e63 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -58,8 +58,6 @@ struct botskill_t int isp; //Instincts of Self Preservation. Personality }; -FArchive &operator<< (FArchive &arc, botskill_t &skill); - enum { BOTINUSE_No, diff --git a/src/dobject.cpp b/src/dobject.cpp index ab4f25bf6..aa874d747 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -478,68 +478,20 @@ size_t DObject::StaticPointerSubstitution (DObject *old, DObject *notOld) // //========================================================================== -void DObject::SerializeUserVars(FArchive &arc) +void DObject::SerializeUserVars(FSerializer &arc) { - PSymbolTable *symt; - FName varname; - DWORD count, j; - int *varloc = NULL; - - symt = &GetClass()->Symbols; - - if (arc.IsStoring()) + if (arc.isWriting()) { // Write all fields that aren't serialized by native code. - GetClass()->WriteValue(arc, this); - } - else if (SaveVersion >= 4535) - { - GetClass()->ReadValue(arc, this); + GetClass()->WriteAllFields(arc, this); } else - { // Old version that only deals with ints - // Read user variables until 'None' is encountered. - arc << varname; - while (varname != NAME_None) - { - PField *var = dyn_cast(symt->FindSymbol(varname, true)); - DWORD wanted = 0; - - if (var != NULL && !(var->Flags & VARF_Native)) - { - PType *type = var->Type; - PArray *arraytype = dyn_cast(type); - if (arraytype != NULL) - { - wanted = arraytype->ElementCount; - type = arraytype->ElementType; - } - else - { - wanted = 1; - } - assert(type == TypeSInt32); - varloc = (int *)(reinterpret_cast(this) + var->Offset); - } - count = arc.ReadCount(); - for (j = 0; j < MIN(wanted, count); ++j) - { - arc << varloc[j]; - } - if (wanted < count) - { - // Ignore remaining values from archive. - for (; j < count; ++j) - { - int foo; - arc << foo; - } - } - arc << varname; - } + { + GetClass()->ReadAllFields(arc, this); } } + //========================================================================== // // diff --git a/src/dobject.h b/src/dobject.h index 0f81ae873..b47915659 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -460,8 +460,6 @@ public: inline bool IsKindOf (const PClass *base) const; inline bool IsA (const PClass *type) const; - void SerializeUserVars(FArchive &arc); - void SerializeUserVars(FSerializer &arc); virtual void Serialize(FSerializer &arc); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index e7a31d8cf..a82d26a54 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -39,7 +39,7 @@ #include "dobject.h" #include "i_system.h" -#include "farchive.h" +#include "serializer.h" #include "actor.h" #include "templates.h" #include "autosegs.h" @@ -47,6 +47,7 @@ #include "a_pickups.h" #include "a_weaponpiece.h" #include "d_player.h" +#include "doomerrors.h" #include "fragglescript/t_fs.h" // MACROS ------------------------------------------------------------------ @@ -390,7 +391,7 @@ bool PType::VisitedNodeSet::Check(const PType *node) // //========================================================================== -void PType::WriteValue(FArchive &ar, const void *addr) const +void PType::WriteValue(FSerializer &ar, const char *key,const void *addr) const { assert(0 && "Cannot write value for this type"); } @@ -401,108 +402,12 @@ void PType::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PType::ReadValue(FArchive &ar, void *addr) const +bool PType::ReadValue(FSerializer &ar, const char *key, void *addr) const { assert(0 && "Cannot read value for this type"); - SkipValue(ar); return false; } -//========================================================================== -// -// PType :: SkipValue STATIC -// -//========================================================================== - -void PType::SkipValue(FArchive &ar) -{ - BYTE tag; - ar << tag; - SkipValue(ar, tag); -} - -void PType::SkipValue(FArchive &ar, int tag) -{ - assert(ar.IsLoading() && "SkipValue passed an archive that is writing"); - BYTE buff[8]; - - switch (tag) - { - case VAL_Zero: case VAL_One: - break; - - case VAL_Int8: case VAL_UInt8: - ar.Read(buff, 1); - break; - - case VAL_Int16: case VAL_UInt16: - ar.Read(buff, 2); - break; - - case VAL_Int32: case VAL_UInt32: case VAL_Float32: - ar.Read(buff, 4); - break; - - case VAL_Int64: case VAL_UInt64: case VAL_Float64: - ar.Read(buff, 8); - break; - - case VAL_Name: - ar.ReadName(); - break; - - case VAL_Object: - { -#if 0 - DObject *skipper; - ar << skipper; -#endif - break; - } - case VAL_State: - { -#if 0 - FState *skipper; - ar << skipper; -#endif - break; - } - case VAL_String: - { - FString skipper; - ar << skipper; - break; - } - case VAL_Array: - { - DWORD count = ar.ReadCount(); - while (count-- > 0) - { - SkipValue(ar); - } - break; - } - case VAL_Struct: - { - const char *label; - for (label = ar.ReadName(); label != NULL; label = ar.ReadName()) - { - SkipValue(ar); - } - break; - } - case VAL_Class: - { - PClass *type; - for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) - { - SkipValue(ar, VAL_Struct); - } - break; - } - } -} - //========================================================================== // // PType :: SetDefaultValue @@ -809,116 +714,41 @@ PInt::PInt(unsigned int size, bool unsign) // // PInt :: WriteValue // -// Write the value using the minimum byte size needed to represent it. This -// means that the value as written is not necessarily of the same type as -// stored, but the signedness information is preserved. -// //========================================================================== -void PInt::WriteValue(FArchive &ar, const void *addr) const +void PInt::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - BYTE bval; - - // The process for bytes is the same whether signed or unsigned, since - // they can't be compacted into a representation with fewer bytes. - if (Size == 1) + if (Size == 8 && Unsigned) { - bval = *(BYTE *)addr; - } - else if (Unsigned) - { - unsigned val; - if (Size == 8) - { - QWORD qval = *(QWORD *)addr; - if (qval & 0xFFFFFFFF00000000llu) - { // Value needs 64 bits - ar.WriteByte(VAL_UInt64); - ar.WriteInt64(qval); - return; - } - // Value can fit in 32 bits or less - val = (unsigned)qval; - goto check_u32; - } - else if (Size == 4) - { - val = *(DWORD *)addr; -check_u32: if (val & 0xFFFF0000u) - { // Value needs 32 bits - ar.WriteByte(VAL_UInt32); - ar.WriteInt32(val); - return; - } - // Value can fit in 16 bits or less - goto check_u16; - } - else// if (Size == 2) - { - val = *(WORD *)addr; -check_u16: if (val & 0xFFFFFF00u) - { // Value needs 16 bits - ar.WriteByte(VAL_UInt16); - ar.WriteInt16(val); - return; - } - // Value can fit in 8 bits - bval = (BYTE)val; - } - } - else // Signed - { - int val; - if (Size == 8) - { - SQWORD qval = *(SQWORD *)addr; - INT_MIN; - if (qval < (-0x7FFFFFFF - 1) || qval > 0x7FFFFFFF) - { // Value needs 64 bits - ar.WriteByte(VAL_Int64); - ar.WriteInt64(qval); - return; - } - // Value can fit in 32 bits or less - val = (int)qval; - goto check_s32; - } - else if (Size == 4) - { - val = *(SDWORD *)addr; -check_s32: if (val < -0x8000 || val > 0x7FFF) - { // Value needs 32 bits - ar.WriteByte(VAL_Int32); - ar.WriteInt32(val); - return; - } - // Value can fit in 16 bits or less - goto check_s16; - } - else// if (Size == 2) - { - val = *(SWORD *)addr; -check_s16: if (val < -0x80 || val > 0x7F) - { // Value needs 16 bits - ar.WriteByte(VAL_Int16); - ar.WriteInt16(val); - return; - } - // Value can fit in 8 bits - bval = (BYTE)val; - } - } - // If we get here, the value fits in a byte. Values of 0 and 1 are - // optimized away into the tag so they don't require any extra space - // to store. - if (bval & 0xFE) - { - BYTE out[2] = { Unsigned ? VAL_UInt8 : VAL_Int8, bval }; - ar.Write(out, 2); + // this is a special case that cannot be represented by an int64_t. + uint64_t val = *(uint64_t*)addr; + ar(key, val); } else { - ar.WriteByte(VAL_Zero + bval); + int64_t val; + switch (Size) + { + case 1: + val = Unsigned ? *(uint8_t*)addr : *(int8_t*)addr; + break; + + case 2: + val = Unsigned ? *(uint16_t*)addr : *(int16_t*)addr; + break; + + case 4: + val = Unsigned ? *(uint32_t*)addr : *(int32_t*)addr; + break; + + case 8: + val = *(int64_t*)addr; + break; + + default: + return; // something invalid + } + ar(key, val); } } @@ -928,47 +758,37 @@ check_s16: if (val < -0x80 || val > 0x7F) // //========================================================================== -bool PInt::ReadValue(FArchive &ar, void *addr) const +bool PInt::ReadValue(FSerializer &ar, const char *key, void *addr) const { - union - { - QWORD uval; - SQWORD sval; - }; - BYTE tag; - union - { - BYTE val8; - WORD val16; - DWORD val32; - float single; - double dbl; - }; + NumericValue val; - ar << tag; - switch (tag) - { - case VAL_Zero: uval = 0; break; - case VAL_One: uval = 1; break; - case VAL_Int8: ar << val8; sval = (SBYTE)val8; break; - case VAL_UInt8: ar << val8; uval = val8; break; - case VAL_Int16: ar << val16; sval = (SWORD)val16; break; - case VAL_UInt16: ar << val16; uval = val16; break; - case VAL_Int32: ar << val32; sval = (SDWORD)val32; break; - case VAL_UInt32: ar << val32; uval = val32; break; - case VAL_Int64: ar << sval; break; - case VAL_UInt64: ar << uval; break; - case VAL_Float32: ar << single; sval = (SQWORD)single; break; - case VAL_Float64: ar << dbl; sval = (SQWORD)dbl; break; - default: SkipValue(ar, tag); return false; // Incompatible type - } + ar(key, val); + if (val.type == NumericValue::NM_invalid) return false; // not found or usable + if (val.type == NumericValue::NM_float) val.signedval = (int64_t)val.floatval; + + // No need to check the unsigned state here. Downcasting to smaller types will yield the same result for both. switch (Size) { - case 1: *(BYTE *)addr = (BYTE)uval; break; - case 2: *(WORD *)addr = (WORD)uval; break; - case 4: *(DWORD *)addr = (DWORD)uval; break; - case 8: *(QWORD *)addr = uval; break; + case 1: + *(uint8_t*)addr = (uint8_t)val.signedval; + break; + + case 2: + *(uint16_t*)addr = (uint16_t)val.signedval; + break; + + case 4: + *(uint32_t*)addr = (uint32_t)val.signedval; + break; + + case 8: + *(uint64_t*)addr = (uint64_t)val.signedval; + break; + + default: + return false; // something invalid } + return true; } @@ -1263,28 +1083,16 @@ void PFloat::SetSymbols(const PFloat::SymbolInitI *sym, size_t count) // //========================================================================== -void PFloat::WriteValue(FArchive &ar, const void *addr) const +void PFloat::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - float singleprecision; if (Size == 8) { - // If it can be written as single precision without information - // loss, then prefer that over writing a full-sized double. - double doubleprecision = *(double *)addr; - singleprecision = (float)doubleprecision; - if (singleprecision != doubleprecision) - { - ar.WriteByte(VAL_Float64); - ar << doubleprecision; - return; - } + ar(key, *(double*)addr); } else { - singleprecision = *(float *)addr; + ar(key, *(float*)addr); } - ar.WriteByte(VAL_Float32); - ar << singleprecision; } //========================================================================== @@ -1293,60 +1101,26 @@ void PFloat::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -static bool ReadValueDbl(FArchive &ar, double *addr, unsigned tag) +bool PFloat::ReadValue(FSerializer &ar, const char *key, void *addr) const { - double val; - union - { - BYTE val8; - WORD val16; - DWORD val32; - QWORD val64; - fixed_t fix; - float single; - angle_t ang; - }; + NumericValue val; - switch (tag) + ar(key, val); + if (val.type == NumericValue::NM_invalid) return false; // not found or usable + else if (val.type == NumericValue::NM_signed) val.floatval = (double)val.signedval; + else if (val.type == NumericValue::NM_unsigned) val.floatval = (double)val.unsignedval; + + if (Size == 8) { - case VAL_Zero: val = 0; break; - case VAL_One: val = 1; break; - case VAL_Int8: ar << val8; val = (SBYTE)val8; break; - case VAL_UInt8: ar << val8; val = val8; break; - case VAL_Int16: ar << val16; val = (SWORD)val16; break; - case VAL_UInt16: ar << val16; val = val16; break; - case VAL_Int32: ar << val32; val = (SDWORD)val32; break; - case VAL_UInt32: ar << val32; val = val32; break; - case VAL_Int64: ar << val64; val = (double)(SQWORD)val64; break; - case VAL_UInt64: ar << val64; val = (double)val64; break; - case VAL_Float32: ar << single; val = single; break; - case VAL_Float64: ar << val; break; - default: PType::SkipValue(ar, tag); return false; // Incompatible type + *(double*)addr = val.floatval; + } + else + { + *(float*)addr = (float)val.floatval; } - *(double *)addr = val; return true; } -bool PFloat::ReadValue(FArchive &ar, void *addr) const -{ - BYTE tag; - ar << tag; - double val; - if (ReadValueDbl(ar, &val, tag)) - { - if (Size == 4) - { - *(float *)addr = (float)val; - } - else - { - *(double *)addr = val; - } - return true; - } - return false; -} - //========================================================================== // // PFloat :: SetValue @@ -1486,10 +1260,9 @@ int PString::GetRegType() const // //========================================================================== -void PString::WriteValue(FArchive &ar, const void *addr) const +void PString::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - ar.WriteByte(VAL_String); - ar.WriteString(*(const FString *)addr); + ar(key, *(FString*)addr); } //========================================================================== @@ -1498,25 +1271,19 @@ void PString::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PString::ReadValue(FArchive &ar, void *addr) const +bool PString::ReadValue(FSerializer &ar, const char *key, void *addr) const { - BYTE tag; - ar << tag; - if (tag == VAL_String) + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) { - ar << *(FString *)addr; - } - else if (tag == VAL_Name) - { - const char *str = ar.ReadName(); - *(FString *)addr = str; + return false; } else { - SkipValue(ar, tag); - return false; + *(FString*)addr = cptr; + return true; } - return true; } //========================================================================== @@ -1578,10 +1345,10 @@ PName::PName() // //========================================================================== -void PName::WriteValue(FArchive &ar, const void *addr) const +void PName::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - ar.WriteByte(VAL_Name); - ar.WriteName(((const FName *)addr)->GetChars()); + const char *cptr = ((const FName*)addr)->GetChars(); + ar.StringPtr(key, cptr); } //========================================================================== @@ -1590,26 +1357,19 @@ void PName::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PName::ReadValue(FArchive &ar, void *addr) const +bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const { - BYTE tag; - ar << tag; - if (tag == VAL_Name) + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) { - *(FName *)addr = FName(ar.ReadName()); - } - else if (tag == VAL_String) - { - FString str; - ar << str; - *(FName *)addr = FName(str); + return false; } else { - SkipValue(ar, tag); - return false; + *(FName*)addr = FName(cptr); + return true; } - return true; } /* PSound *****************************************************************/ @@ -1634,10 +1394,10 @@ PSound::PSound() // //========================================================================== -void PSound::WriteValue(FArchive &ar, const void *addr) const +void PSound::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - ar.WriteByte(VAL_Name); - ar.WriteName(*(const FSoundID *)addr); + const char *cptr = *(const FSoundID *)addr; + ar.StringPtr(key, cptr); } //========================================================================== @@ -1646,28 +1406,19 @@ void PSound::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PSound::ReadValue(FArchive &ar, void *addr) const +bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const { - BYTE tag; - - ar << tag; - if (tag == VAL_Name) + const char *cptr; + ar.StringPtr(key, cptr); + if (cptr == nullptr) { - const char *str = ar.ReadName(); - *(FSoundID *)addr = FSoundID(str); - } - else if (tag == VAL_String) - { - FString str; - ar << str; - *(FSoundID *)addr = FSoundID(str); + return false; } else { - SkipValue(ar, tag); - return false; + *(FSoundID *)addr = FSoundID(cptr); + return true; } - return true; } /* PColor *****************************************************************/ @@ -1740,10 +1491,9 @@ int PStatePointer::GetRegType() const // //========================================================================== -void PStatePointer::WriteValue(FArchive &ar, const void *addr) const +void PStatePointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - ar.WriteByte(VAL_State); - //ar << *(FState **)addr; + ar(key, *(FState **)addr); } //========================================================================== @@ -1752,17 +1502,11 @@ void PStatePointer::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PStatePointer::ReadValue(FArchive &ar, void *addr) const +bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const { - BYTE tag; - ar << tag; - if (tag == VAL_State) - { - //ar << *(FState **)addr; - return true; - } - SkipValue(ar, tag); - return false; + bool res = false; + ::Serialize(ar, key, *(FState **)addr, nullptr, &res); + return res; } /* PPointer ***************************************************************/ @@ -1858,20 +1602,17 @@ void PPointer::GetTypeIDs(intptr_t &id1, intptr_t &id2) const // //========================================================================== -void PPointer::WriteValue(FArchive &ar, const void *addr) const +void PPointer::WriteValue(FSerializer &ar, const char *key,const void *addr) const { -#if 0 if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { - ar.WriteByte(VAL_Object); - ar << *(DObject **)addr; + ar(key, *(DObject **)addr); } else { assert(0 && "Pointer points to a type we don't handle"); I_Error("Attempt to save pointer to unhandled type"); } -#endif } //========================================================================== @@ -1880,18 +1621,14 @@ void PPointer::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PPointer::ReadValue(FArchive &ar, void *addr) const +bool PPointer::ReadValue(FSerializer &ar, const char *key, void *addr) const { -#if 0 - BYTE tag; - ar << tag; - if (tag == VAL_Object && PointedType->IsKindOf(RUNTIME_CLASS(PClass))) + if (PointedType->IsKindOf(RUNTIME_CLASS(PClass))) { - ar << *(DObject **)addr; - return true; + bool res = false; + ::Serialize(ar, key, *(DObject **)addr, nullptr, &res); + return res; } - SkipValue(ar, tag); -#endif return false; } @@ -2106,15 +1843,17 @@ void PArray::GetTypeIDs(intptr_t &id1, intptr_t &id2) const // //========================================================================== -void PArray::WriteValue(FArchive &ar, const void *addr) const +void PArray::WriteValue(FSerializer &ar, const char *key,const void *addr) const { - ar.WriteByte(VAL_Array); - ar.WriteCount(ElementCount); - const BYTE *addrb = (const BYTE *)addr; - for (unsigned i = 0; i < ElementCount; ++i) + if (ar.BeginArray(key)) { - ElementType->WriteValue(ar, addrb); - addrb += ElementSize; + const BYTE *addrb = (const BYTE *)addr; + for (unsigned i = 0; i < ElementCount; ++i) + { + ElementType->WriteValue(ar, nullptr, addrb); + addrb += ElementSize; + } + ar.EndArray(); } } @@ -2124,34 +1863,27 @@ void PArray::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PArray::ReadValue(FArchive &ar, void *addr) const +bool PArray::ReadValue(FSerializer &ar, const char *key, void *addr) const { - bool readsomething = false; - BYTE tag; - - ar << tag; - if (tag == VAL_Array) + if (ar.BeginArray(key)) { - unsigned count = ar.ReadCount(); - unsigned i; + bool readsomething = false; + unsigned count = ar.ArraySize(); + unsigned loop = MIN(count, ElementCount); BYTE *addrb = (BYTE *)addr; - for (i = 0; i < MIN(count, ElementCount); ++i) + for(unsigned i=0;iReadValue(ar, addrb); + readsomething |= ElementType->ReadValue(ar, key, addrb); addrb += ElementSize; } - if (i < ElementCount) + if (loop < ElementCount) { DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", count, ElementCount); - for (; i < ElementCount; ++i) - { - SkipValue(ar); - } } + ar.EndArray(); return readsomething; } - SkipValue(ar, tag); return false; } @@ -2445,10 +2177,13 @@ void PStruct::SetDefaultValue(void *base, unsigned offset, TArray &fields) +void PStruct::WriteFields(FSerializer &ar, const void *addr, const TArray &fields) { for (unsigned i = 0; i < fields.Size(); ++i) { @@ -2483,11 +2217,9 @@ void PStruct::WriteFields(FArchive &ar, const void *addr, const TArray // Skip fields with native serialization if (!(field->Flags & VARF_Native)) { - ar.WriteName(field->SymbolName); - field->Type->WriteValue(ar, (const BYTE *)addr + field->Offset); + field->Type->WriteValue(ar, field->SymbolName.GetChars(), (const BYTE *)addr + field->Offset); } } - ar.WriteName(NULL); } //========================================================================== @@ -2496,36 +2228,33 @@ void PStruct::WriteFields(FArchive &ar, const void *addr, const TArray // //========================================================================== -bool PStruct::ReadFields(FArchive &ar, void *addr) const +bool PStruct::ReadFields(FSerializer &ar, void *addr) const { bool readsomething = false; - const char *label = ar.ReadName(); - if (label == NULL) - { // If there is nothing to restore, we count it as success. - return true; - } - for (; label != NULL; label = ar.ReadName()) + bool foundsomething = false; + const char *label; + while ((label = ar.GetKey())) { + foundsomething = true; + const PSymbol *sym = Symbols.FindSymbol(FName(label, true), true); if (sym == NULL) { DPrintf(DMSG_ERROR, "Cannot find field %s in %s\n", label, TypeName.GetChars()); - SkipValue(ar); } else if (!sym->IsKindOf(RUNTIME_CLASS(PField))) { DPrintf(DMSG_ERROR, "Symbol %s in %s is not a field\n", label, TypeName.GetChars()); - SkipValue(ar); } else { - readsomething |= static_cast(sym)->Type->ReadValue(ar, + readsomething |= static_cast(sym)->Type->ReadValue(ar, label, (BYTE *)addr + static_cast(sym)->Offset); } } - return readsomething; + return readsomething || !foundsomething; } //========================================================================== @@ -2749,7 +2478,7 @@ END_POINTERS // //========================================================================== -static void RecurseWriteFields(const PClass *type, FArchive &ar, const void *addr) +static void RecurseWriteFields(const PClass *type, FSerializer &ar, const void *addr) { if (type != NULL) { @@ -2763,19 +2492,33 @@ static void RecurseWriteFields(const PClass *type, FArchive &ar, const void *add // a more-derived class has variables that shadow a less- // derived class. Whether or not that is a language feature // that will actually be allowed remains to be seen. - ar.UserWriteClass(const_cast(type)); - PStruct::WriteFields(ar, addr, type->Fields); + FString key; + key.Format("class:%s", type->TypeName.GetChars()); + if (ar.BeginObject(key.GetChars())) + { + PStruct::WriteFields(ar, addr, type->Fields); + ar.EndObject(); + } break; } } } } -void PClass::WriteValue(FArchive &ar, const void *addr) const +void PClass::WriteValue(FSerializer &ar, const char *key,const void *addr) const +{ + if (ar.BeginObject(key)) + { + RecurseWriteFields(this, ar, addr); + ar.EndObject(); + } +} + +// Same as WriteValue, but does not create a new object in the serializer +// This is so that user variables do not contain unnecessary subblocks. +void PClass::WriteAllFields(FSerializer &ar, const void *addr) const { - ar.WriteByte(VAL_Class); RecurseWriteFields(this, ar, addr); - ar.UserWriteClass(NULL); } //========================================================================== @@ -2784,20 +2527,32 @@ void PClass::WriteValue(FArchive &ar, const void *addr) const // //========================================================================== -bool PClass::ReadValue(FArchive &ar, void *addr) const +bool PClass::ReadValue(FSerializer &ar, const char *key, void *addr) const { - BYTE tag; - ar << tag; - if (tag != VAL_Class) + if (ar.BeginObject(key)) { - SkipValue(ar, tag); - return false; + bool ret = ReadAllFields(ar, addr); + ar.EndObject(); + return ret; } - else + return true; +} + +bool PClass::ReadAllFields(FSerializer &ar, void *addr) const +{ + bool readsomething = false; + bool foundsomething = false; + const char *key; + while ((key = ar.GetKey())) { - bool readsomething = false; - PClass *type; - for (ar.UserReadClass(type); type != NULL; ar.UserReadClass(type)) + if (strncmp(key, "class:", 6)) + { + // This key does not represent any class fields anymore + break; + } + foundsomething = true; + PClass *type = PClass::FindClass(key + 6); + if (type != nullptr) { // Only read it if the type is related to this one. const PClass *parent; @@ -2808,7 +2563,7 @@ bool PClass::ReadValue(FArchive &ar, void *addr) const break; } } - if (parent != NULL) + if (parent != nullptr) { readsomething |= type->ReadFields(ar, addr); } @@ -2816,11 +2571,15 @@ bool PClass::ReadValue(FArchive &ar, void *addr) const { DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", type->TypeName.GetChars(), TypeName.GetChars()); - SkipValue(ar, VAL_Struct); } } - return readsomething; + else + { + DPrintf(DMSG_ERROR, "Unknown superclass %s of class %s\n", + key+6, TypeName.GetChars()); + } } + return readsomething || !foundsomething; } //========================================================================== diff --git a/src/dobjtype.h b/src/dobjtype.h index d0ccee746..62cfa6753 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -193,15 +193,11 @@ public: // a tag indicating its type. The tag is there so that variable types can be changed // without completely breaking savegames, provided that the change isn't between // totally unrelated types. - virtual void WriteValue(FArchive &ar, const void *addr) const; + virtual void WriteValue(FSerializer &ar, const char *key,const void *addr) const; // Returns true if the stored value was compatible. False otherwise. // If the value was incompatible, then the memory at *addr is unchanged. - virtual bool ReadValue(FArchive &ar, void *addr) const; - - // Skips over a value written with WriteValue - static void SkipValue(FArchive &ar); - static void SkipValue(FArchive &ar, int tag); + virtual bool ReadValue(FSerializer &ar, const char *key,void *addr) const; // Sets the default value for this type at (base + offset) // If the default value is binary 0, then this function doesn't need @@ -354,8 +350,8 @@ class PInt : public PBasicType public: PInt(unsigned int size, bool unsign); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); @@ -383,8 +379,8 @@ class PFloat : public PBasicType public: PFloat(unsigned int size); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; virtual void SetValue(void *addr, int val); virtual void SetValue(void *addr, double val); @@ -421,8 +417,8 @@ public: virtual int GetRegType() const; - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL) const override; void InitializeValue(void *addr, const void *def) const override; void DestroyValue(void *addr) const override; @@ -436,8 +432,8 @@ class PName : public PInt public: PName(); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; }; class PSound : public PInt @@ -446,8 +442,8 @@ class PSound : public PInt public: PSound(); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; }; class PColor : public PInt @@ -465,8 +461,8 @@ class PStatePointer : public PBasicType public: PStatePointer(); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; virtual int GetStoreOp() const; virtual int GetLoadOp() const; @@ -489,8 +485,8 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; protected: PPointer(); @@ -559,8 +555,8 @@ public: virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *special) const override; @@ -622,12 +618,12 @@ public: size_t PropagateMark(); - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *specials) const override; - static void WriteFields(FArchive &ar, const void *addr, const TArray &fields); - bool ReadFields(FArchive &ar, void *addr) const; + static void WriteFields(FSerializer &ar, const void *addr, const TArray &fields); + bool ReadFields(FSerializer &ar, void *addr) const; protected: PStruct(); }; @@ -692,8 +688,10 @@ public: typedef PClassClass MetaClass; MetaClass *GetClass() const; - void WriteValue(FArchive &ar, const void *addr) const override; - bool ReadValue(FArchive &ar, void *addr) const override; + void WriteValue(FSerializer &ar, const char *key,const void *addr) const override; + void WriteAllFields(FSerializer &ar, const void *addr) const; + bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; + bool ReadAllFields(FSerializer &ar, void *addr) const; virtual void DeriveData(PClass *newclass) {} static void StaticInit(); diff --git a/src/json.cpp b/src/json.cpp index 43f185301..1d8d506f8 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -7,26 +7,6 @@ -void DObject::SerializeUserVars(FSerializer &arc) -{ - PSymbolTable *symt; - FName varname; - //DWORD count, j; - int *varloc = NULL; - - symt = &GetClass()->Symbols; - - if (arc.isWriting()) - { - // Write all fields that aren't serialized by native code. - //GetClass()->WriteValue(arc, this); - } - else - { - //GetClass()->ReadValue(arc, this); - } -} - //========================================================================== // // diff --git a/src/serializer.cpp b/src/serializer.cpp index 1386319fb..8e249e9d8 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -44,12 +44,18 @@ struct FJSONObject rapidjson::Value *mObject; rapidjson::Value::MemberIterator mIterator; int mIndex; + bool mRandomAccess; - FJSONObject(rapidjson::Value *v) + FJSONObject(rapidjson::Value *v, bool randomaccess = false) { mObject = v; + mRandomAccess = randomaccess; if (v->IsObject()) mIterator = v->MemberBegin(); - else if (v->IsArray()) mIndex = 0; + else if (v->IsArray()) + { + mIndex = 0; + mIterator = v->MemberEnd(); + } } }; @@ -100,7 +106,7 @@ struct FReader rapidjson::Document doc; doc.Parse(buffer, length); mDocObj = doc.GetObject(); - mObjects.Push(FJSONObject(&mDocObj)); + mObjects.Push(FJSONObject(&mDocObj)); // Todo: Decide if this should be made random access... } rapidjson::Value *FindKey(const char *key) @@ -109,13 +115,23 @@ struct FReader if (obj.mObject->IsObject()) { - if (obj.mIterator != obj.mObject->MemberEnd()) + if (!obj.mRandomAccess) { - if (!strcmp(key, obj.mIterator->name.GetString())) + if (obj.mIterator != obj.mObject->MemberEnd()) { - return &(obj.mIterator++)->value; + if (!strcmp(key, obj.mIterator->name.GetString())) + { + return &(obj.mIterator++)->value; + } } } + else + { + // for unordered searches. This is slower but will not rely on sequential order of items. + auto it = obj.mObject->FindMember(key); + if (it == obj.mObject->MemberEnd()) return nullptr; + return &it->value; + } } else if (obj.mObject->IsArray()) { @@ -227,7 +243,7 @@ void FSerializer::WriteKey(const char *key) // //========================================================================== -bool FSerializer::BeginObject(const char *name) +bool FSerializer::BeginObject(const char *name, bool randomaccess) { if (isWriting()) { @@ -242,7 +258,7 @@ bool FSerializer::BeginObject(const char *name) { if (val->IsObject()) { - r->mObjects.Push(FJSONObject(val)); + r->mObjects.Push(FJSONObject(val, randomaccess)); } else { @@ -530,6 +546,10 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) { charptr = val->GetString(); } + else + { + charptr = nullptr; + } } } return *this; @@ -550,6 +570,20 @@ unsigned FSerializer::GetSize(const char *group) return val.Size(); } +//========================================================================== +// +// +// +//========================================================================== + +const char *FSerializer::GetKey() +{ + if (isWriting()) return nullptr; // we do not know this when writing. + auto &it = r->mObjects.Last().mIterator; + if (it == r->mObjects.Last().mObject->MemberEnd()) return nullptr; + return it->name.GetString(); +} + //========================================================================== // // Writes out all collected objects @@ -994,8 +1028,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe // //========================================================================== -FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/) +FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/, bool *retcode) { + if (retcode) *retcode = true; if (arc.isWriting()) { if (value != nullptr) @@ -1017,10 +1052,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje if (val != nullptr) { } - else + else if (!retcode) { value = nullptr; } + else + { + *retcode = false; + } } return arc; } @@ -1185,18 +1224,55 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor } +//========================================================================== +// +// almost, but not quite the same as the above. +// +//========================================================================== + +template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&clst, PClass **def) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || def == nullptr || clst != *def) + { + arc.WriteKey(key); + arc.w->mWriter.String(clst->TypeName.GetChars()); + } + } + else + { + auto val = arc.r->FindKey(key); + if (val != nullptr) + { + if (val->IsString()) + { + clst = PClass::FindClass(val->GetString()); + } + else + { + I_Error("string type expected for '%s'", key); + } + } + } + return arc; + +} + //========================================================================== // // // //========================================================================== -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def) +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) { @@ -1227,10 +1303,12 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&st { 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]; @@ -1244,7 +1322,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&st } } } - else + else if (!retcode) { I_Error("array type expected for '%s'", key); } @@ -1455,7 +1533,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&fon const char *n; arc.StringPtr(key, n); font = V_GetFont(n); - if (font == NULL) + if (font == nullptr) { Printf("Could not load font %s\n", n); font = SmallFont; @@ -1465,3 +1543,59 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&fon } +//========================================================================== +// +// Handler to retrieve a numeric value of any kind. +// +//========================================================================== + +FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &value, NumericValue *defval) +{ + if (arc.isWriting()) + { + if (!arc.w->inObject() || defval == nullptr || value != *defval) + { + arc.WriteKey(key); + switch (value.type) + { + case NumericValue::NM_signed: + arc.w->mWriter.Int64(value.signedval); + break; + case NumericValue::NM_unsigned: + arc.w->mWriter.Uint64(value.unsignedval); + break; + case NumericValue::NM_float: + arc.w->mWriter.Double(value.floatval); + break; + default: + arc.w->mWriter.Null(); + break; + } + } + } + else + { + auto val = arc.r->FindKey(key); + value.signedval = 0; + value.type = NumericValue::NM_invalid; + if (val != nullptr) + { + if (val->IsUint64()) + { + value.unsignedval = val->GetUint64(); + value.type = NumericValue::NM_unsigned; + } + else if (val->IsInt64()) + { + value.signedval = val->GetInt64(); + value.type = NumericValue::NM_signed; + } + else if (val->IsDouble()) + { + value.floatval = val->GetDouble(); + value.type = NumericValue::NM_float; + } + } + } + return arc; +} diff --git a/src/serializer.h b/src/serializer.h index abc62755a..cec13ae1c 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -14,6 +14,29 @@ extern TArray loadlines; extern TArray loadsides; extern char nulspace[]; +struct NumericValue +{ + enum EType + { + NM_invalid, + NM_signed, + NM_unsigned, + NM_float + } type; + + union + { + int64_t signedval; + uint64_t unsignedval; + double floatval; + }; + + bool operator !=(const NumericValue &other) + { + return type != other.type || signedval != other.signedval; + } +}; + class FSerializer @@ -24,6 +47,7 @@ public: FReader *r = nullptr; int ArraySize(); + void WriteKey(const char *key); public: @@ -34,13 +58,13 @@ public: bool OpenWriter(); bool OpenReader(const char *buffer, size_t length); void Close(); - void WriteKey(const char *key); - bool BeginObject(const char *name); + bool BeginObject(const char *name, bool randomaccess = false); void EndObject(); bool BeginArray(const char *name); void EndArray(); void WriteObjects(); unsigned GetSize(const char *group); + const char *GetKey(); const char *GetOutput(unsigned *len = nullptr); FSerializer &Args(const char *key, int *args, int *defargs, int special); FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr); @@ -130,10 +154,11 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint16_t &value, uint1 FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double *defval); FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *defval); FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval); -FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/); +FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObject ** /*defval*/, bool *retcode = nullptr); FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *defval); 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); template FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) @@ -184,13 +209,19 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&va template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval); template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicColormap *&cm, FDynamicColormap **def); template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor *&clst, PClassActor **def); -template<> FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **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); +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) { From 970c168b135734265aa3275c956fd922ff8eacb6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 19:45:32 +0200 Subject: [PATCH 22/59] - re-added serialization of treanslations. - removed more dead code. --- src/dobject.h | 1 - src/dobjtype.cpp | 4 +-- src/doomdata.h | 1 - src/doomtype.h | 1 - src/info.h | 1 - src/p_saveg.cpp | 32 +---------------- src/p_saveg.h | 5 --- src/p_terrain.cpp | 16 --------- src/r_data/r_translate.cpp | 61 ++++++++++++++++++++++++++------- src/r_data/r_translate.h | 5 +-- src/r_swrenderer.cpp | 1 - src/s_environment.cpp | 25 -------------- src/s_sound.h | 3 -- src/serializer.cpp | 4 +-- src/textures/texturemanager.cpp | 55 ----------------------------- src/textures/textures.h | 4 --- src/v_font.h | 1 - 17 files changed, 56 insertions(+), 164 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index b47915659..51102bc4c 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -39,7 +39,6 @@ class PClass; -class FArchive; class FSerializer; class DObject; diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index a82d26a54..5629fa4aa 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1873,10 +1873,10 @@ bool PArray::ReadValue(FSerializer &ar, const char *key, void *addr) const BYTE *addrb = (BYTE *)addr; for(unsigned i=0;iReadValue(ar, key, addrb); + readsomething |= ElementType->ReadValue(ar, nullptr, addrb); addrb += ElementSize; } - if (loop < ElementCount) + if (loop < count) { DPrintf(DMSG_WARNING, "Array on disk (%u) is bigger than in memory (%u)\n", count, ElementCount); diff --git a/src/doomdata.h b/src/doomdata.h index f809f05ea..eca7dcd4a 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -339,7 +339,6 @@ struct mapthinghexen_t BYTE args[5]; }; -class FArchive; struct FDoomEdEntry; // Internal representation of a mapthing diff --git a/src/doomtype.h b/src/doomtype.h index ccf8ccc3d..129c5f122 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -206,7 +206,6 @@ struct PalEntry #endif }; -class FArchive; class PClassInventory; class FTextureID diff --git a/src/info.h b/src/info.h index 2c8365dd4..0e49e1f8f 100644 --- a/src/info.h +++ b/src/info.h @@ -52,7 +52,6 @@ struct Baggage; class FScanner; struct FActorInfo; -class FArchive; class FIntCVar; enum EStateType diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 581d66fd6..80cad3eb5 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -893,6 +893,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) arc("subsectors", subsectors); StatusBar->SerializeMessages(arc); AM_SerializeMarkers(arc); + FRemapTable::StaticSerializeTranslations(arc); } @@ -933,37 +934,6 @@ void P_SerializeSounds (FArchive &arc) void G_SerializeLevel(FArchive &arc, bool hubLoad) { #if 0 - // Does this level have custom translations? - FRemapTable *trans; - WORD w; - if (arc.IsStoring()) - { - for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i) - { - trans = translationtables[TRANSLATION_LevelScripted][i]; - if (trans != NULL && !trans->IsIdentity()) - { - w = WORD(i); - arc << w; - trans->Serialize(arc); - } - } - w = 0xffff; - arc << w; - } - else - { - while (arc << w, w != 0xffff) - { - trans = translationtables[TRANSLATION_LevelScripted].GetVal(w); - if (trans == NULL) - { - trans = new FRemapTable; - translationtables[TRANSLATION_LevelScripted].SetVal(w, trans); - } - trans->Serialize(arc); - } - } // This must be saved, too, of course! FCanvasTextureInfo::Serialize(arc); diff --git a/src/p_saveg.h b/src/p_saveg.h index a62e5c5b8..fee2874fd 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -41,12 +41,7 @@ struct PNGHandle; // These are the load / save game routines. // Also see farchive.(h|cpp) void P_SerializePlayers (FArchive &arc, bool fakeload); -void P_SerializeWorldActors(FArchive &arc); -void P_SerializeWorld (FArchive &arc); void P_DestroyThinkers(bool hubLoad); -void P_SerializeThinkers (FArchive &arc, bool); -void P_SerializePolyobjs (FArchive &arc); -void P_SerializeSubsectors(FArchive &arc); void P_SerializeSounds (FArchive &arc); void P_ReadACSDefereds (PNGHandle *png); diff --git a/src/p_terrain.cpp b/src/p_terrain.cpp index c7c2848c0..2504bad94 100644 --- a/src/p_terrain.cpp +++ b/src/p_terrain.cpp @@ -46,7 +46,6 @@ #include "s_sound.h" #include "p_local.h" #include "templates.h" -#include "farchive.h" // MACROS ------------------------------------------------------------------ @@ -725,18 +724,3 @@ FName P_GetTerrainName(int terrainnum) } } -void P_SerializeTerrain(FArchive &arc, int &terrainnum) -{ - FName val; - if (arc.IsStoring()) - { - val = P_GetTerrainName(terrainnum); - arc << val; - } - else - { - arc << val; - terrainnum = P_FindTerrain(val); - - } -} diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 1772bc07f..c55f69016 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -46,8 +46,10 @@ #include "i_system.h" #include "w_wad.h" #include "r_data/colormaps.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" +#include "r_data/sprites.h" +#include "r_state.h" #include "gi.h" #include "stats.h" @@ -192,30 +194,63 @@ bool FRemapTable::operator==(const FRemapTable &o) // //---------------------------------------------------------------------------- -void FRemapTable::Serialize(FArchive &arc) +void FRemapTable::Serialize(FSerializer &arc) { -#if 0 int n = NumEntries; - arc << NumEntries; - if (arc.IsStoring()) - { - arc.Write (Remap, NumEntries); - } - else + arc("numentries", NumEntries); + if (arc.isReading()) { if (n != NumEntries) { Free(); Alloc(NumEntries); } - arc.Read (Remap, NumEntries); } - for (int j = 0; j < NumEntries; ++j) + arc.Array("remap", Remap, NumEntries); + arc.Array("palette", Palette, NumEntries); +} + +void FRemapTable::StaticSerializeTranslations(FSerializer &arc) +{ + if (arc.BeginArray("translations")) { - arc << Palette[j]; + // Does this level have custom translations? + FRemapTable *trans; + int w; + if (arc.isWriting()) + { + for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i) + { + trans = translationtables[TRANSLATION_LevelScripted][i]; + if (trans != NULL && !trans->IsIdentity()) + { + if (arc.BeginObject(nullptr)) + { + arc("index", i); + trans->Serialize(arc); + arc.EndObject(); + } + } + } + } + else + { + while (arc.BeginObject(nullptr)) + { + arc("index", w); + trans = translationtables[TRANSLATION_LevelScripted].GetVal(w); + if (trans == NULL) + { + trans = new FRemapTable; + translationtables[TRANSLATION_LevelScripted].SetVal(w, trans); + } + trans->Serialize(arc); + arc.EndObject(); + } + } + arc.EndArray(); } -#endif } //---------------------------------------------------------------------------- diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index 7df9dcb6d..a549f0759 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -5,7 +5,7 @@ #include "tarray.h" class FNativePalette; -class FArchive; +class FSerializer; enum { @@ -36,7 +36,8 @@ struct FRemapTable void UpdateNative(); FNativePalette *GetNative(); bool IsIdentity() const; - void Serialize(FArchive &ar); + void Serialize(FSerializer &arc); + static void StaticSerializeTranslations(FSerializer &arc); void AddIndexRange(int start, int end, int pal1, int pal2); void AddColorRange(int start, int end, int r1,int g1, int b1, int r2, int g2, int b2); void AddDesaturation(int start, int end, double r1, double g1, double b1, double r2, double g2, double b2); diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp index 4d1fd0371..ccf12e7ed 100644 --- a/src/r_swrenderer.cpp +++ b/src/r_swrenderer.cpp @@ -44,7 +44,6 @@ #include "r_data/voxels.h" -class FArchive; void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); void R_SetupColormap(player_t *); void R_SetupFreelook(); diff --git a/src/s_environment.cpp b/src/s_environment.cpp index d3bd8c58c..377537a6c 100644 --- a/src/s_environment.cpp +++ b/src/s_environment.cpp @@ -1,7 +1,6 @@ #include "doomtype.h" #include "tarray.h" #include "s_sound.h" -#include "farchive.h" #include "sc_man.h" #include "cmdlib.h" #include "templates.h" @@ -481,30 +480,6 @@ void S_AddEnvironment (ReverbContainer *settings) } } -FArchive &operator<< (FArchive &arc, ReverbContainer *&env) -{ - WORD id; - - if (arc.IsStoring()) - { - if (env != NULL) - { - arc << env->ID; - } - else - { - id = 0; - arc << id; - } - } - else - { - arc << id; - env = S_FindEnvironment (id); - } - return arc; -} - static void ReadReverbDef (int lump) { FScanner sc; diff --git a/src/s_sound.h b/src/s_sound.h index 774d859c8..c2e0f187e 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -387,9 +387,6 @@ void S_NoiseDebug (); extern ReverbContainer *Environments; extern ReverbContainer *DefaultEnvironments[26]; -class FArchive; -FArchive &operator<< (FArchive &arc, ReverbContainer *&env); - void S_SetEnvironment (const ReverbContainer *settings); ReverbContainer *S_FindEnvironment (const char *name); ReverbContainer *S_FindEnvironment (int id); diff --git a/src/serializer.cpp b/src/serializer.cpp index 8e249e9d8..66d23cd72 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -133,9 +133,9 @@ struct FReader return &it->value; } } - else if (obj.mObject->IsArray()) + else if (obj.mObject->IsArray() && obj.mIndex < obj.mObject->Size()) { - return &obj.mObject[obj.mIndex++]; + return &(*obj.mObject)[obj.mIndex++]; } return nullptr; } diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index ba4db5e1d..9c7e98d08 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -1074,61 +1074,6 @@ FTextureID FTextureManager::PalCheck(FTextureID tex) return *newtex; } -//========================================================================== -// -// FTextureManager :: WriteTexture -// -//========================================================================== - -void FTextureManager::WriteTexture (FArchive &arc, int picnum) -{ - FTexture *pic; - - if (picnum < 0) - { - arc.WriteName(NULL); - return; - } - else if ((size_t)picnum >= Textures.Size()) - { - pic = Textures[0].Texture; - } - else - { - pic = Textures[picnum].Texture; - } - - if (Wads.GetLinkedTexture(pic->SourceLump) == pic) - { - arc.WriteName(Wads.GetLumpFullName(pic->SourceLump)); - } - else - { - arc.WriteName(pic->Name); - } - arc.WriteCount(pic->UseType); -} - -//========================================================================== -// -// FTextureManager :: ReadTexture -// -//========================================================================== - -int FTextureManager::ReadTexture (FArchive &arc) -{ - int usetype; - const char *name; - - name = arc.ReadName (); - if (name != NULL) - { - usetype = arc.ReadCount (); - return GetTexture (name, usetype).GetIndex(); - } - else return -1; -} - //=========================================================================== // // R_GuesstimateNumTextures diff --git a/src/textures/textures.h b/src/textures/textures.h index 14667093c..954936fe1 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -380,9 +380,6 @@ public: int NumTextures () const { return (int)Textures.Size(); } - void WriteTexture (FArchive &arc, int picnum); - int ReadTexture (FArchive &arc); - void UpdateAnimations (DWORD mstime); int GuesstimateNumTextures (); @@ -498,7 +495,6 @@ protected: // A texture that can be drawn to. class DSimpleCanvas; class AActor; -class FArchive; class FCanvasTexture : public FTexture { diff --git a/src/v_font.h b/src/v_font.h index 762ba4cd7..7523e6bf0 100644 --- a/src/v_font.h +++ b/src/v_font.h @@ -39,7 +39,6 @@ class DCanvas; struct FRemapTable; class FTexture; -class FArchive; enum EColorRange { From 3a1191281fb8f50f80121efe6f3028316b0a8324 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 23:13:12 +0200 Subject: [PATCH 23/59] - some preparations for converting the player serialization code. - converted sound and canvas texture serialization. - refactored file_zip, so that it can be used to load loose zip files and extract their compressed data directly. - added handling to FSerializer to generate and consume compressed Zip file entries. If all goes well this will allow saving savegames as Zips when the rework is done, which will make analyzing them a lot easier. --- src/p_saveg.cpp | 685 +++++++++++++++++---------------- src/p_saveg.h | 2 - src/r_defs.h | 13 +- src/r_renderer.h | 6 +- src/r_utility.cpp | 52 ++- src/r_utility.h | 4 +- src/resourcefiles/file_zip.cpp | 191 +++++---- src/resourcefiles/file_zip.h | 64 +++ src/s_sound.cpp | 116 +++--- src/s_sound.h | 2 +- src/serializer.cpp | 104 ++++- src/serializer.h | 11 +- 12 files changed, 720 insertions(+), 530 deletions(-) create mode 100644 src/resourcefiles/file_zip.h diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 80cad3eb5..e2178f995 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -65,287 +65,8 @@ #include "serializer.h" // just the stuff that already got converted to FSerializer so that it can be seen as 'done' when searching. -#define COMMON_STUFF #include "zzz_old.cpp" -void CopyPlayer (player_t *dst, player_t *src, const char *name); -static void ReadOnePlayer (FArchive &arc, bool skipload); -static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload); -static void SpawnExtraPlayers (); - -// -// P_ArchivePlayers -// -void P_SerializePlayers (FArchive &arc, bool skipload) -{ - BYTE numPlayers, numPlayersNow; - int i; - - // Count the number of players present right now. - for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - ++numPlayersNow; - } - } - - if (arc.IsStoring()) - { - // Record the number of players in this save. - arc << numPlayersNow; - - // Record each player's name, followed by their data. - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - arc.WriteString (players[i].userinfo.GetName()); - players[i].Serialize (arc); - } - } - } - else - { - arc << numPlayers; - - // If there is only one player in the game, they go to the - // first player present, no matter what their name. - if (numPlayers == 1) - { - ReadOnePlayer (arc, skipload); - } - else - { - ReadMultiplePlayers (arc, numPlayers, numPlayersNow, skipload); - } - if (!skipload && numPlayersNow > numPlayers) - { - SpawnExtraPlayers (); - } - // Redo pitch limits, since the spawned player has them at 0. - players[consoleplayer].SendPitchLimits(); - } -} - -static void ReadOnePlayer (FArchive &arc, bool skipload) -{ - int i; - char *name = NULL; - bool didIt = false; - - arc << name; - - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - if (!didIt) - { - didIt = true; - player_t playerTemp; - playerTemp.Serialize (arc); - if (!skipload) - { - CopyPlayer (&players[i], &playerTemp, name); - } - } - else - { - if (players[i].mo != NULL) - { - players[i].mo->Destroy(); - players[i].mo = NULL; - } - } - } - } - delete[] name; -} - -static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNow, bool skipload) -{ - // For two or more players, read each player into a temporary array. - int i, j; - char **nametemp = new char *[numPlayers]; - player_t *playertemp = new player_t[numPlayers]; - BYTE *tempPlayerUsed = new BYTE[numPlayers]; - BYTE playerUsed[MAXPLAYERS]; - - for (i = 0; i < numPlayers; ++i) - { - nametemp[i] = NULL; - arc << nametemp[i]; - playertemp[i].Serialize (arc); - tempPlayerUsed[i] = 0; - } - for (i = 0; i < MAXPLAYERS; ++i) - { - playerUsed[i] = playeringame[i] ? 0 : 2; - } - - if (!skipload) - { - // Now try to match players from the savegame with players present - // based on their names. If two players in the savegame have the - // same name, then they are assigned to players in the current game - // on a first-come, first-served basis. - for (i = 0; i < numPlayers; ++i) - { - for (j = 0; j < MAXPLAYERS; ++j) - { - if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0) - { // Found a match, so copy our temp player to the real player - Printf ("Found player %d (%s) at %d\n", i, nametemp[i], j); - CopyPlayer (&players[j], &playertemp[i], nametemp[i]); - playerUsed[j] = 1; - tempPlayerUsed[i] = 1; - break; - } - } - } - - // Any players that didn't have matching names are assigned to existing - // players on a first-come, first-served basis. - for (i = 0; i < numPlayers; ++i) - { - if (tempPlayerUsed[i] == 0) - { - for (j = 0; j < MAXPLAYERS; ++j) - { - if (playerUsed[j] == 0) - { - Printf ("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName()); - CopyPlayer (&players[j], &playertemp[i], nametemp[i]); - playerUsed[j] = 1; - tempPlayerUsed[i] = 1; - break; - } - } - } - } - - // Make sure any extra players don't have actors spawned yet. Happens if the players - // present now got the same slots as they had in the save, but there are not as many - // as there were in the save. - for (j = 0; j < MAXPLAYERS; ++j) - { - if (playerUsed[j] == 0) - { - if (players[j].mo != NULL) - { - players[j].mo->Destroy(); - players[j].mo = NULL; - } - } - } - - // Remove any temp players that were not used. Happens if there are fewer players - // than there were in the save, and they got shuffled. - for (i = 0; i < numPlayers; ++i) - { - if (tempPlayerUsed[i] == 0) - { - playertemp[i].mo->Destroy(); - playertemp[i].mo = NULL; - } - } - } - - delete[] tempPlayerUsed; - delete[] playertemp; - for (i = 0; i < numPlayers; ++i) - { - delete[] nametemp[i]; - } - delete[] nametemp; -} - -void CopyPlayer (player_t *dst, player_t *src, const char *name) -{ - // The userinfo needs to be saved for real players, but it - // needs to come from the save for bots. - userinfo_t uibackup; - userinfo_t uibackup2; - - uibackup.TransferFrom(dst->userinfo); - uibackup2.TransferFrom(src->userinfo); - - int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting - bool attackdown = dst->attackdown; - bool usedown = dst->usedown; - - - *dst = *src; // To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above. - - dst->cheats |= chasecam; - - if (dst->Bot != nullptr) - { - botinfo_t *thebot = bglobal.botinfo; - while (thebot && stricmp (name, thebot->name)) - { - thebot = thebot->next; - } - if (thebot) - { - thebot->inuse = BOTINUSE_Yes; - } - bglobal.botnum++; - dst->userinfo.TransferFrom(uibackup2); - } - else - { - dst->userinfo.TransferFrom(uibackup); - } - // Validate the skin - dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass)); - - // Make sure the player pawn points to the proper player struct. - if (dst->mo != nullptr) - { - dst->mo->player = dst; - } - - // Same for the psprites. - DPSprite *pspr = dst->psprites; - while (pspr) - { - pspr->Owner = dst; - - pspr = pspr->Next; - } - - // Don't let the psprites be destroyed when src is destroyed. - src->psprites = nullptr; - - // These 2 variables may not be overwritten. - dst->attackdown = attackdown; - dst->usedown = usedown; -} - -static void SpawnExtraPlayers () -{ - // If there are more players now than there were in the savegame, - // be sure to spawn the extra players. - int i; - - if (deathmatch) - { - return; - } - - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].mo == NULL) - { - players[i].playerstate = PST_ENTER; - P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); - } - } -} - - // // Thinkers @@ -807,6 +528,351 @@ FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def return Serialize(arc, key, z.Environment, nullptr); } +//========================================================================== +// +// ArchiveSounds +// +//========================================================================== + +void P_SerializeSounds(FSerializer &arc) +{ + S_SerializeSounds(arc); + DSeqNode::SerializeSequences (arc); + char *name = NULL; + BYTE order; + + if (arc.isWriting()) + { + order = S_GetMusic(&name); + } + arc("musicname", name) + ("musicorder", order); + + if (arc.isReading()) + { + if (!S_ChangeMusic(name, order)) + if (level.cdtrack == 0 || !S_ChangeCDMusic(level.cdtrack, level.cdid)) + S_ChangeMusic(level.Music, level.musicorder); + } + delete[] name; +} + +//========================================================================== +// +// +// +//========================================================================== + +void CopyPlayer(player_t *dst, player_t *src, const char *name); +static void ReadOnePlayer(FSerializer &arc, bool skipload); +static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload); +static void SpawnExtraPlayers(); + +//========================================================================== +// +// P_ArchivePlayers +// +//========================================================================== + +void P_SerializePlayers(FSerializer &arc, bool skipload) +{ + BYTE numPlayers, numPlayersNow; + int i; + + // Count the number of players present right now. + for (numPlayersNow = 0, i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + ++numPlayersNow; + } + } + +#if 0 + if (arc.isWriting()) + { + // Record the number of players in this save. + arc << numPlayersNow; + + // Record each player's name, followed by their data. + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + arc.WriteString(players[i].userinfo.GetName()); + players[i].Serialize(arc); + } + } + } + else + { + arc << numPlayers; + + // If there is only one player in the game, they go to the + // first player present, no matter what their name. + if (numPlayers == 1) + { + ReadOnePlayer(arc, skipload); + } + else + { + ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload); + } + if (!skipload && numPlayersNow > numPlayers) + { + SpawnExtraPlayers(); + } + // Redo pitch limits, since the spawned player has them at 0. + players[consoleplayer].SendPitchLimits(); + } +#endif +} + +//========================================================================== +// +// +// +//========================================================================== + +static void ReadOnePlayer(FSerializer &arc, bool skipload) +{ +#if 0 + int i; + char *name = NULL; + bool didIt = false; + + arc << name; + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + if (!didIt) + { + didIt = true; + player_t playerTemp; + playerTemp.Serialize(arc); + if (!skipload) + { + CopyPlayer(&players[i], &playerTemp, name); + } + } + else + { + if (players[i].mo != NULL) + { + players[i].mo->Destroy(); + players[i].mo = NULL; + } + } + } + } + delete[] name; +#endif +} + +//========================================================================== +// +// +// +//========================================================================== + +static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload) +{ +#if 0 + // For two or more players, read each player into a temporary array. + int i, j; + char **nametemp = new char *[numPlayers]; + player_t *playertemp = new player_t[numPlayers]; + BYTE *tempPlayerUsed = new BYTE[numPlayers]; + BYTE playerUsed[MAXPLAYERS]; + + for (i = 0; i < numPlayers; ++i) + { + nametemp[i] = NULL; + arc << nametemp[i]; + playertemp[i].Serialize(arc); + tempPlayerUsed[i] = 0; + } + for (i = 0; i < MAXPLAYERS; ++i) + { + playerUsed[i] = playeringame[i] ? 0 : 2; + } + + if (!skipload) + { + // Now try to match players from the savegame with players present + // based on their names. If two players in the savegame have the + // same name, then they are assigned to players in the current game + // on a first-come, first-served basis. + for (i = 0; i < numPlayers; ++i) + { + for (j = 0; j < MAXPLAYERS; ++j) + { + if (playerUsed[j] == 0 && stricmp(players[j].userinfo.GetName(), nametemp[i]) == 0) + { // Found a match, so copy our temp player to the real player + Printf("Found player %d (%s) at %d\n", i, nametemp[i], j); + CopyPlayer(&players[j], &playertemp[i], nametemp[i]); + playerUsed[j] = 1; + tempPlayerUsed[i] = 1; + break; + } + } + } + + // Any players that didn't have matching names are assigned to existing + // players on a first-come, first-served basis. + for (i = 0; i < numPlayers; ++i) + { + if (tempPlayerUsed[i] == 0) + { + for (j = 0; j < MAXPLAYERS; ++j) + { + if (playerUsed[j] == 0) + { + Printf("Assigned player %d (%s) to %d (%s)\n", i, nametemp[i], j, players[j].userinfo.GetName()); + CopyPlayer(&players[j], &playertemp[i], nametemp[i]); + playerUsed[j] = 1; + tempPlayerUsed[i] = 1; + break; + } + } + } + } + + // Make sure any extra players don't have actors spawned yet. Happens if the players + // present now got the same slots as they had in the save, but there are not as many + // as there were in the save. + for (j = 0; j < MAXPLAYERS; ++j) + { + if (playerUsed[j] == 0) + { + if (players[j].mo != NULL) + { + players[j].mo->Destroy(); + players[j].mo = NULL; + } + } + } + + // Remove any temp players that were not used. Happens if there are fewer players + // than there were in the save, and they got shuffled. + for (i = 0; i < numPlayers; ++i) + { + if (tempPlayerUsed[i] == 0) + { + playertemp[i].mo->Destroy(); + playertemp[i].mo = NULL; + } + } + } + + delete[] tempPlayerUsed; + delete[] playertemp; + for (i = 0; i < numPlayers; ++i) + { + delete[] nametemp[i]; + } + delete[] nametemp; +#endif +} + +//========================================================================== +// +// +// +//========================================================================== + +void CopyPlayer(player_t *dst, player_t *src, const char *name) +{ + // The userinfo needs to be saved for real players, but it + // needs to come from the save for bots. + userinfo_t uibackup; + userinfo_t uibackup2; + + uibackup.TransferFrom(dst->userinfo); + uibackup2.TransferFrom(src->userinfo); + + int chasecam = dst->cheats & CF_CHASECAM; // Remember the chasecam setting + bool attackdown = dst->attackdown; + bool usedown = dst->usedown; + + + *dst = *src; // To avoid memory leaks at this point the userinfo in src must be empty which is taken care of by the TransferFrom call above. + + dst->cheats |= chasecam; + + if (dst->Bot != nullptr) + { + botinfo_t *thebot = bglobal.botinfo; + while (thebot && stricmp(name, thebot->name)) + { + thebot = thebot->next; + } + if (thebot) + { + thebot->inuse = BOTINUSE_Yes; + } + bglobal.botnum++; + dst->userinfo.TransferFrom(uibackup2); + } + else + { + dst->userinfo.TransferFrom(uibackup); + } + // Validate the skin + dst->userinfo.SkinNumChanged(R_FindSkin(skins[dst->userinfo.GetSkin()].name, dst->CurrentPlayerClass)); + + // Make sure the player pawn points to the proper player struct. + if (dst->mo != nullptr) + { + dst->mo->player = dst; + } + + // Same for the psprites. + DPSprite *pspr = dst->psprites; + while (pspr) + { + pspr->Owner = dst; + + pspr = pspr->Next; + } + + // Don't let the psprites be destroyed when src is destroyed. + src->psprites = nullptr; + + // These 2 variables may not be overwritten. + dst->attackdown = attackdown; + dst->usedown = usedown; +} + +//========================================================================== +// +// +// +//========================================================================== + +static void SpawnExtraPlayers() +{ + // If there are more players now than there were in the savegame, + // be sure to spawn the extra players. + int i; + + if (deathmatch) + { + return; + } + + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].mo == NULL) + { + players[i].playerstate = PST_ENTER; + P_SpawnPlayer(&playerstarts[i], i, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); + } + } +} + //============================================================================ // // @@ -838,7 +904,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) } } - //Renderer->StartSerialize(arc); + Renderer->StartSerialize(arc); if (arc.isReading()) { P_DestroyThinkers(hubload); @@ -894,53 +960,10 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) StatusBar->SerializeMessages(arc); AM_SerializeMarkers(arc); FRemapTable::StaticSerializeTranslations(arc); - -} - - - -//========================================================================== -// -// ArchiveSounds -// -//========================================================================== - -void P_SerializeSounds (FArchive &arc) -{ - S_SerializeSounds (arc); - //DSeqNode::SerializeSequences (arc); - char *name = NULL; - BYTE order; - - if (arc.IsStoring ()) - { - order = S_GetMusic (&name); - } - arc << name << order; - if (arc.IsLoading ()) - { - if (!S_ChangeMusic (name, order)) - if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) - S_ChangeMusic (level.Music, level.musicorder); - } - delete[] name; -} - -//========================================================================== -// -// -//========================================================================== - -void G_SerializeLevel(FArchive &arc, bool hubLoad) -{ -#if 0 - - // This must be saved, too, of course! FCanvasTextureInfo::Serialize(arc); - - P_SerializePlayers(arc, hubLoad); + //P_SerializePlayers(arc, hubLoad); P_SerializeSounds(arc); - if (arc.IsLoading()) + if (arc.isReading()) { for (int i = 0; i < numsectors; i++) { @@ -955,6 +978,16 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad) } } Renderer->EndSerialize(arc); -#endif + +} + + +//========================================================================== +// +// +//========================================================================== + +void G_SerializeLevel(FArchive &arc, bool hubLoad) +{ } diff --git a/src/p_saveg.h b/src/p_saveg.h index fee2874fd..63beb72b8 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -40,9 +40,7 @@ struct PNGHandle; // Persistent storage/archiving. // These are the load / save game routines. // Also see farchive.(h|cpp) -void P_SerializePlayers (FArchive &arc, bool fakeload); void P_DestroyThinkers(bool hubLoad); -void P_SerializeSounds (FArchive &arc); void P_ReadACSDefereds (PNGHandle *png); void P_WriteACSDefereds (FILE *file); diff --git a/src/r_defs.h b/src/r_defs.h index 4a0f038de..efd9fc641 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -272,14 +272,13 @@ class ASkyViewpoint; struct secplane_t { - friend FArchive &operator<< (FArchive &arc, secplane_t &plane); // the plane is defined as a*x + b*y + c*z + d = 0 // ic is 1/c, for faster Z calculations -//private: // restore when JSON serializer is done. DVector3 normal; double D, negiC; // negative iC because that also saves a negation in all methods using this. public: + friend FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplane_t *def); void set(double aa, double bb, double cc, double dd) { @@ -437,9 +436,6 @@ public: }; -FArchive &operator<< (FArchive &arc, secplane_t &plane); - - #include "p_3dfloors.h" // Ceiling/floor flags enum @@ -544,8 +540,6 @@ struct extsector_t TArray lightlist; // 3D light list TArray attached; // 3D floors attached to this sector } XFloor; - - void Serialize(FArchive &arc); }; struct FTransform @@ -1033,9 +1027,6 @@ public: extsector_t * e; // This stores data that requires construction/destruction. Such data must not be copied by R_FakeFlat. }; -FArchive &operator<< (FArchive &arc, sector_t::splane &p); - - struct ReverbContainer; struct zone_t { @@ -1198,8 +1189,6 @@ struct side_t vertex_t *V2() const; }; -FArchive &operator<< (FArchive &arc, side_t::part &p); - struct line_t { vertex_t *v1, *v2; // vertices, from v1 to v2 diff --git a/src/r_renderer.h b/src/r_renderer.h index a39520b49..c5385aadc 100644 --- a/src/r_renderer.h +++ b/src/r_renderer.h @@ -5,7 +5,7 @@ struct FRenderer; extern FRenderer *Renderer; -class FArchive; +class FSerializer; class FTexture; class AActor; class player_t; @@ -46,8 +46,8 @@ struct FRenderer virtual void StateChanged(AActor *actor) {} // notify the renderer that serialization of the curent level is about to start/end - virtual void StartSerialize(FArchive &arc) {} - virtual void EndSerialize(FArchive &arc) {} + virtual void StartSerialize(FSerializer &arc) {} + virtual void EndSerialize(FSerializer &arc) {} virtual int GetMaxViewPitch(bool down) = 0; // return value is in plain degrees diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 985081a4b..b7276fe22 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -53,7 +53,7 @@ #include "v_font.h" #include "r_renderer.h" #include "r_data/colormaps.h" -#include "farchive.h" +#include "serializer.h" #include "r_utility.h" #include "d_player.h" #include "p_local.h" @@ -1041,37 +1041,49 @@ void FCanvasTextureInfo::EmptyList () // //========================================================================== -void FCanvasTextureInfo::Serialize(FArchive &arc) +void FCanvasTextureInfo::Serialize(FSerializer &arc) { -#if 0 - if (arc.IsStoring ()) + if (arc.isWriting()) { - FCanvasTextureInfo *probe; - - for (probe = List; probe != NULL; probe = probe->Next) + if (List != nullptr) { - if (probe->Texture != NULL && probe->Viewpoint != NULL) + if (arc.BeginArray("canvastextures")) { - arc << probe->Viewpoint << probe->FOV << probe->PicNum; + FCanvasTextureInfo *probe; + + for (probe = List; probe != nullptr; probe = probe->Next) + { + if (probe->Texture != nullptr && probe->Viewpoint != nullptr) + { + if (arc.BeginObject(nullptr)) + { + arc("viewpoint", probe->Viewpoint) + ("fov", probe->FOV) + ("texture", probe->PicNum) + .EndObject(); + } + } + } } } - AActor *nullactor = NULL; - arc << nullactor; } else { - AActor *viewpoint; - int fov; - FTextureID picnum; - - EmptyList (); - while (arc << viewpoint, viewpoint != NULL) + if (arc.BeginArray("canvastextures")) { - arc << fov << picnum; - Add (viewpoint, picnum, fov); + AActor *viewpoint; + int fov; + FTextureID picnum; + while (arc.BeginObject(nullptr)) + { + arc("viewpoint", viewpoint) + ("fov", fov) + ("texture", picnum) + .EndObject(); + Add(viewpoint, picnum, fov); + } } } -#endif } //========================================================================== diff --git a/src/r_utility.h b/src/r_utility.h index 4d004e835..da9a32d11 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -3,6 +3,8 @@ #include "r_state.h" #include "vectors.h" + +class FSerializer; // // Stuff from r_main.h that's needed outside the rendering code. @@ -113,7 +115,7 @@ struct FCanvasTextureInfo static void Add (AActor *viewpoint, FTextureID picnum, int fov); static void UpdateAll (); static void EmptyList (); - static void Serialize(FArchive &arc); + static void Serialize(FSerializer &arc); static void Mark(); private: diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index 0506a0f3f..fcf5523ad 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -33,7 +33,7 @@ ** */ -#include "resourcefile.h" +#include "file_zip.h" #include "cmdlib.h" #include "templates.h" #include "v_text.h" @@ -44,6 +44,69 @@ #define BUFREADCOMMENT (0x400) +//========================================================================== +// +// Decompression subroutine +// +//========================================================================== + +static bool UncompressZipLump(char *Cache, FileReader *Reader, int Method, int LumpSize, int CompressedSize, int GPFlags) +{ + switch (Method) + { + case METHOD_STORED: + { + Reader->Read(Cache, LumpSize); + break; + } + + case METHOD_DEFLATE: + { + FileReaderZ frz(*Reader, true); + frz.Read(Cache, LumpSize); + break; + } + + case METHOD_BZIP2: + { + FileReaderBZ2 frz(*Reader); + frz.Read(Cache, LumpSize); + break; + } + + case METHOD_LZMA: + { + FileReaderLZMA frz(*Reader, LumpSize, true); + frz.Read(Cache, LumpSize); + break; + } + + case METHOD_IMPLODE: + { + FZipExploder exploder; + exploder.Explode((unsigned char *)Cache, LumpSize, Reader, CompressedSize, GPFlags); + break; + } + + case METHOD_SHRINK: + { + ShrinkLoop((unsigned char *)Cache, LumpSize, Reader, CompressedSize); + break; + } + + default: + assert(0); + return false; + } + return true; +} + +bool FCompressedBuffer::Decompress(char *destbuffer) +{ + MemoryReader mr(mBuffer, mCompressedSize); + return UncompressZipLump(destbuffer, &mr, mMethod, mSize, mCompressedSize, mZipFlags); +} + //----------------------------------------------------------------------- // // Finds the central directory end record in the end of the file. @@ -96,56 +159,6 @@ static DWORD Zip_FindCentralDir(FileReader * fin) return uPosFound; } - -enum -{ - LUMPFZIP_NEEDFILESTART = 128 -}; - -//========================================================================== -// -// Zip Lump -// -//========================================================================== - -struct FZipLump : public FResourceLump -{ - WORD GPFlags; - BYTE Method; - int CompressedSize; - int Position; - - virtual FileReader *GetReader(); - virtual int FillCache(); - -private: - void SetLumpAddress(); - virtual int GetFileOffset() - { - if (Method != METHOD_STORED) return -1; - if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress(); return Position; - } -}; - - -//========================================================================== -// -// Zip file -// -//========================================================================== - -class FZipFile : public FResourceFile -{ - FZipLump *Lumps; - -public: - FZipFile(const char * filename, FileReader *file); - virtual ~FZipFile(); - bool Open(bool quiet); - virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; } -}; - - //========================================================================== // // Zip file @@ -285,6 +298,24 @@ FZipFile::~FZipFile() if (Lumps != NULL) delete [] Lumps; } +//========================================================================== +// +// +// +//========================================================================== + +FCompressedBuffer FZipFile::GetRawLump(int lumpnum) +{ + if ((unsigned)lumpnum >= NumLumps) + { + return{ 0,0,0,0,nullptr }; + } + FZipLump *lmp = &Lumps[lumpnum]; + FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, new char[lmp->CompressedSize] }; + Reader->Seek(lmp->Position, SEEK_SET); + Reader->Read(cbuf.mBuffer, lmp->CompressedSize); +} + //========================================================================== // // SetLumpAddress @@ -348,56 +379,22 @@ int FZipLump::FillCache() Owner->Reader->Seek(Position, SEEK_SET); Cache = new char[LumpSize]; - switch (Method) - { - case METHOD_STORED: - { - Owner->Reader->Read(Cache, LumpSize); - break; - } - - case METHOD_DEFLATE: - { - FileReaderZ frz(*Owner->Reader, true); - frz.Read(Cache, LumpSize); - break; - } - - case METHOD_BZIP2: - { - FileReaderBZ2 frz(*Owner->Reader); - frz.Read(Cache, LumpSize); - break; - } - - case METHOD_LZMA: - { - FileReaderLZMA frz(*Owner->Reader, LumpSize, true); - frz.Read(Cache, LumpSize); - break; - } - - case METHOD_IMPLODE: - { - FZipExploder exploder; - exploder.Explode((unsigned char *)Cache, LumpSize, Owner->Reader, CompressedSize, GPFlags); - break; - } - - case METHOD_SHRINK: - { - ShrinkLoop((unsigned char *)Cache, LumpSize, Owner->Reader, CompressedSize); - break; - } - - default: - assert(0); - return 0; - } + UncompressZipLump(Cache, Owner->Reader, Method, LumpSize, CompressedSize, GPFlags); RefCount = 1; return 1; } +//========================================================================== +// +// +// +//========================================================================== + +int FZipLump::GetFileOffset() +{ + if (Method != METHOD_STORED) return -1; + if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress(); return Position; +} //========================================================================== // diff --git a/src/resourcefiles/file_zip.h b/src/resourcefiles/file_zip.h new file mode 100644 index 000000000..54fbde92b --- /dev/null +++ b/src/resourcefiles/file_zip.h @@ -0,0 +1,64 @@ +#ifndef __FILE_ZIP_H +#define __FILE_ZIP_H + +#include "resourcefile.h" + +// This holds a compresed Zip entry with all needed info to decompress it. +struct FCompressedBuffer +{ + unsigned mSize; + unsigned mCompressedSize; + int mMethod; + int mZipFlags; + char *mBuffer; + + bool Decompress(char *destbuffer); +}; + +enum +{ + LUMPFZIP_NEEDFILESTART = 128 +}; + +//========================================================================== +// +// Zip Lump +// +//========================================================================== + +struct FZipLump : public FResourceLump +{ + WORD GPFlags; + BYTE Method; + int CompressedSize; + int Position; + + virtual FileReader *GetReader(); + virtual int FillCache(); + +private: + void SetLumpAddress(); + virtual int GetFileOffset(); +}; + + +//========================================================================== +// +// Zip file +// +//========================================================================== + +class FZipFile : public FResourceFile +{ + FZipLump *Lumps; + +public: + FZipFile(const char * filename, FileReader *file); + virtual ~FZipFile(); + bool Open(bool quiet); + virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; } + FCompressedBuffer GetRawLump(int lumpnum); +}; + + +#endif \ No newline at end of file diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 0f7d690fa..7716a8d27 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -50,8 +50,9 @@ #include "timidity/timidity.h" #include "g_level.h" #include "po_man.h" -#include "farchive.h" +#include "serializer.h" #include "d_player.h" +#include "r_state.h" // MACROS ------------------------------------------------------------------ @@ -2208,58 +2209,41 @@ void S_StopChannel(FSoundChan *chan) //========================================================================== // -// (FArchive &) << (FSoundID &) +// // //========================================================================== -FArchive &operator<<(FArchive &arc, FSoundID &sid) +static FSerializer &Serialize(FSerializer &arc, const char *key, FSoundChan &chan, FSoundChan *def) { - if (arc.IsStoring()) + if (arc.BeginObject(key)) { - arc.WriteName((const char *)sid); - } - else - { - sid = arc.ReadName(); - } - return arc; -} + arc("sourcetype", chan.SourceType) + ("soundid", chan.SoundID) + ("orgid", chan.OrgID) + ("volume", chan.Volume) + ("distancescale", chan.DistanceScale) + ("pitch", chan.Pitch) + ("chanflags", chan.ChanFlags) + ("entchannel", chan.EntChannel) + ("priority", chan.Priority) + ("nearlimit", chan.NearLimit) + ("starttime", chan.StartTime.AsOne) + ("rolloftype", chan.Rolloff.RolloffType) + ("rolloffmin", chan.Rolloff.MinDistance) + ("rolloffmax", chan.Rolloff.MaxDistance) + ("limitrange", chan.LimitRange); -//========================================================================== -// -// (FArchive &) << (FSoundChan &) -// -//========================================================================== - -static FArchive &operator<<(FArchive &arc, FSoundChan &chan) -{ - arc << chan.SourceType; -#if 0 - switch (chan.SourceType) - { - case SOURCE_None: break; - case SOURCE_Actor: arc << chan.Actor; break; - case SOURCE_Sector: arc << chan.Sector; break; - case SOURCE_Polyobj: /*arc << chan.Poly;*/ break; - case SOURCE_Unattached: arc << chan.Point[0] << chan.Point[1] << chan.Point[2]; break; - default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; + switch (chan.SourceType) + { + case SOURCE_None: break; + case SOURCE_Actor: arc("actor", chan.Actor); break; + case SOURCE_Sector: arc("sector", chan.Sector); break; + case SOURCE_Polyobj: arc("poly", chan.Poly); break; + case SOURCE_Unattached: arc.Array("point", chan.Point, 3); break; + default: I_Error("Unknown sound source type %d\n", chan.SourceType); break; + } + arc.EndObject(); } -#endif - arc << chan.SoundID - << chan.OrgID - << chan.Volume - << chan.DistanceScale - << chan.Pitch - << chan.ChanFlags - << chan.EntChannel - << chan.Priority - << chan.NearLimit - << chan.StartTime - << chan.Rolloff.RolloffType - << chan.Rolloff.MinDistance - << chan.Rolloff.MaxDistance - << chan.LimitRange; - return arc; } @@ -2269,13 +2253,13 @@ static FArchive &operator<<(FArchive &arc, FSoundChan &chan) // //========================================================================== -void S_SerializeSounds(FArchive &arc) +void S_SerializeSounds(FSerializer &arc) { FSoundChan *chan; GSnd->Sync(true); - if (arc.IsStoring()) + if (arc.isWriting()) { TArray chans; @@ -2292,16 +2276,17 @@ void S_SerializeSounds(FArchive &arc) chans.Push(chan); } } - - arc.WriteCount(chans.Size()); - - for (unsigned int i = chans.Size(); i-- != 0; ) + if (chans.Size() > 0 && arc.BeginArray("sounds")) { - // Replace start time with sample position. - QWORD start = chans[i]->StartTime.AsOne; - chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0; - arc << *chans[i]; - chans[i]->StartTime.AsOne = start; + for (unsigned int i = chans.Size(); i-- != 0; ) + { + // Replace start time with sample position. + QWORD start = chans[i]->StartTime.AsOne; + chans[i]->StartTime.AsOne = GSnd ? GSnd->GetPosition(chans[i]) : 0; + arc(nullptr, *chans[i]); + chans[i]->StartTime.AsOne = start; + } + arc.EndArray(); } } else @@ -2309,13 +2294,17 @@ void S_SerializeSounds(FArchive &arc) unsigned int count; S_StopAllChannels(); - count = arc.ReadCount(); - for (unsigned int i = 0; i < count; ++i) + if (arc.BeginArray("sounds")) { - chan = (FSoundChan*)S_GetChannel(NULL); - arc << *chan; - // Sounds always start out evicted when restored from a save. - chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; + count = arc.ArraySize(); + for (unsigned int i = 0; i < count; ++i) + { + chan = (FSoundChan*)S_GetChannel(NULL); + arc(nullptr, *chan); + // Sounds always start out evicted when restored from a save. + chan->ChanFlags |= CHAN_EVICTED | CHAN_ABSTIME; + } + arc.EndArray(); } // The two tic delay is to make sure any screenwipes have finished. // This needs to be two because the game is run for one tic before @@ -2326,7 +2315,6 @@ void S_SerializeSounds(FArchive &arc) // sounds might be heard briefly before pausing for the wipe. RestartEvictionsAt = level.time + 2; } - //DSeqNode::SerializeSequences(arc); GSnd->Sync(false); GSnd->UpdateSounds(); } diff --git a/src/s_sound.h b/src/s_sound.h index c2e0f187e..29b5dfb2d 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -321,7 +321,7 @@ bool S_ChangeSoundVolume(AActor *actor, int channel, float volume); void S_RelinkSound (AActor *from, AActor *to); // Stores/retrieves playing channel information in an archive. -void S_SerializeSounds(FArchive &arc); +void S_SerializeSounds(FSerializer &arc); // Start music using bool S_StartMusic (const char *music_name); diff --git a/src/serializer.cpp b/src/serializer.cpp index 66d23cd72..511bc6086 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -30,6 +30,7 @@ #include "g_shared/a_sharedglobal.h" #include "po_man.h" #include "v_font.h" +#include "w_zip.h" char nulspace[1024 * 1024 * 4]; @@ -133,7 +134,7 @@ struct FReader return &it->value; } } - else if (obj.mObject->IsArray() && obj.mIndex < obj.mObject->Size()) + else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size()) { return &(*obj.mObject)[obj.mIndex++]; } @@ -175,8 +176,33 @@ bool FSerializer::OpenReader(const char *buffer, size_t length) // //========================================================================== -void FSerializer::Close() +bool FSerializer::OpenReader(FCompressedBuffer *input) { + if (input->mSize <= 0 || input->mBuffer == nullptr) return false; + if (w != nullptr || r != nullptr) return false; + + if (input->mMethod == METHOD_STORED) + { + r = new FReader((char*)input->mBuffer, input->mSize); + return true; + } + else + { + char *unpacked = new char[input->mSize]; + input->Decompress(unpacked); + r = new FReader(unpacked, input->mSize); + return true; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FSerializer::Close() +{ if (w != nullptr) { delete w; @@ -622,8 +648,15 @@ void FSerializer::WriteObjects() } } +//========================================================================== +// +// +// +//========================================================================== + const char *FSerializer::GetOutput(unsigned *len) { + if (isReading()) return nullptr; if (len != nullptr) { *len = (unsigned)w->mOutString.GetSize(); @@ -637,6 +670,63 @@ const char *FSerializer::GetOutput(unsigned *len) // //========================================================================== +FCompressedBuffer FSerializer::GetCompressedOutput() +{ + if (isReading()) return{ 0,0,0,0,nullptr }; + FCompressedBuffer buff; + buff.mSize = (unsigned)w->mOutString.GetSize(); + buff.mZipFlags = 0; + + uint8_t *compressbuf = new uint8_t[buff.mSize+1]; + + z_stream stream; + int err; + + stream.next_in = (Bytef *)w->mOutString.GetString(); + stream.avail_in = buff.mSize; + stream.next_out = (Bytef*)compressbuf; + stream.avail_out = buff.mSize; + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + // create output in zip-compatible form as required by FCompressedBuffer + err = deflateInit2(&stream, 8, Z_DEFLATED, -15, 9, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + { + goto error; + } + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + { + deflateEnd(&stream); + goto error; + } + buff.mCompressedSize = stream.total_out; + + err = deflateEnd(&stream); + if (err == Z_OK) + { + buff.mBuffer = new char[buff.mCompressedSize]; + buff.mMethod = METHOD_DEFLATE; + memcpy(buff.mBuffer, compressbuf, buff.mCompressedSize); + delete[] compressbuf; + } + +error: + memcpy(compressbuf, w->mOutString.GetString(), buff.mSize + 1); + buff.mCompressedSize = buff.mSize; + buff.mMethod = METHOD_STORED; + return buff; +} + +//========================================================================== +// +// +// +//========================================================================== + FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval) { if (arc.isWriting()) @@ -916,6 +1006,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *& return SerializePointer(arc, key, value, defval, polyobjs); } +template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval) +{ + return SerializePointer(arc, key, value, defval, polyobjs); +} + template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) { return SerializePointer(arc, key, value, defval, sides); @@ -926,6 +1021,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *& return SerializePointer(arc, key, value, defval, sectors); } +template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval) +{ + return SerializePointer(arc, key, value, defval, sectors); +} + template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) { return SerializePointer(arc, key, value, defval, players); diff --git a/src/serializer.h b/src/serializer.h index cec13ae1c..7b35d4d9a 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -5,6 +5,7 @@ #include #include "tarray.h" #include "r_defs.h" +#include "resourcefiles/file_zip.h" struct FWriter; struct FReader; @@ -38,7 +39,6 @@ struct NumericValue }; - class FSerializer { @@ -57,6 +57,7 @@ public: } bool OpenWriter(); bool OpenReader(const char *buffer, size_t length); + bool OpenReader(FCompressedBuffer *input); void Close(); bool BeginObject(const char *name, bool randomaccess = false); void EndObject(); @@ -66,6 +67,7 @@ public: unsigned GetSize(const char *group); 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); @@ -203,6 +205,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TArray &value, 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); @@ -216,6 +220,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimat 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, sector_t *&value, sector_t **defval); + 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) { @@ -250,7 +257,7 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, PalEntry &pe, P inline FSerializer &Serialize(FSerializer &arc, const char *key, FRenderStyle &style, FRenderStyle *def) { - return Serialize(arc, key, style.AsDWORD, def ? &def->AsDWORD : nullptr); + return arc.Array(key, &style.BlendOp, def ? &def->BlendOp : nullptr, 4); } template From da83d9e2bd5f90f4f8c0f38facab61825e7b7be6 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 01:18:29 +0200 Subject: [PATCH 24/59] - converted player serializer and everything it needs. This means that everything belonging to the level snapshot will be generated in the JSON output. --- src/d_netinfo.cpp | 91 +++++++++++++------------ src/d_player.h | 8 +-- src/d_protocol.cpp | 40 ++++++----- src/d_protocol.h | 4 -- src/d_ticcmd.h | 3 - src/dthinker.h | 1 - src/p_saveg.cpp | 104 +++++++++++++++-------------- src/p_user.cpp | 161 ++++++++++++++++++++++----------------------- src/serializer.cpp | 53 +++++++++++++-- src/serializer.h | 11 +++- src/zzz_old.cpp | 2 +- 11 files changed, 265 insertions(+), 213 deletions(-) diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 0b99fd73d..481421d31 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -56,7 +56,7 @@ #include "r_data/r_translate.h" #include "templates.h" #include "cmdlib.h" -#include "farchive.h" +#include "serializer.h" static FRandom pr_pickteam ("PickRandomTeam"); @@ -878,70 +878,75 @@ void D_ReadUserInfoStrings (int pnum, BYTE **stream, bool update) *stream += strlen (*((char **)stream)) + 1; } -void WriteUserInfo(FArchive &arc, userinfo_t &info) +void WriteUserInfo(FSerializer &arc, userinfo_t &info) { - TMapIterator it(info); - TMap::Pair *pair; - FName name; - UCVarValue val; - int i; - - while (it.NextPair(pair)) + if (arc.BeginObject("userinfo")) { - name = pair->Key; - arc << name; - switch (name.GetIndex()) + TMapIterator it(info); + TMap::Pair *pair; + FString name; + const char *string; + UCVarValue val; + int i; + + while (it.NextPair(pair)) { - case NAME_Skin: - arc.WriteString(skins[info.GetSkin()].name); - break; + name = pair->Key; + name.ToLower(); + switch (pair->Key.GetIndex()) + { + case NAME_Skin: + string = skins[info.GetSkin()].name; + break; - case NAME_PlayerClass: - i = info.GetPlayerClassNum(); - arc.WriteString(i == -1 ? "Random" : PlayerClasses[i].Type->DisplayName.GetChars()); - break; + case NAME_PlayerClass: + i = info.GetPlayerClassNum(); + string = (i == -1 ? "Random" : PlayerClasses[i].Type->DisplayName.GetChars()); + break; - default: - val = pair->Value->GetGenericRep(CVAR_String); - arc.WriteString(val.String); - break; + default: + val = pair->Value->GetGenericRep(CVAR_String); + string = val.String; + break; + } + arc.StringPtr(name, string); } + arc.EndObject(); } - name = NAME_None; - arc << name; } -void ReadUserInfo(FArchive &arc, userinfo_t &info, FString &skin) +void ReadUserInfo(FSerializer &arc, userinfo_t &info, FString &skin) { FName name; FBaseCVar **cvar; - char *str = NULL; UCVarValue val; + const char *key; + const char *str; info.Reset(); skin = NULL; - for (arc << name; name != NAME_None; arc << name) + if (arc.BeginObject("userinfo")) { - cvar = info.CheckKey(name); - arc << str; - if (cvar != NULL && *cvar != NULL) + while ((key = arc.GetKey())) { - switch (name) + arc.StringPtr(key, str); + name = key; + cvar = info.CheckKey(name); + if (cvar != NULL && *cvar != NULL) { - case NAME_Team: info.TeamChanged(atoi(str)); break; - case NAME_Skin: skin = str; break; // Caller must call SkinChanged() once current calss is known - case NAME_PlayerClass: info.PlayerClassChanged(str); break; - default: - val.String = str; - (*cvar)->SetGenericRep(val, CVAR_String); - break; + switch (name) + { + case NAME_Team: info.TeamChanged(atoi(str)); break; + case NAME_Skin: skin = str; break; // Caller must call SkinChanged() once current calss is known + case NAME_PlayerClass: info.PlayerClassChanged(str); break; + default: + val.String = str; + (*cvar)->SetGenericRep(val, CVAR_String); + break; + } } } } - if (str != NULL) - { - delete[] str; - } } CCMD (playerinfo) diff --git a/src/d_player.h b/src/d_player.h index f288f64f9..61a14691f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -371,8 +371,8 @@ struct userinfo_t : TMap int ColorSetChanged(int setnum); }; -void ReadUserInfo(FArchive &arc, userinfo_t &info, FString &skin); -void WriteUserInfo(FArchive &arc, userinfo_t &info); +void ReadUserInfo(FSerializer &arc, userinfo_t &info, FString &skin); +void WriteUserInfo(FSerializer &arc, userinfo_t &info); // // Extended player object info: player_t @@ -384,7 +384,7 @@ public: ~player_t(); player_t &operator= (const player_t &p); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); size_t FixPointers (const DObject *obj, DObject *replacement); size_t PropagateMark(); @@ -542,8 +542,6 @@ public: // Bookkeeping on players - state. extern player_t players[MAXPLAYERS]; -FArchive &operator<< (FArchive &arc, player_t *&p); - void P_CheckPlayerSprite(AActor *mo, int &spritenum, DVector2 &scale); inline void AActor::SetFriendPlayer(player_t *player) diff --git a/src/d_protocol.cpp b/src/d_protocol.cpp index df6f9f23a..6d7200f8c 100644 --- a/src/d_protocol.cpp +++ b/src/d_protocol.cpp @@ -38,7 +38,7 @@ #include "doomdef.h" #include "doomstat.h" #include "cmdlib.h" -#include "farchive.h" +#include "serializer.h" char *ReadString (BYTE **stream) @@ -287,27 +287,33 @@ int PackUserCmd (const usercmd_t *ucmd, const usercmd_t *basis, BYTE **stream) return int(*stream - start); } -FArchive &operator<< (FArchive &arc, ticcmd_t &cmd) +FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &cmd, ticcmd_t *def) { - return arc << cmd.consistancy << cmd.ucmd; + if (arc.BeginObject(key)) + { + arc("consistency", cmd.consistancy) + ("ucmd", cmd.ucmd) + .EndObject(); + } + return arc; } -FArchive &operator<< (FArchive &arc, usercmd_t &cmd) +FSerializer &Serialize(FSerializer &arc, const char *key, usercmd_t &cmd, usercmd_t *def) { - BYTE bytes[256]; - BYTE *stream = bytes; - if (arc.IsStoring ()) + // This used packed data with the old serializer but that's totally counterproductive when + // having a text format that is human-readable. So this compression has been undone here. + // The few bytes of file size it saves are not worth the obfuscation. + + if (arc.BeginObject(key)) { - BYTE len = PackUserCmd (&cmd, NULL, &stream); - arc << len; - arc.Write (bytes, len); - } - else - { - BYTE len; - arc << len; - arc.Read (bytes, len); - UnpackUserCmd (&cmd, NULL, &stream); + arc("buttons", cmd.buttons) + ("pitch", cmd.pitch) + ("yaw", cmd.yaw) + ("roll", cmd.roll) + ("forwardmove", cmd.forwardmove) + ("sidemove", cmd.sidemove) + ("upmove", cmd.upmove) + .EndObject(); } return arc; } diff --git a/src/d_protocol.h b/src/d_protocol.h index 61f7377e3..2813b14dc 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -72,10 +72,6 @@ struct usercmd_t short upmove; }; -class FArchive; - -FArchive &operator<< (FArchive &arc, usercmd_t &cmd); - // When transmitted, the above message is preceded by a byte // indicating which fields are actually present in the message. enum diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 5142a3c4e..7e819629c 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -35,7 +35,4 @@ struct ticcmd_t SWORD consistancy; // checks for net game }; - -FArchive &operator<< (FArchive &arc, ticcmd_t &cmd); - #endif // __D_TICCMD_H__ diff --git a/src/dthinker.h b/src/dthinker.h index d423c3454..4785c0035 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -96,7 +96,6 @@ private: static void DestroyThinkersInList (FThinkerList &list); static void DestroyMostThinkersInList (FThinkerList &list, int stat); static int TickThinkers (FThinkerList *list, FThinkerList *dest); // Returns: # of thinkers ticked - static void SaveList(FArchive &arc, DThinker *node); static void SaveList(FSerializer &arc, DThinker *node); void Remove(); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index e2178f995..59d5f8f04 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -576,7 +576,7 @@ static void SpawnExtraPlayers(); void P_SerializePlayers(FSerializer &arc, bool skipload) { - BYTE numPlayers, numPlayersNow; + int numPlayers, numPlayersNow; int i; // Count the number of players present right now. @@ -588,35 +588,45 @@ void P_SerializePlayers(FSerializer &arc, bool skipload) } } -#if 0 if (arc.isWriting()) { // Record the number of players in this save. - arc << numPlayersNow; - - // Record each player's name, followed by their data. - for (i = 0; i < MAXPLAYERS; ++i) + arc("numplayers", numPlayersNow); + if (arc.BeginArray("players")) { - if (playeringame[i]) + // Record each player's name, followed by their data. + for (i = 0; i < MAXPLAYERS; ++i) { - arc.WriteString(players[i].userinfo.GetName()); - players[i].Serialize(arc); + if (playeringame[i]) + { + if (arc.BeginObject(nullptr)) + { + const char *n = players[i].userinfo.GetName(); + arc.StringPtr("playername", n); + players[i].Serialize(arc); + arc.EndObject(); + } + } } + arc.EndArray(); } } else { - arc << numPlayers; + arc("numplayers", numPlayers); - // If there is only one player in the game, they go to the - // first player present, no matter what their name. - if (numPlayers == 1) + if (arc.BeginArray("players")) { - ReadOnePlayer(arc, skipload); - } - else - { - ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload); + // If there is only one player in the game, they go to the + // first player present, no matter what their name. + if (numPlayers == 1) + { + ReadOnePlayer(arc, skipload); + } + else + { + ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload); + } } if (!skipload && numPlayersNow > numPlayers) { @@ -625,7 +635,6 @@ void P_SerializePlayers(FSerializer &arc, bool skipload) // Redo pitch limits, since the spawned player has them at 0. players[consoleplayer].SendPitchLimits(); } -#endif } //========================================================================== @@ -636,39 +645,39 @@ void P_SerializePlayers(FSerializer &arc, bool skipload) static void ReadOnePlayer(FSerializer &arc, bool skipload) { -#if 0 int i; - char *name = NULL; + const char *name = NULL; bool didIt = false; - arc << name; - - for (i = 0; i < MAXPLAYERS; ++i) + if (arc.BeginObject(nullptr)) { - if (playeringame[i]) + arc.StringPtr("playername", name); + + for (i = 0; i < MAXPLAYERS; ++i) { - if (!didIt) + if (playeringame[i]) { - didIt = true; - player_t playerTemp; - playerTemp.Serialize(arc); - if (!skipload) + if (!didIt) { - CopyPlayer(&players[i], &playerTemp, name); + didIt = true; + player_t playerTemp; + playerTemp.Serialize(arc); + if (!skipload) + { + CopyPlayer(&players[i], &playerTemp, name); + } } - } - else - { - if (players[i].mo != NULL) + else { - players[i].mo->Destroy(); - players[i].mo = NULL; + if (players[i].mo != NULL) + { + players[i].mo->Destroy(); + players[i].mo = NULL; + } } } } } - delete[] name; -#endif } //========================================================================== @@ -679,10 +688,9 @@ static void ReadOnePlayer(FSerializer &arc, bool skipload) static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayersNow, bool skipload) { -#if 0 // For two or more players, read each player into a temporary array. int i, j; - char **nametemp = new char *[numPlayers]; + const char **nametemp = new const char *[numPlayers]; player_t *playertemp = new player_t[numPlayers]; BYTE *tempPlayerUsed = new BYTE[numPlayers]; BYTE playerUsed[MAXPLAYERS]; @@ -690,8 +698,11 @@ static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayers for (i = 0; i < numPlayers; ++i) { nametemp[i] = NULL; - arc << nametemp[i]; - playertemp[i].Serialize(arc); + if (arc.BeginObject(nullptr)) + { + arc.StringPtr("playername", nametemp[i]); + playertemp[i].Serialize(arc); + } tempPlayerUsed[i] = 0; } for (i = 0; i < MAXPLAYERS; ++i) @@ -769,12 +780,7 @@ static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayers delete[] tempPlayerUsed; delete[] playertemp; - for (i = 0; i < numPlayers; ++i) - { - delete[] nametemp[i]; - } delete[] nametemp; -#endif } //========================================================================== @@ -961,7 +967,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) AM_SerializeMarkers(arc); FRemapTable::StaticSerializeTranslations(arc); FCanvasTextureInfo::Serialize(arc); - //P_SerializePlayers(arc, hubLoad); + P_SerializePlayers(arc, hubload); P_SerializeSounds(arc); if (arc.isReading()) { diff --git a/src/p_user.cpp b/src/p_user.cpp index 57846dea4..3c3cdf135 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -3000,18 +3000,17 @@ void P_UnPredictPlayer () } } -void player_t::Serialize(FArchive &arc) +void player_t::Serialize(FSerializer &arc) { -#if 0 - int i; FString skinname; - arc << cls - << mo - << camera - << playerstate - << cmd; - if (arc.IsLoading()) + arc("class", cls) + ("mo", mo) + ("camera", camera) + ("playerstate", playerstate) + ("cmd", cmd); + + if (arc.isReading()) { ReadUserInfo(arc, userinfo, skinname); } @@ -3019,78 +3018,80 @@ void player_t::Serialize(FArchive &arc) { WriteUserInfo(arc, userinfo); } - arc << DesiredFOV << FOV - << viewz - << viewheight - << deltaviewheight - << bob - << Vel - << centering - << health - << inventorytics; - arc << fragcount - << spreecount - << multicount - << lastkilltime - << ReadyWeapon << PendingWeapon - << cheats - << refire - << inconsistant - << killcount - << itemcount - << secretcount - << damagecount - << bonuscount - << hazardcount - << poisoncount - << poisoner - << attacker - << extralight - << fixedcolormap << fixedlightlevel - << morphTics - << MorphedPlayerClass - << MorphStyle - << MorphExitFlash - << PremorphWeapon - << chickenPeck - << jumpTics - << respawn_time - << air_finished - << turnticks - << oldbuttons; - arc << hazardtype - << hazardinterval; - arc << Bot; - arc << BlendR - << BlendG - << BlendB - << BlendA; - arc << WeaponState; - arc << LogText - << ConversationNPC - << ConversationPC - << ConversationNPCAngle.Degrees - << ConversationFaceTalker; - for (i = 0; i < MAXPLAYERS; i++) - arc << frags[i]; + arc("desiredfov", DesiredFOV) + ("fov", FOV) + ("viewz", viewz) + ("viewheight", viewheight) + ("deltaviewheight", deltaviewheight) + ("bob", bob) + ("vel", Vel) + ("centering", centering) + ("health", health) + ("inventorytics", inventorytics) + ("fragcount", fragcount) + ("spreecount", spreecount) + ("multicount", multicount) + ("lastkilltime", lastkilltime) + ("readyweapon", ReadyWeapon) + ("pendingweapon", PendingWeapon) + ("cheats", cheats) + ("refire", refire) + ("inconsistant", inconsistant) + ("killcount", killcount) + ("itemcount", itemcount) + ("secretcount", secretcount) + ("damagecount", damagecount) + ("bonuscount", bonuscount) + ("hazardcount", hazardcount) + ("poisoncount", poisoncount) + ("poisoner", poisoner) + ("attacker", attacker) + ("extralight", extralight) + ("fixedcolormap", fixedcolormap) + ("fixedlightlevel", fixedlightlevel) + ("morphTics", morphTics) + ("morphedplayerclass", MorphedPlayerClass) + ("morphstyle", MorphStyle) + ("morphexitflash", MorphExitFlash) + ("premorphweapon", PremorphWeapon) + ("chickenpeck", chickenPeck) + ("jumptics", jumpTics) + ("respawntime", respawn_time) + ("airfinished", air_finished) + ("turnticks", turnticks) + ("oldbuttons", oldbuttons) + ("hazardtype", hazardtype) + ("hazardinterval", hazardinterval) + ("bot", Bot) + ("blendr", BlendR) + ("blendg", BlendG) + ("blendb", BlendB) + ("blenda", BlendA) + ("weaponstate", WeaponState) + ("logtext", LogText) + ("conversionnpc", ConversationNPC) + ("conversionpc", ConversationPC) + ("conversionnpcangle", ConversationNPCAngle) + ("conversionfacetalker", ConversationFaceTalker) + .Array("frags", frags, MAXPLAYERS) + ("psprites", psprites) + ("currentplayerclass", CurrentPlayerClass) + ("crouchfactor", crouchfactor) + ("crouching", crouching) + ("crouchdir", crouchdir) + ("crouchviewdelta", crouchviewdelta) + ("original_cmd", original_cmd) + ("original_oldbuttons", original_oldbuttons) + ("poisontype", poisontype) + ("poisonpaintype", poisonpaintype) + ("timefreezer", timefreezer) + ("settings_controller", settings_controller) + ("onground", onground) + ("musinfoactor", MUSINFOactor) + ("musinfotics", MUSINFOtics); - arc << psprites; - - arc << CurrentPlayerClass; - - arc << crouchfactor - << crouching - << crouchdir - << crouchviewdelta - << original_cmd - << original_oldbuttons; - arc << poisontype << poisonpaintype; - arc << timefreezer; - arc << settings_controller; - arc << onground; - - if (arc.IsLoading ()) + if (arc.isWriting ()) { // If the player reloaded because they pressed +use after dying, we // don't want +use to still be down after the game is loaded. @@ -3101,8 +3102,6 @@ void player_t::Serialize(FArchive &arc) { userinfo.SkinChanged(skinname, CurrentPlayerClass); } - arc << MUSINFOactor << MUSINFOtics; -#endif } bool P_IsPlayerTotallyFrozen(const player_t *player) diff --git a/src/serializer.cpp b/src/serializer.cpp index 511bc6086..939c3fc7d 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1136,12 +1136,22 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje if (value != nullptr) { int ndx; - int *pndx = arc.w->mObjectMap.CheckKey(value); - if (pndx != nullptr) ndx = *pndx; + if (value == WP_NOCHANGE) + { + ndx = -1; + } else { - ndx = arc.w->mDObjects.Push(value); - arc.w->mObjectMap[value] = ndx; + int *pndx = arc.w->mObjectMap.CheckKey(value); + if (pndx != nullptr) + { + ndx = *pndx; + } + else + { + ndx = arc.w->mDObjects.Push(value); + arc.w->mObjectMap[value] = ndx; + } } Serialize(arc, key, ndx, nullptr); } @@ -1149,8 +1159,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje else { auto val = arc.r->FindKey(key); - if (val != nullptr) + if (val != nullptr && val->IsInt()) { + if (val->GetInt() == -1) + { + value = WP_NOCHANGE; + } + else + { + } } else if (!retcode) { @@ -1302,7 +1319,14 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor if (!arc.w->inObject() || def == nullptr || clst != *def) { arc.WriteKey(key); - arc.w->mWriter.String(clst->TypeName.GetChars()); + if (clst == nullptr) + { + arc.w->mWriter.Null(); + } + else + { + arc.w->mWriter.String(clst->TypeName.GetChars()); + } } } else @@ -1314,6 +1338,10 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor { clst = PClass::FindActor(val->GetString()); } + else if (val->IsNull()) + { + clst = nullptr; + } else { I_Error("string type expected for '%s'", key); @@ -1337,7 +1365,14 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl if (!arc.w->inObject() || def == nullptr || clst != *def) { arc.WriteKey(key); - arc.w->mWriter.String(clst->TypeName.GetChars()); + if (clst == nullptr) + { + arc.w->mWriter.Null(); + } + else + { + arc.w->mWriter.String(clst->TypeName.GetChars()); + } } } else @@ -1349,6 +1384,10 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl { clst = PClass::FindClass(val->GetString()); } + else if (val->IsNull()) + { + clst = nullptr; + } else { I_Error("string type expected for '%s'", key); diff --git a/src/serializer.h b/src/serializer.h index 7b35d4d9a..12edc2e99 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -7,6 +7,9 @@ #include "r_defs.h" #include "resourcefiles/file_zip.h" +struct ticcmd_t; +struct usercmd_t; + struct FWriter; struct FReader; @@ -161,6 +164,8 @@ 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); template FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **) @@ -220,8 +225,10 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimat 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, sector_t *&value, sector_t **defval); +template<> inline FSerializer &Serialize(FSerializer &arc, const char *key, PClassPlayerPawn *&clst, PClassPlayerPawn **def) +{ + return Serialize(arc, key, (PClassActor *&)clst, (PClassActor **)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) diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 92dd10877..e6193b3aa 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -59,7 +59,7 @@ void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) #if 0 // still needed as reference. -FArchive &FArchive::ReadObject(DObject* &obj, PClass *wanttype) +FCrap &FCrap::ReadObject(DObject* &obj, PClass *wanttype) { BYTE objHead; const PClass *type; From 075e98c96705919f0a4f96fd521ad01a2571bbd8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 01:48:23 +0200 Subject: [PATCH 25/59] - use FCompressedBuffer to store level snapshots. FCompressedMemFile has been removed. --- src/farchive.cpp | 165 --------------------------------- src/farchive.h | 25 ----- src/g_game.cpp | 12 +-- src/g_level.cpp | 48 +++++----- src/g_level.h | 7 +- src/g_mapinfo.cpp | 15 +-- src/p_saveg.cpp | 9 -- src/p_saveg.h | 2 +- src/resourcefiles/file_zip.cpp | 1 + src/resourcefiles/file_zip.h | 9 ++ src/s_sound.cpp | 2 +- 11 files changed, 46 insertions(+), 249 deletions(-) diff --git a/src/farchive.cpp b/src/farchive.cpp index 98063be87..3aaf1bd86 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -420,171 +420,6 @@ void FCompressedFile::Explode () } } -FCompressedMemFile::FCompressedMemFile () -{ - m_SourceFromMem = false; - m_ImplodedBuffer = NULL; -} - -/* -FCompressedMemFile::FCompressedMemFile (const char *name, EOpenMode mode) - : FCompressedFile (name, mode) -{ - m_SourceFromMem = false; - m_ImplodedBuffer = NULL; -} -*/ - -FCompressedMemFile::~FCompressedMemFile () -{ - if (m_ImplodedBuffer != NULL) - { - M_Free (m_ImplodedBuffer); - } -} - -bool FCompressedMemFile::Open (const char *name, EOpenMode mode) -{ - if (mode == EWriting) - { - if (name) - { - I_Error ("FCompressedMemFile cannot write to disk"); - } - else - { - return Open (); - } - } - else - { - bool res = FCompressedFile::Open (name, EReading); - if (res) - { - fclose (m_File); - m_File = NULL; - } - return res; - } - return false; -} - -bool FCompressedMemFile::Open (void *memblock) -{ - Close (); - m_Mode = EReading; - m_Buffer = (BYTE *)memblock; - m_SourceFromMem = true; - Explode (); - m_SourceFromMem = false; - return !!m_Buffer; -} - -bool FCompressedMemFile::Open () -{ - Close (); - m_Mode = EWriting; - m_BufferSize = 0; - m_MaxBufferSize = 16384; - m_Buffer = (unsigned char *)M_Malloc (16384); - m_Pos = 0; - return true; -} - -bool FCompressedMemFile::Reopen () -{ - if (m_Buffer == NULL && m_ImplodedBuffer) - { - m_Mode = EReading; - m_Buffer = m_ImplodedBuffer; - m_SourceFromMem = true; - try - { - Explode (); - } - catch(...) - { - // If we just leave things as they are, m_Buffer and m_ImplodedBuffer - // both point to the same memory block and both will try to free it. - m_Buffer = NULL; - m_SourceFromMem = false; - throw; - } - m_SourceFromMem = false; - return true; - } - return false; -} - -void FCompressedMemFile::Close () -{ - if (m_Mode == EWriting) - { - Implode (); - m_ImplodedBuffer = m_Buffer; - m_Buffer = NULL; - } -} - -void FCompressedMemFile::Serialize(FArchive &arc) -{ - if (arc.IsStoring ()) - { - if (m_ImplodedBuffer == NULL) - { - I_Error ("FCompressedMemFile must be compressed before storing"); - } - arc.Write (ZSig, 4); - - DWORD sizes[2]; - sizes[0] = SWAP_DWORD (((DWORD *)m_ImplodedBuffer)[0]); - sizes[1] = SWAP_DWORD (((DWORD *)m_ImplodedBuffer)[1]); - arc.Write (m_ImplodedBuffer, (sizes[0] ? sizes[0] : sizes[1])+8); - } - else - { - Close (); - m_Mode = EReading; - - char sig[4]; - DWORD sizes[2] = { 0, 0 }; - - arc.Read (sig, 4); - - if (sig[0] != ZSig[0] || sig[1] != ZSig[1] || sig[2] != ZSig[2] || sig[3] != ZSig[3]) - I_Error ("Expected to extract a compressed file"); - - arc << sizes[0] << sizes[1]; - DWORD len = sizes[0] == 0 ? sizes[1] : sizes[0]; - - m_Buffer = (BYTE *)M_Malloc (len+8); - ((DWORD *)m_Buffer)[0] = SWAP_DWORD(sizes[0]); - ((DWORD *)m_Buffer)[1] = SWAP_DWORD(sizes[1]); - arc.Read (m_Buffer+8, len); - m_ImplodedBuffer = m_Buffer; - m_Buffer = NULL; - m_Mode = EWriting; - } -} - -bool FCompressedMemFile::IsOpen () const -{ - return !!m_Buffer; -} - -void FCompressedMemFile::GetSizes(unsigned int &compressed, unsigned int &uncompressed) const -{ - if (m_ImplodedBuffer != NULL) - { - compressed = BigLong(*(unsigned int *)m_ImplodedBuffer); - uncompressed = BigLong(*(unsigned int *)(m_ImplodedBuffer + 4)); - } - else - { - compressed = 0; - uncompressed = m_BufferSize; - } -} FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id) : FCompressedFile (file, EWriting, true, false), m_ChunkID (id) diff --git a/src/farchive.h b/src/farchive.h index f216fc78a..484c1018b 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -112,31 +112,6 @@ private: void BeEmpty (); }; -class FCompressedMemFile : public FCompressedFile -{ -public: - FCompressedMemFile (); - FCompressedMemFile (FILE *file); // Create for reading - ~FCompressedMemFile (); - - bool Open (const char *name, EOpenMode mode); // Works for reading only - bool Open (void *memblock); // Open for reading only - bool Open (); // Open for writing only - bool Reopen (); // Re-opens imploded file for reading only - void Close (); - bool IsOpen () const; - void GetSizes(unsigned int &one, unsigned int &two) const; - - void Serialize(FArchive &arc); - -protected: - bool FreeOnExplode () { return !m_SourceFromMem; } - -private: - bool m_SourceFromMem; - unsigned char *m_ImplodedBuffer; -}; - class FPNGChunkFile : public FCompressedFile { public: diff --git a/src/g_game.cpp b/src/g_game.cpp index 3c9d274c8..73293a15e 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1970,11 +1970,7 @@ void G_DoLoadGame () NextSkill = next; } - if (level.info->snapshot != NULL) - { - delete level.info->snapshot; - level.info->snapshot = NULL; - } + level.info->Snapshot.Clean(); BackupSaveName = savename; @@ -2263,11 +2259,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio BackupSaveName = filename; // We don't need the snapshot any longer. - if (level.info->snapshot != NULL) - { - delete level.info->snapshot; - level.info->snapshot = NULL; - } + level.info->Snapshot.Clean(); insave = false; I_FreezeTime(false); diff --git a/src/g_level.cpp b/src/g_level.cpp index 460474383..5908a2b9f 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -85,6 +85,7 @@ #include "r_renderer.h" #include "r_utility.h" #include "p_spec.h" +#include "serializer.h" #include "gi.h" @@ -822,7 +823,7 @@ void G_DoCompleted (void) } else { // Make sure we don't have a snapshot lying around from before. - level.info->ClearSnapshot(); + level.info->Snapshot.Clean(); } } else @@ -1482,19 +1483,20 @@ void G_AirControlChanged () void G_SnapshotLevel () { - if (level.info->snapshot) - delete level.info->snapshot; + level.info->Snapshot.Clean(); if (level.info->isValid()) { level.info->snapshotVer = SAVEVER; - level.info->snapshot = new FCompressedMemFile; - level.info->snapshot->Open (); - FArchive arc (*level.info->snapshot); + FSerializer arc; - SaveVersion = SAVEVER; - G_SerializeLevel (arc, false); + if (arc.OpenWriter()) + { + SaveVersion = SAVEVER; + G_SerializeLevel(arc, false); + level.info->Snapshot = arc.GetCompressedOutput(); + } } } @@ -1507,18 +1509,18 @@ void G_SnapshotLevel () void G_UnSnapshotLevel (bool hubLoad) { - if (level.info->snapshot == NULL) + if (level.info->Snapshot.mBuffer == nullptr) return; if (level.info->isValid()) { SaveVersion = level.info->snapshotVer; - level.info->snapshot->Reopen (); - FArchive arc (*level.info->snapshot); - if (hubLoad) - arc.SetHubTravel (); + FSerializer arc; + if (!arc.OpenReader(&level.info->Snapshot)) return; + + //if (hubLoad) arc.SetHubTravel (); // no idea if this is still needed. + G_SerializeLevel (arc, hubLoad); - arc.Close (); level.FromSnapshot = true; TThinkerIterator it; @@ -1548,7 +1550,7 @@ void G_UnSnapshotLevel (bool hubLoad) } } // No reason to keep the snapshot around once the level's been entered. - level.info->ClearSnapshot(); + level.info->Snapshot.Clean(); if (hubLoad) { // Unlock ACS global strings that were locked when the snapshot was made. @@ -1564,7 +1566,7 @@ void G_UnSnapshotLevel (bool hubLoad) static void writeSnapShot (FArchive &arc, level_info_t *i) { arc << i->snapshotVer << i->MapName; - i->snapshot->Serialize (arc); + //i->snapshot->Serialize (arc); } //========================================================================== @@ -1576,6 +1578,7 @@ void G_WriteSnapshots (FILE *file) { unsigned int i; +#if 0 for (i = 0; i < wadlevelinfos.Size(); i++) { if (wadlevelinfos[i].snapshot) @@ -1589,6 +1592,7 @@ void G_WriteSnapshots (FILE *file) FPNGChunkArchive arc (file, DSNP_ID); writeSnapShot(arc, &TheDefaultLevelInfo); } +#endif FPNGChunkArchive *arc = NULL; @@ -1662,8 +1666,10 @@ void G_ReadSnapshots (PNGHandle *png) arc << MapName; i = FindLevelInfo (MapName); i->snapshotVer = snapver; +#if 0 i->snapshot = new FCompressedMemFile; i->snapshot->Serialize (arc); +#endif chunkLen = (DWORD)M_NextPNGChunk (png, SNAP_ID); } @@ -1676,8 +1682,10 @@ void G_ReadSnapshots (PNGHandle *png) arc << snapver; arc << MapName; TheDefaultLevelInfo.snapshotVer = snapver; +#if 0 TheDefaultLevelInfo.snapshot = new FCompressedMemFile; TheDefaultLevelInfo.snapshot->Serialize (arc); +#endif } chunkLen = (DWORD)M_FindPNGChunk (png, VIST_ID); @@ -1727,12 +1735,10 @@ CCMD(listsnapshots) { for (unsigned i = 0; i < wadlevelinfos.Size(); ++i) { - FCompressedMemFile *snapshot = wadlevelinfos[i].snapshot; - if (snapshot != NULL) + FCompressedBuffer *snapshot = &wadlevelinfos[i].Snapshot; + if (snapshot->mBuffer != nullptr) { - unsigned int comp, uncomp; - snapshot->GetSizes(comp, uncomp); - Printf("%s (%u -> %u bytes)\n", wadlevelinfos[i].MapName.GetChars(), comp, uncomp); + Printf("%s (%u -> %u bytes)\n", wadlevelinfos[i].MapName.GetChars(), snapshot->mCompressedSize, snapshot->mSize); } } } diff --git a/src/g_level.h b/src/g_level.h index 5b994a3d2..70872bc46 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -39,6 +39,7 @@ #include "sc_man.h" #include "s_sound.h" #include "textures/textures.h" +#include "resourcefiles/file_zip.h" struct level_info_t; struct cluster_info_t; @@ -232,7 +233,6 @@ struct FSpecialAction int Args[5]; // must allow 16 bit tags for 666 & 667! }; -class FCompressedMemFile; class DScroller; class FScanner; @@ -295,7 +295,7 @@ struct level_info_t FString LevelName; SBYTE WallVertLight, WallHorizLight; int musicorder; - FCompressedMemFile *snapshot; + FCompressedBuffer Snapshot; DWORD snapshotVer; struct acsdefered_t *defered; float skyspeed1; @@ -346,13 +346,12 @@ struct level_info_t } ~level_info_t() { - ClearSnapshot(); + Snapshot.Clean(); ClearDefered(); } void Reset(); bool isValid(); FString LookupLevelName (); - void ClearSnapshot(); void ClearDefered(); level_info_t *CheckLevelRedirect (); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index ac1552b99..ff4ef0e87 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -196,7 +196,7 @@ void G_ClearSnapshots (void) { for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) { - wadlevelinfos[i].ClearSnapshot(); + wadlevelinfos[i].Snapshot.Clean(); } // Since strings are only locked when snapshotting a level, unlock them // all now, since we got rid of all the snapshots that cared about them. @@ -248,7 +248,7 @@ void level_info_t::Reset() WallVertLight = +8; F1Pic = ""; musicorder = 0; - snapshot = NULL; + Snapshot = { 0,0,0,0,nullptr }; snapshotVer = 0; defered = 0; skyspeed1 = skyspeed2 = 0.f; @@ -334,17 +334,6 @@ FString level_info_t::LookupLevelName() } -//========================================================================== -// -// -//========================================================================== - -void level_info_t::ClearSnapshot() -{ - if (snapshot != NULL) delete snapshot; - snapshot = NULL; -} - //========================================================================== // // diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 59d5f8f04..8f6038bf5 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -988,12 +988,3 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) } -//========================================================================== -// -// -//========================================================================== - -void G_SerializeLevel(FArchive &arc, bool hubLoad) -{ -} - diff --git a/src/p_saveg.h b/src/p_saveg.h index 63beb72b8..43293c57a 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -45,6 +45,6 @@ void P_DestroyThinkers(bool hubLoad); void P_ReadACSDefereds (PNGHandle *png); void P_WriteACSDefereds (FILE *file); -void G_SerializeLevel(FArchive &arc, bool hubLoad); +void G_SerializeLevel(FSerializer &arc, bool hubLoad); #endif // __P_SAVEG_H__ diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index fcf5523ad..244577631 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -314,6 +314,7 @@ FCompressedBuffer FZipFile::GetRawLump(int lumpnum) FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, new char[lmp->CompressedSize] }; Reader->Seek(lmp->Position, SEEK_SET); Reader->Read(cbuf.mBuffer, lmp->CompressedSize); + return cbuf; } //========================================================================== diff --git a/src/resourcefiles/file_zip.h b/src/resourcefiles/file_zip.h index 54fbde92b..dc10d558d 100644 --- a/src/resourcefiles/file_zip.h +++ b/src/resourcefiles/file_zip.h @@ -13,6 +13,15 @@ struct FCompressedBuffer char *mBuffer; bool Decompress(char *destbuffer); + void Clean() + { + mSize = mCompressedSize = 0; + if (mBuffer != nullptr) + { + delete[] mBuffer; + mBuffer = nullptr; + } + } }; enum diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 7716a8d27..12b280a74 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -446,7 +446,7 @@ void S_Start () // Don't start the music if loading a savegame, because the music is stored there. // Don't start the music if revisiting a level in a hub for the same reason. - if (!savegamerestore && (level.info == NULL || level.info->snapshot == NULL || !level.info->isValid())) + if (!savegamerestore && (level.info == nullptr || level.info->Snapshot.mBuffer == nullptr || !level.info->isValid())) { if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) S_ChangeMusic (level.Music, level.musicorder); From 67239cd6236e58a591123523d9a1b361b50c5369 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 09:01:12 +0200 Subject: [PATCH 26/59] - added a FileWriter class (taken from another project of mine) and changed m_png.cpp to use it. This is so that PNGs can be written to memory, not just to an external file. stdio's FILE cannot be easily redirected but a C++ class can. The writer is very simple and primitive right now, allowing no seeking, but for the job at hand it is sufficient. Note that large parts of savegame creation have been disabled, because they are about to be rewritten and it makes no sense to adjust them all before. --- src/files.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++ src/files.h | 42 +++++++++++++++++++++++++++++++++ src/g_game.cpp | 13 +++++++++-- src/m_misc.cpp | 17 +++++++++----- src/m_png.cpp | 40 ++++++++++++++++---------------- src/m_png.h | 13 ++++++----- src/r_renderer.h | 3 ++- src/r_swrenderer.cpp | 2 +- src/r_swrenderer.h | 32 +++++++++++++------------- 9 files changed, 165 insertions(+), 52 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index 0b7203236..6bf38274b 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -600,3 +600,58 @@ char *MemoryArrayReader::Gets(char *strbuf, int len) { return GetsFromBuffer((char*)&buf[0], strbuf, len); } + +//========================================================================== +// +// FileWriter (the motivation here is to have a buffer writing subclass) +// +//========================================================================== + +bool FileWriter::OpenDirect(const char *filename) +{ + File = fopen(filename, "wb"); + return (File != NULL); +} + +FileWriter *FileWriter::Open(const char *filename) +{ + FileWriter *fwrit = new FileWriter(); + if (fwrit->OpenDirect(filename)) + { + return fwrit; + } + delete fwrit; + return NULL; +} + +size_t FileWriter::Write(const void *buffer, size_t len) +{ + if (File != NULL) + { + return fwrite(buffer, 1, len, File); + } + else + { + return 0; + } +} + + +size_t FileWriter::Printf(const char *fmt, ...) +{ + va_list ap; + FString out; + + va_start(ap, fmt); + out.VFormat(fmt, ap); + va_end(ap); + return Write(out.GetChars(), out.Len()); +} + +size_t BufferWriter::Write(const void *buffer, size_t len) +{ + unsigned int ofs = mBuffer.Reserve((unsigned)len); + memcpy(&mBuffer[ofs], buffer, len); + return len; +} + diff --git a/src/files.h b/src/files.h index 5889eb6fe..4c12550c8 100644 --- a/src/files.h +++ b/src/files.h @@ -356,4 +356,46 @@ protected: }; +class FileWriter +{ +protected: + bool OpenDirect(const char *filename); + + FileWriter() + { + File = NULL; + } +public: + virtual ~FileWriter() + { + if (File != NULL) fclose(File); + } + + static FileWriter *Open(const char *filename); + + virtual size_t Write(const void *buffer, size_t len); + size_t Printf(const char *fmt, ...); + +protected: + + FILE *File; + +protected: + bool CloseOnDestruct; +}; + +class BufferWriter : public FileWriter +{ +protected: + TArray mBuffer; +public: + + BufferWriter() {} + virtual size_t Write(const void *buffer, size_t len) override; + TArray *GetBuffer() { return &mBuffer; } +}; + + + + #endif diff --git a/src/g_game.cpp b/src/g_game.cpp index 73293a15e..a221d3a5a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1828,6 +1828,7 @@ bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn) void G_DoLoadGame () { +#if 0 // SAVEGAME char sigcheck[20]; char *text = NULL; char *map; @@ -1981,6 +1982,7 @@ void G_DoLoadGame () // amount of memory in use, so bring it down now by starting a // collection. GC::StartCollection(); +#endif } @@ -2102,6 +2104,7 @@ void G_DoAutoSave () static void PutSaveWads (FILE *file) { +#if 0 // SAVEGAME const char *name; // Name of IWAD @@ -2114,10 +2117,12 @@ static void PutSaveWads (FILE *file) name = Wads.GetWadName (Wads.GetLumpFile (level.lumpnum)); M_AppendPNGText (file, "Map WAD", name); } +#endif } static void PutSaveComment (FILE *file) { +#if 0 // SAVEGAME char comment[256]; const char *readableTime; WORD len; @@ -2147,9 +2152,10 @@ static void PutSaveComment (FILE *file) // Write out the comment M_AppendPNGText (file, "Comment", comment); +#endif } -static void PutSavePic (FILE *file, int width, int height) +static void PutSavePic (FileWriter *file, int width, int height) { if (width <= 0 || height <= 0 || !storesavepic) { @@ -2194,6 +2200,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } SaveVersion = SAVEVER; +#if 0 // SAVEGAME PutSavePic (stdfile, SAVEPICWIDTH, SAVEPICHEIGHT); mysnprintf(buf, countof(buf), GAMENAME " %s", GetVersionString()); M_AppendPNGText (stdfile, "Software", buf); @@ -2249,7 +2256,9 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } fclose(stdfile); } - if (success) +#endif + bool success = true; + if (success) { if (longsavemessages) Printf ("%s (%s)\n", GStrings("GGSAVED"), filename.GetChars()); else Printf ("%s\n", GStrings("GGSAVED")); diff --git a/src/m_misc.cpp b/src/m_misc.cpp index 87f61f253..a37c32094 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -449,10 +449,15 @@ struct pcx_t }; +inline void putc(char chr, FileWriter *file) +{ + file->Write(&chr, 1); +} + // // WritePCXfile // -void WritePCXfile (FILE *file, const BYTE *buffer, const PalEntry *palette, +void WritePCXfile (FileWriter *file, const BYTE *buffer, const PalEntry *palette, ESSType color_type, int width, int height, int pitch) { BYTE temprow[MAXWIDTH * 3]; @@ -480,7 +485,7 @@ void WritePCXfile (FILE *file, const BYTE *buffer, const PalEntry *palette, pcx.palette_type = 1; // not a grey scale memset (pcx.filler, 0, sizeof(pcx.filler)); - fwrite (&pcx, 128, 1, file); + file->Write(&pcx, 128); bytes_per_row_minus_one = ((color_type == SS_PAL) ? width : width * 3) - 1; @@ -593,7 +598,7 @@ void WritePCXfile (FILE *file, const BYTE *buffer, const PalEntry *palette, // // WritePNGfile // -void WritePNGfile (FILE *file, const BYTE *buffer, const PalEntry *palette, +void WritePNGfile (FileWriter *file, const BYTE *buffer, const PalEntry *palette, ESSType color_type, int width, int height, int pitch) { char software[100]; @@ -655,7 +660,7 @@ static bool FindFreeName (FString &fullname, const char *extension) void M_ScreenShot (const char *filename) { - FILE *file; + FileWriter *file; FString autoname; bool writepcx = (stricmp (screenshot_type, "pcx") == 0); // PNG is the default @@ -709,7 +714,7 @@ void M_ScreenShot (const char *filename) { screen->GetFlashedPalette(palette); } - file = fopen (autoname, "wb"); + file = FileWriter::Open(autoname); if (file == NULL) { Printf ("Could not open %s\n", autoname.GetChars()); @@ -726,7 +731,7 @@ void M_ScreenShot (const char *filename) WritePNGfile(file, buffer, palette, color_type, screen->GetWidth(), screen->GetHeight(), pitch); } - fclose(file); + delete file; screen->ReleaseScreenshotBuffer(); if (!screenshot_quiet) diff --git a/src/m_png.cpp b/src/m_png.cpp index 22a9a657d..35cd8587e 100644 --- a/src/m_png.cpp +++ b/src/m_png.cpp @@ -101,7 +101,7 @@ PNGHandle::~PNGHandle () static inline void MakeChunk (void *where, DWORD type, size_t len); static inline void StuffPalette (const PalEntry *from, BYTE *to); -static bool WriteIDAT (FILE *file, const BYTE *data, int len); +static bool WriteIDAT (FileWriter *file, const BYTE *data, int len); static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev, int bpp); static void UnpackPixels (int width, int bytesPerRow, int bitdepth, const BYTE *rowin, BYTE *rowout, bool grayscale); @@ -131,7 +131,7 @@ CVAR(Float, png_gamma, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *palette, +bool M_CreatePNG (FileWriter *file, const BYTE *buffer, const PalEntry *palette, ESSType color_type, int width, int height, int pitch) { BYTE work[8 + // signature @@ -171,7 +171,7 @@ bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *palette, work_len = sizeof(work) - (12+256*3); } - if (fwrite (work, 1, work_len, file) != work_len) + if (file->Write (work, work_len) != work_len) return false; return M_SaveBitmap (buffer, color_type, width, height, pitch, file); @@ -185,7 +185,7 @@ bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *palette, // //========================================================================== -bool M_CreateDummyPNG (FILE *file) +bool M_CreateDummyPNG (FileWriter *file) { static const BYTE dummyPNG[] = { @@ -195,7 +195,7 @@ bool M_CreateDummyPNG (FILE *file) 0,0,0,10,'I','D','A','T', 104,222,99,96,0,0,0,2,0,1,0x9f,0x65,0x0e,0x18 }; - return fwrite (dummyPNG, 1, sizeof(dummyPNG), file) == sizeof(dummyPNG); + return file->Write (dummyPNG, sizeof(dummyPNG)) == sizeof(dummyPNG); } @@ -207,10 +207,10 @@ bool M_CreateDummyPNG (FILE *file) // //========================================================================== -bool M_FinishPNG (FILE *file) +bool M_FinishPNG (FileWriter *file) { static const BYTE iend[12] = { 0,0,0,0,73,69,78,68,174,66,96,130 }; - return fwrite (iend, 1, 12, file) == 12; + return file->Write (iend, 12) == 12; } //========================================================================== @@ -221,13 +221,13 @@ bool M_FinishPNG (FILE *file) // //========================================================================== -bool M_AppendPNGChunk (FILE *file, DWORD chunkID, const BYTE *chunkData, DWORD len) +bool M_AppendPNGChunk (FileWriter *file, DWORD chunkID, const BYTE *chunkData, DWORD len) { DWORD head[2] = { BigLong((unsigned int)len), chunkID }; DWORD crc; - if (fwrite (head, 1, 8, file) == 8 && - (len == 0 || fwrite (chunkData, 1, len, file) == len)) + if (file->Write (head, 8) == 8 && + (len == 0 || file->Write (chunkData, len) == len)) { crc = CalcCRC32 ((BYTE *)&head[1], 4); if (len != 0) @@ -235,7 +235,7 @@ bool M_AppendPNGChunk (FILE *file, DWORD chunkID, const BYTE *chunkData, DWORD l crc = AddCRC32 (crc, chunkData, len); } crc = BigLong((unsigned int)crc); - return fwrite (&crc, 1, 4, file) == 4; + return file->Write (&crc, 4) == 4; } return false; } @@ -248,7 +248,7 @@ bool M_AppendPNGChunk (FILE *file, DWORD chunkID, const BYTE *chunkData, DWORD l // //========================================================================== -bool M_AppendPNGText (FILE *file, const char *keyword, const char *text) +bool M_AppendPNGText (FileWriter *file, const char *keyword, const char *text) { struct { DWORD len, id; char key[80]; } head; int len = (int)strlen (text); @@ -261,8 +261,8 @@ bool M_AppendPNGText (FILE *file, const char *keyword, const char *text) strncpy (head.key, keyword, keylen); head.key[keylen] = 0; - if ((int)fwrite (&head, 1, keylen + 9, file) == keylen + 9 && - (int)fwrite (text, 1, len, file) == len) + if ((int)file->Write (&head, keylen + 9) == keylen + 9 && + (int)file->Write (text, len) == len) { crc = CalcCRC32 ((BYTE *)&head+4, keylen + 5); if (len != 0) @@ -270,7 +270,7 @@ bool M_AppendPNGText (FILE *file, const char *keyword, const char *text) crc = AddCRC32 (crc, (BYTE *)text, len); } crc = BigLong((unsigned int)crc); - return fwrite (&crc, 1, 4, file) == 4; + return file->Write (&crc, 4) == 4; } return false; } @@ -900,7 +900,7 @@ static int SelectFilter(Byte row[5][1 + MAXWIDTH*3], Byte prior[MAXWIDTH*3], int // //========================================================================== -bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FILE *file) +bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FileWriter *file) { #if USE_FILTER_HEURISTIC Byte prior[MAXWIDTH*3]; @@ -1040,7 +1040,7 @@ bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, i // //========================================================================== -static bool WriteIDAT (FILE *file, const BYTE *data, int len) +static bool WriteIDAT (FileWriter *file, const BYTE *data, int len) { DWORD foo[2], crc; @@ -1049,9 +1049,9 @@ static bool WriteIDAT (FILE *file, const BYTE *data, int len) crc = CalcCRC32 ((BYTE *)&foo[1], 4); crc = BigLong ((unsigned int)AddCRC32 (crc, data, len)); - if (fwrite (foo, 1, 8, file) != 8 || - fwrite (data, 1, len, file) != (size_t)len || - fwrite (&crc, 1, 4, file) != 4) + if (file->Write (foo, 8) != 8 || + file->Write (data, len) != (size_t)len || + file->Write (&crc, 4) != 4) { return false; } diff --git a/src/m_png.h b/src/m_png.h index 6434031d2..ef25d70d4 100644 --- a/src/m_png.h +++ b/src/m_png.h @@ -36,6 +36,7 @@ #include #include "doomtype.h" #include "v_video.h" +#include "files.h" // PNG Writing -------------------------------------------------------------- @@ -43,22 +44,22 @@ // The passed file should be a newly created file. // This function writes the PNG signature and the IHDR, gAMA, PLTE, and IDAT // chunks. -bool M_CreatePNG (FILE *file, const BYTE *buffer, const PalEntry *pal, +bool M_CreatePNG (FileWriter *file, const BYTE *buffer, const PalEntry *pal, ESSType color_type, int width, int height, int pitch); // Creates a grayscale 1x1 PNG file. Used for savegames without savepics. -bool M_CreateDummyPNG (FILE *file); +bool M_CreateDummyPNG (FileWriter *file); // Appends any chunk to a PNG file started with M_CreatePNG. -bool M_AppendPNGChunk (FILE *file, DWORD chunkID, const BYTE *chunkData, DWORD len); +bool M_AppendPNGChunk (FileWriter *file, DWORD chunkID, const BYTE *chunkData, DWORD len); // Adds a tEXt chunk to a PNG file started with M_CreatePNG. -bool M_AppendPNGText (FILE *file, const char *keyword, const char *text); +bool M_AppendPNGText (FileWriter *file, const char *keyword, const char *text); // Appends the IEND chunk to a PNG file. -bool M_FinishPNG (FILE *file); +bool M_FinishPNG (FileWriter *file); -bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FILE *file); +bool M_SaveBitmap(const BYTE *from, ESSType color_type, int width, int height, int pitch, FileWriter *file); // PNG Reading -------------------------------------------------------------- diff --git a/src/r_renderer.h b/src/r_renderer.h index c5385aadc..4236993d6 100644 --- a/src/r_renderer.h +++ b/src/r_renderer.h @@ -11,6 +11,7 @@ class AActor; class player_t; struct sector_t; class FCanvasTexture; +class FileWriter; struct FRenderer { @@ -37,7 +38,7 @@ struct FRenderer virtual void RemapVoxels() {} // renders view to a savegame picture - virtual void WriteSavePic (player_t *player, FILE *file, int width, int height) = 0; + virtual void WriteSavePic (player_t *player, FileWriter *file, int width, int height) = 0; // draws player sprites with hardware acceleration (only useful for software rendering) virtual void DrawRemainingPlayerSprites() {} diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp index ccf12e7ed..3c3313430 100644 --- a/src/r_swrenderer.cpp +++ b/src/r_swrenderer.cpp @@ -179,7 +179,7 @@ void FSoftwareRenderer::RemapVoxels() // //=========================================================================== -void FSoftwareRenderer::WriteSavePic (player_t *player, FILE *file, int width, int height) +void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { DCanvas *pic = new DSimpleCanvas (width, height); PalEntry palette[256]; diff --git a/src/r_swrenderer.h b/src/r_swrenderer.h index 2856d9586..f9d5609a0 100644 --- a/src/r_swrenderer.h +++ b/src/r_swrenderer.h @@ -6,35 +6,35 @@ struct FSoftwareRenderer : public FRenderer { // Can be overridden so that the colormaps for sector color/fade won't be built. - virtual bool UsesColormap() const; + virtual bool UsesColormap() const override; // precache one texture void PrecacheTexture(FTexture *tex, int cache); - virtual void Precache(BYTE *texhitlist, TMap &actorhitlist); + virtual void Precache(BYTE *texhitlist, TMap &actorhitlist) override; // render 3D view - virtual void RenderView(player_t *player); + virtual void RenderView(player_t *player) override; // Remap voxel palette - virtual void RemapVoxels(); + virtual void RemapVoxels() override; // renders view to a savegame picture - virtual void WriteSavePic (player_t *player, FILE *file, int width, int height); + virtual void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; // draws player sprites with hardware acceleration (only useful for software rendering) - virtual void DrawRemainingPlayerSprites(); + virtual void DrawRemainingPlayerSprites() override; - virtual int GetMaxViewPitch(bool down); + virtual int GetMaxViewPitch(bool down) override; - void OnModeSet (); - void ErrorCleanup (); - void ClearBuffer(int color); - void Init(); - void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); - void SetupFrame(player_t *player); - void CopyStackedViewParameters(); - void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov); - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back); + void OnModeSet () override; + void ErrorCleanup () override; + void ClearBuffer(int color) override; + void Init() override; + void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) override; + void SetupFrame(player_t *player) override; + void CopyStackedViewParameters() override; + void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) override; + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; }; From dbea80e943b6071da6a268799ebca4400a2c522b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 09:06:04 +0200 Subject: [PATCH 27/59] - put savegame extension in version.h --- src/version.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/version.h b/src/version.h index 57dacaa00..f20bdfa5b 100644 --- a/src/version.h +++ b/src/version.h @@ -71,6 +71,9 @@ const char *GetVersionString(); // Note that SAVEVER is not directly comparable to VERSION. // SAVESIG should match SAVEVER. +// extension for savegames +#define SAVEGAME_EXT "zds" + // MINSAVEVER is the minimum level snapshot version that can be loaded. #define MINSAVEVER 4545 From 810ef8f7750d7328ce295dcff8bd507371bbd87a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 12:19:13 +0200 Subject: [PATCH 28/59] - save global savegame data to JSON as well. This is incomplete and untested, just a safety commit before going on. --- src/c_cmds.cpp | 4 +- src/d_main.cpp | 2 +- src/dthinker.cpp | 85 ------------------- src/dthinker.h | 1 - src/g_game.cpp | 105 ++++++++++------------- src/g_hub.cpp | 88 ++++++++++---------- src/g_hub.h | 6 +- src/g_level.cpp | 49 +++++------ src/g_level.h | 10 ++- src/g_mapinfo.cpp | 19 +---- src/json.cpp | 2 - src/m_misc.cpp | 2 +- src/m_random.cpp | 27 +++--- src/m_random.h | 3 +- src/menu/loadsavemenu.cpp | 2 +- src/p_acs.cpp | 171 ++++++++++++++++---------------------- src/p_acs.h | 11 ++- src/p_saveg.cpp | 11 --- src/p_saveg.h | 4 +- src/r_defs.h | 1 - src/s_sound.h | 2 - src/serializer.cpp | 21 ++++- src/serializer.h | 1 + src/statistics.cpp | 92 +++++++++----------- src/tarray.h | 5 -- src/textures/textures.h | 3 - src/zzz_old.cpp | 80 +++++++++++++----- 27 files changed, 346 insertions(+), 461 deletions(-) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 03a7495e4..f9cea4005 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -811,7 +811,7 @@ CCMD (load) return; } FString fname = argv[1]; - DefaultExtension (fname, ".zds"); + DefaultExtension (fname, "." SAVEGAME_EXT); G_LoadGame (fname); } @@ -831,7 +831,7 @@ CCMD (save) return; } FString fname = argv[1]; - DefaultExtension (fname, ".zds"); + DefaultExtension (fname, "." SAVEGAME_EXT); G_SaveGame (fname, argv.argc() > 2 ? argv[2] : argv[1]); } diff --git a/src/d_main.cpp b/src/d_main.cpp index 9f4568614..689603084 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2580,7 +2580,7 @@ void D_DoomMain (void) { FString file(v); FixPathSeperator (file); - DefaultExtension (file, ".zds"); + DefaultExtension (file, ".zds" SAVEGAME_EXT); G_LoadGame (file); } diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 06295a4b3..0e57f05cf 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -117,91 +117,6 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node) } } -void DThinker::SerializeAll(FArchive &arc, bool hubLoad) -{ -#if 0 - DThinker *thinker; - BYTE stat; - int statcount; - int i; - - // Save lists of thinkers, but not by storing the first one and letting - // the archiver catch the rest. (Which leads to buttloads of recursion - // and makes the file larger.) Instead, we explicitly save each thinker - // in sequence. When restoring an archive, we also have to maintain - // the thinker lists here instead of relying on the archiver to do it - // for us. - - if (arc.IsStoring()) - { - for (statcount = i = 0; i <= MAX_STATNUM; i++) - { - statcount += (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()); - } - arc << statcount; - for (i = 0; i <= MAX_STATNUM; i++) - { - if (!Thinkers[i].IsEmpty() || !FreshThinkers[i].IsEmpty()) - { - stat = i; - arc << stat; - SaveList(arc, Thinkers[i].GetHead()); - SaveList(arc, FreshThinkers[i].GetHead()); - thinker = NULL; - arc << thinker; // Save a final NULL for this list - } - } - } - else - { - // Prevent the constructor from inserting thinkers into a list. - bSerialOverride = true; - - try - { - arc << statcount; - while (statcount > 0) - { - arc << stat << thinker; - while (thinker != NULL) - { - // This may be a player stored in their ancillary list. Remove - // them first before inserting them into the new list. - if (thinker->NextThinker != NULL) - { - thinker->Remove(); - } - // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers - // list. Anything else goes in the regular Thinkers list. - if (thinker->ObjectFlags & OF_EuthanizeMe) - { - // This thinker was destroyed during the loading process. Do - // not link it in to any list. - } - else if (thinker->ObjectFlags & OF_JustSpawned) - { - FreshThinkers[stat].AddTail(thinker); - } - else - { - Thinkers[stat].AddTail(thinker); - } - arc << thinker; - } - statcount--; - } - } - catch (class CDoomError &) - { - bSerialOverride = false; - DestroyAllThinkers(); - throw; - } - bSerialOverride = false; - } -#endif -} - DThinker::DThinker (int statnum) throw() { NextThinker = NULL; diff --git a/src/dthinker.h b/src/dthinker.h index 4785c0035..c6a27e3a6 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -84,7 +84,6 @@ public: DestroyThinkersInList(Thinkers[statnum]); DestroyThinkersInList(FreshThinkers[statnum]); } - static void SerializeAll (FArchive &arc, bool keepPlayers); static void SerializeThinkers(FSerializer &arc, bool keepPlayers); static void MarkRoots(); diff --git a/src/g_game.cpp b/src/g_game.cpp index a221d3a5a..46bd3f7e4 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -84,6 +84,7 @@ #include "a_morph.h" #include "p_spec.h" #include "r_data/colormaps.h" +#include "serializer.h" #include @@ -110,8 +111,7 @@ void G_DoWorldDone (void); void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description); void G_DoAutoSave (); -void STAT_Write(FILE *file); -void STAT_Read(PNGHandle *png); +void STAT_Serialize(FSerializer &file); FIntCVar gameskill ("skill", 2, CVAR_SERVERINFO|CVAR_LATCH); CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH); @@ -2045,7 +2045,7 @@ FString G_BuildSaveName (const char *prefix, int slot) name << prefix; if (slot >= 0) { - name.AppendFormat("%d.zds", slot); + name.AppendFormat("%d.zds" SAVEGAME_EXT, slot); } return name; } @@ -2102,27 +2102,24 @@ void G_DoAutoSave () } -static void PutSaveWads (FILE *file) +static void PutSaveWads (FSerializer &arc) { -#if 0 // SAVEGAME const char *name; // Name of IWAD name = Wads.GetWadName (FWadCollection::IWAD_FILENUM); - M_AppendPNGText (file, "Game WAD", name); + arc.AddString("Game WAD", name); // Name of wad the map resides in if (Wads.GetLumpFile (level.lumpnum) > 1) { name = Wads.GetWadName (Wads.GetLumpFile (level.lumpnum)); - M_AppendPNGText (file, "Map WAD", name); + arc.AddString("Map WAD", name); } -#endif } -static void PutSaveComment (FILE *file) +static void PutSaveComment (FSerializer &arc) { -#if 0 // SAVEGAME char comment[256]; const char *readableTime; WORD len; @@ -2136,7 +2133,7 @@ static void PutSaveComment (FILE *file) strncpy (comment+15, readableTime+10, 9); comment[24] = 0; - M_AppendPNGText (file, "Creation Time", comment); + arc.AddString("Creation Time", comment); // Get level name //strcpy (comment, level.level_name); @@ -2151,8 +2148,7 @@ static void PutSaveComment (FILE *file) comment[len+16] = 0; // Write out the comment - M_AppendPNGText (file, "Comment", comment); -#endif + arc.AddString("Comment", comment); } static void PutSavePic (FileWriter *file, int width, int height) @@ -2169,6 +2165,8 @@ static void PutSavePic (FileWriter *file, int width, int height) void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description) { + TArray savegame_content; + char buf[100]; // Do not even try, if we're not in a level. (Can happen after @@ -2180,7 +2178,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio if (demoplayback) { - filename = G_BuildSaveName ("demosave.zds", -1); + filename = G_BuildSaveName ("demosave." SAVEGAME_EXT, -1); } if (cl_waitforsave) @@ -2189,74 +2187,63 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio insave = true; G_SnapshotLevel (); - FILE *stdfile = fopen (filename, "wb"); + 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. - if (stdfile == NULL) - { - Printf ("Could not create savegame '%s'\n", filename.GetChars()); - insave = false; - I_FreezeTime(false); - return; - } + savegameinfo.OpenWriter(); + savegameglobals.OpenWriter(); SaveVersion = SAVEVER; -#if 0 // SAVEGAME - PutSavePic (stdfile, SAVEPICWIDTH, SAVEPICHEIGHT); + PutSavePic(&savepic, SAVEPICWIDTH, SAVEPICHEIGHT); mysnprintf(buf, countof(buf), GAMENAME " %s", GetVersionString()); - M_AppendPNGText (stdfile, "Software", buf); - M_AppendPNGText (stdfile, "Engine", GAMESIG); - M_AppendPNGText (stdfile, "ZDoom Save Version", SAVESIG); - M_AppendPNGText (stdfile, "Title", description); - M_AppendPNGText (stdfile, "Current Map", level.MapName); - PutSaveWads (stdfile); - PutSaveComment (stdfile); + // put some basic info into the PNG so that this isn't lost when the image gets extracted. + M_AppendPNGText(&savepic, "Software", buf); + M_AppendPNGText(&savepic, "Title", description); + M_AppendPNGText(&savepic, "Current Map", level.MapName); + M_FinishPNG(&savepic); + + savegameinfo.AddString("Software", buf) + .AddString("Engine", GAMESIG) + .AddString("Save Version", SAVESIG) + .AddString("Title", description) + .AddString("Current Map", level.MapName); + + + PutSaveWads (savegameinfo); + PutSaveComment (savegameinfo); // Intermission stats for hubs - G_WriteHubInfo(stdfile); + G_SerializeHub(savegameglobals); { FString vars = C_GetMassCVarString(CVAR_SERVERINFO); - M_AppendPNGText (stdfile, "Important CVARs", vars.GetChars()); + savegameglobals.AddString("importantcvars", vars.GetChars()); } if (level.time != 0 || level.maptime != 0) { - DWORD time[2] = { DWORD(BigLong(TICRATE)), DWORD(BigLong(level.time)) }; - M_AppendPNGChunk (stdfile, MAKE_ID('p','t','I','c'), (BYTE *)&time, 8); + int tic = TICRATE; + savegameglobals("ticrate", tic); + savegameglobals("leveltime", level.time); } - G_WriteSnapshots (stdfile); - STAT_Write(stdfile); - FRandom::StaticWriteRNGState (stdfile); - P_WriteACSDefereds (stdfile); - - P_WriteACSVars(stdfile); + STAT_Serialize(savegameglobals); + FRandom::StaticWriteRNGState(savegameglobals); + P_WriteACSDefereds(savegameglobals); + P_WriteACSVars(savegameglobals); if (NextSkill != -1) { - BYTE next = NextSkill; - M_AppendPNGChunk (stdfile, MAKE_ID('s','n','X','t'), &next, 1); + savegameglobals("nextskill", NextSkill); } - M_FinishPNG (stdfile); - fclose (stdfile); + //G_WriteSnapshots (stdfile); + M_NotifyNewSave (filename.GetChars(), description, okForQuicksave); - // Check whether the file is ok. - bool success = false; - stdfile = fopen (filename.GetChars(), "rb"); - if (stdfile != NULL) - { - PNGHandle *pngh = M_VerifyPNG(stdfile); - if (pngh != NULL) - { - success = true; - delete pngh; - } - fclose(stdfile); - } -#endif + // Check whether the file is ok. (todo when new format is ready) bool success = true; if (success) { diff --git a/src/g_hub.cpp b/src/g_hub.cpp index e43607710..cabcef2d3 100644 --- a/src/g_hub.cpp +++ b/src/g_hub.cpp @@ -43,7 +43,7 @@ #include "m_png.h" #include "gstrings.h" #include "wi_stuff.h" -#include "farchive.h" +#include "serializer.h" //========================================================================== @@ -54,7 +54,7 @@ struct FHubInfo { - int finished_ep; + int levelnum; int maxkills; int maxitems; @@ -65,7 +65,7 @@ struct FHubInfo FHubInfo &operator=(const wbstartstruct_t &wbs) { - finished_ep = wbs.finished_ep; + levelnum = wbs.finished_ep; maxkills = wbs.maxkills; maxsecret= wbs.maxsecret; maxitems = wbs.maxitems; @@ -80,54 +80,54 @@ static TArray hubdata; void G_LeavingHub(int mode, cluster_info_t * cluster, wbstartstruct_t * wbs) { - unsigned int i,j; + unsigned int i, j; if (cluster->flags & CLUSTER_HUB) { - for(i=0;imaxkills=wbs->maxitems=wbs->maxsecret=0; - for(i=0;imaxkills = wbs->maxitems = wbs->maxsecret = 0; + for (i = 0; i < MAXPLAYERS; i++) { - wbs->plyr[i].sitems=wbs->plyr[i].skills=wbs->plyr[i].ssecret=0; + wbs->plyr[i].sitems = wbs->plyr[i].skills = wbs->plyr[i].ssecret = 0; } - for(i=0;imaxkills += hubdata[i].maxkills; wbs->maxitems += hubdata[i].maxitems; wbs->maxsecret += hubdata[i].maxsecret; - for(j=0;jplyr[j].sitems += hubdata[i].plyr[j].sitems; wbs->plyr[j].skills += hubdata[i].plyr[j].skills; wbs->plyr[j].ssecret += hubdata[i].plyr[j].ssecret; } } - if (cluster->ClusterName.IsNotEmpty()) + if (cluster->ClusterName.IsNotEmpty()) { if (cluster->flags & CLUSTER_LOOKUPNAME) { @@ -140,7 +140,7 @@ void G_LeavingHub(int mode, cluster_info_t * cluster, wbstartstruct_t * wbs) } } } - if (mode!=FINISH_SameHub) hubdata.Clear(); + if (mode != FINISH_SameHub) hubdata.Clear(); } //========================================================================== @@ -148,39 +148,41 @@ void G_LeavingHub(int mode, cluster_info_t * cluster, wbstartstruct_t * wbs) // Serialize intermission info for hubs // //========================================================================== -#define HUBS_ID MAKE_ID('h','u','B','s') -static void G_SerializeHub(FArchive & arc) +FSerializer &Serialize(FSerializer &arc, const char *key, wbplayerstruct_t &h, wbplayerstruct_t *def) { - int i=hubdata.Size(); - arc << i; - if (i>0) + if (arc.BeginObject(key)) { - if (arc.IsStoring()) arc.Write(&hubdata[0], i * sizeof(FHubInfo)); - else - { - hubdata.Resize(i); - arc.Read(&hubdata[0], i * sizeof(FHubInfo)); - } + arc("in", h.in) + ("kills", h.skills) + ("items", h.sitems) + ("secrets", h.ssecret) + ("time", h.stime) + ("fragcount", h.fragcount) + .Array("frags", h.frags, MAXPLAYERS) + .EndObject(); } - else hubdata.Clear(); + return arc; } -void G_WriteHubInfo (FILE *file) +FSerializer &Serialize(FSerializer &arc, const char *key, FHubInfo &h, FHubInfo *def) { - FPNGChunkArchive arc(file, HUBS_ID); - G_SerializeHub(arc); + if (arc.BeginObject(key)) + { + arc("levelnum", h.levelnum) + ("maxkills", h.maxkills) + ("maxitems", h.maxitems) + ("maxsecret", h.maxsecret) + ("maxfrags", h.maxfrags) + .Array("players", h.plyr, MAXPLAYERS) + .EndObject(); + } + return arc; } -void G_ReadHubInfo (PNGHandle *png) +void G_SerializeHub(FSerializer &arc) { - int chunklen; - - if ((chunklen = M_FindPNGChunk (png, HUBS_ID)) != 0) - { - FPNGChunkArchive arc (png->File->GetFile(), HUBS_ID, chunklen); - G_SerializeHub(arc); - } + arc("hubinfo", hubdata); } void G_ClearHubInfo() diff --git a/src/g_hub.h b/src/g_hub.h index 8f148479c..245bb8f93 100644 --- a/src/g_hub.h +++ b/src/g_hub.h @@ -1,14 +1,12 @@ #ifndef __G_HUB_H #define __G_HUB_H -#include - struct PNGHandle; struct cluster_info_t; struct wbstartstruct_t; +class FSerializer; -void G_WriteHubInfo (FILE *file); -void G_ReadHubInfo (PNGHandle *png); +void G_SerializeHub (FSerializer &file); void G_LeavingHub(int mode, cluster_info_t * cluster, struct wbstartstruct_t * wbs); #endif diff --git a/src/g_level.cpp b/src/g_level.cpp index 5908a2b9f..d917794b3 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1748,38 +1748,33 @@ CCMD(listsnapshots) // //========================================================================== -static void writeDefereds (FArchive &arc, level_info_t *i) +void P_WriteACSDefereds (FSerializer &arc) { - arc << i->MapName << i->defered; -} + bool found = false; -//========================================================================== -// -// -//========================================================================== - -void P_WriteACSDefereds (FILE *file) -{ - FPNGChunkArchive *arc = NULL; - - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + // only write this stuff if needed + for (auto &wi : wadlevelinfos) { - if (wadlevelinfos[i].defered) + if (wi.deferred.Size() > 0) { - if (arc == NULL) - { - arc = new FPNGChunkArchive (file, ACSD_ID); - } - writeDefereds (*arc, (level_info_t *)&wadlevelinfos[i]); + found = true; + break; } } - - if (arc != NULL) + if (found && arc.BeginObject("deferred")) { - // Signal end of defereds - FString empty = ""; - (*arc) << empty; - delete arc; + for (auto &wi : wadlevelinfos) + { + if (wi.deferred.Size() > 0) + { + if (wi.deferred.Size() > 0 && arc.BeginObject(nullptr)) + { + arc(wi.MapName, wi.deferred) + .EndObject(); + } + } + } + arc.EndObject(); } } @@ -1790,6 +1785,7 @@ void P_WriteACSDefereds (FILE *file) void P_ReadACSDefereds (PNGHandle *png) { +#if 0 FString MapName; size_t chunklen; @@ -1806,10 +1802,11 @@ void P_ReadACSDefereds (PNGHandle *png) { I_Error("Unknown map '%s' in savegame", MapName.GetChars()); } - arc << i->defered; + arc << i->deferred; } } png->File->ResetFilePtr(); +#endif } diff --git a/src/g_level.h b/src/g_level.h index 70872bc46..4d730b2f2 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -38,6 +38,7 @@ #include "doomdef.h" #include "sc_man.h" #include "s_sound.h" +#include "p_acs.h" #include "textures/textures.h" #include "resourcefiles/file_zip.h" @@ -224,8 +225,6 @@ enum ELevelFlags : unsigned int }; -struct acsdefered_t; - struct FSpecialAction { FName Type; // this is initialized before the actors... @@ -297,7 +296,7 @@ struct level_info_t int musicorder; FCompressedBuffer Snapshot; DWORD snapshotVer; - struct acsdefered_t *defered; + TArray deferred; float skyspeed1; float skyspeed2; DWORD fadeto; @@ -352,7 +351,10 @@ struct level_info_t void Reset(); bool isValid(); FString LookupLevelName (); - void ClearDefered(); + void ClearDefered() + { + deferred.Clear(); + } level_info_t *CheckLevelRedirect (); template diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index ff4ef0e87..e6b9c093b 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -250,7 +250,7 @@ void level_info_t::Reset() musicorder = 0; Snapshot = { 0,0,0,0,nullptr }; snapshotVer = 0; - defered = 0; + deferred.Clear(); skyspeed1 = skyspeed2 = 0.f; fadeto = 0; outsidefog = 0xff000000; @@ -334,23 +334,6 @@ FString level_info_t::LookupLevelName() } -//========================================================================== -// -// -//========================================================================== - -void level_info_t::ClearDefered() -{ - acsdefered_t *def = defered; - while (def) - { - acsdefered_t *next = def->next; - delete def; - def = next; - } - defered = NULL; -} - //========================================================================== // // diff --git a/src/json.cpp b/src/json.cpp index 1d8d506f8..982d9fb79 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -50,10 +50,8 @@ CCMD(writejson) DWORD t = I_MSTime(); FSerializer arc; arc.OpenWriter(); - arc.BeginObject(nullptr); G_SerializeLevel(arc, false); arc.WriteObjects(); - arc.EndObject(); DWORD tt = I_MSTime(); Printf("JSON generation took %d ms\n", tt - t); FILE *f = fopen("out.json", "wb"); diff --git a/src/m_misc.cpp b/src/m_misc.cpp index a37c32094..824fe0533 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -449,7 +449,7 @@ struct pcx_t }; -inline void putc(char chr, FileWriter *file) +inline void putc(unsigned char chr, FileWriter *file) { file->Write(&chr, 1); } diff --git a/src/m_random.cpp b/src/m_random.cpp index c0516e1f5..cc63fd5ed 100644 --- a/src/m_random.cpp +++ b/src/m_random.cpp @@ -61,7 +61,7 @@ #include "doomstat.h" #include "m_random.h" -#include "farchive.h" +#include "serializer.h" #include "b_bot.h" #include "m_png.h" #include "m_crc32.h" @@ -291,24 +291,29 @@ DWORD FRandom::StaticSumSeeds () // //========================================================================== -void FRandom::StaticWriteRNGState (FILE *file) +void FRandom::StaticWriteRNGState (FSerializer &arc) { FRandom *rng; - FPNGChunkArchive arc (file, RAND_ID); - arc << rngseed; + arc("rngseed", rngseed); - for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) + if (arc.BeginArray("rngs")) { - // Only write those RNGs that have names - if (rng->NameCRC != 0) + for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) { - arc << rng->NameCRC << rng->idx; - for (int i = 0; i < SFMT::N32; ++i) + // Only write those RNGs that have names + if (rng->NameCRC != 0) { - arc << rng->sfmt.u[i]; + if (arc.BeginObject(nullptr)) + { + arc("crc", rng->NameCRC) + ("index", rng->idx) + .Array("u", rng->sfmt.u, SFMT::N32) + .EndObject(); + } } } + arc.EndArray(); } } @@ -323,6 +328,7 @@ void FRandom::StaticWriteRNGState (FILE *file) void FRandom::StaticReadRNGState (PNGHandle *png) { +#if 0 FRandom *rng; size_t len = M_FindPNGChunk (png, RAND_ID); @@ -367,6 +373,7 @@ void FRandom::StaticReadRNGState (PNGHandle *png) } png->File->ResetFilePtr(); } +#endif } //========================================================================== diff --git a/src/m_random.h b/src/m_random.h index fab21b4ee..141585925 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -40,6 +40,7 @@ #include "sfmt/SFMT.h" struct PNGHandle; +class FSerializer; class FRandom { @@ -175,7 +176,7 @@ public: static void StaticClearRandom (); static DWORD StaticSumSeeds (); static void StaticReadRNGState (PNGHandle *png); - static void StaticWriteRNGState (FILE *file); + static void StaticWriteRNGState (FSerializer &file); static FRandom *StaticFindRNG(const char *name); #ifndef NDEBUG diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index f32146d50..4ccd21db6 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -213,7 +213,7 @@ void DLoadSaveMenu::ReadSaveStrings () LastSaved = LastAccessed = -1; quickSaveSlot = NULL; - filter = G_BuildSaveName ("*.zds", -1); + filter = G_BuildSaveName ("*." SAVEGAME_EXT, -1); filefirst = I_FindFirst (filter.GetChars(), &c_file); if (filefirst != ((void *)(-1))) { diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 22b44aa1c..c5ef8e2ee 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -770,11 +770,11 @@ void ACSStringPool::ReadStrings(PNGHandle *png, DWORD id) // // ACSStringPool :: WriteStrings // -// Writes strings to a PNG chunk. +// Writes strings to a serializer // //============================================================================ -void ACSStringPool::WriteStrings(FILE *file, DWORD id) const +void ACSStringPool::WriteStrings(FSerializer &file, const char *key) const { int32_t i, poolsize = (int32_t)Pool.Size(); @@ -782,20 +782,29 @@ void ACSStringPool::WriteStrings(FILE *file, DWORD id) const { // No need to write if we don't have anything. return; } - FPNGChunkArchive arc(file, id); - - arc << poolsize; - for (i = 0; i < poolsize; ++i) + if (file.BeginObject(key)) { - PoolEntry *entry = &Pool[i]; - if (entry->Next != FREE_ENTRY) + file("poolsize", poolsize); + if (file.BeginArray("pool")) { - arc.WriteCount(i); - arc.WriteString(entry->Str); - arc.WriteCount(entry->LockCount); + for (i = 0; i < poolsize; ++i) + { + PoolEntry *entry = &Pool[i]; + if (entry->Next != FREE_ENTRY) + { + if (file.BeginObject(nullptr)) + { + file("index", i) + ("string", entry->Str) + ("lockcount", entry->LockCount) + .EndObject(); + } + } + } + file.EndArray(); } + file.EndObject(); } - arc.WriteCount(-1); } //============================================================================ @@ -945,7 +954,7 @@ void P_ClearACSVars(bool alsoglobal) // //============================================================================ -static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id) +static void WriteVars (FSerializer &file, SDWORD *vars, size_t count, const char *key) { size_t i, j; @@ -963,12 +972,7 @@ static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id) if (vars[j] != 0) break; } - FPNGChunkArchive arc (file, id); - for (i = 0; i <= j; ++i) - { - DWORD var = vars[i]; - arc << var; - } + file.Array(key, vars, int(j+1)); } } @@ -1009,9 +1013,9 @@ static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id) // //============================================================================ -static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int count, DWORD id) +static void WriteArrayVars (FSerializer &file, FWorldGlobalArray *vars, unsigned int count, const char *key) { - unsigned int i, j; + unsigned int i; // Find the first non-empty array. for (i = 0; i < count; ++i) @@ -1021,28 +1025,31 @@ static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int co } if (i < count) { - // Find last non-empty array. Anything beyond the last stored array - // will be emptied at load time. - for (j = count-1; j > i; --j) + if (file.BeginObject(key)) { - if (vars[j].CountUsed() != 0) - break; - } - FPNGChunkArchive arc (file, id); - arc.WriteCount (i); - arc.WriteCount (j); - for (; i <= j; ++i) - { - arc.WriteCount (vars[i].CountUsed()); - - FWorldGlobalArray::ConstIterator it(vars[i]); - const FWorldGlobalArray::Pair *pair; - - while (it.NextPair (pair)) + for(;iKey); - arc.WriteCount (pair->Value); + if (vars[i].CountUsed()) + { + FString arraykey; + + arraykey.Format("%d", i); + if (file.BeginObject(arraykey)) + { + FWorldGlobalArray::ConstIterator it(vars[i]); + const FWorldGlobalArray::Pair *pair; + + while (it.NextPair(pair)) + { + arraykey.Format("%d", pair->Key); + int v = pair->Value; + file(arraykey.GetChars(), v); + } + file.EndObject(); + } + } } + file.EndObject(); } } } @@ -1108,13 +1115,13 @@ void P_ReadACSVars(PNGHandle *png) // //============================================================================ -void P_WriteACSVars(FILE *stdfile) +void P_WriteACSVars(FSerializer &arc) { - WriteVars (stdfile, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); - WriteVars (stdfile, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); - WriteArrayVars (stdfile, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); - WriteArrayVars (stdfile, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); - GlobalACSStrings.WriteStrings(stdfile, MAKE_ID('a','s','T','r')); + WriteVars (arc, ACS_WorldVars, NUM_WORLDVARS, "acsworldvars"); + WriteVars (arc, ACS_GlobalVars, NUM_GLOBALVARS, "acsglobalvars"); + WriteArrayVars (arc, ACS_WorldArrays, NUM_WORLDVARS, "acsworldarrays"); + WriteArrayVars (arc, ACS_GlobalArrays, NUM_GLOBALVARS, "acsglobalarrays"); + GlobalACSStrings.WriteStrings(arc, "acsglobalstrings"); } //---- Inventory functions --------------------------------------// @@ -9672,15 +9679,13 @@ static void SetScriptState (int script, DLevelScript::EScriptState state) void P_DoDeferedScripts () { - acsdefered_t *def; const ScriptPtr *scriptdata; FBehavior *module; // Handle defered scripts in this step, too - def = level.info->defered; - while (def) + for(int i = level.info->deferred.Size()-1; i>=0; i--) { - acsdefered_t *next = def->next; + acsdefered_t *def = &level.info->deferred[i]; switch (def->type) { case acsdefered_t::defexecute: @@ -9711,39 +9716,35 @@ void P_DoDeferedScripts () DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); break; } - delete def; - def = next; } - level.info->defered = NULL; + level.info->deferred.Clear(); } static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who) { if (i) { - acsdefered_t *def = new acsdefered_t; + acsdefered_t &def = i->deferred[i->deferred.Reserve(1)]; int j; - def->next = i->defered; - def->type = type; - def->script = script; - for (j = 0; (size_t)j < countof(def->args) && j < argcount; ++j) + def.type = type; + def.script = script; + for (j = 0; (size_t)j < countof(def.args) && j < argcount; ++j) { - def->args[j] = args[j]; + def.args[j] = args[j]; } - while ((size_t)j < countof(def->args)) + while ((size_t)j < countof(def.args)) { - def->args[j++] = 0; + def.args[j++] = 0; } if (who != NULL && who->player != NULL) { - def->playernum = int(who->player - players); + def.playernum = int(who->player - players); } else { - def->playernum = -1; + def.playernum = -1; } - i->defered = def; DPrintf (DMSG_SPAMMY, "%s on map %s deferred\n", ScriptPresentation(script).GetChars(), i->MapName.GetChars()); } } @@ -9821,43 +9822,15 @@ void P_TerminateScript (int script, const char *map) SetScriptState (script, DLevelScript::SCRIPT_PleaseRemove); } -FArchive &operator<< (FArchive &arc, acsdefered_t *&defertop) +FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def) { - BYTE more; - - if (arc.IsStoring ()) + if (arc.BeginObject(key)) { - acsdefered_t *defer = defertop; - more = 1; - while (defer) - { - BYTE type; - arc << more; - type = (BYTE)defer->type; - arc << type; - P_SerializeACSScriptNumber(arc, defer->script, false); - arc << defer->playernum << defer->args[0] << defer->args[1] << defer->args[2]; - defer = defer->next; - } - more = 0; - arc << more; - } - else - { - acsdefered_t **defer = &defertop; - - arc << more; - while (more) - { - *defer = new acsdefered_t; - arc << more; - (*defer)->type = (acsdefered_t::EType)more; - P_SerializeACSScriptNumber(arc, (*defer)->script, false); - arc << (*defer)->playernum << (*defer)->args[0] << (*defer)->args[1] << (*defer)->args[2]; - defer = &((*defer)->next); - arc << more; - } - *defer = NULL; + arc.Enum("type", defer.type) + .ScriptNum("script", defer.script) + .Array("args", defer.args, 3) + ("player", defer.playernum) + .EndObject(); } return arc; } diff --git a/src/p_acs.h b/src/p_acs.h index 512780f8c..5ff048e8b 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -44,6 +44,8 @@ class FFont; class FileReader; +struct line_t; +struct PNGHandle; enum @@ -95,7 +97,7 @@ public: void Clear(); void Dump() const; void ReadStrings(PNGHandle *png, DWORD id); - void WriteStrings(FILE *file, DWORD id) const; + void WriteStrings(FSerializer &file, const char *key) const; private: int FindString(const char *str, size_t len, unsigned int h, unsigned int bucketnum); @@ -121,9 +123,8 @@ extern ACSStringPool GlobalACSStrings; void P_CollectACSGlobalStrings(); void P_ReadACSVars(PNGHandle *); -void P_WriteACSVars(FILE*); +void P_WriteACSVars(FSerializer &); void P_ClearACSVars(bool); -void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte); struct ACSProfileInfo { @@ -964,8 +965,6 @@ private: // The structure used to control scripts between maps struct acsdefered_t { - struct acsdefered_t *next; - enum EType { defexecute, @@ -978,6 +977,6 @@ struct acsdefered_t int playernum; }; -FArchive &operator<< (FArchive &arc, acsdefered_t *&defer); +FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def); #endif //__P_ACS_H__ diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 8f6038bf5..3b9b33c3f 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -72,17 +72,6 @@ // Thinkers // -// -// P_ArchiveThinkers -// - -void P_SerializeThinkers (FArchive &arc, bool hubLoad) -{ - arc.EnableThinkers(); - //DImpactDecal::Im ::SerializeTime (arc); - DThinker::SerializeAll (arc, hubLoad); -} - void P_DestroyThinkers(bool hubLoad) { if (hubLoad) diff --git a/src/p_saveg.h b/src/p_saveg.h index 43293c57a..f09ca48da 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -34,8 +34,8 @@ #ifndef __P_SAVEG_H__ #define __P_SAVEG_H__ -class FArchive; struct PNGHandle; +class FSerializer; // Persistent storage/archiving. // These are the load / save game routines. @@ -43,7 +43,7 @@ struct PNGHandle; void P_DestroyThinkers(bool hubLoad); void P_ReadACSDefereds (PNGHandle *png); -void P_WriteACSDefereds (FILE *file); +void P_WriteACSDefereds (FSerializer &); void G_SerializeLevel(FSerializer &arc, bool hubLoad); diff --git a/src/r_defs.h b/src/r_defs.h index efd9fc641..a21d23616 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -159,7 +159,6 @@ class FScanner; class FBitmap; struct FCopyInfo; class DInterpolation; -class FArchive; enum { diff --git a/src/s_sound.h b/src/s_sound.h index 29b5dfb2d..86915ea40 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -176,8 +176,6 @@ public: } }; -FArchive &operator<<(FArchive &arc, FSoundID &sid); - extern FRolloffInfo S_Rolloff; extern BYTE *S_SoundCurve; extern int S_SoundCurveSize; diff --git a/src/serializer.cpp b/src/serializer.cpp index 939c3fc7d..884475413 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -149,11 +149,12 @@ struct FReader // //========================================================================== -bool FSerializer::OpenWriter() +bool FSerializer::OpenWriter(bool randomaccess) { if (w != nullptr || r != nullptr) return false; w = new FWriter; + BeginObject(nullptr, randomaccess); return true; } @@ -587,6 +588,22 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) // //========================================================================== +FSerializer &FSerializer::AddString(const char *key, const char *charptr) +{ + if (isWriting()) + { + WriteKey(key); + w->mWriter.String(charptr); + } + return *this; +} + +//========================================================================== +// +// +// +//========================================================================== + unsigned FSerializer::GetSize(const char *group) { if (isWriting()) return -1; // we do not know this when writing. @@ -657,6 +674,7 @@ void FSerializer::WriteObjects() const char *FSerializer::GetOutput(unsigned *len) { if (isReading()) return nullptr; + EndObject(); if (len != nullptr) { *len = (unsigned)w->mOutString.GetSize(); @@ -674,6 +692,7 @@ FCompressedBuffer FSerializer::GetCompressedOutput() { if (isReading()) return{ 0,0,0,0,nullptr }; FCompressedBuffer buff; + EndObject(); buff.mSize = (unsigned)w->mOutString.GetSize(); buff.mZipFlags = 0; diff --git a/src/serializer.h b/src/serializer.h index 12edc2e99..4d910aec6 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -75,6 +75,7 @@ public: FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr); 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); FSerializer &ScriptNum(const char *key, int &num); bool isReading() const { diff --git a/src/statistics.cpp b/src/statistics.cpp index 5d14bad0a..536d60a44 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -70,7 +70,7 @@ #include "r_sky.h" #include "p_lnspec.h" #include "m_crc32.h" -#include "farchive.h" +#include "serializer.h" CVAR(Int, savestatistics, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(String, statfile, "zdoomstat.txt", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -87,7 +87,7 @@ struct OneLevel int totalkills, killcount; int totalsecrets, secretcount; int leveltime; - char levelname[9]; + FString Levelname; }; // Current game's statistics @@ -408,13 +408,12 @@ static void StoreLevelStats() { for(i=0;imEpisodeMap; - arc << startlevel; - } - for(int j = 0; j < i; j++) - { - OneLevel &l = LevelData[j]; - - arc << l.totalkills - << l.killcount - << l.totalsecrets - << l.secretcount - << l.leveltime; - - if (arc.IsStoring()) arc.WriteName(l.levelname); - else strcpy(l.levelname, arc.ReadName()); + else + { + if (StartEpisode != NULL) startlevel = StartEpisode->mEpisodeMap; + arc("startlevel", startlevel); + } + arc("levels", LevelData); + arc.EndObject(); } } -#define STAT_ID MAKE_ID('s','T','a','t') - -void STAT_Write(FILE *file) -{ - FPNGChunkArchive arc (file, STAT_ID); - SerializeStatistics(arc); -} - -void STAT_Read(PNGHandle *png) -{ - DWORD chunkLen = (DWORD)M_FindPNGChunk (png, STAT_ID); - if (chunkLen != 0) - { - FPNGChunkArchive arc (png->File->GetFile(), STAT_ID, chunkLen); - SerializeStatistics(arc); - } -} //========================================================================== // @@ -588,7 +574,7 @@ FString GetStatString() { OneLevel *l = &LevelData[i]; compose.AppendFormat("Level %s - Kills: %d/%d - Secrets: %d/%d - Time: %d:%02d\n", - l->levelname, l->killcount, l->totalkills, l->secretcount, l->totalsecrets, + l->Levelname.GetChars(), l->killcount, l->totalkills, l->secretcount, l->totalsecrets, l->leveltime/(60*TICRATE), (l->leveltime/TICRATE)%60); } return compose; diff --git a/src/tarray.h b/src/tarray.h index 4d16ddfe2..6e37ea039 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -49,9 +49,6 @@ #include "m_alloc.h" -class FArchive; - - template class TIterator { public: @@ -83,8 +80,6 @@ struct FArray template class TArray { - template friend FArchive &operator<< (FArchive &arc, TArray &self); - public: typedef TIterator iterator; diff --git a/src/textures/textures.h b/src/textures/textures.h index 954936fe1..298b169f2 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -9,7 +9,6 @@ struct FRemapTable; struct FCopyInfo; class FScanner; class PClassInventory; -class FArchive; // Texture IDs class FTextureManager; @@ -21,8 +20,6 @@ public: FNullTextureID() : FTextureID(0) {} }; -FArchive &operator<< (FArchive &arc, FTextureID &tex); - // // Animating textures and planes // diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index e6193b3aa..f2ce37bfd 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -37,26 +37,6 @@ -void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte) -{ - arc << scriptnum; - // If the script number is negative, then it's really a name. - // So read/store the name after it. - if (scriptnum < 0) - { - if (arc.IsStoring()) - { - arc.WriteName(FName(ENamedName(-scriptnum)).GetChars()); - } - else - { - const char *nam = arc.ReadName(); - scriptnum = -FName(nam); - } - } -} - - #if 0 // still needed as reference. FCrap &FCrap::ReadObject(DObject* &obj, PClass *wanttype) @@ -181,3 +161,63 @@ FCrap &FCrap::ReadObject(DObject* &obj, PClass *wanttype) } #endif +#if 0 +void DThinker::SerializeAll(FArchive &arc, bool hubLoad) +{ + DThinker *thinker; + BYTE stat; + int statcount; + int i; + + if (arc.IsStoring()) + { + } + else + { + // Prevent the constructor from inserting thinkers into a list. + bSerialOverride = true; + + try + { + arc << statcount; + while (statcount > 0) + { + arc << stat << thinker; + while (thinker != NULL) + { + // This may be a player stored in their ancillary list. Remove + // them first before inserting them into the new list. + if (thinker->NextThinker != NULL) + { + thinker->Remove(); + } + // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers + // list. Anything else goes in the regular Thinkers list. + if (thinker->ObjectFlags & OF_EuthanizeMe) + { + // This thinker was destroyed during the loading process. Do + // not link it in to any list. + } + else if (thinker->ObjectFlags & OF_JustSpawned) + { + FreshThinkers[stat].AddTail(thinker); + } + else + { + Thinkers[stat].AddTail(thinker); + } + arc << thinker; + } + statcount--; + } + } + catch (class CDoomError &) + { + bSerialOverride = false; + DestroyAllThinkers(); + throw; + } + bSerialOverride = false; + } +} +#endif From ac3c00883d4660433947c199a712ad6ffc8bcf42 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 17:37:56 +0200 Subject: [PATCH 29/59] - implemented saving of new format savegames as zips. The code for that was adapted from zipdir.c. --- src/d_main.cpp | 2 +- src/farchive.cpp | 26 ----- src/farchive.h | 1 - src/g_game.cpp | 34 ++++++- src/g_level.cpp | 96 ++++++++---------- src/g_level.h | 4 +- src/g_mapinfo.cpp | 3 +- src/menu/loadsavemenu.cpp | 4 +- src/p_saveg.cpp | 1 + src/resourcefiles/file_zip.cpp | 173 ++++++++++++++++++++++++++++++++- src/resourcefiles/file_zip.h | 2 + src/serializer.cpp | 4 +- src/serializer.h | 2 +- src/statistics.cpp | 1 + src/version.h | 4 - 15 files changed, 253 insertions(+), 104 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 689603084..2dbf1273c 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2580,7 +2580,7 @@ void D_DoomMain (void) { FString file(v); FixPathSeperator (file); - DefaultExtension (file, ".zds" SAVEGAME_EXT); + DefaultExtension (file, "." SAVEGAME_EXT); G_LoadGame (file); } diff --git a/src/farchive.cpp b/src/farchive.cpp index 3aaf1bd86..3d57e4f71 100644 --- a/src/farchive.cpp +++ b/src/farchive.cpp @@ -962,32 +962,6 @@ PClass *FArchive::ReadStoredClass (const PClass *wanttype) return type; } -void FArchive::UserWriteClass (PClass *type) -{ - BYTE id; - - if (type == NULL) - { - id = 2; - Write (&id, 1); - } - else - { - DWORD *arcid; - if (NULL == (arcid = ClassToArchive.CheckKey(type))) - { - id = 1; - Write (&id, 1); - WriteClass (type); - } - else - { - id = 0; - Write (&id, 1); - WriteCount (*arcid); - } - } -} void FArchive::UserReadClass (PClass *&type) { diff --git a/src/farchive.h b/src/farchive.h index 484c1018b..ee0310898 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -152,7 +152,6 @@ virtual void Read (void *mem, unsigned int len); void WriteCount (DWORD count); DWORD ReadCount (); - void UserWriteClass (PClass *info); void UserReadClass (PClass *&info); template void UserReadClass(T *&info) { diff --git a/src/g_game.cpp b/src/g_game.cpp index 46bd3f7e4..7ed832e0a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -85,6 +85,8 @@ #include "p_spec.h" #include "r_data/colormaps.h" #include "serializer.h" +#include "w_zip.h" +#include "resourcefiles/resourcefile.h" #include @@ -112,6 +114,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio void G_DoAutoSave (); void STAT_Serialize(FSerializer &file); +bool WriteZip(const char *filename, TArray &filenames, TArray &content); FIntCVar gameskill ("skill", 2, CVAR_SERVERINFO|CVAR_LATCH); CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH); @@ -1886,7 +1889,7 @@ void G_DoLoadGame () SaveVersion = 0; if (!M_GetPNGText (png, "ZDoom Save Version", sigcheck, 20) || - 0 != strncmp (sigcheck, SAVESIG, 9) || // ZDOOMSAVE is the first 9 chars + 0 != strncmp (sigcheck, "SAVEVER", 9) || // ZDOOMSAVE is the first 9 chars (SaveVersion = atoi (sigcheck+9)) < MINSAVEVER) { delete png; @@ -2045,7 +2048,7 @@ FString G_BuildSaveName (const char *prefix, int slot) name << prefix; if (slot >= 0) { - name.AppendFormat("%d.zds" SAVEGAME_EXT, slot); + name.AppendFormat("%d." SAVEGAME_EXT, slot); } return name; } @@ -2166,6 +2169,7 @@ static void PutSavePic (FileWriter *file, int width, int height) void G_DoSaveGame (bool okForQuicksave, FString filename, const char *description) { TArray savegame_content; + TArray savegame_filenames; char buf[100]; @@ -2203,9 +2207,10 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio M_AppendPNGText(&savepic, "Current Map", level.MapName); M_FinishPNG(&savepic); + int ver = SAVEVER; savegameinfo.AddString("Software", buf) .AddString("Engine", GAMESIG) - .AddString("Save Version", SAVESIG) + ("Save Version", ver) .AddString("Title", description) .AddString("Current Map", level.MapName); @@ -2232,17 +2237,36 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio FRandom::StaticWriteRNGState(savegameglobals); P_WriteACSDefereds(savegameglobals); P_WriteACSVars(savegameglobals); + G_WriteVisited(savegameglobals); + if (NextSkill != -1) { savegameglobals("nextskill", NextSkill); } - //G_WriteSnapshots (stdfile); + auto picdata = savepic.GetBuffer(); + FCompressedBuffer bufpng = { picdata->Size(), picdata->Size(), METHOD_STORED, 0, crc32(0, &(*picdata)[0], picdata->Size()), (char*)&(*picdata)[0] }; + + savegame_content.Push(bufpng); + savegame_filenames.Push("savepic.png"); + savegame_content.Push(savegameinfo.GetCompressedOutput()); + savegame_filenames.Push("info.json"); + savegame_content.Push(savegameglobals.GetCompressedOutput()); + savegame_filenames.Push("globals.json"); + + G_WriteSnapshots (savegame_filenames, savegame_content); + WriteZip(filename, savegame_filenames, savegame_content); + M_NotifyNewSave (filename.GetChars(), description, okForQuicksave); + // delete the JSON buffers we created just above. Everything else will + // either still be needed or taken care of automatically. + delete[] savegame_content[1].mBuffer; + delete[] savegame_content[2].mBuffer; + // Check whether the file is ok. (todo when new format is ready) bool success = true; if (success) @@ -2252,6 +2276,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } else Printf(PRINT_HIGH, "Save failed\n"); + FResourceFile *test = FResourceFile::OpenResourceFile(filename, nullptr); + BackupSaveName = filename; // We don't need the snapshot any longer. diff --git a/src/g_level.cpp b/src/g_level.cpp index d917794b3..2acb1335c 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1487,8 +1487,6 @@ void G_SnapshotLevel () if (level.info->isValid()) { - level.info->snapshotVer = SAVEVER; - FSerializer arc; if (arc.OpenWriter()) @@ -1514,7 +1512,6 @@ void G_UnSnapshotLevel (bool hubLoad) if (level.info->isValid()) { - SaveVersion = level.info->snapshotVer; FSerializer arc; if (!arc.OpenReader(&level.info->Snapshot)) return; @@ -1563,10 +1560,28 @@ void G_UnSnapshotLevel (bool hubLoad) // //========================================================================== -static void writeSnapShot (FArchive &arc, level_info_t *i) +void G_WriteSnapshots(TArray &filenames, TArray &buffers) { - arc << i->snapshotVer << i->MapName; - //i->snapshot->Serialize (arc); + unsigned int i; + FString filename; + + for (i = 0; i < wadlevelinfos.Size(); i++) + { + if (wadlevelinfos[i].Snapshot.mCompressedSize > 0) + { + filename << wadlevelinfos[i].MapName << ".json"; + filename.ToLower(); + filenames.Push(filename); + buffers.Push(wadlevelinfos[i].Snapshot); + } + } + if (TheDefaultLevelInfo.Snapshot.mCompressedSize > 0) + { + filename << TheDefaultLevelInfo.MapName << ".json"; + filename.ToLower(); + filenames.Push(filename); + buffers.Push(TheDefaultLevelInfo.Snapshot); + } } //========================================================================== @@ -1574,72 +1589,39 @@ static void writeSnapShot (FArchive &arc, level_info_t *i) // //========================================================================== -void G_WriteSnapshots (FILE *file) +void G_WriteVisited(FSerializer &arc) { - unsigned int i; - -#if 0 - for (i = 0; i < wadlevelinfos.Size(); i++) + if (arc.BeginArray("visited")) { - if (wadlevelinfos[i].snapshot) + // Write out which levels have been visited + for (auto & wi : wadlevelinfos) { - FPNGChunkArchive arc (file, SNAP_ID); - writeSnapShot (arc, (level_info_t *)&wadlevelinfos[i]); - } - } - if (TheDefaultLevelInfo.snapshot != NULL) - { - FPNGChunkArchive arc (file, DSNP_ID); - writeSnapShot(arc, &TheDefaultLevelInfo); - } -#endif - - FPNGChunkArchive *arc = NULL; - - // Write out which levels have been visited - for (i = 0; i < wadlevelinfos.Size(); ++i) - { - if (wadlevelinfos[i].flags & LEVEL_VISITED) - { - if (arc == NULL) + if (wi.flags & LEVEL_VISITED) { - arc = new FPNGChunkArchive (file, VIST_ID); + arc.AddString(nullptr, wi.MapName); } - (*arc) << wadlevelinfos[i].MapName; } - } - - if (arc != NULL) - { - FString empty = ""; - (*arc) << empty; - delete arc; + arc.EndArray(); } // Store player classes to be used when spawning a random class if (multiplayer) { - FPNGChunkArchive arc2 (file, RCLS_ID); - for (i = 0; i < MAXPLAYERS; ++i) - { - SBYTE cnum = SinglePlayerClass[i]; - arc2 << cnum; - } + arc.Array("randomclasses", SinglePlayerClass, MAXPLAYERS); } - // Store player classes that are currently in use - FPNGChunkArchive arc3 (file, PCLS_ID); - for (i = 0; i < MAXPLAYERS; ++i) + if (arc.BeginObject("playerclasses")) { - BYTE pnum; - if (playeringame[i]) + for (int i = 0; i < MAXPLAYERS; ++i) { - pnum = i; - arc3 << pnum; - arc3.UserWriteClass (players[i].cls); + if (playeringame[i]) + { + FString key; + key.Format("%d", i); + arc(key, players[i].cls); + } } - pnum = 255; - arc3 << pnum; + arc.EndObject(); } } @@ -1665,7 +1647,6 @@ void G_ReadSnapshots (PNGHandle *png) arc << snapver; arc << MapName; i = FindLevelInfo (MapName); - i->snapshotVer = snapver; #if 0 i->snapshot = new FCompressedMemFile; i->snapshot->Serialize (arc); @@ -1681,7 +1662,6 @@ void G_ReadSnapshots (PNGHandle *png) arc << snapver; arc << MapName; - TheDefaultLevelInfo.snapshotVer = snapver; #if 0 TheDefaultLevelInfo.snapshot = new FCompressedMemFile; TheDefaultLevelInfo.snapshot->Serialize (arc); diff --git a/src/g_level.h b/src/g_level.h index 4d730b2f2..4e86978ce 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -295,7 +295,6 @@ struct level_info_t SBYTE WallVertLight, WallHorizLight; int musicorder; FCompressedBuffer Snapshot; - DWORD snapshotVer; TArray deferred; float skyspeed1; float skyspeed2; @@ -535,7 +534,8 @@ void G_SnapshotLevel (void); void G_UnSnapshotLevel (bool keepPlayers); struct PNGHandle; void G_ReadSnapshots (PNGHandle *png); -void G_WriteSnapshots (FILE *file); +void G_WriteSnapshots (TArray &, TArray &); +void G_WriteVisited(FSerializer &arc); void G_ClearHubInfo(); enum ESkillProperty diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index e6b9c093b..8f0e2da13 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -248,8 +248,7 @@ void level_info_t::Reset() WallVertLight = +8; F1Pic = ""; musicorder = 0; - Snapshot = { 0,0,0,0,nullptr }; - snapshotVer = 0; + Snapshot = { 0,0,0,0,0,nullptr }; deferred.Clear(); skyspeed1 = skyspeed2 = 0.f; fadeto = 0; diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 4ccd21db6..6333ab3bf 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -241,7 +241,7 @@ void DLoadSaveMenu::ReadSaveStrings () title[SAVESTRINGSIZE] = 0; - if (NULL != (png = M_VerifyPNG (file))) + if (false)//NULL != (png = M_VerifyPNG (file))) { char *ver = M_GetPNGText (png, "ZDoom Save Version"); char *engine = M_GetPNGText (png, "Engine"); @@ -251,7 +251,7 @@ void DLoadSaveMenu::ReadSaveStrings () { strncpy (title, I_FindName(&c_file), SAVESTRINGSIZE); } - if (strncmp (ver, SAVESIG, 9) == 0 && + if (strncmp (ver, "SAVESIG", 9) == 0 && atoi (ver+9) >= MINSAVEVER && engine != NULL) { diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 3b9b33c3f..1b9f1a057 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -898,6 +898,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) I_Error("Savegame is from a different level"); } } + arc("saveversion", SaveVersion); Renderer->StartSerialize(arc); if (arc.isReading()) diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index 244577631..e2574d045 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -33,6 +33,7 @@ ** */ +#include #include "file_zip.h" #include "cmdlib.h" #include "templates.h" @@ -265,6 +266,7 @@ bool FZipFile::Open(bool quiet) lump_p->Flags = LUMPF_ZIPFILE | LUMPFZIP_NEEDFILESTART; lump_p->Method = BYTE(zip_fh->Method); lump_p->GPFlags = zip_fh->Flags; + lump_p->CRC32 = zip_fh->CRC32; lump_p->CompressedSize = LittleLong(zip_fh->CompressedSize); lump_p->Position = LittleLong(zip_fh->LocalHeaderOffset); lump_p->CheckEmbedded(); @@ -308,10 +310,11 @@ FCompressedBuffer FZipFile::GetRawLump(int lumpnum) { if ((unsigned)lumpnum >= NumLumps) { - return{ 0,0,0,0,nullptr }; + return{ 0,0,0,0,0,nullptr }; } FZipLump *lmp = &Lumps[lumpnum]; - FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, new char[lmp->CompressedSize] }; + + FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, lmp->CRC32, new char[lmp->CompressedSize] }; Reader->Seek(lmp->Position, SEEK_SET); Reader->Read(cbuf.mBuffer, lmp->CompressedSize); return cbuf; @@ -426,3 +429,169 @@ FResourceFile *CheckZip(const char *filename, FileReader *file, bool quiet) +//========================================================================== +// +// time_to_dos +// +// Converts time from struct tm to the DOS format used by zip files. +// +//========================================================================== + +static void time_to_dos(struct tm *time, unsigned short *dosdate, unsigned short *dostime) +{ + if (time == NULL || time->tm_year < 80) + { + *dosdate = *dostime = 0; + } + else + { + *dosdate = LittleShort((time->tm_year - 80) * 512 + (time->tm_mon + 1) * 32 + time->tm_mday); + *dostime = LittleShort(time->tm_hour * 2048 + time->tm_min * 32 + time->tm_sec / 2); + } +} + +//========================================================================== +// +// append_to_zip +// +// Write a given file to the zipFile. +// +// zipfile: zip object to be written to +// +// returns: position = success, -1 = error +// +//========================================================================== + +int AppendToZip(FILE *zip_file, const char *filename, FCompressedBuffer &content, uint16_t date, uint16_t time) +{ + FZipLocalFileHeader local; + int position; + + local.Magic = ZIP_LOCALFILE; + local.VersionToExtract[0] = 20; + local.VersionToExtract[1] = 0; + local.Flags = content.mMethod == METHOD_DEFLATE ? LittleShort(2) : LittleShort((uint16_t)content.mZipFlags); + local.Method = LittleShort(content.mMethod); + local.ModDate = date; + local.ModTime = time; + local.CRC32 = content.mCRC32; + local.UncompressedSize = LittleLong(content.mSize); + local.CompressedSize = LittleLong(content.mCompressedSize); + local.NameLength = LittleShort((unsigned short)strlen(filename)); + local.ExtraLength = 0; + + // Fill in local directory header. + + position = (int)ftell(zip_file); + + // Write out the header, file name, and file data. + if (fwrite(&local, sizeof(local), 1, zip_file) != 1 || + fwrite(filename, strlen(filename), 1, zip_file) != 1 || + fwrite(content.mBuffer, 1, content.mCompressedSize, zip_file) != content.mCompressedSize) + { + return -1; + } + return position; +} + + +//========================================================================== +// +// write_central_dir +// +// Writes the central directory entry for a file. +// +//========================================================================== + +int AppendCentralDirectory(FILE *zip_file, const char *filename, FCompressedBuffer &content, uint16_t date, uint16_t time, int position) +{ + FZipCentralDirectoryInfo dir; + + dir.Magic = ZIP_CENTRALFILE; + dir.VersionMadeBy[0] = 20; + dir.VersionMadeBy[1] = 0; + dir.VersionToExtract[0] = 20; + dir.VersionToExtract[1] = 0; + dir.Flags = content.mMethod == METHOD_DEFLATE ? LittleShort(2) : LittleShort((uint16_t)content.mZipFlags); + dir.Method = LittleShort(content.mMethod); + dir.ModTime = time; + dir.ModDate = date; + dir.CRC32 = content.mCRC32; + dir.CompressedSize = LittleLong(content.mCompressedSize); + dir.UncompressedSize = LittleLong(content.mSize); + dir.NameLength = LittleShort((unsigned short)strlen(filename)); + dir.ExtraLength = 0; + dir.CommentLength = 0; + dir.StartingDiskNumber = 0; + dir.InternalAttributes = 0; + dir.ExternalAttributes = 0; + dir.LocalHeaderOffset = LittleLong(position); + + if (fwrite(&dir, sizeof(dir), 1, zip_file) != 1 || + fwrite(filename, strlen(filename), 1, zip_file) != 1) + { + return -1; + } + return 0; +} + +bool WriteZip(const char *filename, TArray &filenames, TArray &content) +{ + // try to determine local time + struct tm *ltime; + time_t ttime; + uint16_t mydate, mytime; + ttime = time(nullptr); + ltime = localtime(&ttime); + time_to_dos(ltime, &mydate, &mytime); + + TArray positions; + + if (filenames.Size() != content.Size()) return false; + + FILE *f = fopen(filename, "wb"); + if (f != nullptr) + { + for (unsigned i = 0; i < filenames.Size(); i++) + { + int pos = AppendToZip(f, filenames[i], content[i], mydate, mytime); + if (pos == -1) + { + fclose(f); + remove(filename); + return false; + } + positions.Push(pos); + } + + int dirofs = (int)ftell(f); + for (unsigned i = 0; i < filenames.Size(); i++) + { + if (AppendCentralDirectory(f, filenames[i], content[i], mydate, mytime, positions[i]) < 0) + { + fclose(f); + remove(filename); + return false; + } + } + + // Write the directory terminator. + FZipEndOfCentralDirectory dirend; + dirend.Magic = ZIP_ENDOFDIR; + dirend.DiskNumber = 0; + dirend.FirstDisk = 0; + dirend.NumEntriesOnAllDisks = dirend.NumEntries = LittleShort(filenames.Size()); + dirend.DirectoryOffset = dirofs; + dirend.DirectorySize = LittleLong(ftell(f) - dirofs); + dirend.ZipCommentLength = 0; + if (fwrite(&dirend, sizeof(dirend), 1, f) != 1) + { + fclose(f); + remove(filename); + return false; + } + fclose(f); + return true; + } + return false; +} diff --git a/src/resourcefiles/file_zip.h b/src/resourcefiles/file_zip.h index dc10d558d..e802b41e8 100644 --- a/src/resourcefiles/file_zip.h +++ b/src/resourcefiles/file_zip.h @@ -10,6 +10,7 @@ struct FCompressedBuffer unsigned mCompressedSize; int mMethod; int mZipFlags; + unsigned mCRC32; char *mBuffer; bool Decompress(char *destbuffer); @@ -41,6 +42,7 @@ struct FZipLump : public FResourceLump BYTE Method; int CompressedSize; int Position; + unsigned CRC32; virtual FileReader *GetReader(); virtual int FillCache(); diff --git a/src/serializer.cpp b/src/serializer.cpp index 884475413..ea18a3a58 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -690,11 +690,12 @@ const char *FSerializer::GetOutput(unsigned *len) FCompressedBuffer FSerializer::GetCompressedOutput() { - if (isReading()) return{ 0,0,0,0,nullptr }; + if (isReading()) return{ 0,0,0,0,0,nullptr }; FCompressedBuffer buff; EndObject(); buff.mSize = (unsigned)w->mOutString.GetSize(); buff.mZipFlags = 0; + buff.mCRC32 = crc32(0, (const Bytef*)w->mOutString.GetString(), buff.mSize); uint8_t *compressbuf = new uint8_t[buff.mSize+1]; @@ -731,6 +732,7 @@ FCompressedBuffer FSerializer::GetCompressedOutput() buff.mMethod = METHOD_DEFLATE; memcpy(buff.mBuffer, compressbuf, buff.mCompressedSize); delete[] compressbuf; + return buff; } error: diff --git a/src/serializer.h b/src/serializer.h index 4d910aec6..312d53393 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -58,7 +58,7 @@ public: { Close(); } - bool OpenWriter(); + bool OpenWriter(bool randomaccess = true); bool OpenReader(const char *buffer, size_t length); bool OpenReader(FCompressedBuffer *input); void Close(); diff --git a/src/statistics.cpp b/src/statistics.cpp index 536d60a44..92a459e15 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -527,6 +527,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, OneLevel &l, OneLevel ("levelname", l.Levelname) .EndObject(); } + return arc; } void STAT_Serialize(FSerializer &arc) diff --git a/src/version.h b/src/version.h index f20bdfa5b..439a21a83 100644 --- a/src/version.h +++ b/src/version.h @@ -81,10 +81,6 @@ const char *GetVersionString(); // SVN revision ever got. #define SAVEVER 4550 -#define SAVEVERSTRINGIFY2(x) #x -#define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) -#define SAVESIG "ZDOOMSAVE" SAVEVERSTRINGIFY(SAVEVER) - // This is so that derivates can use the same savegame versions without worrying about engine compatibility #define GAMESIG "ZDOOM" #define BASEWAD "zdoom.pk3" From 88c27e2cc011873f1ae726b5b1507c29a4270faa Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 21 Sep 2016 21:57:24 +0200 Subject: [PATCH 30/59] - allow the compact and pretty writers for JSON to coexist by wrapping the whole stuff into another class that calls the proper one as needed. Due to the implementation it is not possible to decide at run time how this should behave so there have to be two different objects for either mode. - savegame code handles new format. --- src/g_game.cpp | 25 ++- src/g_game.h | 4 +- src/g_level.cpp | 4 +- src/json.cpp | 30 --- src/m_png.cpp | 20 +- src/m_png.h | 4 +- src/menu/loadsavemenu.cpp | 321 ++++++++++++++++------------- src/resourcefiles/resourcefile.cpp | 22 +- src/resourcefiles/resourcefile.h | 3 +- src/serializer.cpp | 265 ++++++++++++++++-------- src/serializer.h | 6 +- src/textures/pngtexture.cpp | 11 +- 12 files changed, 423 insertions(+), 292 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index 7ed832e0a..aee960345 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -117,6 +117,7 @@ void STAT_Serialize(FSerializer &file); bool WriteZip(const char *filename, TArray &filenames, TArray &content); FIntCVar gameskill ("skill", 2, CVAR_SERVERINFO|CVAR_LATCH); +CVAR(Bool, save_formatted, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // use formatted JSON for saves (more readable but a larger files and a bit slower. CVAR (Int, deathmatch, 0, CVAR_SERVERINFO|CVAR_LATCH); CVAR (Bool, chasedemo, false, 0); CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -1778,7 +1779,7 @@ void G_LoadGame (const char* name, bool hidecon) } } -static bool CheckSingleWad (char *name, bool &printRequires, bool printwarn) +static bool CheckSingleWad (const char *name, bool &printRequires, bool printwarn) { if (name == NULL) { @@ -1798,22 +1799,20 @@ static bool CheckSingleWad (char *name, bool &printRequires, bool printwarn) } } printRequires = true; - delete[] name; return false; } - delete[] name; return true; } // Return false if not all the needed wads have been loaded. -bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn) +bool G_CheckSaveGameWads (FSerializer &arc, bool printwarn) { - char *text; bool printRequires = false; + FString text; - text = M_GetPNGText (png, "Game WAD"); + arc("Game WAD", text); CheckSingleWad (text, printRequires, printwarn); - text = M_GetPNGText (png, "Map WAD"); + arc("Map WAD", text); CheckSingleWad (text, printRequires, printwarn); if (printRequires) @@ -2195,8 +2194,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio 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(); - savegameglobals.OpenWriter(); + savegameinfo.OpenWriter(true); + savegameglobals.OpenWriter(save_formatted); SaveVersion = SAVEVER; PutSavePic(&savepic, SAVEPICWIDTH, SAVEPICHEIGHT); @@ -2267,16 +2266,16 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio delete[] savegame_content[1].mBuffer; delete[] savegame_content[2].mBuffer; - // Check whether the file is ok. (todo when new format is ready) - bool success = true; - if (success) + // Check whether the file is ok by trying to open it. + FResourceFile *test = FResourceFile::OpenResourceFile(filename, nullptr, true); + if (test != nullptr) { + delete test; if (longsavemessages) Printf ("%s (%s)\n", GStrings("GGSAVED"), filename.GetChars()); else Printf ("%s\n", GStrings("GGSAVED")); } else Printf(PRINT_HIGH, "Save failed\n"); - FResourceFile *test = FResourceFile::OpenResourceFile(filename, nullptr); BackupSaveName = filename; diff --git a/src/g_game.h b/src/g_game.h index bf4099854..0b0d094f5 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -68,8 +68,8 @@ void G_ScreenShot (char *filename); FString G_BuildSaveName (const char *prefix, int slot); -struct PNGHandle; -bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn); +class FSerializer; +bool G_CheckSaveGameWads (FSerializer &arc, bool printwarn); enum EFinishLevelType { diff --git a/src/g_level.cpp b/src/g_level.cpp index 2acb1335c..e795f4a34 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -94,7 +94,7 @@ void STAT_StartNewGame(const char *lev); void STAT_ChangeLevel(const char *newl); - +EXTERN_CVAR(Bool, save_formatted) EXTERN_CVAR (Float, sv_gravity) EXTERN_CVAR (Float, sv_aircontrol) EXTERN_CVAR (Int, disableautosave) @@ -1489,7 +1489,7 @@ void G_SnapshotLevel () { FSerializer arc; - if (arc.OpenWriter()) + if (arc.OpenWriter(save_formatted)) { SaveVersion = SAVEVER; G_SerializeLevel(arc, false); diff --git a/src/json.cpp b/src/json.cpp index 982d9fb79..1bdc5d90e 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -37,33 +37,3 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) { } } - - - - - - -void G_SerializeLevel(FSerializer &arc, bool); - -CCMD(writejson) -{ - DWORD t = I_MSTime(); - FSerializer arc; - arc.OpenWriter(); - G_SerializeLevel(arc, false); - arc.WriteObjects(); - DWORD tt = I_MSTime(); - Printf("JSON generation took %d ms\n", tt - t); - FILE *f = fopen("out.json", "wb"); - unsigned siz; - const char *str = arc.GetOutput(&siz); - fwrite(str, 1, siz, f); - fclose(f); - /* - DWORD ttt = I_MSTime(); - Printf("JSON save took %d ms\n", ttt - tt); - FDocument doc(arc.w->mOutString.GetString(), arc.w->mOutString.GetSize()); - DWORD tttt = I_MSTime(); - Printf("JSON parse took %d ms\n", tttt - ttt); - */ -} diff --git a/src/m_png.cpp b/src/m_png.cpp index 35cd8587e..b122212a5 100644 --- a/src/m_png.cpp +++ b/src/m_png.cpp @@ -80,7 +80,7 @@ PNGHandle::PNGHandle (FILE *file) : File(0), bDeleteFilePtr(true), ChunkPt(0) File = new FileReader(file); } -PNGHandle::PNGHandle (FileReader *file) : File(file), bDeleteFilePtr(false), ChunkPt(0) {} +PNGHandle::PNGHandle (FileReader *file, bool takereader) : File(file), bDeleteFilePtr(takereader), ChunkPt(0) {} PNGHandle::~PNGHandle () { for (unsigned int i = 0; i < TextChunks.Size(); ++i) @@ -374,15 +374,14 @@ bool M_GetPNGText (PNGHandle *png, const char *keyword, char *buffer, size_t buf // //========================================================================== -PNGHandle *M_VerifyPNG (FILE *file) +PNGHandle *M_VerifyPNG (FileReader *filer, bool takereader) { PNGHandle::Chunk chunk; - FileReader *filer; PNGHandle *png; DWORD data[2]; bool sawIDAT = false; - if (fread (&data, 1, 8, file) != 8) + if (filer->Read(&data, 8) != 8) { return NULL; } @@ -390,7 +389,7 @@ PNGHandle *M_VerifyPNG (FILE *file) { // Does not have PNG signature return NULL; } - if (fread (&data, 1, 8, file) != 8) + if (filer->Read (&data, 8) != 8) { return NULL; } @@ -400,8 +399,7 @@ PNGHandle *M_VerifyPNG (FILE *file) } // It looks like a PNG so far, so start creating a PNGHandle for it - png = new PNGHandle (file); - filer = png->File; + png = new PNGHandle (filer, takereader); chunk.ID = data[1]; chunk.Offset = 16; chunk.Size = BigLong((unsigned int)data[0]); @@ -430,7 +428,7 @@ PNGHandle *M_VerifyPNG (FILE *file) sawIDAT = true; } chunk.ID = data[1]; - chunk.Offset = ftell (file); + chunk.Offset = filer->Tell(); chunk.Size = BigLong((unsigned int)data[0]); png->Chunks.Push (chunk); @@ -454,6 +452,12 @@ PNGHandle *M_VerifyPNG (FILE *file) return NULL; } +PNGHandle *M_VerifyPNG(FILE *file) +{ + FileReader *fr = new FileReader(file); + return M_VerifyPNG(fr, true); +} + //========================================================================== // // M_FreePNG diff --git a/src/m_png.h b/src/m_png.h index ef25d70d4..9fb9eeeff 100644 --- a/src/m_png.h +++ b/src/m_png.h @@ -80,7 +80,7 @@ struct PNGHandle unsigned int ChunkPt; PNGHandle(FILE *file); - PNGHandle(FileReader *file); + PNGHandle(FileReader *file, bool takereader = false); ~PNGHandle(); }; @@ -88,7 +88,7 @@ struct PNGHandle // the signature, but also checking for the IEND chunk. CRC checking of // each chunk is not done. If it is valid, you get a PNGHandle to pass to // the following functions. -PNGHandle *M_VerifyPNG (FileReader *file); +PNGHandle *M_VerifyPNG (FileReader *file, bool takereader = false); PNGHandle *M_VerifyPNG (FILE *file); // Finds a chunk in a PNG file. The file pointer will be positioned at the diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 6333ab3bf..91492e88d 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -46,6 +46,8 @@ #include "doomstat.h" #include "gi.h" #include "d_gui.h" +#include "serializer.h" +#include "resourcefiles/resourcefile.h" @@ -86,6 +88,10 @@ protected: int commentRight; int commentBottom; + // this needs to be kept in memory so that the texture can access it when it needs to. + FileReader *currentSavePic; + TArray SavePicData; + static int InsertSaveNode (FSaveGameNode *node); static void ReadSaveStrings (); @@ -221,103 +227,135 @@ void DLoadSaveMenu::ReadSaveStrings () { // I_FindName only returns the file's name and not its full path FString filepath = G_BuildSaveName (I_FindName(&c_file), -1); - FILE *file = fopen (filepath, "rb"); - if (file != NULL) + FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, nullptr, true, true); + if (savegame != nullptr) { - PNGHandle *png; - char sig[16]; - char title[SAVESTRINGSIZE+1]; - bool oldVer = true; - bool addIt = false; + bool oldVer = false; bool missing = false; - - // ZDoom 1.23 betas 21-33 have the savesig first. - // Earlier versions have the savesig second. - // Later versions have the savegame encapsulated inside a PNG. - // - // Old savegame versions are always added to the menu so - // the user can easily delete them if desired. - - title[SAVESTRINGSIZE] = 0; - - if (false)//NULL != (png = M_VerifyPNG (file))) + FResourceLump *info = savegame->FindLump("info.json"); + if (info == nullptr) { - char *ver = M_GetPNGText (png, "ZDoom Save Version"); - char *engine = M_GetPNGText (png, "Engine"); - if (ver != NULL) - { - if (!M_GetPNGText (png, "Title", title, SAVESTRINGSIZE)) - { - strncpy (title, I_FindName(&c_file), SAVESTRINGSIZE); - } - if (strncmp (ver, "SAVESIG", 9) == 0 && - atoi (ver+9) >= MINSAVEVER && - engine != NULL) - { - // Was saved with a compatible ZDoom version, - // so check if it's for the current game. - // If it is, add it. Otherwise, ignore it. - char *iwad = M_GetPNGText (png, "Game WAD"); - if (iwad != NULL) - { - if (stricmp (iwad, Wads.GetWadName (FWadCollection::IWAD_FILENUM)) == 0) - { - addIt = true; - oldVer = false; - missing = !G_CheckSaveGameWads (png, false); - } - delete[] iwad; - } - } - else - { // An old version - addIt = true; - } - delete[] ver; - } - if (engine != NULL) - { - delete[] engine; - } - delete png; + // savegame info not found. This is not a savegame so leave it alone. + delete savegame; + continue; } - else + void *data = info->CacheLump(); + FSerializer arc; + if (arc.OpenReader((const char *)data, info->LumpSize, true)) { - fseek (file, 0, SEEK_SET); - if (fread (sig, 1, 16, file) == 16) + int savever = 0; + FString engine; + FString iwad; + FString title; + + arc("Save Version", savever); + arc("Engine", engine); + arc("Game WAD", iwad); + arc("Title", title); + + if (engine.Compare(GAMESIG) != 0 || savever > SAVEVER) { - - if (strncmp (sig, "ZDOOMSAVE", 9) == 0) - { - if (fread (title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) - { - addIt = true; - } - } - else - { - memcpy (title, sig, 16); - if (fread (title + 16, 1, SAVESTRINGSIZE-16, file) == SAVESTRINGSIZE-16 && - fread (sig, 1, 16, file) == 16 && - strncmp (sig, "ZDOOMSAVE", 9) == 0) - { - addIt = true; - } - } + // different engine or newer version: + // not our business. Leave it alone. + delete savegame; + continue; + } + + if (savever < MINSAVEVER) + { + // old, incompatible savegame. List as not usable. + oldVer = true; + } + else if (iwad.CompareNoCase(Wads.GetWadName(FWadCollection::IWAD_FILENUM)) == 0) + { + missing = !G_CheckSaveGameWads(arc, false); } - } - if (addIt) - { FSaveGameNode *node = new FSaveGameNode; node->Filename = filepath; node->bOldVersion = oldVer; node->bMissingWads = missing; - memcpy (node->Title, title, SAVESTRINGSIZE); - InsertSaveNode (node); + strncpy(node->Title, title.GetChars(), SAVESTRINGSIZE); + InsertSaveNode(node); + delete savegame; + } + + } + else // check for old formats. + { + FILE *file = fopen (filepath, "rb"); + if (file != NULL) + { + PNGHandle *png; + char sig[16]; + char title[SAVESTRINGSIZE+1]; + bool oldVer = true; + bool addIt = false; + bool missing = false; + + // ZDoom 1.23 betas 21-33 have the savesig first. + // Earlier versions have the savesig second. + // Later versions have the savegame encapsulated inside a PNG. + // + // Old savegame versions are always added to the menu so + // the user can easily delete them if desired. + + title[SAVESTRINGSIZE] = 0; + + + if (NULL != (png = M_VerifyPNG (file))) + { + char *ver = M_GetPNGText (png, "ZDoom Save Version"); + if (ver != NULL) + { + // An old version + if (!M_GetPNGText(png, "Title", title, SAVESTRINGSIZE)) + { + strncpy(title, I_FindName(&c_file), SAVESTRINGSIZE); + } + addIt = true; + delete[] ver; + } + delete png; + } + else + { + fseek (file, 0, SEEK_SET); + if (fread (sig, 1, 16, file) == 16) + { + + if (strncmp (sig, "ZDOOMSAVE", 9) == 0) + { + if (fread (title, 1, SAVESTRINGSIZE, file) == SAVESTRINGSIZE) + { + addIt = true; + } + } + else + { + memcpy (title, sig, 16); + if (fread (title + 16, 1, SAVESTRINGSIZE-16, file) == SAVESTRINGSIZE-16 && + fread (sig, 1, 16, file) == 16 && + strncmp (sig, "ZDOOMSAVE", 9) == 0) + { + addIt = true; + } + } + } + } + + if (addIt) + { + FSaveGameNode *node = new FSaveGameNode; + node->Filename = filepath; + node->bOldVersion = true; + node->bMissingWads = false; + memcpy (node->Title, title, SAVESTRINGSIZE); + InsertSaveNode (node); + } + fclose (file); } - fclose (file); } } while (I_FindNext (filefirst, &c_file) == 0); I_FindClose (filefirst); @@ -407,6 +445,7 @@ DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, FListMenuDescriptor *desc) listboxHeight = listboxRows * rowHeight + 1; listboxRight = listboxLeft + listboxWidth; listboxBottom = listboxTop + listboxHeight; + currentSavePic = nullptr; commentLeft = savepicLeft; commentTop = savepicTop + savepicHeight + 16; @@ -418,6 +457,8 @@ DLoadSaveMenu::DLoadSaveMenu(DMenu *parent, FListMenuDescriptor *desc) void DLoadSaveMenu::Destroy() { + if (currentSavePic != nullptr) delete currentSavePic; + currentSavePic = nullptr; ClearSaveStuff (); } @@ -429,17 +470,23 @@ void DLoadSaveMenu::Destroy() void DLoadSaveMenu::UnloadSaveData () { - if (SavePic != NULL) + if (SavePic != nullptr) { delete SavePic; } - if (SaveComment != NULL) + if (SaveComment != nullptr) { V_FreeBrokenLines (SaveComment); } + if (currentSavePic != nullptr) + { + delete currentSavePic; + } - SavePic = NULL; - SaveComment = NULL; + SavePic = nullptr; + SaveComment = nullptr; + currentSavePic = nullptr; + SavePicData.Clear(); } //============================================================================= @@ -465,8 +512,7 @@ void DLoadSaveMenu::ClearSaveStuff () void DLoadSaveMenu::ExtractSaveData (int index) { - FILE *file; - PNGHandle *png; + FResourceFile *resf; FSaveGameNode *node; UnloadSaveData (); @@ -475,66 +521,61 @@ void DLoadSaveMenu::ExtractSaveData (int index) (node = SaveGames[index]) && !node->Filename.IsEmpty() && !node->bOldVersion && - (file = fopen (node->Filename.GetChars(), "rb")) != NULL) + (resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), nullptr, true)) != nullptr) { - if (NULL != (png = M_VerifyPNG (file))) + FResourceLump *info = resf->FindLump("info.json"); + if (info == nullptr) { - char *time, *pcomment, *comment; - size_t commentlen, totallen, timelen; + // this should not happen because the file has already been verified. + return; + } + void *data = info->CacheLump(); + FSerializer arc; + if (arc.OpenReader((const char *)data, info->LumpSize, true)) + { + FString time, pcomment, comment; - // Extract comment - time = M_GetPNGText (png, "Creation Time"); - pcomment = M_GetPNGText (png, "Comment"); - if (pcomment != NULL) - { - commentlen = strlen (pcomment); - } - else - { - commentlen = 0; - } - if (time != NULL) - { - timelen = strlen (time); - totallen = timelen + commentlen + 3; - } - else - { - timelen = 0; - totallen = commentlen + 1; - } - if (totallen != 0) - { - comment = new char[totallen]; + arc("Creation Time", time); + arc("Comment", pcomment); - if (timelen) - { - memcpy (comment, time, timelen); - comment[timelen] = '\n'; - comment[timelen+1] = '\n'; - timelen += 2; - } - if (commentlen) - { - memcpy (comment + timelen, pcomment, commentlen); - } - comment[timelen+commentlen] = 0; - SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, comment); - delete[] comment; - delete[] time; - delete[] pcomment; - } + comment = time; + if (time.Len() > 0) comment += "\n\n"; + comment += pcomment; + + SaveComment = V_BreakLines (SmallFont, 216*screen->GetWidth()/640/CleanXfac, comment.GetChars()); // Extract pic - SavePic = PNGTexture_CreateFromFile(png, node->Filename); - delete png; - if (SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1) + FResourceLump *pic = resf->FindLump("savepic.png"); + if (pic != nullptr) { - delete SavePic; - SavePic = NULL; + FileReader *reader = pic->NewReader(); + if (reader != nullptr) + { + // copy to a memory buffer which gets accessed through a memory reader and PNGHandle. + // We cannot use the actual lump as backing for the texture because that requires keeping the + // savegame file open. + SavePicData.Resize(pic->LumpSize); + reader->Read(&SavePicData[0], pic->LumpSize); + reader = new MemoryReader(&SavePicData[0], SavePicData.Size()); + PNGHandle *png = M_VerifyPNG(reader); + if (png != nullptr) + { + SavePic = PNGTexture_CreateFromFile(png, node->Filename); + currentSavePic = reader; // must be kept so that the texture can read from it. + delete png; + if (SavePic->GetWidth() == 1 && SavePic->GetHeight() == 1) + { + delete SavePic; + SavePic = nullptr; + delete currentSavePic; + currentSavePic = nullptr; + SavePicData.Clear(); + } + } + } } } - fclose (file); + delete resf; } } diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index c66430a66..2daf4aee1 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -270,7 +270,7 @@ FResourceFile *CheckDir(const char *filename, FileReader *file, bool quiet); static CheckFunc funcs[] = { CheckWad, CheckZip, Check7Z, CheckPak, CheckGRP, CheckRFF, CheckLump }; -FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader *file, bool quiet) +FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader *file, bool quiet, bool containeronly) { if (file == NULL) { @@ -283,7 +283,7 @@ FResourceFile *FResourceFile::OpenResourceFile(const char *filename, FileReader return NULL; } } - for(size_t i = 0; i < countof(funcs); i++) + for(size_t i = 0; i < countof(funcs) - containeronly; i++) { FResourceFile *resfile = funcs[i](filename, file, quiet); if (resfile != NULL) return resfile; @@ -560,6 +560,24 @@ void FResourceFile::FindStrifeTeaserVoices () { } +//========================================================================== +// +// Finds a lump by a given name. Used for savegames +// +//========================================================================== + +FResourceLump *FResourceFile::FindLump(const char *name) +{ + for (unsigned i = 0; i < NumLumps; i++) + { + FResourceLump *lump = GetLump(i); + if (!stricmp(name, lump->FullName)) + { + return lump; + } + } + return nullptr; +} //========================================================================== // diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index b96075f5d..b79c2cc59 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -77,7 +77,7 @@ private: void JunkLeftoverFilters(void *lumps, size_t lumpsize, DWORD max); public: - static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false); + static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false, bool containeronly = false); static FResourceFile *OpenDirectory(const char *filename, bool quiet = false); virtual ~FResourceFile(); FileReader *GetReader() const { return Reader; } @@ -88,6 +88,7 @@ public: virtual void FindStrifeTeaserVoices (); virtual bool Open(bool quiet) = 0; virtual FResourceLump *GetLump(int no) = 0; + FResourceLump *FindLump(const char *name); }; struct FUncompressedLump : public FResourceLump diff --git a/src/serializer.cpp b/src/serializer.cpp index ea18a3a58..33b90524f 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -3,10 +3,6 @@ #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 #define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseFullPrecisionFlag -#ifdef _DEBUG -#define PRETTY -#endif - #include "rapidjson/rapidjson.h" #include "rapidjson/writer.h" #include "rapidjson/prettywriter.h" @@ -69,26 +65,126 @@ struct FJSONObject struct FWriter { - // Since this is done by template parameters, we'd have to template the entire serializer to allow this switch at run time. Argh! -#ifndef PRETTY typedef rapidjson::Writer > Writer; -#else - typedef rapidjson::PrettyWriter > Writer; -#endif + typedef rapidjson::PrettyWriter > PrettyWriter; - Writer mWriter; + Writer *mWriter1; + PrettyWriter *mWriter2; TArray mInObject; rapidjson::StringBuffer mOutString; TArray mDObjects; TMap mObjectMap; - FWriter() : mWriter(mOutString) - {} + 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 String(const char *k) + { + if (mWriter1) mWriter1->String(k); + else if (mWriter2) mWriter2->String(k); + } + + void String(const char *k, int size) + { + if (mWriter1) mWriter1->String(k, size); + else if (mWriter2) mWriter2->String(k, size); + } + + 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); + } + }; //========================================================================== @@ -100,14 +196,13 @@ struct FWriter struct FReader { TArray mObjects; - rapidjson::Value mDocObj; // just because RapidJSON is stupid and does not allow direct access to what's in the document. + rapidjson::Document mDoc; - FReader(const char *buffer, size_t length) + FReader(const char *buffer, size_t length, bool randomaccess) { rapidjson::Document doc; - doc.Parse(buffer, length); - mDocObj = doc.GetObject(); - mObjects.Push(FJSONObject(&mDocObj)); // Todo: Decide if this should be made random access... + mDoc.Parse(buffer, length); + mObjects.Push(FJSONObject(&mDoc, randomaccess)); } rapidjson::Value *FindKey(const char *key) @@ -149,12 +244,12 @@ struct FReader // //========================================================================== -bool FSerializer::OpenWriter(bool randomaccess) +bool FSerializer::OpenWriter(bool pretty) { if (w != nullptr || r != nullptr) return false; - w = new FWriter; + w = new FWriter(pretty); - BeginObject(nullptr, randomaccess); + BeginObject(nullptr); return true; } @@ -164,10 +259,10 @@ bool FSerializer::OpenWriter(bool randomaccess) // //========================================================================== -bool FSerializer::OpenReader(const char *buffer, size_t length) +bool FSerializer::OpenReader(const char *buffer, size_t length, bool randomaccess) { if (w != nullptr || r != nullptr) return false; - r = new FReader(buffer, length); + r = new FReader(buffer, length, randomaccess); return true; } @@ -177,21 +272,21 @@ bool FSerializer::OpenReader(const char *buffer, size_t length) // //========================================================================== -bool FSerializer::OpenReader(FCompressedBuffer *input) +bool FSerializer::OpenReader(FCompressedBuffer *input, bool randomaccess) { if (input->mSize <= 0 || input->mBuffer == nullptr) return false; if (w != nullptr || r != nullptr) return false; if (input->mMethod == METHOD_STORED) { - r = new FReader((char*)input->mBuffer, input->mSize); + r = new FReader((char*)input->mBuffer, input->mSize, randomaccess); return true; } else { char *unpacked = new char[input->mSize]; input->Decompress(unpacked); - r = new FReader(unpacked, input->mSize); + r = new FReader(unpacked, input->mSize, randomaccess); return true; } } @@ -260,7 +355,7 @@ void FSerializer::WriteKey(const char *key) { I_Error("missing element name"); } - w->mWriter.Key(key); + w->Key(key); } } @@ -275,7 +370,7 @@ bool FSerializer::BeginObject(const char *name, bool randomaccess) if (isWriting()) { WriteKey(name); - w->mWriter.StartObject(); + w->StartObject(); w->mInObject.Push(true); } else @@ -312,7 +407,7 @@ void FSerializer::EndObject() { if (w->inObject()) { - w->mWriter.EndObject(); + w->EndObject(); w->mInObject.Pop(); } else @@ -337,7 +432,7 @@ bool FSerializer::BeginArray(const char *name) if (isWriting()) { WriteKey(name); - w->mWriter.StartArray(); + w->StartArray(); w->mInObject.Push(false); } else @@ -374,7 +469,7 @@ void FSerializer::EndArray() { if (!w->inObject()) { - w->mWriter.EndArray(); + w->EndArray(); w->mInObject.Pop(); } else @@ -404,19 +499,19 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe } WriteKey(key); - w->mWriter.StartArray(); + w->StartArray(); for (int i = 0; i < 5; i++) { if (i == 0 && args[i] < 0 && P_IsACSSpecial(special)) { - w->mWriter.String(FName(ENamedName(-args[i])).GetChars()); + w->String(FName(ENamedName(-args[i])).GetChars()); } else { - w->mWriter.Int(args[i]); + w->Int(args[i]); } } - w->mWriter.EndArray(); + w->EndArray(); } else { @@ -465,11 +560,11 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num) WriteKey(key); if (num < 0) { - w->mWriter.String(FName(ENamedName(-num)).GetChars()); + w->String(FName(ENamedName(-num)).GetChars()); } else { - w->mWriter.Int(num); + w->Int(num); } } else @@ -527,7 +622,7 @@ FSerializer &FSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *d { if (w->inObject() && def != nullptr && *def == spritenum) return *this; WriteKey(key); - w->mWriter.String(sprites[spritenum].name, 4); + w->String(sprites[spritenum].name, 4); } else { @@ -562,7 +657,7 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) if (isWriting()) { WriteKey(key); - w->mWriter.String(charptr); + w->String(charptr); } else { @@ -593,7 +688,7 @@ FSerializer &FSerializer::AddString(const char *key, const char *charptr) if (isWriting()) { WriteKey(key); - w->mWriter.String(charptr); + w->String(charptr); } return *this; } @@ -608,7 +703,7 @@ unsigned FSerializer::GetSize(const char *group) { if (isWriting()) return -1; // we do not know this when writing. - const rapidjson::Value &val = r->mDocObj[group]; + const rapidjson::Value &val = r->mDoc[group]; if (!val.IsArray()) return -1; return val.Size(); } @@ -645,15 +740,15 @@ void FSerializer::WriteObjects() player_t *player; BeginObject(nullptr); - w->mWriter.Key("classtype"); - w->mWriter.String(obj->GetClass()->TypeName.GetChars()); + w->Key("classtype"); + w->String(obj->GetClass()->TypeName.GetChars()); if (obj->IsKindOf(RUNTIME_CLASS(AActor)) && (player = static_cast(obj)->player) && player->mo == obj) { - w->mWriter.Key("playerindex"); - w->mWriter.Int(int(player - players)); + w->Key("playerindex"); + w->Int(int(player - players)); } obj->SerializeUserVars(*this); @@ -755,7 +850,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *def if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Bool(value); + arc.w->Bool(value); } } else @@ -789,7 +884,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_ if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Int64(value); + arc.w->Int64(value); } } else @@ -823,7 +918,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint6 if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Uint64(value); + arc.w->Uint64(value); } } else @@ -858,7 +953,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_ if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Int(value); + arc.w->Int(value); } } else @@ -892,7 +987,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3 if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Uint(value); + arc.w->Uint(value); } } else @@ -968,7 +1063,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.Double(value); + arc.w->Double(value); } } else @@ -1077,14 +1172,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe if (!value.Exists()) { arc.WriteKey(key); - arc.w->mWriter.Null(); + arc.w->Null(); return arc; } if (value.isNull()) { // save 'no texture' in a more space saving way arc.WriteKey(key); - arc.w->mWriter.Int(0); + arc.w->Int(0); return arc; } FTextureID chk = value; @@ -1101,10 +1196,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe name = pic->Name; } arc.WriteKey(key); - arc.w->mWriter.StartArray(); - arc.w->mWriter.String(name); - arc.w->mWriter.Int(pic->UseType); - arc.w->mWriter.EndArray(); + arc.w->StartArray(); + arc.w->String(name); + arc.w->Int(pic->UseType); + arc.w->EndArray(); } } else @@ -1215,7 +1310,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d if (!arc.w->inObject() || defval == nullptr || value != *defval) { arc.WriteKey(key); - arc.w->mWriter.String(value.GetChars()); + arc.w->String(value.GetChars()); } } else @@ -1252,11 +1347,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicCol } arc.WriteKey(key); - arc.w->mWriter.StartArray(); - arc.w->mWriter.Uint(cm->Color); - arc.w->mWriter.Uint(cm->Fade); - arc.w->mWriter.Uint(cm->Desaturate); - arc.w->mWriter.EndArray(); + arc.w->StartArray(); + arc.w->Uint(cm->Color); + arc.w->Uint(cm->Fade); + arc.w->Uint(cm->Desaturate); + arc.w->EndArray(); } else { @@ -1300,8 +1395,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI { arc.WriteKey(key); const char *sn = (const char*)sid; - if (sn != nullptr) arc.w->mWriter.String(sn); - else arc.w->mWriter.Null(); + if (sn != nullptr) arc.w->String(sn); + else arc.w->Null(); } } else @@ -1342,11 +1437,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor arc.WriteKey(key); if (clst == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { - arc.w->mWriter.String(clst->TypeName.GetChars()); + arc.w->String(clst->TypeName.GetChars()); } } } @@ -1388,11 +1483,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl arc.WriteKey(key); if (clst == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { - arc.w->mWriter.String(clst->TypeName.GetChars()); + arc.w->String(clst->TypeName.GetChars()); } } } @@ -1436,7 +1531,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState arc.WriteKey(key); if (state == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { @@ -1444,14 +1539,14 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState if (info != NULL) { - arc.w->mWriter.StartArray(); - arc.w->mWriter.String(info->TypeName.GetChars()); - arc.w->mWriter.Uint((uint32_t)(state - info->OwnedStates)); - arc.w->mWriter.EndArray(); + arc.w->StartArray(); + arc.w->String(info->TypeName.GetChars()); + arc.w->Uint((uint32_t)(state - info->OwnedStates)); + arc.w->EndArray(); } else { - arc.w->mWriter.Null(); + arc.w->Null(); } } } @@ -1507,11 +1602,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial arc.WriteKey(key); if (node == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { - arc.w->mWriter.Uint(node->ThisNodeNum); + arc.w->Uint(node->ThisNodeNum); } } } @@ -1560,11 +1655,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p arc.WriteKey(key); if (pstr == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { - arc.w->mWriter.String(pstr->GetChars()); + arc.w->String(pstr->GetChars()); } } } @@ -1604,7 +1699,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString if (!arc.w->inObject() || def == nullptr || pstr.Compare(*def) != 0) { arc.WriteKey(key); - arc.w->mWriter.String(pstr.GetChars()); + arc.w->String(pstr.GetChars()); } } else @@ -1645,11 +1740,11 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr arc.WriteKey(key); if (pstr == nullptr) { - arc.w->mWriter.Null(); + arc.w->Null(); } else { - arc.w->mWriter.String(pstr); + arc.w->String(pstr); } } } @@ -1719,16 +1814,16 @@ FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &value, N switch (value.type) { case NumericValue::NM_signed: - arc.w->mWriter.Int64(value.signedval); + arc.w->Int64(value.signedval); break; case NumericValue::NM_unsigned: - arc.w->mWriter.Uint64(value.unsignedval); + arc.w->Uint64(value.unsignedval); break; case NumericValue::NM_float: - arc.w->mWriter.Double(value.floatval); + arc.w->Double(value.floatval); break; default: - arc.w->mWriter.Null(); + arc.w->Null(); break; } } diff --git a/src/serializer.h b/src/serializer.h index 312d53393..a5a7fda52 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -58,9 +58,9 @@ public: { Close(); } - bool OpenWriter(bool randomaccess = true); - bool OpenReader(const char *buffer, size_t length); - bool OpenReader(FCompressedBuffer *input); + bool OpenWriter(bool pretty = true); + bool OpenReader(const char *buffer, size_t length, bool randomaccess = false); + bool OpenReader(FCompressedBuffer *input, bool randomaccess = false); void Close(); bool BeginObject(const char *name, bool randomaccess = false); void EndObject(); diff --git a/src/textures/pngtexture.cpp b/src/textures/pngtexture.cpp index e47fa62c0..d34b00607 100644 --- a/src/textures/pngtexture.cpp +++ b/src/textures/pngtexture.cpp @@ -66,6 +66,7 @@ protected: FString SourceFile; BYTE *Pixels; Span **Spans; + FileReader *fr; BYTE BitDepth; BYTE ColorType; @@ -209,6 +210,8 @@ FPNGTexture::FPNGTexture (FileReader &lump, int lumpnum, const FString &filename DWORD len, id; int i; + if (lumpnum == -1) fr = &lump; + UseType = TEX_MiscPatch; LeftOffset = 0; TopOffset = 0; @@ -462,7 +465,7 @@ void FPNGTexture::MakeTexture () } else { - lump = new FileReader(SourceFile.GetChars()); + lump = fr;// new FileReader(SourceFile.GetChars()); } Pixels = new BYTE[Width*Height]; @@ -599,7 +602,7 @@ void FPNGTexture::MakeTexture () delete[] tempix; } } - delete lump; + if (lump != fr) delete lump; } //=========================================================================== @@ -624,7 +627,7 @@ int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCo } else { - lump = new FileReader(SourceFile.GetChars()); + lump = fr;// new FileReader(SourceFile.GetChars()); } lump->Seek(33, SEEK_SET); @@ -683,7 +686,7 @@ int FPNGTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCo lump->Read(&len, 4); lump->Read(&id, 4); M_ReadIDAT (lump, Pixels, Width, Height, pixwidth, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); - delete lump; + if (lump != fr) delete lump; switch (ColorType) { From 1c9dbc3c360eea7d664b7de7fa6369ea78bff023 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 00:18:31 +0200 Subject: [PATCH 31/59] - converted the basic savegame loader so that all remnant of FArchive have been removed now. --- src/g_doom/a_doommisc.cpp | 1 - src/g_game.cpp | 132 +++++++++++++++++++------------------ src/g_level.cpp | 93 +++++++++++--------------- src/g_level.h | 1 + src/m_random.cpp | 50 +++++--------- src/m_random.h | 2 +- src/p_acs.cpp | 135 +++++++++++++++----------------------- src/p_acs.h | 4 +- src/p_saveg.h | 2 +- src/serializer.cpp | 4 +- src/serializer.h | 2 + 11 files changed, 187 insertions(+), 239 deletions(-) diff --git a/src/g_doom/a_doommisc.cpp b/src/g_doom/a_doommisc.cpp index c281173af..129c32f1e 100644 --- a/src/g_doom/a_doommisc.cpp +++ b/src/g_doom/a_doommisc.cpp @@ -14,7 +14,6 @@ #include "a_specialspot.h" #include "templates.h" #include "m_bbox.h" -#include "farchive.h" #include "portal.h" #include "d_player.h" #include "p_maputl.h" diff --git a/src/g_game.cpp b/src/g_game.cpp index aee960345..6eeb40219 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1830,10 +1830,6 @@ bool G_CheckSaveGameWads (FSerializer &arc, bool printwarn) void G_DoLoadGame () { -#if 0 // SAVEGAME - char sigcheck[20]; - char *text = NULL; - char *map; bool hidecon; if (gameaction != ga_autoloadgame) @@ -1843,76 +1839,82 @@ void G_DoLoadGame () hidecon = gameaction == ga_loadgamehidecon; gameaction = ga_nothing; - FILE *stdfile = fopen (savename.GetChars(), "rb"); - if (stdfile == NULL) + FResourceFile *resfile = FResourceFile::OpenResourceFile(savename.GetChars(), nullptr, true, true); + if (resfile == nullptr) { Printf ("Could not read savegame '%s'\n", savename.GetChars()); return; } - PNGHandle *png = M_VerifyPNG (stdfile); - if (png == NULL) + FResourceLump *info = resfile->FindLump("info.json"); + if (info == nullptr) { - fclose (stdfile); - Printf ("'%s' is not a valid (PNG) savegame\n", savename.GetChars()); + delete resfile; + Printf ("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars()); return; } SaveVersion = 0; + void *data = info->CacheLump(); + FSerializer arc; + if (!arc.OpenReader((const char *)data, info->LumpSize, true)) + { + Printf("Failed to access savegame info\n"); + delete resfile; + return; + } + // Check whether this savegame actually has been created by a compatible engine. // Since there are ZDoom derivates using the exact same savegame format but // with mutual incompatibilities this check simplifies things significantly. - char *engine = M_GetPNGText (png, "Engine"); - if (engine == NULL || 0 != strcmp (engine, GAMESIG)) + FString savever, engine, map; + arc("Save Version", SaveVersion); + arc("Engine", engine); + arc("Current Map", map); + + if (engine.CompareNoCase(GAMESIG) != 0) { // Make a special case for the message printed for old savegames that don't // have this information. - if (engine == NULL) + if (engine.IsEmpty()) { Printf ("Savegame is from an incompatible version\n"); } else { Printf ("Savegame is from another ZDoom-based engine: %s\n", engine); - delete[] engine; } - delete png; - fclose (stdfile); + delete resfile; return; } - if (engine != NULL) - { - delete[] engine; - } - SaveVersion = 0; - if (!M_GetPNGText (png, "ZDoom Save Version", sigcheck, 20) || - 0 != strncmp (sigcheck, "SAVEVER", 9) || // ZDOOMSAVE is the first 9 chars - (SaveVersion = atoi (sigcheck+9)) < MINSAVEVER) + if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER) { - delete png; - fclose (stdfile); + delete resfile; Printf ("Savegame is from an incompatible version"); - if (SaveVersion != 0) + if (SaveVersion < MINSAVEVER) { Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER); } + else + { + Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER); + } Printf("\n"); return; } - if (!G_CheckSaveGameWads (png, true)) + if (!G_CheckSaveGameWads (arc, true)) { - fclose (stdfile); + delete resfile; return; } - map = M_GetPNGText (png, "Current Map"); - if (map == NULL) + if (map.IsEmpty()) { Printf ("Savegame is missing the current map\n"); - fclose (stdfile); + delete resfile; return; } @@ -1922,35 +1924,46 @@ void G_DoLoadGame () { gamestate = GS_HIDECONSOLE; } + // we are done with info.json. + arc.Close(); + + info = resfile->FindLump("global.json"); + if (info == nullptr) + { + delete resfile; + Printf("'%s' is not a valid savegame: Missing 'global.json'.\n", savename.GetChars()); + return; + } + + data = info->CacheLump(); + if (!arc.OpenReader((const char *)data, info->LumpSize, true)) + { + Printf("Failed to access savegame info\n"); + delete resfile; + return; + } + // Read intermission data for hubs - G_ReadHubInfo(png); + G_SerializeHub(arc); bglobal.RemoveAllBots (true); - text = M_GetPNGText (png, "Important CVARs"); - if (text != NULL) + arc("importantcvars", map); + if (!map.IsEmpty()) { - BYTE *vars_p = (BYTE *)text; + BYTE *vars_p = (BYTE *)map.GetChars(); C_ReadCVars (&vars_p); - delete[] text; } + DWORD time[2] = { 0,1 }; + + arc("ticrate", time[0]) + ("leveltime", time[1]); // dearchive all the modifications - if (M_FindPNGChunk (png, MAKE_ID('p','t','I','c')) == 8) - { - DWORD time[2]; - fread (&time, 8, 1, stdfile); - time[0] = BigLong((unsigned int)time[0]); - time[1] = BigLong((unsigned int)time[1]); - level.time = Scale (time[1], TICRATE, time[0]); - } - else - { // No ptIc chunk so we don't know how long the user was playing - level.time = 0; - } + level.time = Scale (time[1], TICRATE, time[0]); - G_ReadSnapshots (png); + //G_ReadSnapshots(png); // load a base level savegamerestore = true; // Use the player actors in the savegame @@ -1960,31 +1973,24 @@ void G_DoLoadGame () delete[] map; savegamerestore = false; - STAT_Read(png); - FRandom::StaticReadRNGState(png); - P_ReadACSDefereds(png); - P_ReadACSVars(png); + STAT_Serialize(arc); + FRandom::StaticReadRNGState(arc); + P_ReadACSDefereds(arc); + P_ReadACSVars(arc); NextSkill = -1; - if (M_FindPNGChunk (png, MAKE_ID('s','n','X','t')) == 1) - { - BYTE next; - fread (&next, 1, 1, stdfile); - NextSkill = next; - } + arc("nextskill", NextSkill); level.info->Snapshot.Clean(); BackupSaveName = savename; - delete png; - fclose (stdfile); + delete resfile; // At this point, the GC threshold is likely a lot higher than the // amount of memory in use, so bring it down now by starting a // collection. GC::StartCollection(); -#endif } diff --git a/src/g_level.cpp b/src/g_level.cpp index e795f4a34..76b67a9bb 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1630,83 +1630,71 @@ void G_WriteVisited(FSerializer &arc) // //========================================================================== -void G_ReadSnapshots (PNGHandle *png) +void G_ReadSnapshots(PNGHandle *png) { DWORD chunkLen; FString MapName; level_info_t *i; - G_ClearSnapshots (); + G_ClearSnapshots(); - chunkLen = (DWORD)M_FindPNGChunk (png, SNAP_ID); + chunkLen = (DWORD)M_FindPNGChunk(png, SNAP_ID); while (chunkLen != 0) { - FPNGChunkArchive arc (png->File->GetFile(), SNAP_ID, chunkLen); +#if 0 + FPNGChunkArchive arc(png->File->GetFile(), SNAP_ID, chunkLen); DWORD snapver; arc << snapver; arc << MapName; - i = FindLevelInfo (MapName); -#if 0 + i = FindLevelInfo(MapName); i->snapshot = new FCompressedMemFile; - i->snapshot->Serialize (arc); + i->snapshot->Serialize(arc); + chunkLen = (DWORD)M_NextPNGChunk(png, SNAP_ID); #endif - chunkLen = (DWORD)M_NextPNGChunk (png, SNAP_ID); } - chunkLen = (DWORD)M_FindPNGChunk (png, DSNP_ID); + chunkLen = (DWORD)M_FindPNGChunk(png, DSNP_ID); if (chunkLen != 0) { - FPNGChunkArchive arc (png->File->GetFile(), DSNP_ID, chunkLen); +#if 0 + FPNGChunkArchive arc(png->File->GetFile(), DSNP_ID, chunkLen); DWORD snapver; arc << snapver; arc << MapName; -#if 0 TheDefaultLevelInfo.snapshot = new FCompressedMemFile; - TheDefaultLevelInfo.snapshot->Serialize (arc); + TheDefaultLevelInfo.snapshot->Serialize(arc); #endif } +} - chunkLen = (DWORD)M_FindPNGChunk (png, VIST_ID); - if (chunkLen != 0) +void G_ReadVisited(FSerializer &arc) +{ + if (arc.BeginArray("visited")) { - FPNGChunkArchive arc (png->File->GetFile(), VIST_ID, chunkLen); - - while (arc << MapName, MapName.Len() > 0) + for (int s = arc.ArraySize(); s > 0; s--) { - i = FindLevelInfo(MapName); - i->flags |= LEVEL_VISITED; + FString str; + arc(nullptr, str); + auto i = FindLevelInfo(str); + if (i != nullptr) i->flags |= LEVEL_VISITED; } + arc.EndArray(); } - chunkLen = (DWORD)M_FindPNGChunk (png, RCLS_ID); - if (chunkLen != 0) + arc.Array("randomclasses", SinglePlayerClass, MAXPLAYERS); + + if (arc.BeginObject("playerclasses")) { - FPNGChunkArchive arc (png->File->GetFile(), PCLS_ID, chunkLen); - SBYTE cnum; - - for (DWORD j = 0; j < chunkLen; ++j) + for (int i = 0; i < MAXPLAYERS; ++i) { - arc << cnum; - SinglePlayerClass[j] = cnum; + FString key; + key.Format("%d", i); + arc(key, players[i].cls); } + arc.EndObject(); } - - chunkLen = (DWORD)M_FindPNGChunk (png, PCLS_ID); - if (chunkLen != 0) - { - FPNGChunkArchive arc (png->File->GetFile(), RCLS_ID, chunkLen); - BYTE pnum; - - arc << pnum; - while (pnum != 255) - { - arc.UserReadClass (players[pnum].cls); - arc << pnum; - } - } - png->File->ResetFilePtr(); } //========================================================================== @@ -1749,8 +1737,7 @@ void P_WriteACSDefereds (FSerializer &arc) { if (wi.deferred.Size() > 0 && arc.BeginObject(nullptr)) { - arc(wi.MapName, wi.deferred) - .EndObject(); + arc(wi.MapName, wi.deferred); } } } @@ -1763,30 +1750,28 @@ void P_WriteACSDefereds (FSerializer &arc) // //========================================================================== -void P_ReadACSDefereds (PNGHandle *png) +void P_ReadACSDefereds (FSerializer &arc) { -#if 0 FString MapName; size_t chunklen; P_RemoveDefereds (); - if ((chunklen = M_FindPNGChunk (png, ACSD_ID)) != 0) + if (arc.BeginObject("deferred")) { - FPNGChunkArchive arc (png->File->GetFile(), ACSD_ID, chunklen); + const char *key; - while (arc << MapName, MapName.Len() > 0) + while ((key = arc.GetKey())) { - level_info_t *i = FindLevelInfo(MapName); + level_info_t *i = FindLevelInfo(key); if (i == NULL) { - I_Error("Unknown map '%s' in savegame", MapName.GetChars()); + I_Error("Unknown map '%s' in savegame", key); } - arc << i->deferred; + arc(key, i->deferred); } + arc.EndObject(); } - png->File->ResetFilePtr(); -#endif } diff --git a/src/g_level.h b/src/g_level.h index 4e86978ce..55a6647ab 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -536,6 +536,7 @@ struct PNGHandle; void G_ReadSnapshots (PNGHandle *png); void G_WriteSnapshots (TArray &, TArray &); void G_WriteVisited(FSerializer &arc); +void G_ReadVisited(FSerializer &arc); void G_ClearHubInfo(); enum ESkillProperty diff --git a/src/m_random.cpp b/src/m_random.cpp index cc63fd5ed..ec6200a7d 100644 --- a/src/m_random.cpp +++ b/src/m_random.cpp @@ -326,54 +326,36 @@ void FRandom::StaticWriteRNGState (FSerializer &arc) // //========================================================================== -void FRandom::StaticReadRNGState (PNGHandle *png) +void FRandom::StaticReadRNGState(FSerializer &arc) { -#if 0 FRandom *rng; - size_t len = M_FindPNGChunk (png, RAND_ID); - - if (len != 0) + arc("rngseed", rng); + if (arc.BeginArray("rngs")) { - const size_t sizeof_rng = sizeof(rng->NameCRC) + sizeof(rng->idx) + sizeof(rng->sfmt.u); - const int rngcount = (int)((len-4) / sizeof_rng); - int i; - DWORD crc; + int count = arc.ArraySize(); - FPNGChunkArchive arc (png->File->GetFile(), RAND_ID, len); - - arc << rngseed; - FRandom::StaticClearRandom (); - - for (i = rngcount; i; --i) + for (int i = 0; i < count; i++) { - arc << crc; - for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) + if (arc.BeginObject(nullptr)) { - if (rng->NameCRC == crc) + uint32_t crc; + arc("crc", crc); + + for (rng = FRandom::RNGList; rng != NULL; rng = rng->Next) { - arc << rng->idx; - for (int i = 0; i < SFMT::N32; ++i) + if (rng->NameCRC == crc) { - arc << rng->sfmt.u[i]; + arc("index", rng->idx) + .Array("u", rng->sfmt.u, SFMT::N32); + break; } - break; - } - } - if (rng == NULL) - { // The RNG was removed. Skip it. - int idx; - DWORD sfmt; - arc << idx; - for (int i = 0; i < SFMT::N32; ++i) - { - arc << sfmt; } + arc.EndObject(); } + arc.EndArray(); } - png->File->ResetFilePtr(); } -#endif } //========================================================================== diff --git a/src/m_random.h b/src/m_random.h index 141585925..d8c94cb9f 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -175,7 +175,7 @@ public: // Static interface static void StaticClearRandom (); static DWORD StaticSumSeeds (); - static void StaticReadRNGState (PNGHandle *png); + static void StaticReadRNGState (FSerializer &arc); static void StaticWriteRNGState (FSerializer &file); static FRandom *StaticFindRNG(const char *name); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index c5ef8e2ee..59373237f 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -722,47 +722,42 @@ void ACSStringPool::FindFirstFreeEntry(unsigned base) // //============================================================================ -void ACSStringPool::ReadStrings(PNGHandle *png, DWORD id) +void ACSStringPool::ReadStrings(FSerializer &file, const char *key) { Clear(); - size_t len = M_FindPNGChunk(png, id); - if (len != 0) + int32_t i, j, poolsize; + + file("poolsize", poolsize); + Pool.Resize(poolsize); + for (auto &p : Pool) { - FPNGChunkArchive arc(png->File->GetFile(), id, len); - int32_t i, j, poolsize; - unsigned int h, bucketnum; - char *str = NULL; - - arc << poolsize; - - Pool.Resize(poolsize); - i = 0; - j = arc.ReadCount(); - while (j >= 0) + p.Next = FREE_ENTRY; + p.LockCount = 0; + } + if (file.BeginArray("pool")) + { + j = file.ArraySize(); + for (int i = 0; i < j; i++) { - // Mark skipped entries as free - for (; i < j; ++i) + if (file.BeginObject(nullptr)) { - Pool[i].Next = FREE_ENTRY; - Pool[i].LockCount = 0; + i = -1; + file("index", i); + if (i >= 0 && i < Pool.Size()) + { + file("string", Pool[i].Str) + ("lockcount", Pool[i].LockCount); + + unsigned h = SuperFastHash(Pool[i].Str, Pool[i].Str.Len()); + unsigned bucketnum = h % NUM_BUCKETS; + Pool[i].Hash = h; + Pool[i].Next = PoolBuckets[bucketnum]; + PoolBuckets[bucketnum] = i; + } + file.EndObject(); } - arc << str; - h = SuperFastHash(str, strlen(str)); - bucketnum = h % NUM_BUCKETS; - Pool[i].Str = str; - Pool[i].Hash = h; - Pool[i].LockCount = arc.ReadCount(); - Pool[i].Next = PoolBuckets[bucketnum]; - PoolBuckets[bucketnum] = i; - i++; - j = arc.ReadCount(); } - if (str != NULL) - { - delete[] str; - } - FindFirstFreeEntry(0); } } @@ -982,29 +977,10 @@ static void WriteVars (FSerializer &file, SDWORD *vars, size_t count, const char // //============================================================================ -static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id) +static void ReadVars (FSerializer &arc, SDWORD *vars, size_t count, const char *key) { - size_t len = M_FindPNGChunk (png, id); - size_t used = 0; - - if (len != 0) - { - DWORD var; - size_t i; - FPNGChunkArchive arc (png->File->GetFile(), id, len); - used = len / 4; - - for (i = 0; i < used; ++i) - { - arc << var; - vars[i] = var; - } - png->File->ResetFilePtr(); - } - if (used < count) - { - memset (&vars[used], 0, (count-used)*4); - } + memset(&vars[0], 0, count * 4); + arc.Array(key, vars, count); } //============================================================================ @@ -1060,37 +1036,32 @@ static void WriteArrayVars (FSerializer &file, FWorldGlobalArray *vars, unsigned // //============================================================================ -static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count, DWORD id) +static void ReadArrayVars (FSerializer &file, FWorldGlobalArray *vars, size_t count, const char *key) { - size_t len = M_FindPNGChunk (png, id); - unsigned int i, k; - for (i = 0; i < count; ++i) { - vars[i].Clear (); + vars[i].Clear(); } - if (len != 0) + if (file.BeginObject(key)) { - DWORD max, size; - FPNGChunkArchive arc (png->File->GetFile(), id, len); - - i = arc.ReadCount (); - max = arc.ReadCount (); - - for (; i <= max; ++i) + const char *arraykey; + while ((arraykey = file.GetKey())) { - size = arc.ReadCount (); - for (k = 0; k < size; ++k) + int i = (int)strtol(arraykey, nullptr, 10); + if (file.BeginObject(arraykey)) { - SDWORD key, val; - key = arc.ReadCount(); - - val = arc.ReadCount(); - vars[i].Insert (key, val); + while ((arraykey = file.GetKey())) + { + int k = (int)strtol(arraykey, nullptr, 10); + int val; + file(arraykey, val); + vars[i].Insert(k, val); + } + file.EndObject(); } } - png->File->ResetFilePtr(); + file.EndObject(); } } @@ -1100,13 +1071,13 @@ static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count // //============================================================================ -void P_ReadACSVars(PNGHandle *png) +void P_ReadACSVars(FSerializer &arc) { - ReadVars (png, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); - ReadVars (png, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); - ReadArrayVars (png, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); - ReadArrayVars (png, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); - GlobalACSStrings.ReadStrings(png, MAKE_ID('a','s','T','r')); + ReadVars (arc, ACS_WorldVars, NUM_WORLDVARS, "acsworldvars"); + ReadVars (arc, ACS_GlobalVars, NUM_GLOBALVARS, "acsglobalvars"); + ReadArrayVars (arc, ACS_WorldArrays, NUM_WORLDVARS, "acsworldarrays"); + ReadArrayVars (arc, ACS_GlobalArrays, NUM_GLOBALVARS, "acsglobalarrays"); + GlobalACSStrings.ReadStrings(arc, "acsglobalstrings"); } //============================================================================ diff --git a/src/p_acs.h b/src/p_acs.h index 5ff048e8b..9cd209297 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -96,7 +96,7 @@ public: void PurgeStrings(); void Clear(); void Dump() const; - void ReadStrings(PNGHandle *png, DWORD id); + void ReadStrings(FSerializer &file, const char *key); void WriteStrings(FSerializer &file, const char *key) const; private: @@ -122,7 +122,7 @@ private: extern ACSStringPool GlobalACSStrings; void P_CollectACSGlobalStrings(); -void P_ReadACSVars(PNGHandle *); +void P_ReadACSVars(FSerializer &); void P_WriteACSVars(FSerializer &); void P_ClearACSVars(bool); diff --git a/src/p_saveg.h b/src/p_saveg.h index f09ca48da..05d7e3fd4 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -42,7 +42,7 @@ class FSerializer; // Also see farchive.(h|cpp) void P_DestroyThinkers(bool hubLoad); -void P_ReadACSDefereds (PNGHandle *png); +void P_ReadACSDefereds (FSerializer &); void P_WriteACSDefereds (FSerializer &); void G_SerializeLevel(FSerializer &arc, bool hubLoad); diff --git a/src/serializer.cpp b/src/serializer.cpp index 33b90524f..301e284a9 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -730,7 +730,7 @@ const char *FSerializer::GetKey() void FSerializer::WriteObjects() { - if (isWriting()) + if (isWriting() && w->mDObjects.Size()) { BeginArray("objects"); // we cannot use the C++11 shorthand syntax here because the array may grow while being processed. @@ -769,6 +769,7 @@ void FSerializer::WriteObjects() const char *FSerializer::GetOutput(unsigned *len) { if (isReading()) return nullptr; + WriteObjects(); EndObject(); if (len != nullptr) { @@ -787,6 +788,7 @@ FCompressedBuffer FSerializer::GetCompressedOutput() { if (isReading()) return{ 0,0,0,0,0,nullptr }; FCompressedBuffer buff; + WriteObjects(); EndObject(); buff.mSize = (unsigned)w->mOutString.GetSize(); buff.mZipFlags = 0; diff --git a/src/serializer.h b/src/serializer.h index a5a7fda52..1592d41e0 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -111,6 +111,8 @@ public: if (BeginArray(key)) { + int max = ArraySize(); + if (max < count) count = max; for (int i = 0; i < count; i++) { Serialize(*this, nullptr, obj[i], (T*)nullptr); From f93e4813d1785d820cad8c59f66477aa1ce1881b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 00:48:22 +0200 Subject: [PATCH 32/59] - removed farchive.cpp and .h --- src/CMakeLists.txt | 1 - src/dobject.cpp | 1 - src/dthinker.cpp | 1 - src/farchive.cpp | 987 -------------------------------- src/farchive.h | 253 -------- src/g_game.cpp | 1 - src/g_hexen/a_hexenmisc.cpp | 1 - src/g_level.cpp | 1 - src/g_mapinfo.cpp | 1 - src/g_raven/a_minotaur.cpp | 1 - src/g_shared/a_armor.cpp | 1 - src/g_shared/a_pickups.cpp | 1 - src/g_shared/a_puzzleitems.cpp | 1 - src/g_shared/a_skies.cpp | 2 +- src/g_shared/a_weapons.cpp | 1 - src/m_random.cpp | 2 +- src/p_acs.cpp | 25 +- src/p_mobj.cpp | 1 - src/p_pspr.cpp | 1 - src/p_saveg.cpp | 1 - src/p_states.cpp | 1 - src/p_user.cpp | 1 - src/portal.cpp | 1 - src/r_data/renderstyle.cpp | 1 - src/r_data/renderstyle.h | 13 +- src/r_main.cpp | 1 - src/textures/anim_switches.cpp | 1 - src/textures/animations.cpp | 1 - src/textures/texturemanager.cpp | 2 +- src/v_blend.cpp | 1 - src/v_font.cpp | 1 - 31 files changed, 22 insertions(+), 1286 deletions(-) delete mode 100644 src/farchive.cpp delete mode 100644 src/farchive.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94f7c9ab3..d6878a19e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1038,7 +1038,6 @@ set (PCH_SOURCES dthinker.cpp edata.cpp f_wipe.cpp - farchive.cpp files.cpp g_doomedmap.cpp g_game.cpp diff --git a/src/dobject.cpp b/src/dobject.cpp index aa874d747..b5af56151 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -47,7 +47,6 @@ #include "stats.h" #include "a_sharedglobal.h" #include "dsectoreffect.h" -#include "farchive.h" #include "serializer.h" //========================================================================== diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 0e57f05cf..3cb40cb8f 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -38,7 +38,6 @@ #include "statnums.h" #include "i_system.h" #include "doomerrors.h" -#include "farchive.h" #include "serializer.h" #include "d_player.h" diff --git a/src/farchive.cpp b/src/farchive.cpp deleted file mode 100644 index 3d57e4f71..000000000 --- a/src/farchive.cpp +++ /dev/null @@ -1,987 +0,0 @@ -/* -** farchive.cpp -** Implements an archiver for DObject serialization. -** -**--------------------------------------------------------------------------- -** Copyright 1998-2009 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -** The structure of the archive file generated is influenced heavily by the -** description of the MFC archive format published somewhere in the MSDN -** library. -** -** Two major shortcomings of the format I use are that there is no version -** control and no support for storing the non-default portions of objects. -** The latter would allow for easier extension of objects in future -** releases even without a versioning system. -*/ - -#include -#include -#include -#include - -#include "doomtype.h" -#include "farchive.h" -#include "m_swap.h" -#include "m_crc32.h" -#include "cmdlib.h" -#include "i_system.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "d_player.h" -#include "m_misc.h" -#include "dobject.h" - -// These are special tokens found in the data stream of an archive. -// Whenever a new object is encountered, it gets created using new and -// is then asked to serialize itself before processing of the previous -// object continues. This can result in some very deep recursion if -// you aren't careful about how you organize your data. - -#define NEW_OBJ ((BYTE)1) // Data for a new object follows -#define NEW_CLS_OBJ ((BYTE)2) // Data for a new class and object follows -#define OLD_OBJ ((BYTE)3) // Reference to an old object follows -#define NULL_OBJ ((BYTE)4) // Load as NULL -#define M1_OBJ ((BYTE)44) // Load as (DObject*)-1 - -#define NEW_PLYR_OBJ ((BYTE)5) // Data for a new player follows -#define NEW_PLYR_CLS_OBJ ((BYTE)6) // Data for a new class and player follows - -#define NEW_NAME ((BYTE)27) // A new name follows -#define OLD_NAME ((BYTE)28) // Reference to an old name follows -#define NIL_NAME ((BYTE)33) // Load as NULL - -#define NEW_SPRITE ((BYTE)11) // A new sprite name follows -#define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows - -#ifdef __BIG_ENDIAN__ -static inline WORD SWAP_WORD(WORD x) { return x; } -static inline DWORD SWAP_DWORD(DWORD x) { return x; } -static inline QWORD SWAP_QWORD(QWORD x) { return x; } -static inline void SWAP_FLOAT(float x) { } -static inline void SWAP_DOUBLE(double &dst, double src) { dst = src; } -#else -#ifdef _MSC_VER -static inline WORD SWAP_WORD(WORD x) { return _byteswap_ushort(x); } -static inline DWORD SWAP_DWORD(DWORD x) { return _byteswap_ulong(x); } -static inline QWORD SWAP_QWORD(QWORD x) { return _byteswap_uint64(x); } -static inline void SWAP_DOUBLE(double &dst, double &src) -{ - union twiddle { QWORD q; double d; } tdst, tsrc; - tsrc.d = src; - tdst.q = _byteswap_uint64(tsrc.q); - dst = tdst.d; -} -#else -static inline WORD SWAP_WORD(WORD x) { return (((x)<<8) | ((x)>>8)); } -static inline DWORD SWAP_DWORD(DWORD x) { return x = (((x)>>24) | (((x)>>8)&0xff00) | (((x)<<8)&0xff0000) | ((x)<<24)); } -static inline QWORD SWAP_QWORD(QWORD x) -{ - union { QWORD q; DWORD d[2]; } t, u; - t.q = x; - u.d[0] = SWAP_DWORD(t.d[1]); - u.d[1] = SWAP_DWORD(t.d[0]); - return u.q; -} -static inline void SWAP_DOUBLE(double &dst, double &src) -{ - union twiddle { double f; DWORD d[2]; } tdst, tsrc; - DWORD t; - - tsrc.f = src; - t = tsrc.d[0]; - tdst.d[0] = SWAP_DWORD(tsrc.d[1]); - tdst.d[1] = SWAP_DWORD(t); - dst = tdst.f; -} -#endif -static inline void SWAP_FLOAT(float &x) -{ - union twiddle { DWORD i; float f; } t; - t.f = x; - t.i = SWAP_DWORD(t.i); - x = t.f; -} -#endif - -// Output buffer size for compression; need some extra space. -// I assume the description in zlib.h is accurate. -#define OUT_LEN(a) ((a) + (a) / 1000 + 12) - -void FCompressedFile::BeEmpty () -{ - m_Pos = 0; - m_BufferSize = 0; - m_MaxBufferSize = 0; - m_Buffer = NULL; - m_File = NULL; - m_NoCompress = false; - m_Mode = ENotOpen; -} - -static const char LZOSig[4] = { 'F', 'L', 'Z', 'O' }; -static const char ZSig[4] = { 'F', 'L', 'Z', 'L' }; - -FCompressedFile::FCompressedFile () -{ - BeEmpty (); -} - -FCompressedFile::FCompressedFile (const char *name, EOpenMode mode, bool dontCompress) -{ - BeEmpty (); - Open (name, mode); - m_NoCompress = dontCompress; -} - -FCompressedFile::FCompressedFile (FILE *file, EOpenMode mode, bool dontCompress, bool postopen) -{ - BeEmpty (); - m_Mode = mode; - m_File = file; - m_NoCompress = dontCompress; - if (postopen) - { - PostOpen (); - } -} - -FCompressedFile::~FCompressedFile () -{ - Close (); -} - -bool FCompressedFile::Open (const char *name, EOpenMode mode) -{ - Close (); - if (name == NULL) - return false; - m_Mode = mode; - m_File = fopen (name, mode == EReading ? "rb" : "wb"); - PostOpen (); - return !!m_File; -} - -void FCompressedFile::PostOpen () -{ - if (m_File && m_Mode == EReading) - { - char sig[4]; - fread (sig, 4, 1, m_File); - if (sig[0] != ZSig[0] || sig[1] != ZSig[1] || sig[2] != ZSig[2] || sig[3] != ZSig[3]) - { - fclose (m_File); - m_File = NULL; - if (sig[0] == LZOSig[0] && sig[1] == LZOSig[1] && sig[2] == LZOSig[2] && sig[3] == LZOSig[3]) - { - Printf ("Compressed files from older ZDooms are not supported.\n"); - } - return; - } - else - { - DWORD sizes[2]; - fread (sizes, sizeof(DWORD), 2, m_File); - sizes[0] = SWAP_DWORD (sizes[0]); - sizes[1] = SWAP_DWORD (sizes[1]); - unsigned int len = sizes[0] == 0 ? sizes[1] : sizes[0]; - m_Buffer = (BYTE *)M_Malloc (len+8); - fread (m_Buffer+8, len, 1, m_File); - sizes[0] = SWAP_DWORD (sizes[0]); - sizes[1] = SWAP_DWORD (sizes[1]); - ((DWORD *)m_Buffer)[0] = sizes[0]; - ((DWORD *)m_Buffer)[1] = sizes[1]; - Explode (); - } - } -} - -void FCompressedFile::Close () -{ - if (m_File) - { - if (m_Mode == EWriting) - { - Implode (); - fwrite (ZSig, 4, 1, m_File); - fwrite (m_Buffer, m_BufferSize + 8, 1, m_File); - } - fclose (m_File); - m_File = NULL; - } - if (m_Buffer) - { - M_Free (m_Buffer); - m_Buffer = NULL; - } - BeEmpty (); -} - -void FCompressedFile::Flush () -{ -} - -FFile::EOpenMode FCompressedFile::Mode () const -{ - return m_Mode; -} - -bool FCompressedFile::IsOpen () const -{ - return !!m_File; -} - -FFile &FCompressedFile::Write (const void *mem, unsigned int len) -{ - if (m_Mode == EWriting) - { - if (m_Pos + len > m_MaxBufferSize) - { - do - { - m_MaxBufferSize = m_MaxBufferSize ? m_MaxBufferSize * 2 : 16384; - } - while (m_Pos + len > m_MaxBufferSize); - m_Buffer = (BYTE *)M_Realloc (m_Buffer, m_MaxBufferSize); - } - if (len == 1) - m_Buffer[m_Pos] = *(BYTE *)mem; - else - memcpy (m_Buffer + m_Pos, mem, len); - m_Pos += len; - if (m_Pos > m_BufferSize) - m_BufferSize = m_Pos; - } - else - { - I_Error ("Tried to write to reading cfile"); - } - return *this; -} - -FFile &FCompressedFile::Read (void *mem, unsigned int len) -{ - if (m_Mode == EReading) - { - if (m_Pos + len > m_BufferSize) - { - I_Error ("Attempt to read past end of cfile"); - } - if (len == 1) - *(BYTE *)mem = m_Buffer[m_Pos]; - else - memcpy (mem, m_Buffer + m_Pos, len); - m_Pos += len; - } - else - { - I_Error ("Tried to read from writing cfile"); - } - return *this; -} - -unsigned int FCompressedFile::Tell () const -{ - return m_Pos; -} - -FFile &FCompressedFile::Seek (int pos, ESeekPos ofs) -{ - if (ofs == ESeekRelative) - pos += m_Pos; - else if (ofs == ESeekEnd) - pos = m_BufferSize - pos; - - if (pos < 0) - m_Pos = 0; - else if ((unsigned)pos > m_BufferSize) - m_Pos = m_BufferSize; - else - m_Pos = pos; - - return *this; -} - -CVAR (Bool, nofilecompression, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - -void FCompressedFile::Implode () -{ - uLong outlen; - uLong len = m_BufferSize; - Byte *compressed = NULL; - BYTE *oldbuf = m_Buffer; - int r; - - if (!nofilecompression && !m_NoCompress) - { - outlen = OUT_LEN(len); - do - { - compressed = new Bytef[outlen]; - r = compress (compressed, &outlen, m_Buffer, len); - if (r == Z_BUF_ERROR) - { - delete[] compressed; - outlen += 1024; - } - } while (r == Z_BUF_ERROR); - - // If the data could not be compressed, store it as-is. - if (r != Z_OK || outlen >= len) - { - DPrintf (DMSG_SPAMMY, "cfile could not be compressed\n"); - outlen = 0; - } - else - { - DPrintf (DMSG_SPAMMY, "cfile shrank from %lu to %lu bytes\n", len, outlen); - } - } - else - { - outlen = 0; - } - - m_MaxBufferSize = m_BufferSize = ((outlen == 0) ? len : outlen); - m_Buffer = (BYTE *)M_Malloc (m_BufferSize + 8); - m_Pos = 0; - - DWORD *lens = (DWORD *)(m_Buffer); - lens[0] = BigLong((unsigned int)outlen); - lens[1] = BigLong((unsigned int)len); - - if (outlen == 0) - memcpy (m_Buffer + 8, oldbuf, len); - else - memcpy (m_Buffer + 8, compressed, outlen); - if (compressed) - delete[] compressed; - M_Free (oldbuf); -} - -void FCompressedFile::Explode () -{ - uLong expandsize, cprlen; - unsigned char *expand; - - if (m_Buffer) - { - unsigned int *ints = (unsigned int *)(m_Buffer); - cprlen = BigLong(ints[0]); - expandsize = BigLong(ints[1]); - - expand = (unsigned char *)M_Malloc (expandsize); - if (cprlen) - { - int r; - uLong newlen; - - newlen = expandsize; - r = uncompress (expand, &newlen, m_Buffer + 8, cprlen); - if (r != Z_OK || newlen != expandsize) - { - M_Free (expand); - I_Error ("Could not decompress buffer: %s", M_ZLibError(r).GetChars()); - } - } - else - { - memcpy (expand, m_Buffer + 8, expandsize); - } - if (FreeOnExplode ()) - M_Free (m_Buffer); - m_Buffer = expand; - m_BufferSize = expandsize; - } -} - - -FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id) - : FCompressedFile (file, EWriting, true, false), m_ChunkID (id) -{ -} - -FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id, size_t chunklen) - : FCompressedFile (file, EReading, true, false), m_ChunkID (id) -{ - m_Buffer = (BYTE *)M_Malloc (chunklen); - m_BufferSize = (unsigned int)chunklen; - fread (m_Buffer, chunklen, 1, m_File); - // Skip the CRC for now. Maybe later it will be used. - fseek (m_File, 4, SEEK_CUR); -} - -// Unlike FCompressedFile::Close, m_File is left open -void FPNGChunkFile::Close () -{ - DWORD data[2]; - DWORD crc; - - if (m_File) - { - if (m_Mode == EWriting) - { - crc = CalcCRC32 ((BYTE *)&m_ChunkID, 4); - crc = AddCRC32 (crc, (BYTE *)m_Buffer, m_BufferSize); - - data[0] = BigLong(m_BufferSize); - data[1] = m_ChunkID; - fwrite (data, 8, 1, m_File); - fwrite (m_Buffer, m_BufferSize, 1, m_File); - crc = SWAP_DWORD (crc); - fwrite (&crc, 4, 1, m_File); - } - m_File = NULL; - } - FCompressedFile::Close (); -} - -FPNGChunkArchive::FPNGChunkArchive (FILE *file, DWORD id) - : FArchive (), Chunk (file, id) -{ - AttachToFile (Chunk); -} - -FPNGChunkArchive::FPNGChunkArchive (FILE *file, DWORD id, size_t len) - : FArchive (), Chunk (file, id, len) -{ - AttachToFile (Chunk); -} - -FPNGChunkArchive::~FPNGChunkArchive () -{ - // Close before FArchive's destructor, because Chunk will be - // destroyed before the FArchive is destroyed. - Close (); -} - -//============================================ -// -// FArchive -// -//============================================ - -FArchive::FArchive () -{ -} - -FArchive::FArchive (FFile &file) -{ - AttachToFile (file); -} - -void FArchive::AttachToFile (FFile &file) -{ - m_HubTravel = false; - m_File = &file; - if (file.Mode() == FFile::EReading) - { - m_Loading = true; - m_Storing = false; - } - else - { - m_Loading = false; - m_Storing = true; - } - m_Persistent = file.IsPersistent(); - - ClassToArchive.Clear(); - ArchiveToClass.Clear(); - - ObjectToArchive.Clear(); - ArchiveToObject.Clear(); - - memset(m_NameHash, 0xFF, sizeof(m_NameHash)); - m_Names.Clear(); - m_NameStorage.Clear(); - - m_NumSprites = 0; - m_SpriteMap = new int[sprites.Size()]; - for (size_t s = 0; s < sprites.Size(); ++s) - { - m_SpriteMap[s] = -1; - } -} - -FArchive::~FArchive () -{ - Close (); - if (m_SpriteMap) - delete[] m_SpriteMap; -} - -void FArchive::Write (const void *mem, unsigned int len) -{ - m_File->Write (mem, len); -} - -void FArchive::Read (void *mem, unsigned int len) -{ - m_File->Read (mem, len); -} - -void FArchive::Close () -{ - if (m_File) - { - m_File->Close (); - m_File = NULL; - DPrintf (DMSG_SPAMMY, "Processed %u objects\n", ArchiveToObject.Size()); - } -} - -void FArchive::WriteByte(BYTE val) -{ - m_File->Write(&val, 1); -} - -void FArchive::WriteInt16(WORD val) -{ - WORD out = SWAP_WORD(val); - m_File->Write(&out, 2); -} - -void FArchive::WriteInt32(DWORD val) -{ - int out = SWAP_DWORD(val); - m_File->Write(&out, 4); -} - -void FArchive::WriteInt64(QWORD val) -{ - long long out = SWAP_QWORD(val); - m_File->Write(&out, 8); -} - -void FArchive::WriteCount (DWORD count) -{ - BYTE out; - - do - { - out = count & 0x7f; - if (count >= 0x80) - out |= 0x80; - Write (&out, sizeof(BYTE)); - count >>= 7; - } while (count); - -} - -DWORD FArchive::ReadCount () -{ - BYTE in; - DWORD count = 0; - int ofs = 0; - - do - { - Read (&in, sizeof(BYTE)); - count |= (in & 0x7f) << ofs; - ofs += 7; - } while (in & 0x80); - - return count; -} - -void FArchive::WriteName (const char *name) -{ - BYTE id; - - if (name == NULL) - { - id = NIL_NAME; - Write (&id, 1); - } - else - { - DWORD index = FindName (name); - if (index != NameMap::NO_INDEX) - { - id = OLD_NAME; - Write (&id, 1); - WriteCount (index); - } - else - { - AddName (name); - id = NEW_NAME; - Write (&id, 1); - WriteString (name); - } - } -} - -const char *FArchive::ReadName () -{ - BYTE id; - - operator<< (id); - if (id == NIL_NAME) - { - return NULL; - } - else if (id == OLD_NAME) - { - DWORD index = ReadCount (); - if (index >= m_Names.Size()) - { - I_Error ("Name %u has not been read yet\n", index); - } - return &m_NameStorage[m_Names[index].StringStart]; - } - else if (id == NEW_NAME) - { - DWORD index; - DWORD size = ReadCount (); - char *str; - - index = (DWORD)m_NameStorage.Reserve (size); - str = &m_NameStorage[index]; - Read (str, size-1); - str[size-1] = 0; - AddName (index); - return str; - } - else - { - I_Error ("Expected a name but got something else\n"); - return NULL; - } -} - -void FArchive::WriteString (const char *str) -{ - if (str == NULL) - { - WriteCount (0); - } - else - { - DWORD size = (DWORD)(strlen (str) + 1); - WriteCount (size); - Write (str, size - 1); - } -} - -void FArchive::WriteString(const FString &str) -{ - // The count includes the '\0' terminator, but we don't - // actually write it out. - WriteCount(DWORD(str.Len() + 1)); - Write(str, DWORD(str.Len())); -} - -FArchive &FArchive::operator<< (char *&str) -{ - if (m_Storing) - { - WriteString (str); - } - else - { - DWORD size = ReadCount (); - char *str2; - - if (size == 0) - { - str2 = NULL; - } - else - { - str2 = new char[size]; - size--; - Read (str2, size); - str2[size] = 0; - ReplaceString ((char **)&str, str2); - } - if (str) - { - delete[] str; - } - str = str2; - } - return *this; -} - -FArchive &FArchive::operator<< (FString &str) -{ - if (m_Storing) - { - WriteString (str); - } - else - { - DWORD size = ReadCount(); - - if (size == 0) - { - str = ""; - } - else - { - char *str2 = (char *)alloca(size*sizeof(char)); - size--; - Read (str2, size); - str = FString(str2, size); - } - } - return *this; -} - -FArchive &FArchive::operator<< (BYTE &c) -{ - if (m_Storing) - Write (&c, sizeof(BYTE)); - else - Read (&c, sizeof(BYTE)); - return *this; -} - -FArchive &FArchive::operator<< (WORD &w) -{ - if (m_Storing) - { - WORD temp = SWAP_WORD(w); - Write (&temp, sizeof(WORD)); - } - else - { - Read (&w, sizeof(WORD)); - w = SWAP_WORD(w); - } - return *this; -} - -FArchive &FArchive::operator<< (DWORD &w) -{ - if (m_Storing) - { - DWORD temp = SWAP_DWORD(w); - Write (&temp, sizeof(DWORD)); - } - else - { - Read (&w, sizeof(DWORD)); - w = SWAP_DWORD(w); - } - return *this; -} - -FArchive &FArchive::operator<< (QWORD &w) -{ - if (m_Storing) - { - QWORD temp = SWAP_QWORD(w); - Write (&temp, sizeof(QWORD)); - } - else - { - Read (&w, sizeof(QWORD)); - w = SWAP_QWORD(w); - } - return *this; -} - -FArchive &FArchive::operator<< (float &w) -{ - if (m_Storing) - { - float temp = w; - SWAP_FLOAT(temp); - Write (&temp, sizeof(float)); - } - else - { - Read (&w, sizeof(float)); - SWAP_FLOAT(w); - } - return *this; -} - -FArchive &FArchive::operator<< (double &w) -{ - if (m_Storing) - { - double temp; - SWAP_DOUBLE(temp,w); - Write (&temp, sizeof(double)); - } - else - { - Read (&w, sizeof(double)); - SWAP_DOUBLE(w,w); - } - return *this; -} - -FArchive &FArchive::operator<< (FName &n) -{ // In an archive, a "name" is a string that might be stored multiple times, - // so it is only stored once. It is still treated as a normal string. In the - // rest of the game, a name is a unique identifier for a number. - if (m_Storing) - { - WriteName (n.GetChars()); - } - else - { - n = FName(ReadName()); - } - return *this; -} - -DWORD FArchive::AddName (const char *name) -{ - DWORD index; - unsigned int hash = MakeKey (name) % EObjectHashSize; - - index = FindName (name, hash); - if (index == NameMap::NO_INDEX) - { - DWORD namelen = (DWORD)(strlen (name) + 1); - DWORD strpos = (DWORD)m_NameStorage.Reserve (namelen); - NameMap mapper = { strpos, (DWORD)m_NameHash[hash] }; - - memcpy (&m_NameStorage[strpos], name, namelen); - m_NameHash[hash] = index = (DWORD)m_Names.Push (mapper); - } - return index; -} - -DWORD FArchive::AddName (unsigned int start) -{ - DWORD hash = MakeKey (&m_NameStorage[start]) % EObjectHashSize; - NameMap mapper = { (DWORD)start, (DWORD)m_NameHash[hash] }; - return (DWORD)(m_NameHash[hash] = m_Names.Push (mapper)); -} - -DWORD FArchive::FindName (const char *name) const -{ - return FindName (name, MakeKey (name) % EObjectHashSize); -} - -DWORD FArchive::FindName (const char *name, unsigned int bucket) const -{ - unsigned int map = m_NameHash[bucket]; - - while (map != NameMap::NO_INDEX) - { - const NameMap *mapping = &m_Names[map]; - if (strcmp (name, &m_NameStorage[mapping->StringStart]) == 0) - { - return (DWORD)map; - } - map = mapping->HashNext; - } - return (DWORD)map; -} - -DWORD FArchive::WriteClass (PClass *info) -{ - if (ClassToArchive.CheckKey(info) != NULL) - { - I_Error ("Attempt to write '%s' twice.\n", info->TypeName.GetChars()); - } - DWORD index = ArchiveToClass.Push(info); - ClassToArchive[info] = index; - WriteString (info->TypeName.GetChars()); - return index; -} - -PClass *FArchive::ReadClass () -{ - struct String { - String() { val = NULL; } - ~String() { if (val) delete[] val; } - char *val; - } typeName; - - operator<< (typeName.val); - FName zaname(typeName.val, true); - if (zaname != NAME_None) - { - PClass *type = PClass::FindClass(zaname); - if (type != NULL) - { - ClassToArchive[type] = ArchiveToClass.Push(type); - return type; - } - } - I_Error ("Unknown class '%s'\n", typeName.val); - return NULL; -} - -PClass *FArchive::ReadClass (const PClass *wanttype) -{ - PClass *type = ReadClass (); - if (!type->IsDescendantOf (wanttype)) - { - I_Error ("Expected to extract an object of type '%s'.\n" - "Found one of type '%s' instead.\n", - wanttype->TypeName.GetChars(), type->TypeName.GetChars()); - } - return type; -} - -PClass *FArchive::ReadStoredClass (const PClass *wanttype) -{ - DWORD index = ReadCount (); - PClass *type = ArchiveToClass[index]; - if (!type->IsDescendantOf (wanttype)) - { - I_Error ("Expected to extract an object of type '%s'.\n" - "Found one of type '%s' instead.\n", - wanttype->TypeName.GetChars(), type->TypeName.GetChars()); - } - return type; -} - - -void FArchive::UserReadClass (PClass *&type) -{ - BYTE newclass; - - Read (&newclass, 1); - switch (newclass) - { - case 0: - type = ReadStoredClass (RUNTIME_CLASS(DObject)); - break; - case 1: - type = ReadClass (); - break; - case 2: - type = NULL; - break; - default: - I_Error ("Unknown class type %d in archive.\n", newclass); - break; - } -} - diff --git a/src/farchive.h b/src/farchive.h deleted file mode 100644 index ee0310898..000000000 --- a/src/farchive.h +++ /dev/null @@ -1,253 +0,0 @@ -/* -** farchive.h -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -#ifndef __FARCHIVE_H__ -#define __FARCHIVE_H__ - -#include -#include "dobject.h" -#include "r_state.h" -#include "tflags.h" - -class FFile -{ -public: - enum EOpenMode - { - EReading, - EWriting, - ENotOpen - }; - - enum ESeekPos - { - ESeekSet, - ESeekRelative, - ESeekEnd - }; - -virtual ~FFile () {} - -virtual bool Open (const char *name, EOpenMode mode) = 0; -virtual void Close () = 0; -virtual void Flush () = 0; -virtual EOpenMode Mode () const = 0; -virtual bool IsPersistent () const = 0; -virtual bool IsOpen () const = 0; - -virtual FFile& Write (const void *, unsigned int) = 0; -virtual FFile& Read (void *, unsigned int) = 0; - -virtual unsigned int Tell () const = 0; -virtual FFile& Seek (int, ESeekPos) = 0; -inline FFile& Seek (unsigned int i, ESeekPos p) { return Seek ((int)i, p); } -}; - -class FCompressedFile : public FFile -{ -public: - FCompressedFile (); - FCompressedFile (const char *name, EOpenMode mode, bool dontcompress = false); - FCompressedFile (FILE *file, EOpenMode mode, bool dontcompress = false, bool postopen=true); - ~FCompressedFile (); - - bool Open (const char *name, EOpenMode mode); - void Close (); - void Flush (); - EOpenMode Mode () const; - bool IsPersistent () const { return true; } - bool IsOpen () const; - unsigned int GetSize () const { return m_BufferSize; } - - FFile &Write (const void *, unsigned int); - FFile &Read (void *, unsigned int); - unsigned int Tell () const; - FFile &Seek (int, ESeekPos); - -protected: - unsigned int m_Pos; - unsigned int m_BufferSize; - unsigned int m_MaxBufferSize; - unsigned char *m_Buffer; - bool m_NoCompress; - EOpenMode m_Mode; - FILE *m_File; - - void Implode (); - void Explode (); - virtual bool FreeOnExplode () { return true; } - void PostOpen (); - -private: - void BeEmpty (); -}; - -class FPNGChunkFile : public FCompressedFile -{ -public: - FPNGChunkFile (FILE *file, DWORD id); // Create for writing - FPNGChunkFile (FILE *file, DWORD id, size_t chunklen); // Create for reading - - void Close (); - -private: - DWORD m_ChunkID; -}; - -class FArchive -{ -public: - FArchive (FFile &file); - virtual ~FArchive (); - - inline bool IsLoading () const { return m_Loading; } - inline bool IsStoring () const { return m_Storing; } - inline bool IsPeristent () const { return m_Persistent; } - - void SetHubTravel () { m_HubTravel = true; } - - void Close (); - -virtual void Write (const void *mem, unsigned int len); -virtual void Read (void *mem, unsigned int len); - - void WriteString(const FString &str); - void WriteString (const char *str); - - void WriteByte(BYTE val); - void WriteInt16(WORD val); - void WriteInt32(DWORD val); - void WriteInt64(QWORD val); - - void WriteCount (DWORD count); - DWORD ReadCount (); - - void UserReadClass (PClass *&info); - template void UserReadClass(T *&info) - { - PClass *myclass; - UserReadClass(myclass); - info = dyn_cast(myclass); - } - - FArchive& operator<< (BYTE &c); - FArchive& operator<< (WORD &s); - FArchive& operator<< (DWORD &i); - FArchive& operator<< (QWORD &i); - FArchive& operator<< (QWORD_UNION &i) { return operator<< (i.AsOne); } - FArchive& operator<< (float &f); - FArchive& operator<< (double &d); - FArchive& operator<< (char *&str); - FArchive& operator<< (FName &n); - FArchive& operator<< (FString &str); - - void WriteName (const char *name); - const char *ReadName (); // The returned name disappears with the archive, unlike strings - - -inline FArchive& operator<< (SBYTE &c) { return operator<< ((BYTE &)c); } -inline FArchive& operator<< (SWORD &s) { return operator<< ((WORD &)s); } -inline FArchive& operator<< (SDWORD &i) { return operator<< ((DWORD &)i); } -inline FArchive& operator<< (SQWORD &i) { return operator<< ((QWORD &)i); } -inline FArchive& operator<< (unsigned char *&str) { return operator<< ((char *&)str); } -inline FArchive& operator<< (signed char *&str) { return operator<< ((char *&)str); } -inline FArchive& operator<< (bool &b) { return operator<< ((BYTE &)b); } - - void EnableThinkers() - { - m_ThinkersAllowed = true; - } - - bool ThinkersAllowed() - { - return m_ThinkersAllowed; - } - -protected: - enum { EObjectHashSize = 137 }; - - DWORD WriteClass (PClass *info); - PClass *ReadClass (); - PClass *ReadClass (const PClass *wanttype); - PClass *ReadStoredClass (const PClass *wanttype); - DWORD AddName (const char *name); - DWORD AddName (unsigned int start); // Name has already been added to storage - DWORD FindName (const char *name) const; - DWORD FindName (const char *name, unsigned int bucket) const; - - bool m_Persistent; // meant for persistent storage (disk)? - bool m_Loading; // extracting objects? - bool m_Storing; // inserting objects? - bool m_HubTravel; // travelling inside a hub? - FFile *m_File; // unerlying file object - - TMap ClassToArchive; // Maps PClass to archive type index - TArray ArchiveToClass; // Maps archive type index to PClass - - TMap ObjectToArchive; // Maps objects to archive index - TArray ArchiveToObject; // Maps archive index to objects - - struct NameMap - { - DWORD StringStart; // index into m_NameStorage - DWORD HashNext; // next in hash bucket - enum { NO_INDEX = 0xffffffff }; - }; - TArray m_Names; - TArray m_NameStorage; - unsigned int m_NameHash[EObjectHashSize]; - - int *m_SpriteMap; - size_t m_NumSprites; - bool m_ThinkersAllowed = false; - - FArchive (); - void AttachToFile (FFile &file); - -private: - FArchive (const FArchive &) {} - void operator= (const FArchive &) {} -}; - -// Create an FPNGChunkFile and FArchive in one step -class FPNGChunkArchive : public FArchive -{ -public: - FPNGChunkArchive (FILE *file, DWORD chunkid); - FPNGChunkArchive (FILE *file, DWORD chunkid, size_t chunklen); - ~FPNGChunkArchive (); - FPNGChunkFile Chunk; -}; - - -#endif //__FARCHIVE_H__ diff --git a/src/g_game.cpp b/src/g_game.cpp index 6eeb40219..f646b2bd1 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -78,7 +78,6 @@ #include "p_acs.h" #include "p_effect.h" #include "m_joy.h" -#include "farchive.h" #include "r_renderer.h" #include "r_utility.h" #include "a_morph.h" diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 122129ede..31a17c3a3 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -17,7 +17,6 @@ #include "p_terrain.h" #include "m_bbox.h" #include "ravenshared.h" -#include "farchive.h" #include "v_palette.h" #include "g_game.h" #include "p_blockmap.h" diff --git a/src/g_level.cpp b/src/g_level.cpp index 76b67a9bb..b5b0b6972 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -81,7 +81,6 @@ #include "a_sharedglobal.h" #include "a_strifeglobal.h" #include "r_data/colormaps.h" -#include "farchive.h" #include "r_renderer.h" #include "r_utility.h" #include "p_spec.h" diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 8f0e2da13..bc0470474 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -45,7 +45,6 @@ #include "i_system.h" #include "gi.h" #include "gstrings.h" -#include "farchive.h" #include "p_acs.h" #include "doomstat.h" #include "d_player.h" diff --git a/src/g_raven/a_minotaur.cpp b/src/g_raven/a_minotaur.cpp index 909b317e7..9f58ce4c3 100644 --- a/src/g_raven/a_minotaur.cpp +++ b/src/g_raven/a_minotaur.cpp @@ -11,7 +11,6 @@ #include "thingdef/thingdef.h" #include "g_level.h" #include "doomstat.h" -#include "farchive.h" #include "a_pickups.h" #include "d_player.h" #include "serializer.h" diff --git a/src/g_shared/a_armor.cpp b/src/g_shared/a_armor.cpp index e05b564f3..50472e51d 100644 --- a/src/g_shared/a_armor.cpp +++ b/src/g_shared/a_armor.cpp @@ -6,7 +6,6 @@ #include "templates.h" #include "g_level.h" #include "d_player.h" -#include "farchive.h" #include "serializer.h" diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 1ce531eea..e80e54818 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -18,7 +18,6 @@ #include "g_level.h" #include "g_game.h" #include "doomstat.h" -#include "farchive.h" #include "d_player.h" #include "p_spec.h" #include "serializer.h" diff --git a/src/g_shared/a_puzzleitems.cpp b/src/g_shared/a_puzzleitems.cpp index 512d83b49..233066edc 100644 --- a/src/g_shared/a_puzzleitems.cpp +++ b/src/g_shared/a_puzzleitems.cpp @@ -7,7 +7,6 @@ #include "c_console.h" #include "doomstat.h" #include "v_font.h" -#include "farchive.h" IMPLEMENT_CLASS(PClassPuzzleItem) diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 1da7cd712..72091e098 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -36,8 +36,8 @@ #include "a_sharedglobal.h" #include "p_local.h" #include "p_lnspec.h" -#include "farchive.h" #include "r_sky.h" +#include "r_state.h" #include "portal.h" // arg0 = Visibility*4 for this skybox diff --git a/src/g_shared/a_weapons.cpp b/src/g_shared/a_weapons.cpp index eb73b22f2..023d16ec6 100644 --- a/src/g_shared/a_weapons.cpp +++ b/src/g_shared/a_weapons.cpp @@ -17,7 +17,6 @@ #include "doomstat.h" #include "g_level.h" #include "d_net.h" -#include "farchive.h" #include "serializer.h" #define BONUSADD 6 diff --git a/src/m_random.cpp b/src/m_random.cpp index ec6200a7d..11075916e 100644 --- a/src/m_random.cpp +++ b/src/m_random.cpp @@ -330,7 +330,7 @@ void FRandom::StaticReadRNGState(FSerializer &arc) { FRandom *rng; - arc("rngseed", rng); + arc("rngseed", rngseed); if (arc.BeginArray("rngs")) { int count = arc.ArraySize(); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 59373237f..cb82d86e8 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -74,7 +74,6 @@ #include "p_setup.h" #include "po_man.h" #include "actorptrselect.h" -#include "farchive.h" #include "serializer.h" #include "decallib.h" #include "p_terrain.h" @@ -726,7 +725,7 @@ void ACSStringPool::ReadStrings(FSerializer &file, const char *key) { Clear(); - int32_t i, j, poolsize; + int poolsize; file("poolsize", poolsize); Pool.Resize(poolsize); @@ -737,22 +736,22 @@ void ACSStringPool::ReadStrings(FSerializer &file, const char *key) } if (file.BeginArray("pool")) { - j = file.ArraySize(); + int j = file.ArraySize(); for (int i = 0; i < j; i++) { if (file.BeginObject(nullptr)) { - i = -1; - file("index", i); - if (i >= 0 && i < Pool.Size()) + unsigned ii = UINT_MAX; + file("index", ii); + if (ii < Pool.Size()) { - file("string", Pool[i].Str) - ("lockcount", Pool[i].LockCount); + file("string", Pool[ii].Str) + ("lockcount", Pool[ii].LockCount); - unsigned h = SuperFastHash(Pool[i].Str, Pool[i].Str.Len()); + unsigned h = SuperFastHash(Pool[ii].Str, Pool[ii].Str.Len()); unsigned bucketnum = h % NUM_BUCKETS; - Pool[i].Hash = h; - Pool[i].Next = PoolBuckets[bucketnum]; + Pool[ii].Hash = h; + Pool[ii].Next = PoolBuckets[bucketnum]; PoolBuckets[bucketnum] = i; } file.EndObject(); @@ -980,7 +979,7 @@ static void WriteVars (FSerializer &file, SDWORD *vars, size_t count, const char static void ReadVars (FSerializer &arc, SDWORD *vars, size_t count, const char *key) { memset(&vars[0], 0, count * 4); - arc.Array(key, vars, count); + arc.Array(key, vars, (int)count); } //============================================================================ @@ -1038,7 +1037,7 @@ static void WriteArrayVars (FSerializer &file, FWorldGlobalArray *vars, unsigned static void ReadArrayVars (FSerializer &file, FWorldGlobalArray *vars, size_t count, const char *key) { - for (i = 0; i < count; ++i) + for (size_t i = 0; i < count; ++i) { vars[i].Clear(); } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 70fff197a..f6139f843 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -64,7 +64,6 @@ #include "v_palette.h" #include "p_enemy.h" #include "gstrings.h" -#include "farchive.h" #include "r_data/colormaps.h" #include "r_renderer.h" #include "po_man.h" diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 93b4506e3..a40208ccd 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -27,7 +27,6 @@ #include "templates.h" #include "thingdef/thingdef.h" #include "g_level.h" -#include "farchive.h" #include "d_player.h" #include "serializer.h" diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 1b9f1a057..0fe896ec6 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -52,7 +52,6 @@ #include "po_man.h" #include "p_setup.h" #include "r_data/colormaps.h" -#include "farchive.h" #include "p_lnspec.h" #include "p_acs.h" #include "p_terrain.h" diff --git a/src/p_states.cpp b/src/p_states.cpp index a56229e05..1a8116e53 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -34,7 +34,6 @@ ** */ #include "actor.h" -#include "farchive.h" #include "templates.h" #include "cmdlib.h" #include "i_system.h" diff --git a/src/p_user.cpp b/src/p_user.cpp index 3c3cdf135..0f3b6a516 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -52,7 +52,6 @@ #include "g_level.h" #include "d_net.h" #include "gstrings.h" -#include "farchive.h" #include "serializer.h" #include "r_renderer.h" #include "d_player.h" diff --git a/src/portal.cpp b/src/portal.cpp index 90cff83db..cef79820f 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -45,7 +45,6 @@ #include "c_cvars.h" #include "m_bbox.h" #include "p_tags.h" -#include "farchive.h" #include "v_text.h" #include "a_sharedglobal.h" #include "i_system.h" diff --git a/src/r_data/renderstyle.cpp b/src/r_data/renderstyle.cpp index f84c941d9..6e656f715 100644 --- a/src/r_data/renderstyle.cpp +++ b/src/r_data/renderstyle.cpp @@ -32,7 +32,6 @@ ** */ -#include "farchive.h" #include "templates.h" #include "renderstyle.h" #include "c_cvars.h" diff --git a/src/r_data/renderstyle.h b/src/r_data/renderstyle.h index 78687cec0..501c14c5f 100644 --- a/src/r_data/renderstyle.h +++ b/src/r_data/renderstyle.h @@ -34,6 +34,7 @@ **--------------------------------------------------------------------------- ** */ +#include // also #defines OPAQUE #ifdef OPAQUE @@ -130,15 +131,15 @@ union FRenderStyle { struct { - BYTE BlendOp; // Of ERenderOp type - BYTE SrcAlpha; // Of ERenderAlpha type - BYTE DestAlpha; // Of ERenderAlpha type - BYTE Flags; + uint8_t BlendOp; // Of ERenderOp type + uint8_t SrcAlpha; // Of ERenderAlpha type + uint8_t DestAlpha; // Of ERenderAlpha type + uint8_t Flags; }; - uint32 AsDWORD; + uint32_t AsDWORD; inline FRenderStyle &operator= (ERenderStyle legacy); - operator uint32() const { return AsDWORD; } + operator uint32_t() const { return AsDWORD; } bool operator==(const FRenderStyle &o) const { return AsDWORD == o.AsDWORD; } void CheckFuzz(); bool IsVisible(double alpha) const throw(); diff --git a/src/r_main.cpp b/src/r_main.cpp index 040b57b71..1e0de7ecc 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -57,7 +57,6 @@ #include "st_start.h" #include "v_font.h" #include "r_data/colormaps.h" -#include "farchive.h" #include "p_maputl.h" // MACROS ------------------------------------------------------------------ diff --git a/src/textures/anim_switches.cpp b/src/textures/anim_switches.cpp index 9a2aa2441..14fb4db84 100644 --- a/src/textures/anim_switches.cpp +++ b/src/textures/anim_switches.cpp @@ -43,7 +43,6 @@ #include "cmdlib.h" #include "sc_man.h" #include "gi.h" -#include "farchive.h" static int SortSwitchDefs (const void *a, const void *b) diff --git a/src/textures/animations.cpp b/src/textures/animations.cpp index a2b1c1319..74f9b1f55 100644 --- a/src/textures/animations.cpp +++ b/src/textures/animations.cpp @@ -43,7 +43,6 @@ #include "templates.h" #include "w_wad.h" #include "g_level.h" -#include "farchive.h" #include "serializer.h" // MACROS ------------------------------------------------------------------ diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 9c7e98d08..3fb01dc6c 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -40,6 +40,7 @@ #include "templates.h" #include "i_system.h" #include "r_data/r_translate.h" +#include "r_data/sprites.h" #include "c_dispatch.h" #include "v_text.h" #include "sc_man.h" @@ -48,7 +49,6 @@ #include "cmdlib.h" #include "g_level.h" #include "m_fixed.h" -#include "farchive.h" #include "v_video.h" #include "r_renderer.h" #include "r_sky.h" diff --git a/src/v_blend.cpp b/src/v_blend.cpp index b8f3e713d..e1552105f 100644 --- a/src/v_blend.cpp +++ b/src/v_blend.cpp @@ -51,7 +51,6 @@ #include "colormatcher.h" #include "v_palette.h" #include "d_player.h" -#include "farchive.h" diff --git a/src/v_font.cpp b/src/v_font.cpp index dd2bf7068..0eb28a67c 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -90,7 +90,6 @@ The FON2 header is followed by variable length data: #include "cmdlib.h" #include "sc_man.h" #include "hu_stuff.h" -#include "farchive.h" #include "textures/textures.h" #include "r_data/r_translate.h" #include "colormatcher.h" From 5dfc396bb96d296016c81673dfc464fbd10288dc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 01:28:05 +0200 Subject: [PATCH 33/59] - read snapshots from zip. --- src/g_game.cpp | 7 +-- src/g_game.h | 1 - src/g_hub.h | 1 - src/g_level.cpp | 71 ++++++++++++++++-------------- src/g_level.h | 3 +- src/m_random.h | 1 - src/p_acs.h | 1 - src/p_saveg.h | 1 - src/resourcefiles/file_zip.cpp | 15 +++---- src/resourcefiles/file_zip.h | 24 +--------- src/resourcefiles/resourcefile.cpp | 18 ++++++++ src/resourcefiles/resourcefile.h | 23 ++++++++++ 12 files changed, 90 insertions(+), 76 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index f646b2bd1..ee826aa68 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1962,7 +1962,8 @@ void G_DoLoadGame () // dearchive all the modifications level.time = Scale (time[1], TICRATE, time[0]); - //G_ReadSnapshots(png); + G_ReadSnapshots(resfile); + G_ReadVisited(arc); // load a base level savegamerestore = true; // Use the player actors in the savegame @@ -2268,8 +2269,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio // delete the JSON buffers we created just above. Everything else will // either still be needed or taken care of automatically. - delete[] savegame_content[1].mBuffer; - delete[] savegame_content[2].mBuffer; + savegame_content[1].Clean(); + savegame_content[2].Clean(); // Check whether the file is ok by trying to open it. FResourceFile *test = FResourceFile::OpenResourceFile(filename, nullptr, true); diff --git a/src/g_game.h b/src/g_game.h index 0b0d094f5..0a11527fd 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -24,7 +24,6 @@ #define __G_GAME__ struct event_t; -struct PNGHandle; // diff --git a/src/g_hub.h b/src/g_hub.h index 245bb8f93..372546b83 100644 --- a/src/g_hub.h +++ b/src/g_hub.h @@ -1,7 +1,6 @@ #ifndef __G_HUB_H #define __G_HUB_H -struct PNGHandle; struct cluster_info_t; struct wbstartstruct_t; class FSerializer; diff --git a/src/g_level.cpp b/src/g_level.cpp index b5b0b6972..3cf054b67 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1568,7 +1568,7 @@ void G_WriteSnapshots(TArray &filenames, TArray &buf { if (wadlevelinfos[i].Snapshot.mCompressedSize > 0) { - filename << wadlevelinfos[i].MapName << ".json"; + filename << wadlevelinfos[i].MapName << ".map.json"; filename.ToLower(); filenames.Push(filename); buffers.Push(wadlevelinfos[i].Snapshot); @@ -1576,7 +1576,7 @@ void G_WriteSnapshots(TArray &filenames, TArray &buf } if (TheDefaultLevelInfo.Snapshot.mCompressedSize > 0) { - filename << TheDefaultLevelInfo.MapName << ".json"; + filename << TheDefaultLevelInfo.MapName << ".mapd.json"; filename.ToLower(); filenames.Push(filename); buffers.Push(TheDefaultLevelInfo.Snapshot); @@ -1629,45 +1629,48 @@ void G_WriteVisited(FSerializer &arc) // //========================================================================== -void G_ReadSnapshots(PNGHandle *png) +void G_ReadSnapshots(FResourceFile *resf) { - DWORD chunkLen; FString MapName; level_info_t *i; G_ClearSnapshots(); - chunkLen = (DWORD)M_FindPNGChunk(png, SNAP_ID); - while (chunkLen != 0) + for (unsigned j = 0; j < resf->LumpCount(); j++) { -#if 0 - FPNGChunkArchive arc(png->File->GetFile(), SNAP_ID, chunkLen); - DWORD snapver; - - arc << snapver; - arc << MapName; - i = FindLevelInfo(MapName); - i->snapshot = new FCompressedMemFile; - i->snapshot->Serialize(arc); - chunkLen = (DWORD)M_NextPNGChunk(png, SNAP_ID); -#endif - } - - chunkLen = (DWORD)M_FindPNGChunk(png, DSNP_ID); - if (chunkLen != 0) - { -#if 0 - FPNGChunkArchive arc(png->File->GetFile(), DSNP_ID, chunkLen); - DWORD snapver; - - arc << snapver; - arc << MapName; - TheDefaultLevelInfo.snapshot = new FCompressedMemFile; - TheDefaultLevelInfo.snapshot->Serialize(arc); -#endif + FResourceLump * resl = resf->GetLump(j); + if (resl != nullptr) + { + auto ptr = strstr(resl->FullName, ".map.json"); + if (ptr != nullptr) + { + ptrdiff_t maplen = ptr - resl->FullName.GetChars(); + FString mapname(resl->FullName.GetChars(), (size_t)maplen); + i = FindLevelInfo(mapname); + if (i != nullptr) + { + i->Snapshot = resl->GetRawData(); + } + } + else + { + auto ptr = strstr(resl->FullName, ".mapd.json"); + if (ptr != nullptr) + { + ptrdiff_t maplen = ptr - resl->FullName.GetChars(); + FString mapname(resl->FullName.GetChars(), (size_t)maplen); + TheDefaultLevelInfo.Snapshot = resl->GetRawData(); + } + } + } } } +//========================================================================== +// +// +//========================================================================== + void G_ReadVisited(FSerializer &arc) { if (arc.BeginArray("visited")) @@ -1696,6 +1699,9 @@ void G_ReadVisited(FSerializer &arc) } } +//========================================================================== +// +// //========================================================================== CCMD(listsnapshots) @@ -1752,8 +1758,7 @@ void P_WriteACSDefereds (FSerializer &arc) void P_ReadACSDefereds (FSerializer &arc) { FString MapName; - size_t chunklen; - + P_RemoveDefereds (); if (arc.BeginObject("deferred")) diff --git a/src/g_level.h b/src/g_level.h index 55a6647ab..9f8c5efd5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -532,8 +532,7 @@ void G_ClearSnapshots (void); void P_RemoveDefereds (); void G_SnapshotLevel (void); void G_UnSnapshotLevel (bool keepPlayers); -struct PNGHandle; -void G_ReadSnapshots (PNGHandle *png); +void G_ReadSnapshots (FResourceFile *); void G_WriteSnapshots (TArray &, TArray &); void G_WriteVisited(FSerializer &arc); void G_ReadVisited(FSerializer &arc); diff --git a/src/m_random.h b/src/m_random.h index d8c94cb9f..b3e0f3b28 100644 --- a/src/m_random.h +++ b/src/m_random.h @@ -39,7 +39,6 @@ #include "basictypes.h" #include "sfmt/SFMT.h" -struct PNGHandle; class FSerializer; class FRandom diff --git a/src/p_acs.h b/src/p_acs.h index 9cd209297..7e899d269 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -45,7 +45,6 @@ class FFont; class FileReader; struct line_t; -struct PNGHandle; enum diff --git a/src/p_saveg.h b/src/p_saveg.h index 05d7e3fd4..6c4c74f46 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -34,7 +34,6 @@ #ifndef __P_SAVEG_H__ #define __P_SAVEG_H__ -struct PNGHandle; class FSerializer; // Persistent storage/archiving. diff --git a/src/resourcefiles/file_zip.cpp b/src/resourcefiles/file_zip.cpp index e2574d045..73193062c 100644 --- a/src/resourcefiles/file_zip.cpp +++ b/src/resourcefiles/file_zip.cpp @@ -306,17 +306,12 @@ FZipFile::~FZipFile() // //========================================================================== -FCompressedBuffer FZipFile::GetRawLump(int lumpnum) +FCompressedBuffer FZipLump::GetRawData() { - if ((unsigned)lumpnum >= NumLumps) - { - return{ 0,0,0,0,0,nullptr }; - } - FZipLump *lmp = &Lumps[lumpnum]; - - FCompressedBuffer cbuf = { (unsigned)lmp->LumpSize, (unsigned)lmp->CompressedSize, lmp->Method, lmp->GPFlags, lmp->CRC32, new char[lmp->CompressedSize] }; - Reader->Seek(lmp->Position, SEEK_SET); - Reader->Read(cbuf.mBuffer, lmp->CompressedSize); + FCompressedBuffer cbuf = { (unsigned)LumpSize, (unsigned)CompressedSize, Method, GPFlags, CRC32, new char[CompressedSize] }; + if (Flags & LUMPFZIP_NEEDFILESTART) SetLumpAddress(); + Owner->Reader->Seek(Position, SEEK_SET); + Owner->Reader->Read(cbuf.mBuffer, CompressedSize); return cbuf; } diff --git a/src/resourcefiles/file_zip.h b/src/resourcefiles/file_zip.h index e802b41e8..427d9cbf3 100644 --- a/src/resourcefiles/file_zip.h +++ b/src/resourcefiles/file_zip.h @@ -3,28 +3,6 @@ #include "resourcefile.h" -// This holds a compresed Zip entry with all needed info to decompress it. -struct FCompressedBuffer -{ - unsigned mSize; - unsigned mCompressedSize; - int mMethod; - int mZipFlags; - unsigned mCRC32; - char *mBuffer; - - bool Decompress(char *destbuffer); - void Clean() - { - mSize = mCompressedSize = 0; - if (mBuffer != nullptr) - { - delete[] mBuffer; - mBuffer = nullptr; - } - } -}; - enum { LUMPFZIP_NEEDFILESTART = 128 @@ -50,6 +28,7 @@ struct FZipLump : public FResourceLump private: void SetLumpAddress(); virtual int GetFileOffset(); + FCompressedBuffer GetRawData(); }; @@ -68,7 +47,6 @@ public: virtual ~FZipFile(); bool Open(bool quiet); virtual FResourceLump *GetLump(int no) { return ((unsigned)no < NumLumps)? &Lumps[no] : NULL; } - FCompressedBuffer GetRawLump(int lumpnum); }; diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 2daf4aee1..912998d9a 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -40,6 +40,7 @@ #include "doomerrors.h" #include "gi.h" #include "doomstat.h" +#include "w_zip.h" //========================================================================== @@ -191,6 +192,23 @@ void FResourceLump::CheckEmbedded() } +//========================================================================== +// +// this is just for completeness. For non-Zips only an uncompressed lump can +// be returned. +// +//========================================================================== + +FCompressedBuffer FResourceLump::GetRawData() +{ + FCompressedBuffer cbuf = { (unsigned)LumpSize, (unsigned)LumpSize, METHOD_STORED, 0, 0, new char[LumpSize] }; + memcpy(cbuf.mBuffer, CacheLump(), LumpSize); + cbuf.mCRC32 = crc32(0, (BYTE*)cbuf.mBuffer, LumpSize); + ReleaseCache(); + return cbuf; +} + + //========================================================================== // // Returns the owner's FileReader if it can be used to access this lump diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index b79c2cc59..bedcad3bb 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -8,6 +8,28 @@ class FResourceFile; class FTexture; +// This holds a compresed Zip entry with all needed info to decompress it. +struct FCompressedBuffer +{ + unsigned mSize; + unsigned mCompressedSize; + int mMethod; + int mZipFlags; + unsigned mCRC32; + char *mBuffer; + + bool Decompress(char *destbuffer); + void Clean() + { + mSize = mCompressedSize = 0; + if (mBuffer != nullptr) + { + delete[] mBuffer; + mBuffer = nullptr; + } + } +}; + struct FResourceLump { friend class FResourceFile; @@ -46,6 +68,7 @@ struct FResourceLump virtual int GetIndexNum() const { return 0; } void LumpNameSetup(FString iname); void CheckEmbedded(); + virtual FCompressedBuffer GetRawData(); void *CacheLump(); int ReleaseCache(); From e01965b747f65013c7a555ae24e7e3132335bdbf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 11:22:15 +0200 Subject: [PATCH 34/59] - fixed deserialization of level.Scrolls. --- src/p_saveg.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 0fe896ec6..08175b41c 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -288,12 +288,13 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t ("scrolls", scroll, nul) .EndObject(); - if (!scroll.isZero()) + if (arc.isReading() && !scroll.isZero()) { if (level.Scrolls.Size() == 0) { level.Scrolls.Resize(numsectors); memset(&level.Scrolls[0], 0, sizeof(level.Scrolls[0])*level.Scrolls.Size()); + level.Scrolls[p.sectornum] = scroll; } } } From 68dc75bf9e96e68e946a5f5aa9ee698b0f0f21b5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 11:51:29 +0200 Subject: [PATCH 35/59] - fixed: array clamping may only be done when reading a savegame. - fixed generation of savegame names for level snapshots. --- src/g_level.cpp | 4 ++-- src/serializer.h | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index 43e2cdef1..a66ed99a9 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1571,7 +1571,7 @@ void G_WriteSnapshots(TArray &filenames, TArray &buf { if (wadlevelinfos[i].Snapshot.mCompressedSize > 0) { - filename << wadlevelinfos[i].MapName << ".map.json"; + filename.Format("%s.map.json", wadlevelinfos[i].MapName.GetChars()); filename.ToLower(); filenames.Push(filename); buffers.Push(wadlevelinfos[i].Snapshot); @@ -1579,7 +1579,7 @@ void G_WriteSnapshots(TArray &filenames, TArray &buf } if (TheDefaultLevelInfo.Snapshot.mCompressedSize > 0) { - filename << TheDefaultLevelInfo.MapName << ".mapd.json"; + filename.Format("%s.mapd.json", TheDefaultLevelInfo.MapName.GetChars()); filename.ToLower(); filenames.Push(filename); buffers.Push(TheDefaultLevelInfo.Snapshot); diff --git a/src/serializer.h b/src/serializer.h index 1592d41e0..17d8c970e 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -111,8 +111,11 @@ public: if (BeginArray(key)) { - int max = ArraySize(); - if (max < count) count = max; + if (isReading()) + { + int max = ArraySize(); + if (max < count) count = max; + } for (int i = 0; i < count; i++) { Serialize(*this, nullptr, obj[i], (T*)nullptr); From d5b771afdd88432175d6f30409209e1d38020e50 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 11:53:09 +0200 Subject: [PATCH 36/59] - fixed: Both array serializers need the bounds check. --- src/serializer.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/serializer.h b/src/serializer.h index 17d8c970e..8b05cf9ec 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -134,6 +134,11 @@ public: } if (BeginArray(key)) { + if (isReading()) + { + int max = ArraySize(); + if (max < count) count = max; + } for (int i = 0; i < count; i++) { Serialize(*this, nullptr, obj[i], def ? &def[i] : nullptr); From f52e2a8cc47f8fb805183e7c2a9f1abcf6c52f17 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 12:36:29 +0200 Subject: [PATCH 37/59] - removed bogus BeginObject call in deferred script serializer. --- src/g_level.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index a66ed99a9..aa521ff00 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1743,7 +1743,7 @@ void P_WriteACSDefereds (FSerializer &arc) { if (wi.deferred.Size() > 0) { - if (wi.deferred.Size() > 0 && arc.BeginObject(nullptr)) + if (wi.deferred.Size() > 0) { arc(wi.MapName, wi.deferred); } From d9dbf26f6361d1a8ab6c0b7005371eeec7f78d52 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 17:39:09 +0200 Subject: [PATCH 38/59] - do not list savegames for different IWADs. --- src/menu/loadsavemenu.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 91492e88d..c33de3725 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -271,6 +271,12 @@ void DLoadSaveMenu::ReadSaveStrings () { missing = !G_CheckSaveGameWads(arc, false); } + else + { + // different game. Skip this. + delete savegame; + continue; + } FSaveGameNode *node = new FSaveGameNode; node->Filename = filepath; From c22e8c50afb6e0605f8c2b48d14a15e5b132a0fc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 19:36:23 +0200 Subject: [PATCH 39/59] - fixed some errors with parsing globals.json. It looks like this file is being processed correctly now. --- src/g_game.cpp | 17 ++++++++-------- src/m_random.cpp | 2 +- src/p_acs.cpp | 51 ++++++++++++++++++++++++---------------------- src/serializer.cpp | 2 +- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/g_game.cpp b/src/g_game.cpp index ee826aa68..ef104d67b 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1926,11 +1926,11 @@ void G_DoLoadGame () // we are done with info.json. arc.Close(); - info = resfile->FindLump("global.json"); + info = resfile->FindLump("globals.json"); if (info == nullptr) { delete resfile; - Printf("'%s' is not a valid savegame: Missing 'global.json'.\n", savename.GetChars()); + Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars()); return; } @@ -1948,14 +1948,15 @@ void G_DoLoadGame () bglobal.RemoveAllBots (true); - arc("importantcvars", map); - if (!map.IsEmpty()) + FString cvar; + arc("importantcvars",cvar); + if (!cvar.IsEmpty()) { - BYTE *vars_p = (BYTE *)map.GetChars(); + BYTE *vars_p = (BYTE *)cvar.GetChars(); C_ReadCVars (&vars_p); } - DWORD time[2] = { 0,1 }; + DWORD time[2] = { 1,0 }; arc("ticrate", time[0]) ("leveltime", time[1]); @@ -1970,7 +1971,6 @@ void G_DoLoadGame () bool demoplaybacksave = demoplayback; G_InitNew (map, false); demoplayback = demoplaybacksave; - delete[] map; savegamerestore = false; STAT_Serialize(arc); @@ -1981,7 +1981,8 @@ void G_DoLoadGame () NextSkill = -1; arc("nextskill", NextSkill); - level.info->Snapshot.Clean(); + if (level.info != nullptr) + level.info->Snapshot.Clean(); BackupSaveName = savename; diff --git a/src/m_random.cpp b/src/m_random.cpp index 11075916e..e0a55d095 100644 --- a/src/m_random.cpp +++ b/src/m_random.cpp @@ -353,8 +353,8 @@ void FRandom::StaticReadRNGState(FSerializer &arc) } arc.EndObject(); } - arc.EndArray(); } + arc.EndArray(); } } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index aea40945f..d7b042698 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -725,36 +725,39 @@ void ACSStringPool::ReadStrings(FSerializer &file, const char *key) { Clear(); - int poolsize; + if (file.BeginObject(key)) + { + int poolsize = 0; - file("poolsize", poolsize); - Pool.Resize(poolsize); - for (auto &p : Pool) - { - p.Next = FREE_ENTRY; - p.LockCount = 0; - } - if (file.BeginArray("pool")) - { - int j = file.ArraySize(); - for (int i = 0; i < j; i++) + file("poolsize", poolsize); + Pool.Resize(poolsize); + for (auto &p : Pool) { - if (file.BeginObject(nullptr)) + p.Next = FREE_ENTRY; + p.LockCount = 0; + } + if (file.BeginArray("pool")) + { + int j = file.ArraySize(); + for (int i = 0; i < j; i++) { - unsigned ii = UINT_MAX; - file("index", ii); - if (ii < Pool.Size()) + if (file.BeginObject(nullptr)) { - file("string", Pool[ii].Str) - ("lockcount", Pool[ii].LockCount); + unsigned ii = UINT_MAX; + file("index", ii); + if (ii < Pool.Size()) + { + file("string", Pool[ii].Str) + ("lockcount", Pool[ii].LockCount); - unsigned h = SuperFastHash(Pool[ii].Str, Pool[ii].Str.Len()); - unsigned bucketnum = h % NUM_BUCKETS; - Pool[ii].Hash = h; - Pool[ii].Next = PoolBuckets[bucketnum]; - PoolBuckets[bucketnum] = i; + unsigned h = SuperFastHash(Pool[ii].Str, Pool[ii].Str.Len()); + unsigned bucketnum = h % NUM_BUCKETS; + Pool[ii].Hash = h; + Pool[ii].Next = PoolBuckets[bucketnum]; + PoolBuckets[bucketnum] = i; + } + file.EndObject(); } - file.EndObject(); } } } diff --git a/src/serializer.cpp b/src/serializer.cpp index 301e284a9..251c9a0f4 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -51,7 +51,6 @@ struct FJSONObject else if (v->IsArray()) { mIndex = 0; - mIterator = v->MemberEnd(); } } }; @@ -717,6 +716,7 @@ unsigned FSerializer::GetSize(const char *group) const char *FSerializer::GetKey() { if (isWriting()) return nullptr; // we do not know this when writing. + if (!r->mObjects.Last().mObject->IsObject()) return nullptr; // non-objects do not have keys. auto &it = r->mObjects.Last().mIterator; if (it == r->mObjects.Last().mObject->MemberEnd()) return nullptr; return it->name.GetString(); From 604b2b316b41d10e4157a60d4cb8306f760714b5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 22 Sep 2016 21:17:34 +0200 Subject: [PATCH 40/59] - we need to save the OF_JustSpawned flag to insert thinkers into the proper list. --- src/dobject.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dobject.cpp b/src/dobject.cpp index b5af56151..8fba75aa6 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -499,6 +499,13 @@ void DObject::SerializeUserVars(FSerializer &arc) void DObject::Serialize(FSerializer &arc) { + int fresh = ObjectFlags & OF_JustSpawned; + int freshdef = 0; + arc("justspawned", fresh, freshdef); + if (arc.isReading()) + { + ObjectFlags |= fresh; + } ObjectFlags |= OF_SerialSuccess; } From c17da32dbdd308b66d649ed0b71237a74ca8e01a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 00:45:41 +0200 Subject: [PATCH 41/59] - added object deserialization. It seems to work, at least the stuff I sampled looked like it was properly reatored and it triggers no error condition. - always make the top level object randomaccess when opening a JSON file for reading. Some things won't work right if this is opened for sequential access. --- src/dobject.h | 1 + src/dthinker.h | 2 +- src/g_game.cpp | 4 +- src/g_level.cpp | 2 - src/menu/loadsavemenu.cpp | 4 +- src/serializer.cpp | 136 +++++++++++++++++++++++++++++++++++--- src/serializer.h | 23 +++++-- src/zzz_old.cpp | 5 -- 8 files changed, 149 insertions(+), 28 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index 51102bc4c..538c7aba4 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -209,6 +209,7 @@ enum EObjectFlags OF_JustSpawned = 1 << 8, // Thinker was spawned this tic OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list + OF_LoadedPlayer = 1 << 11, // this gets flagged during deserialization so that the player checks in there can be simplified. }; template class TObjPtr; diff --git a/src/dthinker.h b/src/dthinker.h index c6a27e3a6..3f3a37b78 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -88,6 +88,7 @@ public: static void MarkRoots(); static DThinker *FirstThinker (int statnum); + static bool bSerialOverride; private: enum no_link_type { NO_LINK }; @@ -100,7 +101,6 @@ private: static FThinkerList Thinkers[MAX_STATNUM+2]; // Current thinkers static FThinkerList FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers - static bool bSerialOverride; friend struct FThinkerList; friend class FThinkerIterator; diff --git a/src/g_game.cpp b/src/g_game.cpp index ef104d67b..3147f694d 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1857,7 +1857,7 @@ void G_DoLoadGame () void *data = info->CacheLump(); FSerializer arc; - if (!arc.OpenReader((const char *)data, info->LumpSize, true)) + if (!arc.OpenReader((const char *)data, info->LumpSize)) { Printf("Failed to access savegame info\n"); delete resfile; @@ -1935,7 +1935,7 @@ void G_DoLoadGame () } data = info->CacheLump(); - if (!arc.OpenReader((const char *)data, info->LumpSize, true)) + if (!arc.OpenReader((const char *)data, info->LumpSize)) { Printf("Failed to access savegame info\n"); delete resfile; diff --git a/src/g_level.cpp b/src/g_level.cpp index aa521ff00..de2724849 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1517,8 +1517,6 @@ void G_UnSnapshotLevel (bool hubLoad) FSerializer arc; if (!arc.OpenReader(&level.info->Snapshot)) return; - //if (hubLoad) arc.SetHubTravel (); // no idea if this is still needed. - G_SerializeLevel (arc, hubLoad); level.FromSnapshot = true; diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index c33de3725..99c34ab3e 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -242,7 +242,7 @@ void DLoadSaveMenu::ReadSaveStrings () } void *data = info->CacheLump(); FSerializer arc; - if (arc.OpenReader((const char *)data, info->LumpSize, true)) + if (arc.OpenReader((const char *)data, info->LumpSize)) { int savever = 0; FString engine; @@ -537,7 +537,7 @@ void DLoadSaveMenu::ExtractSaveData (int index) } void *data = info->CacheLump(); FSerializer arc; - if (arc.OpenReader((const char *)data, info->LumpSize, true)) + if (arc.OpenReader((const char *)data, info->LumpSize)) { FString time, pcomment, comment; diff --git a/src/serializer.cpp b/src/serializer.cpp index 251c9a0f4..e8017aba0 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -27,6 +27,7 @@ #include "po_man.h" #include "v_font.h" #include "w_zip.h" +#include "doomerrors.h" char nulspace[1024 * 1024 * 4]; @@ -196,12 +197,15 @@ struct FReader { TArray mObjects; rapidjson::Document mDoc; + TArray mDObjects; + int mPlayers[MAXPLAYERS]; FReader(const char *buffer, size_t length, bool randomaccess) { rapidjson::Document doc; mDoc.Parse(buffer, length); mObjects.Push(FJSONObject(&mDoc, randomaccess)); + memset(mPlayers, 0, sizeof(mPlayers)); } rapidjson::Value *FindKey(const char *key) @@ -258,10 +262,11 @@ bool FSerializer::OpenWriter(bool pretty) // //========================================================================== -bool FSerializer::OpenReader(const char *buffer, size_t length, bool randomaccess) +bool FSerializer::OpenReader(const char *buffer, size_t length) { if (w != nullptr || r != nullptr) return false; - r = new FReader(buffer, length, randomaccess); + r = new FReader(buffer, length, true); + ReadObjects(); return true; } @@ -271,23 +276,23 @@ bool FSerializer::OpenReader(const char *buffer, size_t length, bool randomacces // //========================================================================== -bool FSerializer::OpenReader(FCompressedBuffer *input, bool randomaccess) +bool FSerializer::OpenReader(FCompressedBuffer *input) { if (input->mSize <= 0 || input->mBuffer == nullptr) return false; if (w != nullptr || r != nullptr) return false; if (input->mMethod == METHOD_STORED) { - r = new FReader((char*)input->mBuffer, input->mSize, randomaccess); - return true; + r = new FReader((char*)input->mBuffer, input->mSize, true); } else { char *unpacked = new char[input->mSize]; input->Decompress(unpacked); - r = new FReader(unpacked, input->mSize, randomaccess); - return true; + r = new FReader(unpacked, input->mSize, true); } + ReadObjects(); + return true; } //========================================================================== @@ -400,7 +405,7 @@ bool FSerializer::BeginObject(const char *name, bool randomaccess) // //========================================================================== -void FSerializer::EndObject() +void FSerializer::EndObject(bool endwarning) { if (isWriting()) { @@ -416,6 +421,13 @@ void FSerializer::EndObject() } else { + if (endwarning && !r->mObjects.Last().mRandomAccess) + { + if (r->mObjects.Last().mIterator != r->mObjects.Last().mObject->MemberEnd()) + { + I_Error("Incomplete read of sequential object"); + } + } r->mObjects.Pop(); } } @@ -482,6 +494,22 @@ void FSerializer::EndArray() } } +//========================================================================== +// +// Discards an entry (only needed for sequential access) +// +//========================================================================== + +FSerializer &FSerializer::Discard(const char *key) +{ + if (isReading()) + { + // just get the key and advance the iterator, if present + if (!r->mObjects.Last().mRandomAccess) r->FindKey(key); + } + return *this; +} + //========================================================================== // // Special handler for args (because ACS specials' arg0 needs special treatment.) @@ -760,6 +788,94 @@ void FSerializer::WriteObjects() } } +//========================================================================== +// +// Writes out all collected objects +// +//========================================================================== + +void FSerializer::ReadObjects() +{ + bool founderrors = false; + + if (isReading() && BeginArray("objects")) + { + // Do not link any thinker that's being created here. This will be done by deserializing the thinker list later. + try + { + DThinker::bSerialOverride = true; + r->mDObjects.Resize(ArraySize()); + // First create all the objects + for (unsigned i = 0; i < r->mDObjects.Size(); i++) + { + if (BeginObject(nullptr)) + { + FString clsname; // do not deserialize the class type directly so that we can print appropriate errors. + + Serialize(*this, "classtype", clsname, nullptr); + PClass *cls = PClass::FindClass(clsname); + if (cls == nullptr) + { + Printf("Unknown object class '%d' in savegame", clsname.GetChars()); + founderrors = true; + } + else + { + r->mDObjects[i] = cls->CreateNew(); + } + EndObject(); + } + } + // Now that everything has been created and we can retrieve the pointers we can deserialize it. + + if (!founderrors) + { + // Reset to start; + r->mObjects.Last().mIndex = 0; + + for (unsigned i = 0; i < r->mDObjects.Size(); i++) + { + if (BeginObject(nullptr)) + { + Discard("classtype"); + + int pindex = -1; + Serialize(*this, "playerindex", pindex, nullptr); + auto obj = r->mDObjects[i]; + if (pindex >= 0 && pindex < MAXPLAYERS) + { + obj->ObjectFlags |= OF_LoadedPlayer; + r->mPlayers[pindex] = int(i); + } + obj->SerializeUserVars(*this); + obj->Serialize(*this); + try + { + EndObject(true); + } + catch (CRecoverableError &err) + { + I_Error("%s\n while restoring %s", err.GetMessage(), obj->GetClass()->TypeName.GetChars()); + } + } + } + } + EndArray(); + DThinker::bSerialOverride = false; + } + catch(...) + { + // make sure this flag gets unset, even if something in here throws an error. + DThinker::bSerialOverride = false; + throw; + } + } + if (founderrors) + { + I_Error("Failed to restore all objects in savegame"); + } +} + //========================================================================== // // @@ -1209,7 +1325,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe auto val = arc.r->FindKey(key); if (val != nullptr) { - if (val->IsObject()) + if (val->IsArray()) { const rapidjson::Value &nameval = (*val)[0]; const rapidjson::Value &typeval = (*val)[1]; @@ -1251,7 +1367,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje if (retcode) *retcode = true; if (arc.isWriting()) { - if (value != nullptr) + if (value != nullptr && !(value->ObjectFlags & OF_EuthanizeMe)) { int ndx; if (value == WP_NOCHANGE) diff --git a/src/serializer.h b/src/serializer.h index 8b05cf9ec..5ff33dbdd 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -16,7 +16,16 @@ struct FReader; extern TArray loadsectors; extern TArray loadlines; extern TArray loadsides; -extern char nulspace[]; + +inline bool nullcmp(const void *buffer, size_t length) +{ + const char *p = (const char *)buffer; + for (; length > 0; length--) + { + if (*p++ != 0) return false; + } + return true; +} struct NumericValue { @@ -51,6 +60,8 @@ public: int ArraySize(); void WriteKey(const char *key); + void WriteObjects(); + void ReadObjects(); public: @@ -59,18 +70,18 @@ public: Close(); } bool OpenWriter(bool pretty = true); - bool OpenReader(const char *buffer, size_t length, bool randomaccess = false); - bool OpenReader(FCompressedBuffer *input, bool randomaccess = false); + bool OpenReader(const char *buffer, size_t length); + bool OpenReader(FCompressedBuffer *input); void Close(); bool BeginObject(const char *name, bool randomaccess = false); - void EndObject(); + void EndObject(bool endwarning = false); bool BeginArray(const char *name); void EndArray(); - void WriteObjects(); unsigned GetSize(const char *group); const char *GetKey(); const char *GetOutput(unsigned *len = nullptr); FCompressedBuffer GetCompressedOutput(); + FSerializer &Discard(const char *key); 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); @@ -104,7 +115,7 @@ public: template FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false) { - if (fullcompare && isWriting() && !memcmp(obj, nulspace, count * sizeof(T))) + if (fullcompare && isWriting() && nullcmp(obj, count * sizeof(T))) { return *this; } diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index f2ce37bfd..387d40e96 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -174,9 +174,6 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad) } else { - // Prevent the constructor from inserting thinkers into a list. - bSerialOverride = true; - try { arc << statcount; @@ -213,11 +210,9 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad) } catch (class CDoomError &) { - bSerialOverride = false; DestroyAllThinkers(); throw; } - bSerialOverride = false; } } #endif From f397a4943c4a1be67e13e22e26c6c7344f351db1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 08:14:40 +0200 Subject: [PATCH 42/59] - added OutputDebugString calls to I_Error functions in Win32, so that these messages can be seen in the debug output. --- src/win32/i_system.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 5ada51b69..2075bd276 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -797,6 +797,7 @@ void I_FatalError(const char *error, ...) va_start(argptr, error); myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr); va_end(argptr); + OutputDebugString(errortext); // Record error to log (if logging) if (Logfile) @@ -832,6 +833,7 @@ void I_Error(const char *error, ...) va_start(argptr, error); myvsnprintf(errortext, MAX_ERRORTEXT, error, argptr); va_end(argptr); + OutputDebugString(errortext); throw CRecoverableError(errortext); } From 4964f94de164a4bfb7ddee6c965cdcd7a4102eef Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 08:26:36 +0200 Subject: [PATCH 43/59] - added a destructor zo DFsScript, because if this gets deleted outside the GC process it'll leave an allocated buffer behind, so make sure it always gets destroyed. --- src/fragglescript/t_script.cpp | 13 +++++++++++++ src/fragglescript/t_script.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp index bbf333acf..f1376b7eb 100644 --- a/src/fragglescript/t_script.cpp +++ b/src/fragglescript/t_script.cpp @@ -162,6 +162,19 @@ DFsScript::DFsScript() lastiftrue = false; } +//========================================================================== +// +// This is here to delete the locally allocated buffer in case this +// gets forcibly destroyed +// +//========================================================================== + +DFsScript::~DFsScript() +{ + if (data != NULL) delete[] data; + data = NULL; +} + //========================================================================== // // diff --git a/src/fragglescript/t_script.h b/src/fragglescript/t_script.h index 7f89652e6..3734e16b8 100644 --- a/src/fragglescript/t_script.h +++ b/src/fragglescript/t_script.h @@ -336,6 +336,7 @@ public: // true or false DFsScript(); + ~DFsScript(); void Destroy(); void Serialize(FSerializer &ar); From 657140f985bfdf13184863910983f8afb1e3bbbb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 08:27:31 +0200 Subject: [PATCH 44/59] - fixed: The canvastexture serializer did not call EndArray to close the containing object. --- src/r_utility.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/r_utility.cpp b/src/r_utility.cpp index b7276fe22..b009912f0 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -1064,6 +1064,7 @@ void FCanvasTextureInfo::Serialize(FSerializer &arc) } } } + arc.EndArray(); } } } @@ -1082,6 +1083,7 @@ void FCanvasTextureInfo::Serialize(FSerializer &arc) .EndObject(); Add(viewpoint, picnum, fov); } + arc.EndArray(); } } } From cad2be46ac4aea1cfe509192dd55be83f411752e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 08:49:30 +0200 Subject: [PATCH 45/59] - fixed several Destroy methods which blanketly assumed that the object's pointers were valid to use without checks. This is not the case if deserialization prematurely aborts. The entire object may be invalid if something in the deserializer I_Error's out. --- src/g_shared/a_decals.cpp | 9 ++++--- src/g_shared/a_sectoraction.cpp | 33 ++++++++++++++----------- src/g_shared/a_skies.cpp | 5 +++- src/r_data/r_interpolate.cpp | 44 ++++++++++++++++++++++----------- 4 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index ebbaaf197..7eea30fef 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -116,13 +116,16 @@ void DBaseDecal::Destroy () void DBaseDecal::Remove () { - if (WallPrev == nullptr) Side->AttachedDecals = WallNext; + if (WallPrev == nullptr) + { + if (Side != nullptr) Side->AttachedDecals = WallNext; + } else WallPrev->WallNext = WallNext; if (WallNext != nullptr) WallNext->WallPrev = WallPrev; - WallPrev = NULL; - WallNext = NULL; + WallPrev = nullptr; + WallNext = nullptr; } void DBaseDecal::Serialize(FSerializer &arc) diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index 397fa1a89..da3830d74 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -49,23 +49,26 @@ bool ASectorAction::IsActivatedByUse() const void ASectorAction::Destroy () { - // Remove ourself from this sector's list of actions - AActor *probe = Sector->SecActTarget; - union + if (Sector != nullptr) { - AActor **act; - ASectorAction **secact; - } prev; - prev.secact = &Sector->SecActTarget; + // Remove ourself from this sector's list of actions + AActor *probe = Sector->SecActTarget; + union + { + AActor **act; + ASectorAction **secact; + } prev; + prev.secact = &Sector->SecActTarget; - while (probe && probe != this) - { - prev.act = &probe->tracer; - probe = probe->tracer; - } - if (probe != NULL) - { - *prev.act = probe->tracer; + while (probe && probe != this) + { + prev.act = &probe->tracer; + probe = probe->tracer; + } + if (probe != nullptr) + { + *prev.act = probe->tracer; + } } Super::Destroy (); diff --git a/src/g_shared/a_skies.cpp b/src/g_shared/a_skies.cpp index 72091e098..89c785e3e 100644 --- a/src/g_shared/a_skies.cpp +++ b/src/g_shared/a_skies.cpp @@ -170,7 +170,10 @@ void ASectorSilencer::BeginPlay () void ASectorSilencer::Destroy () { - Sector->Flags &= ~SECF_SILENT; + if (Sector != nullptr) + { + Sector->Flags &= ~SECF_SILENT; + } Super::Destroy (); } diff --git a/src/r_data/r_interpolate.cpp b/src/r_data/r_interpolate.cpp index df8773879..c944f0fa5 100644 --- a/src/r_data/r_interpolate.cpp +++ b/src/r_data/r_interpolate.cpp @@ -418,15 +418,18 @@ DSectorPlaneInterpolation::DSectorPlaneInterpolation(sector_t *_sector, bool _pl void DSectorPlaneInterpolation::Destroy() { - if (ceiling) + if (sector != nullptr) { - sector->interpolations[sector_t::CeilingMove] = NULL; + if (ceiling) + { + sector->interpolations[sector_t::CeilingMove] = nullptr; + } + else + { + sector->interpolations[sector_t::FloorMove] = nullptr; + } + sector = nullptr; } - else - { - sector->interpolations[sector_t::FloorMove] = NULL; - } - for(unsigned i=0; iDelRef(); @@ -593,13 +596,17 @@ DSectorScrollInterpolation::DSectorScrollInterpolation(sector_t *_sector, bool _ void DSectorScrollInterpolation::Destroy() { - if (ceiling) + if (sector != nullptr) { - sector->interpolations[sector_t::CeilingScroll] = NULL; - } - else - { - sector->interpolations[sector_t::FloorScroll] = NULL; + if (ceiling) + { + sector->interpolations[sector_t::CeilingScroll] = nullptr; + } + else + { + sector->interpolations[sector_t::FloorScroll] = nullptr; + } + sector = nullptr; } Super::Destroy(); } @@ -694,7 +701,11 @@ DWallScrollInterpolation::DWallScrollInterpolation(side_t *_side, int _part) void DWallScrollInterpolation::Destroy() { - side->textures[part].interpolation = NULL; + if (side != nullptr) + { + side->textures[part].interpolation = nullptr; + side = nullptr; + } Super::Destroy(); } @@ -788,7 +799,10 @@ DPolyobjInterpolation::DPolyobjInterpolation(FPolyObj *po) void DPolyobjInterpolation::Destroy() { - poly->interpolation = NULL; + if (poly != nullptr) + { + poly->interpolation = nullptr; + } Super::Destroy(); } From a83ea4ddd29c95b4b280ed4a527629934dc8bf92 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 09:20:58 +0200 Subject: [PATCH 46/59] - fixed two typos in property names. --- src/g_shared/a_decals.cpp | 2 +- src/p_acs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 7eea30fef..e02ab1a52 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -144,7 +144,7 @@ void DBaseDecal::Serialize(FSerializer &arc) ("renderflags", RenderFlags) ("renderstyle", RenderStyle) ("side", Side) - ("sectpr", Sector); + ("sector", Sector); } void DBaseDecal::GetXY (side_t *wall, double &ox, double &oy) const diff --git a/src/p_acs.cpp b/src/p_acs.cpp index d7b042698..a310a3944 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2932,7 +2932,7 @@ void DACSThinker::Serialize(FSerializer &arc) { DLevelScript *script = nullptr; RunningScripts.Clear(); - if (arc.BeginArray("runniongscripts")) + if (arc.BeginArray("runningscripts")) { auto cnt = arc.ArraySize(); for (int i = 0; i < cnt; i++) From 5a3f1dcdb605607623809da24cc41b62eb900c3b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 09:38:55 +0200 Subject: [PATCH 47/59] - made reading of objects from the savegame work. It turned out this may not be done automatically when opening the savegame - it has to be done later, after the pre-spawned map thinkers and all connected objects have been destroyed. The object deserializer also has to be rather careful about dealing with parse errors, because if something goes wrong a whole batch of uninitialized or partially initialized objects will be left behind to destroy. This means that no object class may assume that anything but the default constructor has been run on it and needs to check any variable it may reference. --- src/p_acs.cpp | 1 + src/p_saveg.cpp | 2 +- src/serializer.cpp | 85 ++++++++++++++++++++++++++++++++-------------- src/serializer.h | 2 +- 4 files changed, 62 insertions(+), 28 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index a310a3944..e06109678 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -2941,6 +2941,7 @@ void DACSThinker::Serialize(FSerializer &arc) arc(nullptr, srs); RunningScripts[srs.scriptnum] = srs.lscript; } + arc.EndArray(); } } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 08175b41c..72717f2d6 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -904,7 +904,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) if (arc.isReading()) { P_DestroyThinkers(hubload); - // ReadObjects + arc.ReadObjects(); } arc("level.flags", level.flags) diff --git a/src/serializer.cpp b/src/serializer.cpp index e8017aba0..bd09d90e9 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -199,6 +199,7 @@ struct FReader rapidjson::Document mDoc; TArray mDObjects; int mPlayers[MAXPLAYERS]; + bool mObjectsRead; FReader(const char *buffer, size_t length, bool randomaccess) { @@ -206,6 +207,7 @@ struct FReader mDoc.Parse(buffer, length); mObjects.Push(FJSONObject(&mDoc, randomaccess)); memset(mPlayers, 0, sizeof(mPlayers)); + mObjectsRead = false; } rapidjson::Value *FindKey(const char *key) @@ -266,7 +268,6 @@ bool FSerializer::OpenReader(const char *buffer, size_t length) { if (w != nullptr || r != nullptr) return false; r = new FReader(buffer, length, true); - ReadObjects(); return true; } @@ -291,7 +292,6 @@ bool FSerializer::OpenReader(FCompressedBuffer *input) input->Decompress(unpacked); r = new FReader(unpacked, input->mSize, true); } - ReadObjects(); return true; } @@ -416,6 +416,7 @@ void FSerializer::EndObject(bool endwarning) } else { + assert(false && "EndObject call not inside an object"); I_Error("EndObject call not inside an object"); } } @@ -425,6 +426,7 @@ void FSerializer::EndObject(bool endwarning) { if (r->mObjects.Last().mIterator != r->mObjects.Last().mObject->MemberEnd()) { + assert(false && "Incomplete read of sequential object"); I_Error("Incomplete read of sequential object"); } } @@ -797,6 +799,7 @@ void FSerializer::WriteObjects() void FSerializer::ReadObjects() { bool founderrors = false; + unsigned i; if (isReading() && BeginArray("objects")) { @@ -827,6 +830,7 @@ void FSerializer::ReadObjects() } } // Now that everything has been created and we can retrieve the pointers we can deserialize it. + r->mObjectsRead = true; if (!founderrors) { @@ -835,45 +839,54 @@ void FSerializer::ReadObjects() for (unsigned i = 0; i < r->mDObjects.Size(); i++) { - if (BeginObject(nullptr)) + auto obj = r->mDObjects[i]; + try { - Discard("classtype"); - - int pindex = -1; - Serialize(*this, "playerindex", pindex, nullptr); - auto obj = r->mDObjects[i]; - if (pindex >= 0 && pindex < MAXPLAYERS) - { - obj->ObjectFlags |= OF_LoadedPlayer; - r->mPlayers[pindex] = int(i); - } - obj->SerializeUserVars(*this); - obj->Serialize(*this); - try + if (BeginObject(nullptr)) { + int pindex = -1; + Discard("classtype"); + Serialize(*this, "playerindex", pindex, nullptr); + if (obj != nullptr) + { + if (pindex >= 0 && pindex < MAXPLAYERS) + { + obj->ObjectFlags |= OF_LoadedPlayer; + r->mPlayers[pindex] = int(i); + } + obj->SerializeUserVars(*this); + obj->Serialize(*this); + } EndObject(true); } - catch (CRecoverableError &err) - { - I_Error("%s\n while restoring %s", err.GetMessage(), obj->GetClass()->TypeName.GetChars()); - } + } + catch (CRecoverableError &err) + { + I_Error("%s\n while restoring %s", err.GetMessage(),obj? obj->GetClass()->TypeName.GetChars() : "invalid object"); } } } EndArray(); DThinker::bSerialOverride = false; + if (founderrors) + { + I_Error("Failed to restore all objects in savegame"); + } } catch(...) { + // nuke all objects we created here. + for (auto obj : r->mDObjects) + { + obj->Destroy(); + } + r->mDObjects.Clear(); + // make sure this flag gets unset, even if something in here throws an error. DThinker::bSerialOverride = false; throw; } } - if (founderrors) - { - I_Error("Failed to restore all objects in savegame"); - } } //========================================================================== @@ -1367,13 +1380,17 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje if (retcode) *retcode = true; if (arc.isWriting()) { - if (value != nullptr && !(value->ObjectFlags & OF_EuthanizeMe)) + if (value != nullptr) { int ndx; if (value == WP_NOCHANGE) { ndx = -1; } + else if (value->ObjectFlags & OF_EuthanizeMe) + { + return arc; + } else { int *pndx = arc.w->mObjectMap.CheckKey(value); @@ -1392,15 +1409,31 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje } else { + if (!arc.r->mObjectsRead) + { + // If you want to read objects, you MUST call ReadObjects first, even if there's only nullptr's. + assert(false && "Attempt to read object reference without calling ReadObjects first"); + I_Error("Attempt to read object reference without calling ReadObjects first"); + } auto val = arc.r->FindKey(key); if (val != nullptr && val->IsInt()) { - if (val->GetInt() == -1) + int index = val->GetInt(); + if (index == -1) { value = WP_NOCHANGE; } else { + assert(index >= 0 && index < (int)arc.r->mDObjects.Size()); + if (index >= 0 && index < (int)arc.r->mDObjects.Size()) + { + value = arc.r->mDObjects[index]; + } + else + { + I_Error("Invalid object reference for '%s'", key); + } } } else if (!retcode) diff --git a/src/serializer.h b/src/serializer.h index 5ff33dbdd..7b35294d2 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -61,7 +61,6 @@ public: int ArraySize(); void WriteKey(const char *key); void WriteObjects(); - void ReadObjects(); public: @@ -73,6 +72,7 @@ public: bool OpenReader(const char *buffer, size_t length); bool OpenReader(FCompressedBuffer *input); void Close(); + void ReadObjects(); bool BeginObject(const char *name, bool randomaccess = false); void EndObject(bool endwarning = false); bool BeginArray(const char *name); From 86e9282193fce0df638a34e5f9fcf81b4ea0ad71 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 14:04:05 +0200 Subject: [PATCH 48/59] - removed the sequential processing of JSON objects because the benefit is too small. After testing with a savegame on ZDCMP2 which is probably the largest map in existence, timing both methods resulted in a speed difference of less than 40 ms (70 vs 110 ms for reading all sectory, linedefs, sidedefs and objects). This compares to an overall restoration time, including reloading the level, precaching all textures and setting everything up, of approx. 1.2 s, meaning an increase of 3% of the entire reloading time. That's simply not worth all the negative side effects that may happen with a method that highly depends on proper code construction. On the other hand, using random access means that a savegame version change is only needed now when the semantics of a field change, but not if some get added or deleted. - do not I_Error out in the serializer unless caused by a programming error. It is better to let the serializer finish, collect all the errors and I_Error out when the game is known to be in a stable enough state to allow unwinding. --- src/d_netinfo.cpp | 2 +- src/dobjtype.cpp | 18 +- src/dthinker.cpp | 8 +- src/dthinker.h | 1 + src/g_game.cpp | 281 ++++++++++++++++---------------- src/g_level.cpp | 2 +- src/g_shared/a_sectoraction.cpp | 1 + src/p_acs.cpp | 47 +++--- src/p_saveg.cpp | 10 +- src/serializer.cpp | 259 ++++++++++++++++++----------- src/serializer.h | 7 +- 11 files changed, 364 insertions(+), 272 deletions(-) diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 0bd3ded1f..3827df491 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -931,7 +931,7 @@ void ReadUserInfo(FSerializer &arc, userinfo_t &info, FString &skin) { while ((key = arc.GetKey())) { - arc.StringPtr(key, str); + arc.StringPtr(nullptr, str); name = key; cvar = info.CheckKey(name); if (cvar != NULL && *cvar != NULL) diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index 5629fa4aa..9693da137 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -2250,7 +2250,7 @@ bool PStruct::ReadFields(FSerializer &ar, void *addr) const } else { - readsomething |= static_cast(sym)->Type->ReadValue(ar, label, + readsomething |= static_cast(sym)->Type->ReadValue(ar, nullptr, (BYTE *)addr + static_cast(sym)->Offset); } } @@ -2543,11 +2543,19 @@ bool PClass::ReadAllFields(FSerializer &ar, void *addr) const bool readsomething = false; bool foundsomething = false; const char *key; + key = ar.GetKey(); + if (strcmp(key, "classtype")) + { + // this does not represent a DObject + Printf(TEXTCOLOR_RED "trying to read user variables but got a non-object (first key is '%s')", key); + ar.mErrors++; + return false; + } while ((key = ar.GetKey())) { if (strncmp(key, "class:", 6)) { - // This key does not represent any class fields anymore + // We have read all user variable blocks. break; } foundsomething = true; @@ -2565,7 +2573,11 @@ bool PClass::ReadAllFields(FSerializer &ar, void *addr) const } if (parent != nullptr) { - readsomething |= type->ReadFields(ar, addr); + if (ar.BeginObject(nullptr)) + { + readsomething |= type->ReadFields(ar, addr); + ar.EndObject(); + } } else { diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 3cb40cb8f..0cd2c6a68 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -394,8 +394,12 @@ void DThinker::Tick () size_t DThinker::PropagateMark() { - assert(NextThinker != NULL && !(NextThinker->ObjectFlags & OF_EuthanizeMe)); - assert(PrevThinker != NULL && !(PrevThinker->ObjectFlags & OF_EuthanizeMe)); + // Do not choke on partially initialized objects (as happens when loading a savegame fails) + if (NextThinker != nullptr || PrevThinker != nullptr) + { + assert(NextThinker != nullptr && !(NextThinker->ObjectFlags & OF_EuthanizeMe)); + assert(PrevThinker != nullptr && !(PrevThinker->ObjectFlags & OF_EuthanizeMe)); + } GC::Mark(NextThinker); GC::Mark(PrevThinker); return Super::PropagateMark(); diff --git a/src/dthinker.h b/src/dthinker.h index 3f3a37b78..9ce8943b4 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -105,6 +105,7 @@ private: friend struct FThinkerList; friend class FThinkerIterator; friend class DObject; + friend class FSerializer; DThinker *NextThinker, *PrevThinker; }; diff --git a/src/g_game.cpp b/src/g_game.cpp index 3147f694d..5d1f83b82 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __APPLE__ #include #endif @@ -1844,154 +1845,162 @@ void G_DoLoadGame () Printf ("Could not read savegame '%s'\n", savename.GetChars()); return; } - - FResourceLump *info = resfile->FindLump("info.json"); - if (info == nullptr) + try { - delete resfile; - Printf ("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars()); - return; - } - - SaveVersion = 0; - - void *data = info->CacheLump(); - FSerializer arc; - if (!arc.OpenReader((const char *)data, info->LumpSize)) - { - Printf("Failed to access savegame info\n"); - delete resfile; - return; - } - - // Check whether this savegame actually has been created by a compatible engine. - // Since there are ZDoom derivates using the exact same savegame format but - // with mutual incompatibilities this check simplifies things significantly. - FString savever, engine, map; - arc("Save Version", SaveVersion); - arc("Engine", engine); - arc("Current Map", map); - - if (engine.CompareNoCase(GAMESIG) != 0) - { - // Make a special case for the message printed for old savegames that don't - // have this information. - if (engine.IsEmpty()) + FResourceLump *info = resfile->FindLump("info.json"); + if (info == nullptr) { - Printf ("Savegame is from an incompatible version\n"); + delete resfile; + Printf("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars()); + return; } - else + + SaveVersion = 0; + + void *data = info->CacheLump(); + FSerializer arc; + if (!arc.OpenReader((const char *)data, info->LumpSize)) { - Printf ("Savegame is from another ZDoom-based engine: %s\n", engine); + Printf("Failed to access savegame info\n"); + delete resfile; + return; } - delete resfile; - return; - } - if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER) - { - delete resfile; - Printf ("Savegame is from an incompatible version"); - if (SaveVersion < MINSAVEVER) + // Check whether this savegame actually has been created by a compatible engine. + // Since there are ZDoom derivates using the exact same savegame format but + // with mutual incompatibilities this check simplifies things significantly. + FString savever, engine, map; + arc("Save Version", SaveVersion); + arc("Engine", engine); + arc("Current Map", map); + + if (engine.CompareNoCase(GAMESIG) != 0) { - Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER); + // Make a special case for the message printed for old savegames that don't + // have this information. + if (engine.IsEmpty()) + { + Printf("Savegame is from an incompatible version\n"); + } + else + { + Printf("Savegame is from another ZDoom-based engine: %s\n", engine); + } + delete resfile; + return; } - else + + if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER) { - Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER); + delete resfile; + Printf("Savegame is from an incompatible version"); + if (SaveVersion < MINSAVEVER) + { + Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER); + } + else + { + Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER); + } + Printf("\n"); + return; } - Printf("\n"); + + if (!G_CheckSaveGameWads(arc, true)) + { + delete resfile; + return; + } + + if (map.IsEmpty()) + { + Printf("Savegame is missing the current map\n"); + delete resfile; + return; + } + + // Now that it looks like we can load this save, hide the fullscreen console if it was up + // when the game was selected from the menu. + if (hidecon && gamestate == GS_FULLCONSOLE) + { + gamestate = GS_HIDECONSOLE; + } + // we are done with info.json. + arc.Close(); + + info = resfile->FindLump("globals.json"); + if (info == nullptr) + { + delete resfile; + Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars()); + return; + } + + data = info->CacheLump(); + if (!arc.OpenReader((const char *)data, info->LumpSize)) + { + Printf("Failed to access savegame info\n"); + delete resfile; + return; + } + + + // Read intermission data for hubs + G_SerializeHub(arc); + + bglobal.RemoveAllBots(true); + + FString cvar; + arc("importantcvars", cvar); + if (!cvar.IsEmpty()) + { + BYTE *vars_p = (BYTE *)cvar.GetChars(); + C_ReadCVars(&vars_p); + } + + DWORD time[2] = { 1,0 }; + + arc("ticrate", time[0]) + ("leveltime", time[1]); + // dearchive all the modifications + level.time = Scale(time[1], TICRATE, time[0]); + + G_ReadSnapshots(resfile); + delete resfile; // we no longer need the resource file below this point + resfile = nullptr; + G_ReadVisited(arc); + + // load a base level + savegamerestore = true; // Use the player actors in the savegame + bool demoplaybacksave = demoplayback; + G_InitNew(map, false); + demoplayback = demoplaybacksave; + savegamerestore = false; + + STAT_Serialize(arc); + FRandom::StaticReadRNGState(arc); + P_ReadACSDefereds(arc); + P_ReadACSVars(arc); + + NextSkill = -1; + arc("nextskill", NextSkill); + + if (level.info != nullptr) + level.info->Snapshot.Clean(); + + BackupSaveName = savename; + + // At this point, the GC threshold is likely a lot higher than the + // amount of memory in use, so bring it down now by starting a + // collection. + GC::StartCollection(); + } + catch (...) + { + // delete the resource file if anything goes wrong in here. + if (resfile != nullptr) delete resfile; return; } - - if (!G_CheckSaveGameWads (arc, true)) - { - delete resfile; - return; - } - - if (map.IsEmpty()) - { - Printf ("Savegame is missing the current map\n"); - delete resfile; - return; - } - - // Now that it looks like we can load this save, hide the fullscreen console if it was up - // when the game was selected from the menu. - if (hidecon && gamestate == GS_FULLCONSOLE) - { - gamestate = GS_HIDECONSOLE; - } - // we are done with info.json. - arc.Close(); - - info = resfile->FindLump("globals.json"); - if (info == nullptr) - { - delete resfile; - Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars()); - return; - } - - data = info->CacheLump(); - if (!arc.OpenReader((const char *)data, info->LumpSize)) - { - Printf("Failed to access savegame info\n"); - delete resfile; - return; - } - - - // Read intermission data for hubs - G_SerializeHub(arc); - - bglobal.RemoveAllBots (true); - - FString cvar; - arc("importantcvars",cvar); - if (!cvar.IsEmpty()) - { - BYTE *vars_p = (BYTE *)cvar.GetChars(); - C_ReadCVars (&vars_p); - } - - DWORD time[2] = { 1,0 }; - - arc("ticrate", time[0]) - ("leveltime", time[1]); - // dearchive all the modifications - level.time = Scale (time[1], TICRATE, time[0]); - - G_ReadSnapshots(resfile); - G_ReadVisited(arc); - - // load a base level - savegamerestore = true; // Use the player actors in the savegame - bool demoplaybacksave = demoplayback; - G_InitNew (map, false); - demoplayback = demoplaybacksave; - savegamerestore = false; - - STAT_Serialize(arc); - FRandom::StaticReadRNGState(arc); - P_ReadACSDefereds(arc); - P_ReadACSVars(arc); - - NextSkill = -1; - arc("nextskill", NextSkill); - - if (level.info != nullptr) - level.info->Snapshot.Clean(); - - BackupSaveName = savename; - - delete resfile; - - // At this point, the GC threshold is likely a lot higher than the - // amount of memory in use, so bring it down now by starting a - // collection. - GC::StartCollection(); } diff --git a/src/g_level.cpp b/src/g_level.cpp index de2724849..dcf97ba4f 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1773,7 +1773,7 @@ void P_ReadACSDefereds (FSerializer &arc) { I_Error("Unknown map '%s' in savegame", key); } - arc(key, i->deferred); + arc(nullptr, i->deferred); } arc.EndObject(); } diff --git a/src/g_shared/a_sectoraction.cpp b/src/g_shared/a_sectoraction.cpp index da3830d74..38435e16b 100644 --- a/src/g_shared/a_sectoraction.cpp +++ b/src/g_shared/a_sectoraction.cpp @@ -69,6 +69,7 @@ void ASectorAction::Destroy () { *prev.act = probe->tracer; } + Sector = nullptr; } Super::Destroy (); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e06109678..3bd7218b3 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1051,13 +1051,13 @@ static void ReadArrayVars (FSerializer &file, FWorldGlobalArray *vars, size_t co while ((arraykey = file.GetKey())) { int i = (int)strtol(arraykey, nullptr, 10); - if (file.BeginObject(arraykey)) + if (file.BeginObject(nullptr)) { while ((arraykey = file.GetKey())) { int k = (int)strtol(arraykey, nullptr, 10); int val; - file(arraykey, val); + file(nullptr, val); vars[i].Insert(k, val); } file.EndObject(); @@ -1612,10 +1612,9 @@ void FBehavior::StaticSerializeModuleStates (FSerializer &arc) { auto modnum = StaticModules.Size(); - if (arc.BeginObject("acsmodules")) + if (arc.BeginArray("acsmodules")) { - arc("count", modnum); - + int modnum = arc.ArraySize(); if (modnum != StaticModules.Size()) { I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); @@ -1623,35 +1622,31 @@ void FBehavior::StaticSerializeModuleStates (FSerializer &arc) for (modnum = 0; modnum < StaticModules.Size(); ++modnum) { - if (arc.BeginArray("modules")) + FBehavior *module = StaticModules[modnum]; + const char *modname = module->ModuleName; + int ModSize = module->GetDataSize(); + + if (arc.BeginObject(nullptr)) { - FBehavior *module = StaticModules[modnum]; - const char *modname = module->ModuleName; - int ModSize = module->GetDataSize(); + arc.StringPtr("modname", modname) + ("modsize", ModSize); - if (arc.BeginObject(nullptr)) + if (arc.isReading()) { - arc.StringPtr("modname", modname) - ("modsize", ModSize); - - if (arc.isReading()) + if (stricmp(modname, module->ModuleName) != 0) { - if (stricmp(modname, module->ModuleName) != 0) - { - I_Error("Level was saved with a different set or order of ACS modules. (Have %s, save has %s)", module->ModuleName, modname); - } - else if (ModSize != module->GetDataSize()) - { - I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); - } + I_Error("Level was saved with a different set or order of ACS modules. (Have %s, save has %s)", module->ModuleName, modname); + } + else if (ModSize != module->GetDataSize()) + { + I_Error("ACS module %s has changed from what was saved. (Have %d bytes, save has %d bytes)", module->ModuleName, module->GetDataSize(), ModSize); } - module->SerializeVars(arc); - arc.EndObject(); } - arc.EndArray(); + module->SerializeVars(arc); + arc.EndObject(); } } - arc.EndObject(); + arc.EndArray(); } } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 72717f2d6..e6c521b1b 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -904,6 +904,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) if (arc.isReading()) { P_DestroyThinkers(hubload); + interpolator.ClearInterpolations(); arc.ReadObjects(); } @@ -933,15 +934,14 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) sky1texture = level.skytexture1; sky2texture = level.skytexture2; R_InitSkyMap(); - interpolator.ClearInterpolations(); + G_AirControlChanged(); } - G_AirControlChanged(); // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON - FBehavior::StaticSerializeModuleStates(arc); +//FBehavior::StaticSerializeModuleStates(arc); // The order here is important: First world state, then portal state, then thinkers, and last polyobjects. arc.Array("linedefs", lines, &loadlines[0], numlines); arc.Array("sidedefs", sides, &loadsides[0], numsides); @@ -950,7 +950,8 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) arc("lineportals", linePortals); arc("sectorportals", sectorPortals); if (arc.isReading()) P_CollectLinkedPortals(); - DThinker::SerializeThinkers(arc, !hubload); + +// DThinker::SerializeThinkers(arc, !hubload); arc.Array("polyobjs", polyobjs, po_NumPolyobjs); arc("subsectors", subsectors); StatusBar->SerializeMessages(arc); @@ -959,6 +960,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) FCanvasTextureInfo::Serialize(arc); P_SerializePlayers(arc, hubload); P_SerializeSounds(arc); + if (arc.isReading()) { for (int i = 0; i < numsectors; i++) diff --git a/src/serializer.cpp b/src/serializer.cpp index bd09d90e9..4d9da256b 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -28,6 +28,7 @@ #include "v_font.h" #include "w_zip.h" #include "doomerrors.h" +#include "v_text.h" char nulspace[1024 * 1024 * 4]; @@ -42,12 +43,10 @@ struct FJSONObject rapidjson::Value *mObject; rapidjson::Value::MemberIterator mIterator; int mIndex; - bool mRandomAccess; - FJSONObject(rapidjson::Value *v, bool randomaccess = false) + FJSONObject(rapidjson::Value *v) { mObject = v; - mRandomAccess = randomaccess; if (v->IsObject()) mIterator = v->MemberBegin(); else if (v->IsArray()) { @@ -198,37 +197,34 @@ struct FReader TArray mObjects; rapidjson::Document mDoc; TArray mDObjects; + rapidjson::Value *mKeyValue = nullptr; int mPlayers[MAXPLAYERS]; - bool mObjectsRead; + bool mObjectsRead = false; - FReader(const char *buffer, size_t length, bool randomaccess) + FReader(const char *buffer, size_t length) { rapidjson::Document doc; mDoc.Parse(buffer, length); - mObjects.Push(FJSONObject(&mDoc, randomaccess)); + mObjects.Push(FJSONObject(&mDoc)); memset(mPlayers, 0, sizeof(mPlayers)); - mObjectsRead = false; } - + rapidjson::Value *FindKey(const char *key) { FJSONObject &obj = mObjects.Last(); if (obj.mObject->IsObject()) { - if (!obj.mRandomAccess) + if (key == nullptr) { - if (obj.mIterator != obj.mObject->MemberEnd()) - { - if (!strcmp(key, obj.mIterator->name.GetString())) - { - return &(obj.mIterator++)->value; - } - } + // we are performing an iteration of the object through GetKey. + auto p = mKeyValue; + mKeyValue = nullptr; + return p; } else { - // for unordered searches. This is slower but will not rely on sequential order of items. + // Find the given key by name; auto it = obj.mObject->FindMember(key); if (it == obj.mObject->MemberEnd()) return nullptr; return &it->value; @@ -252,8 +248,9 @@ struct FReader bool FSerializer::OpenWriter(bool pretty) { if (w != nullptr || r != nullptr) return false; - w = new FWriter(pretty); + mErrors = 0; + w = new FWriter(pretty); BeginObject(nullptr); return true; } @@ -267,7 +264,9 @@ bool FSerializer::OpenWriter(bool pretty) bool FSerializer::OpenReader(const char *buffer, size_t length) { if (w != nullptr || r != nullptr) return false; - r = new FReader(buffer, length, true); + + mErrors = 0; + r = new FReader(buffer, length); return true; } @@ -282,15 +281,17 @@ bool FSerializer::OpenReader(FCompressedBuffer *input) if (input->mSize <= 0 || input->mBuffer == nullptr) return false; if (w != nullptr || r != nullptr) return false; + mErrors = 0; if (input->mMethod == METHOD_STORED) { - r = new FReader((char*)input->mBuffer, input->mSize, true); + r = new FReader((char*)input->mBuffer, input->mSize); } else { char *unpacked = new char[input->mSize]; input->Decompress(unpacked); - r = new FReader(unpacked, input->mSize, true); + r = new FReader(unpacked, input->mSize); + delete[] unpacked; } return true; } @@ -310,9 +311,27 @@ 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(); + } + } + } + delete r; r = nullptr; } + if (mErrors > 0) + { + I_Error("%d errors parsing JSON", mErrors); + } } //========================================================================== @@ -369,7 +388,7 @@ void FSerializer::WriteKey(const char *key) // //========================================================================== -bool FSerializer::BeginObject(const char *name, bool randomaccess) +bool FSerializer::BeginObject(const char *name) { if (isWriting()) { @@ -382,13 +401,16 @@ bool FSerializer::BeginObject(const char *name, bool randomaccess) auto val = r->FindKey(name); if (val != nullptr) { + assert(val->IsObject()); if (val->IsObject()) { - r->mObjects.Push(FJSONObject(val, randomaccess)); + r->mObjects.Push(FJSONObject(val)); } else { - I_Error("Object expected for '%s'", name); + Printf(TEXTCOLOR_RED "Object expected for '%s'", name); + mErrors++; + return false; } } else @@ -405,7 +427,7 @@ bool FSerializer::BeginObject(const char *name, bool randomaccess) // //========================================================================== -void FSerializer::EndObject(bool endwarning) +void FSerializer::EndObject() { if (isWriting()) { @@ -422,14 +444,6 @@ void FSerializer::EndObject(bool endwarning) } else { - if (endwarning && !r->mObjects.Last().mRandomAccess) - { - if (r->mObjects.Last().mIterator != r->mObjects.Last().mObject->MemberEnd()) - { - assert(false && "Incomplete read of sequential object"); - I_Error("Incomplete read of sequential object"); - } - } r->mObjects.Pop(); } } @@ -453,13 +467,16 @@ bool FSerializer::BeginArray(const char *name) auto val = r->FindKey(name); if (val != nullptr) { + assert(val->IsArray()); if (val->IsArray()) { r->mObjects.Push(FJSONObject(val)); } else { - I_Error("Array expected for '%s'", name); + Printf(TEXTCOLOR_RED "Array expected for '%s'", name); + mErrors++; + return false; } } else @@ -487,6 +504,7 @@ void FSerializer::EndArray() } else { + assert(false && "EndArray call not inside an array"); I_Error("EndArray call not inside an array"); } } @@ -496,22 +514,6 @@ void FSerializer::EndArray() } } -//========================================================================== -// -// Discards an entry (only needed for sequential access) -// -//========================================================================== - -FSerializer &FSerializer::Discard(const char *key) -{ - if (isReading()) - { - // just get the key and advance the iterator, if present - if (!r->mObjects.Last().mRandomAccess) r->FindKey(key); - } - return *this; -} - //========================================================================== // // Special handler for args (because ACS specials' arg0 needs special treatment.) @@ -563,13 +565,17 @@ FSerializer &FSerializer::Args(const char *key, int *args, int *defargs, int spe } else { - I_Error("Integer expected for '%s[%d]'", key, i); + assert(false && "Integer expected"); + Printf(TEXTCOLOR_RED "Integer expected for '%s[%d]'", key, i); + mErrors++; } } } else { - I_Error("array expected for '%s'", key); + assert(false && "array expected"); + Printf(TEXTCOLOR_RED "array expected for '%s'", key); + mErrors++; } } } @@ -611,7 +617,9 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num) } else { - I_Error("Integer expected for '%s'", key); + assert(false && "Integer expected"); + Printf(TEXTCOLOR_RED "Integer expected for '%s'", key); + mErrors++; } } } @@ -739,7 +747,8 @@ unsigned FSerializer::GetSize(const char *group) //========================================================================== // -// +// gets the key pointed to by the iterator, caches its value +// and returns the key string. // //========================================================================== @@ -749,7 +758,8 @@ const char *FSerializer::GetKey() if (!r->mObjects.Last().mObject->IsObject()) return nullptr; // non-objects do not have keys. auto &it = r->mObjects.Last().mIterator; if (it == r->mObjects.Last().mObject->MemberEnd()) return nullptr; - return it->name.GetString(); + r->mKeyValue = &it->value; + return (it++)->name.GetString(); } //========================================================================== @@ -773,6 +783,9 @@ void FSerializer::WriteObjects() w->Key("classtype"); w->String(obj->GetClass()->TypeName.GetChars()); + obj->SerializeUserVars(*this); + obj->Serialize(*this); + obj->CheckIfSerialized(); if (obj->IsKindOf(RUNTIME_CLASS(AActor)) && (player = static_cast(obj)->player) && player->mo == obj) @@ -781,9 +794,6 @@ void FSerializer::WriteObjects() w->Int(int(player - players)); } - obj->SerializeUserVars(*this); - obj->Serialize(*this); - obj->CheckIfSerialized(); EndObject(); } EndArray(); @@ -799,8 +809,7 @@ void FSerializer::WriteObjects() void FSerializer::ReadObjects() { bool founderrors = false; - unsigned i; - + if (isReading() && BeginArray("objects")) { // Do not link any thinker that's being created here. This will be done by deserializing the thinker list later. @@ -845,7 +854,6 @@ void FSerializer::ReadObjects() if (BeginObject(nullptr)) { int pindex = -1; - Discard("classtype"); Serialize(*this, "playerindex", pindex, nullptr); if (obj != nullptr) { @@ -857,20 +865,23 @@ void FSerializer::ReadObjects() obj->SerializeUserVars(*this); obj->Serialize(*this); } - EndObject(true); + EndObject(); } } catch (CRecoverableError &err) { - I_Error("%s\n while restoring %s", err.GetMessage(),obj? obj->GetClass()->TypeName.GetChars() : "invalid object"); + Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(),obj? obj->GetClass()->TypeName.GetChars() : "invalid object"); + mErrors++; } } } EndArray(); DThinker::bSerialOverride = false; + assert(!founderrors); if (founderrors) { - I_Error("Failed to restore all objects in savegame"); + Printf(TEXTCOLOR_RED "Failed to restore all objects in savegame"); + mErrors++; } } catch(...) @@ -989,13 +1000,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *def auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsBool()); if (val->IsBool()) { value = val->GetBool(); } else { - I_Error("boolean type expected for '%s'", key); + Printf(TEXTCOLOR_RED "boolean type expected for '%s'", key); + arc.mErrors++; } } } @@ -1023,13 +1036,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_ auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsInt64()); if (val->IsInt64()) { value = val->GetInt64(); } else { - I_Error("integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + arc.mErrors++; } } } @@ -1057,13 +1072,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint6 auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsUint64()); if (val->IsUint64()) { value = val->GetUint64(); } else { - I_Error("integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + arc.mErrors++; } } } @@ -1092,13 +1109,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, int32_t &value, int32_ auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsInt()); if (val->IsInt()) { value = val->GetInt(); } else { - I_Error("integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + arc.mErrors++; } } } @@ -1126,13 +1145,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3 auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsUint()); if (val->IsUint()) { value = val->GetUint(); } else { - I_Error("integer type expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer type expected for '%s'", key); + arc.mErrors++; } } } @@ -1202,13 +1223,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, double &value, double auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsDouble()); if (val->IsDouble()) { value = val->GetDouble(); } else { - I_Error("float type expected for '%s'", key); + Printf(TEXTCOLOR_RED "float type expected for '%s'", key); + arc.mErrors++; } } } @@ -1239,6 +1262,7 @@ 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) { ptrdiff_t vv = value == nullptr ? -1 : value - base; @@ -1342,13 +1366,16 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe { const rapidjson::Value &nameval = (*val)[0]; const rapidjson::Value &typeval = (*val)[1]; + assert(nameval.IsString() && typeval.IsInt()); if (nameval.IsString() && typeval.IsInt()) { value = TexMan.GetTexture(nameval.GetString(), typeval.GetInt()); } else { - I_Error("object does not represent a texture for '%s'", key); + Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key); + value.SetNull(); + arc.mErrors++; } } else if (val->IsNull()) @@ -1361,7 +1388,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe } else { - I_Error("object does not represent a texture for '%s'", key); + assert(false && "not a texture"); + Printf(TEXTCOLOR_RED "object does not represent a texture for '%s'", key); + value.SetNull(); + arc.mErrors++; } } } @@ -1432,7 +1462,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje } else { - I_Error("Invalid object reference for '%s'", key); + assert(false && "invalid object reference"); + Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key); + value = nullptr; + arc.mErrors++; } } } @@ -1469,13 +1502,16 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsString()); if (val->IsString()) { value = val->GetString(); } else { - I_Error("String expected for '%s'", key); + Printf(TEXTCOLOR_RED "String expected for '%s'", key); + arc.mErrors++; + value = NAME_None; } } } @@ -1509,7 +1545,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicCol auto val = arc.r->FindKey(key); if (val != nullptr) { - if (val->IsObject()) + if (val->IsArray()) { const rapidjson::Value &colorval = (*val)[0]; const rapidjson::Value &fadeval = (*val)[1]; @@ -1517,16 +1553,12 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDynamicCol if (colorval.IsUint() && fadeval.IsUint() && desatval.IsUint()) { cm = GetSpecialLights(colorval.GetUint(), fadeval.GetUint(), desatval.GetUint()); - } - else - { - I_Error("object does not represent a colormap for '%s'", key); + return arc; } } - else - { - I_Error("object does not represent a colormap for '%s'", key); - } + assert(false && "not a colormap"); + Printf(TEXTCOLOR_RED "object does not represent a colormap for '%s'", key); + cm = &NormalLight; } } return arc; @@ -1555,6 +1587,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsString() || val->IsNull()); if (val->IsString()) { sid = val->GetString(); @@ -1565,7 +1598,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI } else { - I_Error("string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + sid = 0; + arc.mErrors++; } } } @@ -1601,6 +1636,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsString() || val->IsNull()); if (val->IsString()) { clst = PClass::FindActor(val->GetString()); @@ -1611,7 +1647,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClassActor } else { - I_Error("string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + clst = nullptr; + arc.mErrors++; } } } @@ -1657,7 +1695,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, PClass *&cl } else { - I_Error("string type expected for '%s'", key); + Printf(TEXTCOLOR_RED "string type expected for '%s'", key); + clst = nullptr; + arc.mErrors++; } } } @@ -1719,18 +1759,33 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState const rapidjson::Value &ndx = (*val)[1]; state = nullptr; - if (cls.IsString() && ndx.IsInt()) + assert(cls.IsString() && ndx.IsUint()); + if (cls.IsString() && ndx.IsUint()) { PClassActor *clas = PClass::FindActor(cls.GetString()); - if (clas) + if (clas && ndx.GetUint() < (unsigned)clas->NumOwnedStates) { - state = clas->OwnedStates + ndx.GetInt(); + state = clas->OwnedStates + 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'", cls.GetString(), ndx.GetInt(), key); + } + } + else + { + assert(false && "not a state"); + Printf(TEXTCOLOR_RED "data does not represent a state for '%s'", key); + arc.mErrors++; } } else if (!retcode) { - I_Error("array type expected for '%s'", key); + assert(false && "not an array"); + Printf(TEXTCOLOR_RED "array type expected for '%s'", key); + arc.mErrors++; } } } @@ -1766,6 +1821,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsUint() || val->IsNull()); if (val->IsNull()) { node = nullptr; @@ -1774,7 +1830,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial { if (val->GetUint() >= StrifeDialogues.Size()) { - node = NULL; + node = nullptr; } else { @@ -1783,7 +1839,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial } else { - I_Error("integer expected for '%s'", key); + Printf(TEXTCOLOR_RED "integer expected for '%s'", key); + arc.mErrors++; + node = nullptr; } } } @@ -1819,6 +1877,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsNull() || val->IsString()); if (val->IsNull()) { pstr = nullptr; @@ -1829,7 +1888,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p } else { - I_Error("string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'", key); + pstr = nullptr; + arc.mErrors++; } } } @@ -1858,6 +1919,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsNull() || val->IsString()); if (val->IsNull()) { pstr = ""; @@ -1868,7 +1930,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FString &pstr, FString } else { - I_Error("string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'", key); + pstr = ""; + arc.mErrors++; } } } @@ -1904,6 +1968,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr auto val = arc.r->FindKey(key); if (val != nullptr) { + assert(val->IsNull() || val->IsString()); if (val->IsNull()) { pstr = nullptr; @@ -1914,7 +1979,9 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr } else { - I_Error("string expected for '%s'", key); + Printf(TEXTCOLOR_RED "string expected for '%s'", key); + pstr = nullptr; + arc.mErrors++; } } } @@ -1941,7 +2008,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&fon font = V_GetFont(n); if (font == nullptr) { - Printf("Could not load font %s\n", n); + Printf(TEXTCOLOR_ORANGE "Could not load font %s\n", n); font = SmallFont; } return arc; diff --git a/src/serializer.h b/src/serializer.h index 7b35294d2..4730136a2 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -73,15 +73,14 @@ public: bool OpenReader(FCompressedBuffer *input); void Close(); void ReadObjects(); - bool BeginObject(const char *name, bool randomaccess = false); - void EndObject(bool endwarning = false); + bool BeginObject(const char *name); + void EndObject(); bool BeginArray(const char *name); void EndArray(); unsigned GetSize(const char *group); const char *GetKey(); const char *GetOutput(unsigned *len = nullptr); FCompressedBuffer GetCompressedOutput(); - FSerializer &Discard(const char *key); 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); @@ -167,6 +166,8 @@ public: obj = (T)val; return *this; } + + int mErrors = 0; }; FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval); From edb7f7959e2d5a423325cb5a29f183191c89d5de Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 14:06:51 +0200 Subject: [PATCH 49/59] - re-enable some commented-out stuff. --- src/p_saveg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index e6c521b1b..6459fc60f 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -941,7 +941,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON -//FBehavior::StaticSerializeModuleStates(arc); + FBehavior::StaticSerializeModuleStates(arc); // The order here is important: First world state, then portal state, then thinkers, and last polyobjects. arc.Array("linedefs", lines, &loadlines[0], numlines); arc.Array("sidedefs", sides, &loadsides[0], numsides); @@ -951,7 +951,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) arc("sectorportals", sectorPortals); if (arc.isReading()) P_CollectLinkedPortals(); -// DThinker::SerializeThinkers(arc, !hubload); + DThinker::SerializeThinkers(arc, !hubload); arc.Array("polyobjs", polyobjs, po_NumPolyobjs); arc("subsectors", subsectors); StatusBar->SerializeMessages(arc); From 01d28e3eb2ddba3f95f6d98d37b260b6f238d5d0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 17:49:33 +0200 Subject: [PATCH 50/59] - added the last missing bits of the savegame code - thinker list deserialization and handling of players during hub travel. Now testing is what remains... --- src/CMakeLists.txt | 1 - src/dthinker.cpp | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/json.cpp | 39 ----------------------- src/p_saveg.cpp | 6 +--- src/serializer.cpp | 58 ++++++++++++++++++++++++++-------- src/serializer.h | 10 +++--- 6 files changed, 130 insertions(+), 63 deletions(-) delete mode 100644 src/json.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d6878a19e..ab5f137ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1001,7 +1001,6 @@ set( FASTMATH_SOURCES ) set (PCH_SOURCES - json.cpp actorptrselect.cpp am_map.cpp b_bot.cpp diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 0cd2c6a68..5dbded654 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -116,6 +116,85 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node) } } +//========================================================================== +// +// +// +//========================================================================== + +void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) +{ + //DThinker *thinker; + //BYTE stat; + //int statcount; + int i; + + if (arc.isWriting()) + { + arc.BeginArray("thinkers"); + for (i = 0; i <= MAX_STATNUM; i++) + { + arc.BeginArray(nullptr); + SaveList(arc, Thinkers[i].GetHead()); + SaveList(arc, FreshThinkers[i].GetHead()); + arc.EndArray(); + } + arc.EndArray(); + } + else + { + if (arc.BeginArray("thinkers")) + { + for (i = 0; i <= MAX_STATNUM; i++) + { + if (arc.BeginArray(nullptr)) + { + int size = arc.ArraySize(); + for (int j = 0; j < size; j++) + { + DThinker *thinker; + arc(nullptr, thinker); + if (thinker != nullptr) + { + // This may be a player stored in their ancillary list. Remove + // them first before inserting them into the new list. + if (thinker->NextThinker != nullptr) + { + thinker->Remove(); + } + // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers + // list. Anything else goes in the regular Thinkers list. + if (thinker->ObjectFlags & OF_EuthanizeMe) + { + // This thinker was destroyed during the loading process. Do + // not link it into any list. + } + else if (thinker->ObjectFlags & OF_JustSpawned) + { + FreshThinkers[i].AddTail(thinker); + thinker->PostSerialize(); + } + else + { + Thinkers[i].AddTail(thinker); + thinker->PostSerialize(); + } + } + } + arc.EndArray(); + } + } + arc.EndArray(); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + DThinker::DThinker (int statnum) throw() { NextThinker = NULL; diff --git a/src/json.cpp b/src/json.cpp deleted file mode 100644 index 1bdc5d90e..000000000 --- a/src/json.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "serializer.h" -#include "r_local.h" -#include "p_setup.h" -#include "c_dispatch.h" -#include "i_system.h" -#include "a_sharedglobal.h" - - - -//========================================================================== -// -// -// -//========================================================================== - - -void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) -{ - //DThinker *thinker; - //BYTE stat; - //int statcount; - int i; - - if (arc.isWriting()) - { - arc.BeginArray("thinkers"); - for (i = 0; i <= MAX_STATNUM; i++) - { - arc.BeginArray(nullptr); - SaveList(arc, Thinkers[i].GetHead()); - SaveList(arc, FreshThinkers[i].GetHead()); - arc.EndArray(); - } - arc.EndArray(); - } - else - { - } -} diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 6459fc60f..fb1504083 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -63,10 +63,6 @@ #include "r_renderer.h" #include "serializer.h" -// just the stuff that already got converted to FSerializer so that it can be seen as 'done' when searching. -#include "zzz_old.cpp" - - // // Thinkers // @@ -905,7 +901,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) { P_DestroyThinkers(hubload); interpolator.ClearInterpolations(); - arc.ReadObjects(); + arc.ReadObjects(hubload); } arc("level.flags", level.flags) diff --git a/src/serializer.cpp b/src/serializer.cpp index 4d9da256b..3045515f7 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -31,6 +31,7 @@ #include "v_text.h" char nulspace[1024 * 1024 * 4]; +bool save_full = false; // for testing. Should be removed afterward. //========================================================================== // @@ -206,7 +207,7 @@ struct FReader rapidjson::Document doc; mDoc.Parse(buffer, length); mObjects.Push(FJSONObject(&mDoc)); - memset(mPlayers, 0, sizeof(mPlayers)); + memset(mPlayers, -1, sizeof(mPlayers)); } rapidjson::Value *FindKey(const char *key) @@ -806,7 +807,7 @@ void FSerializer::WriteObjects() // //========================================================================== -void FSerializer::ReadObjects() +void FSerializer::ReadObjects(bool hubtravel) { bool founderrors = false; @@ -823,6 +824,7 @@ void FSerializer::ReadObjects() if (BeginObject(nullptr)) { FString clsname; // do not deserialize the class type directly so that we can print appropriate errors. + int pindex = -1; Serialize(*this, "classtype", clsname, nullptr); PClass *cls = PClass::FindClass(clsname); @@ -830,6 +832,7 @@ void FSerializer::ReadObjects() { Printf("Unknown object class '%d' in savegame", clsname.GetChars()); founderrors = true; + r->mDObjects[i] = RUNTIME_CLASS(AActor)->CreateNew(); // make sure we got at least a valid pointer for the duration of the loading process. } else { @@ -849,33 +852,60 @@ void FSerializer::ReadObjects() for (unsigned i = 0; i < r->mDObjects.Size(); i++) { auto obj = r->mDObjects[i]; - try + if (BeginObject(nullptr)) { - if (BeginObject(nullptr)) + if (obj != nullptr) { int pindex = -1; - Serialize(*this, "playerindex", pindex, nullptr); - if (obj != nullptr) + if (hubtravel) { - if (pindex >= 0 && pindex < MAXPLAYERS) + // mark this as a hub travelling player. This needs to be taken care of later and be replaced with the real travelling player, + // but that's better done at the end of this loop so that inventory ownership is not getting messed up. + Serialize(*this, "playerindex", pindex, nullptr); + if (hubtravel && pindex >= 0 && pindex < MAXPLAYERS) { - obj->ObjectFlags |= OF_LoadedPlayer; r->mPlayers[pindex] = int(i); } + } + try + { obj->SerializeUserVars(*this); obj->Serialize(*this); } - EndObject(); + catch (CRecoverableError &err) + { + // In case something in here throws an error, let's continue and deal with it later. + Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object"); + mErrors++; + } } - } - catch (CRecoverableError &err) - { - Printf(TEXTCOLOR_RED "'%s'\n while restoring %s", err.GetMessage(),obj? obj->GetClass()->TypeName.GetChars() : "invalid object"); - mErrors++; + EndObject(); } } } EndArray(); + + if (hubtravel) + { + for (int i = 0; i < MAXPLAYERS; i++) + { + int pindex = r->mPlayers[i]; + if (pindex != -1) + { + if (players[i].mo != nullptr) + { + r->mDObjects[pindex]->Destroy(); + DObject::StaticPointerSubstitution(r->mDObjects[pindex], players[i].mo); + r->mDObjects[pindex] = players[i].mo; + } + else + { + players[i].mo = static_cast(r->mDObjects[pindex]); + } + } + } + } + DThinker::bSerialOverride = false; assert(!founderrors); if (founderrors) diff --git a/src/serializer.h b/src/serializer.h index 4730136a2..f1d35e1f8 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -7,6 +7,8 @@ #include "r_defs.h" #include "resourcefiles/file_zip.h" +extern bool save_full; + struct ticcmd_t; struct usercmd_t; @@ -72,7 +74,7 @@ public: bool OpenReader(const char *buffer, size_t length); bool OpenReader(FCompressedBuffer *input); void Close(); - void ReadObjects(); + void ReadObjects(bool hubtravel); bool BeginObject(const char *name); void EndObject(); bool BeginArray(const char *name); @@ -108,13 +110,13 @@ public: template FSerializer &operator()(const char *key, T &obj, T &def) { - return Serialize(*this, key, obj, &def); + return Serialize(*this, key, obj, save_full? nullptr : &def); } template FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false) { - if (fullcompare && isWriting() && nullcmp(obj, count * sizeof(T))) + if (!save_full && fullcompare && isWriting() && nullcmp(obj, count * sizeof(T))) { return *this; } @@ -138,7 +140,7 @@ public: template FSerializer &Array(const char *key, T *obj, T *def, int count, bool fullcompare = false) { - if (fullcompare && isWriting() && def != nullptr && !memcmp(obj, def, count * sizeof(T))) + if (!save_full && fullcompare && isWriting() && def != nullptr && !memcmp(obj, def, count * sizeof(T))) { return *this; } From b844ab137e6935af8bdecf42cc7464dd8925f045 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 18:12:38 +0200 Subject: [PATCH 51/59] - added some comments about the special player treatment in hub travels because this is not really intuitive... --- src/dobject.h | 1 - src/serializer.cpp | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dobject.h b/src/dobject.h index 538c7aba4..51102bc4c 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -209,7 +209,6 @@ enum EObjectFlags OF_JustSpawned = 1 << 8, // Thinker was spawned this tic OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list - OF_LoadedPlayer = 1 << 11, // this gets flagged during deserialization so that the player checks in there can be simplified. }; template class TObjPtr; diff --git a/src/serializer.cpp b/src/serializer.cpp index 3045515f7..246a3539a 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -818,7 +818,7 @@ void FSerializer::ReadObjects(bool hubtravel) { DThinker::bSerialOverride = true; r->mDObjects.Resize(ArraySize()); - // First create all the objects + // First iteration: create all the objects but do nothing with them yet. for (unsigned i = 0; i < r->mDObjects.Size(); i++) { if (BeginObject(nullptr)) @@ -833,6 +833,7 @@ void FSerializer::ReadObjects(bool hubtravel) Printf("Unknown object class '%d' in savegame", clsname.GetChars()); founderrors = true; r->mDObjects[i] = RUNTIME_CLASS(AActor)->CreateNew(); // make sure we got at least a valid pointer for the duration of the loading process. + r->mDObjects[i]->Destroy(); // but we do not want to keep this around, so destroy it right away. } else { @@ -859,8 +860,7 @@ void FSerializer::ReadObjects(bool hubtravel) int pindex = -1; if (hubtravel) { - // mark this as a hub travelling player. This needs to be taken care of later and be replaced with the real travelling player, - // but that's better done at the end of this loop so that inventory ownership is not getting messed up. + // mark this as a hub travelling player. This needs to be substituted later, but that's better done when all objects have been loaded. Serialize(*this, "playerindex", pindex, nullptr); if (hubtravel && pindex >= 0 && pindex < MAXPLAYERS) { @@ -885,6 +885,16 @@ void FSerializer::ReadObjects(bool hubtravel) } EndArray(); + // Special treatment for hub travel: We do not want the player pawn which exited the level when this snapshot was made + // but the one that got freshly created when the map was loaded in P_SetupLevel. + // The loop above marked all objects that represent a player, so now we go through them, and if a corresponding + // player had been spawned on map load it gets substituted here. + + // The substitution does not take place inside the above loop to ensure that no changes to the loaded objects + // can occur afterward. At this point everything has been loaded and the substitution is a simple matter of + // calling DObject::StaticPointerSubstitution. + + // If no corresponding player can be founded among the freshly spawned ones, the one from the snapshot is kept. if (hubtravel) { for (int i = 0; i < MAXPLAYERS; i++) From d28d02839eac3751942f6632d6c0ea5f55a49e10 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 19:19:26 +0200 Subject: [PATCH 52/59] - fixed: An actor's default stencil color should be set in DECORATE instead of doing some hackery elsewhere to compensate for the lack of initialization. --- wadsrc/static/actors/actor.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index b8b805219..374e2472d 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -37,6 +37,7 @@ ACTOR Actor native //: Thinker MissileHeight 32 SpriteAngle 0 SpriteRotation 0 + StencilColor "00 00 00" // Functions native bool CheckClass(class checkclass, int ptr_select = AAPTR_DEFAULT, bool match_superclass = false); From e4924c3d47e92f7f110d3b18c654a3e16e27f8e9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 20:05:12 +0200 Subject: [PATCH 53/59] - added some missing End... calls in player deserialization code. - fixed reading of music name. In its current state the code is now capable of reading an E1M1 savegame and continuing play. --- src/d_netinfo.cpp | 1 + src/p_mobj.cpp | 1 - src/p_saveg.cpp | 9 +- src/serializer.cpp | 8 +- src/zzz_old.cpp | 218 --------------------------------------------- 5 files changed, 13 insertions(+), 224 deletions(-) delete mode 100644 src/zzz_old.cpp diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 3827df491..d3ce8c943 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -948,6 +948,7 @@ void ReadUserInfo(FSerializer &arc, userinfo_t &info, FString &skin) } } } + arc.EndObject(); } } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f6139f843..657d5db49 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -323,7 +323,6 @@ void AActor::PostSerialize() LinkToWorld(false, Sector); AddToHash(); - SetShade(fillcolor); if (player) { if (playeringame[player - players] && diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index fb1504083..30f191e4e 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -523,14 +523,14 @@ void P_SerializeSounds(FSerializer &arc) { S_SerializeSounds(arc); DSeqNode::SerializeSequences (arc); - char *name = NULL; + const char *name = NULL; BYTE order; if (arc.isWriting()) { - order = S_GetMusic(&name); + order = S_GetMusic((char **)&name); } - arc("musicname", name) + arc.StringPtr("musicname", name) ("musicorder", order); if (arc.isReading()) @@ -612,6 +612,7 @@ void P_SerializePlayers(FSerializer &arc, bool skipload) { ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload); } + arc.EndArray(); } if (!skipload && numPlayersNow > numPlayers) { @@ -662,6 +663,7 @@ static void ReadOnePlayer(FSerializer &arc, bool skipload) } } } + arc.EndObject(); } } @@ -687,6 +689,7 @@ static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayers { arc.StringPtr("playername", nametemp[i]); playertemp[i].Serialize(arc); + arc.EndObject(); } tempPlayerUsed[i] = 0; } diff --git a/src/serializer.cpp b/src/serializer.cpp index 246a3539a..45e920f21 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -695,7 +695,10 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr) if (isWriting()) { WriteKey(key); - w->String(charptr); + if (charptr != nullptr) + w->String(charptr); + else + w->Null(); } else { @@ -894,7 +897,7 @@ void FSerializer::ReadObjects(bool hubtravel) // can occur afterward. At this point everything has been loaded and the substitution is a simple matter of // calling DObject::StaticPointerSubstitution. - // If no corresponding player can be founded among the freshly spawned ones, the one from the snapshot is kept. + // If no corresponding player can be found among the freshly spawned ones, the one from the snapshot is kept. if (hubtravel) { for (int i = 0; i < MAXPLAYERS; i++) @@ -904,6 +907,7 @@ void FSerializer::ReadObjects(bool hubtravel) { if (players[i].mo != nullptr) { + // Destroy the old pawn before substituting the pointer so that its inventory also gets properly destroyed. r->mDObjects[pindex]->Destroy(); DObject::StaticPointerSubstitution(r->mDObjects[pindex], players[i].mo); r->mDObjects[pindex] = players[i].mo; diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp deleted file mode 100644 index 387d40e96..000000000 --- a/src/zzz_old.cpp +++ /dev/null @@ -1,218 +0,0 @@ - -#include "a_doomglobal.h" -#include "a_hexenglobal.h" -#include "a_strifeglobal.h" -#include "ravenshared.h" -#include "a_weaponpiece.h" -#include "d_dehacked.h" - -// For NULL states, which aren't owned by any actor, the owner -// is recorded as AActor with the following state. AActor should -// never actually have this many states of its own, so this -// is (relatively) safe. - -#define NULL_STATE_INDEX 127 - -// These are special tokens found in the data stream of an archive. -// Whenever a new object is encountered, it gets created using new and -// is then asked to serialize itself before processing of the previous -// object continues. This can result in some very deep recursion if -// you aren't careful about how you organize your data. - -#define NEW_OBJ ((BYTE)1) // Data for a new object follows -#define NEW_CLS_OBJ ((BYTE)2) // Data for a new class and object follows -#define OLD_OBJ ((BYTE)3) // Reference to an old object follows -#define NULL_OBJ ((BYTE)4) // Load as NULL -#define M1_OBJ ((BYTE)44) // Load as (DObject*)-1 - -#define NEW_PLYR_OBJ ((BYTE)5) // Data for a new player follows -#define NEW_PLYR_CLS_OBJ ((BYTE)6) // Data for a new class and player follows - -#define NEW_NAME ((BYTE)27) // A new name follows -#define OLD_NAME ((BYTE)28) // Reference to an old name follows -#define NIL_NAME ((BYTE)33) // Load as NULL - -#define NEW_SPRITE ((BYTE)11) // A new sprite name follows -#define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows - - - -#if 0 -// still needed as reference. -FCrap &FCrap::ReadObject(DObject* &obj, PClass *wanttype) -{ - BYTE objHead; - const PClass *type; - BYTE playerNum; - DWORD index; - DObject *newobj; - - operator<< (objHead); - - switch (objHead) - { - case NULL_OBJ: - obj = NULL; - break; - - case M1_OBJ: - obj = (DObject *)~0; - break; - - case OLD_OBJ: - index = ReadCount(); - if (index >= ArchiveToObject.Size()) - { - I_Error("Object reference too high (%u; max is %u)\n", index, ArchiveToObject.Size()); - } - obj = ArchiveToObject[index]; - break; - - case NEW_PLYR_CLS_OBJ: - operator<< (playerNum); - if (m_HubTravel) - { - // If travelling inside a hub, use the existing player actor - type = ReadClass(wanttype); - // Printf ("New player class: %s (%u)\n", type->Name, m_File->Tell()); - obj = players[playerNum].mo; - - // But also create a new one so that we can get past the one - // stored in the archive. - AActor *tempobj = static_cast(type->CreateNew()); - MapObject(obj != NULL ? obj : tempobj); - tempobj->SerializeUserVars(*this); - tempobj->Serialize(*this); - tempobj->CheckIfSerialized(); - // If this player is not present anymore, keep the new body - // around just so that the load will succeed. - if (obj != NULL) - { - // When the temporary player's inventory items were loaded, - // they became owned by the real player. Undo that now. - for (AInventory *item = tempobj->Inventory; item != NULL; item = item->Inventory) - { - item->Owner = tempobj; - } - tempobj->Destroy(); - } - else - { - obj = tempobj; - players[playerNum].mo = static_cast(obj); - } - break; - } - /* fallthrough when not travelling to a previous level */ - case NEW_CLS_OBJ: - type = ReadClass(wanttype); - // Printf ("New class: %s (%u)\n", type->Name, m_File->Tell()); - newobj = obj = type->CreateNew(); - MapObject(obj); - newobj->SerializeUserVars(*this); - newobj->Serialize(*this); - newobj->CheckIfSerialized(); - break; - - case NEW_PLYR_OBJ: - operator<< (playerNum); - if (m_HubTravel) - { - type = ReadStoredClass(wanttype); - // Printf ("Use player class: %s (%u)\n", type->Name, m_File->Tell()); - obj = players[playerNum].mo; - - AActor *tempobj = static_cast(type->CreateNew()); - MapObject(obj != NULL ? obj : tempobj); - tempobj->SerializeUserVars(*this); - tempobj->Serialize(*this); - tempobj->CheckIfSerialized(); - if (obj != NULL) - { - for (AInventory *item = tempobj->Inventory; - item != NULL; item = item->Inventory) - { - item->Owner = tempobj; - } - tempobj->Destroy(); - } - else - { - obj = tempobj; - players[playerNum].mo = static_cast(obj); - } - break; - } - /* fallthrough when not travelling to a previous level */ - case NEW_OBJ: - type = ReadStoredClass(wanttype); - // Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell()); - obj = type->CreateNew(); - MapObject(obj); - obj->SerializeUserVars(*this); - obj->Serialize(*this); - obj->CheckIfSerialized(); - break; - - default: - I_Error("Unknown object code (%d) in archive\n", objHead); - } - return *this; -} -#endif - -#if 0 -void DThinker::SerializeAll(FArchive &arc, bool hubLoad) -{ - DThinker *thinker; - BYTE stat; - int statcount; - int i; - - if (arc.IsStoring()) - { - } - else - { - try - { - arc << statcount; - while (statcount > 0) - { - arc << stat << thinker; - while (thinker != NULL) - { - // This may be a player stored in their ancillary list. Remove - // them first before inserting them into the new list. - if (thinker->NextThinker != NULL) - { - thinker->Remove(); - } - // Thinkers with the OF_JustSpawned flag set go in the FreshThinkers - // list. Anything else goes in the regular Thinkers list. - if (thinker->ObjectFlags & OF_EuthanizeMe) - { - // This thinker was destroyed during the loading process. Do - // not link it in to any list. - } - else if (thinker->ObjectFlags & OF_JustSpawned) - { - FreshThinkers[stat].AddTail(thinker); - } - else - { - Thinkers[stat].AddTail(thinker); - } - arc << thinker; - } - statcount--; - } - } - catch (class CDoomError &) - { - DestroyAllThinkers(); - throw; - } - } -} -#endif From 02b3884dffe2042c09277b4b6e26613f48ee4d75 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 20:13:22 +0200 Subject: [PATCH 54/59] - added copyright header to serializer.cpp. --- src/serializer.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/serializer.cpp b/src/serializer.cpp index 45e920f21..b6e05d173 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1,3 +1,37 @@ +/* +** serializer.cpp +** Savegame wrapper around RapidJSON +** +**--------------------------------------------------------------------------- +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + #define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 // disable this insanity which is bound to make the code break over time. #define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 #define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 From 36bf099d54a971112eced46014e1ed78745427e5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 21:24:56 +0200 Subject: [PATCH 55/59] - fixed: object pointers as array members may not be skipped if they are null. - changed S_GetMusic to return a const pointer to the actual music name instead of a copy. The only thing this is used for is the savegame code and it has no use for a copy, it can work far more efficiently with a const pointer. --- src/p_saveg.cpp | 3 +-- src/s_sound.cpp | 4 ++-- src/s_sound.h | 2 +- src/serializer.cpp | 40 +++++++++++++++++++++++++++------------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 30f191e4e..20d82f67c 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -528,7 +528,7 @@ void P_SerializeSounds(FSerializer &arc) if (arc.isWriting()) { - order = S_GetMusic((char **)&name); + order = S_GetMusic(&name); } arc.StringPtr("musicname", name) ("musicorder", order); @@ -539,7 +539,6 @@ void P_SerializeSounds(FSerializer &arc) if (level.cdtrack == 0 || !S_ChangeCDMusic(level.cdtrack, level.cdid)) S_ChangeMusic(level.Music, level.musicorder); } - delete[] name; } //========================================================================== diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 12b280a74..866af965a 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -2608,13 +2608,13 @@ void S_MIDIDeviceChanged() // //========================================================================== -int S_GetMusic (char **name) +int S_GetMusic (const char **name) { int order; if (mus_playing.name.IsNotEmpty()) { - *name = copystring (mus_playing.name); + *name = mus_playing.name; order = mus_playing.baseorder; } else diff --git a/src/s_sound.h b/src/s_sound.h index 86915ea40..9b917e25c 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -334,7 +334,7 @@ void S_RestartMusic (); void S_MIDIDeviceChanged(); -int S_GetMusic (char **name); +int S_GetMusic (const char **name); // Stops the music for sure. void S_StopMusic (bool force); diff --git a/src/serializer.cpp b/src/serializer.cpp index b6e05d173..7df5bd8fd 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -1514,6 +1514,10 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje } Serialize(arc, key, ndx, nullptr); } + else if (!arc.w->inObject()) + { + arc.w->Null(); + } } else { @@ -1524,30 +1528,40 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje I_Error("Attempt to read object reference without calling ReadObjects first"); } auto val = arc.r->FindKey(key); - if (val != nullptr && val->IsInt()) + if (val != nullptr) { - int index = val->GetInt(); - if (index == -1) + if (val->IsNull()) { - value = WP_NOCHANGE; + value = nullptr; + return arc; } - else + else if (val->IsInt()) { - assert(index >= 0 && index < (int)arc.r->mDObjects.Size()); - if (index >= 0 && index < (int)arc.r->mDObjects.Size()) + int index = val->GetInt(); + if (index == -1) { - value = arc.r->mDObjects[index]; + value = WP_NOCHANGE; } else { - assert(false && "invalid object reference"); - Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key); - value = nullptr; - arc.mErrors++; + assert(index >= 0 && index < (int)arc.r->mDObjects.Size()); + if (index >= 0 && index < (int)arc.r->mDObjects.Size()) + { + value = arc.r->mDObjects[index]; + } + else + { + assert(false && "invalid object reference"); + Printf(TEXTCOLOR_RED "Invalid object reference for '%s'", key); + value = nullptr; + arc.mErrors++; + if (retcode) *retcode = false; + } } + return arc; } } - else if (!retcode) + if (!retcode) { value = nullptr; } From 2318db0b1a6d0aad0215792f60b8e1456b531e7f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 22:03:44 +0200 Subject: [PATCH 56/59] - let DWaggleBase use the interpolation pointer of its parent instead of defining its own one. - do not call checking code for loading savegames when writing them in ACS module serializer. --- src/p_acs.cpp | 9 ++++++--- src/p_floor.cpp | 16 ++++------------ src/p_spec.h | 1 - 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 3bd7218b3..fefe5cd8f 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1614,10 +1614,13 @@ void FBehavior::StaticSerializeModuleStates (FSerializer &arc) if (arc.BeginArray("acsmodules")) { - int modnum = arc.ArraySize(); - if (modnum != StaticModules.Size()) + if (arc.isReading()) { - I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); + int modnum = arc.ArraySize(); + if (modnum != StaticModules.Size()) + { + I_Error("Level was saved with a different number of ACS modules. (Have %d, save has %d)", StaticModules.Size(), modnum); + } } for (modnum = 0; modnum < StaticModules.Size(); ++modnum) diff --git a/src/p_floor.cpp b/src/p_floor.cpp index db4887ab8..8d09ea2d5 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -1103,9 +1103,7 @@ bool EV_DoChange (line_t *line, EChange changetype, int tag) // //========================================================================== -IMPLEMENT_POINTY_CLASS (DWaggleBase) - DECLARE_POINTER(m_Interpolation) -END_POINTERS +IMPLEMENT_CLASS (DWaggleBase) IMPLEMENT_CLASS (DFloorWaggle) IMPLEMENT_CLASS (DCeilingWaggle) @@ -1124,8 +1122,7 @@ void DWaggleBase::Serialize(FSerializer &arc) ("scale", m_Scale) ("scaledelta", m_ScaleDelta) ("ticker", m_Ticker) - ("state", m_State) - ("interpolation", m_Interpolation); + ("state", m_State); } //========================================================================== @@ -1145,11 +1142,6 @@ DWaggleBase::DWaggleBase (sector_t *sec) void DWaggleBase::Destroy() { - if (m_Interpolation != NULL) - { - m_Interpolation->DelRef(); - m_Interpolation = NULL; - } Super::Destroy(); } @@ -1244,7 +1236,7 @@ DFloorWaggle::DFloorWaggle (sector_t *sec) : Super (sec) { sec->floordata = this; - m_Interpolation = sec->SetInterpolation(sector_t::FloorMove, true); + interpolation = sec->SetInterpolation(sector_t::FloorMove, true); } void DFloorWaggle::Tick () @@ -1266,7 +1258,7 @@ DCeilingWaggle::DCeilingWaggle (sector_t *sec) : Super (sec) { sec->ceilingdata = this; - m_Interpolation = sec->SetInterpolation(sector_t::CeilingMove, true); + interpolation = sec->SetInterpolation(sector_t::CeilingMove, true); } void DCeilingWaggle::Tick () diff --git a/src/p_spec.h b/src/p_spec.h index 23237bb15..41d47c721 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -612,7 +612,6 @@ protected: double m_ScaleDelta; int m_Ticker; int m_State; - TObjPtr m_Interpolation; friend bool EV_StartWaggle (int tag, line_t *line, int height, int speed, int offset, int timer, bool ceiling); From 69291b9cf9e2e5b0cfbba65c7e6c20c049513abe Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 23:03:11 +0200 Subject: [PATCH 57/59] - completely eliminated the horrible fudging that went on with players during hub travel. This means that DestroyMostThinkers and the player substitution mechanism in the object deserializer are gone now. I wish I had realized this the last time it came up - it would have saved me a lot of trouble. But as it turns out, the more recent travelling code makes all of this completely unnecessary, working perfectly fine with deleting the player pawns along with the rest of the thinkers before loading the stored ones from the savegame (and getting rid of those in G_FinishTravel.) And with a sane savegame format that does not depend on side effects from how the thinker serializing handled linking into the lists the old code was even harmful, leaving voodoo dolls behind. I had the exact same effect when I tried to reshuffle some things for reliably restoring portals, but did not make the connection to interference between two mutually incompatible player travelling mechanisms that just worked by sheer happenstance with the original order of things. --- src/dthinker.cpp | 51 ---------------------------------------------- src/dthinker.h | 2 -- src/p_saveg.cpp | 13 +----------- src/serializer.cpp | 49 -------------------------------------------- 4 files changed, 1 insertion(+), 114 deletions(-) diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 5dbded654..114d260df 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -332,24 +332,6 @@ void DThinker::DestroyAllThinkers () GC::FullGC(); } -// Destroy all thinkers except for player-controlled actors -// Players are simply removed from the list of thinkers and -// will be added back after serialization is complete. -void DThinker::DestroyMostThinkers () -{ - int i; - - for (i = 0; i <= MAX_STATNUM; i++) - { - if (i != STAT_TRAVELLING) - { - DestroyMostThinkersInList (Thinkers[i], i); - DestroyMostThinkersInList (FreshThinkers[i], i); - } - } - GC::FullGC(); -} - void DThinker::DestroyThinkersInList (FThinkerList &list) { if (list.Sentinel != NULL) @@ -364,39 +346,6 @@ void DThinker::DestroyThinkersInList (FThinkerList &list) } } -void DThinker::DestroyMostThinkersInList (FThinkerList &list, int stat) -{ - if (stat != STAT_PLAYER) - { - DestroyThinkersInList (list); - } - else if (list.Sentinel != NULL) - { // If it's a voodoo doll, destroy it. Otherwise, simply remove - // it from the list. G_FinishTravel() will find it later from - // a players[].mo link and destroy it then, after copying various - // information to a new player. - for (DThinker *probe = list.Sentinel->NextThinker; probe != list.Sentinel; probe = list.Sentinel->NextThinker) - { - if (!probe->IsKindOf(RUNTIME_CLASS(APlayerPawn)) || // <- should not happen - static_cast(probe)->player == NULL || - static_cast(probe)->player->mo != probe) - { - probe->Destroy(); - } - else - { - probe->Remove(); - // Technically, this doesn't need to be in any list now, since - // it's only going to be found later and destroyed before ever - // needing to tick again, but by moving it to a separate list, - // I can keep my debug assertions that all thinkers are either - // euthanizing or in a list. - Thinkers[MAX_STATNUM+1].AddTail(probe); - } - } - } -} - void DThinker::RunThinkers () { int i, count; diff --git a/src/dthinker.h b/src/dthinker.h index 9ce8943b4..3f9647297 100644 --- a/src/dthinker.h +++ b/src/dthinker.h @@ -78,7 +78,6 @@ public: static void RunThinkers (); static void RunThinkers (int statnum); static void DestroyAllThinkers (); - static void DestroyMostThinkers (); static void DestroyThinkersInList(int statnum) { DestroyThinkersInList(Thinkers[statnum]); @@ -94,7 +93,6 @@ private: enum no_link_type { NO_LINK }; DThinker(no_link_type) throw(); static void DestroyThinkersInList (FThinkerList &list); - static void DestroyMostThinkersInList (FThinkerList &list, int stat); static int TickThinkers (FThinkerList *list, FThinkerList *dest); // Returns: # of thinkers ticked static void SaveList(FSerializer &arc, DThinker *node); void Remove(); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 20d82f67c..80c55fecc 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -63,17 +63,6 @@ #include "r_renderer.h" #include "serializer.h" -// -// Thinkers -// - -void P_DestroyThinkers(bool hubLoad) -{ - if (hubLoad) - DThinker::DestroyMostThinkers(); - else - DThinker::DestroyAllThinkers(); -} //========================================================================== // @@ -901,7 +890,7 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) Renderer->StartSerialize(arc); if (arc.isReading()) { - P_DestroyThinkers(hubload); + DThinker::DestroyAllThinkers(); interpolator.ClearInterpolations(); arc.ReadObjects(hubload); } diff --git a/src/serializer.cpp b/src/serializer.cpp index 7df5bd8fd..ae1c55a7a 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -824,14 +824,6 @@ void FSerializer::WriteObjects() obj->SerializeUserVars(*this); obj->Serialize(*this); obj->CheckIfSerialized(); - if (obj->IsKindOf(RUNTIME_CLASS(AActor)) && - (player = static_cast(obj)->player) && - player->mo == obj) - { - w->Key("playerindex"); - w->Int(int(player - players)); - } - EndObject(); } EndArray(); @@ -895,15 +887,6 @@ void FSerializer::ReadObjects(bool hubtravel) if (obj != nullptr) { int pindex = -1; - if (hubtravel) - { - // mark this as a hub travelling player. This needs to be substituted later, but that's better done when all objects have been loaded. - Serialize(*this, "playerindex", pindex, nullptr); - if (hubtravel && pindex >= 0 && pindex < MAXPLAYERS) - { - r->mPlayers[pindex] = int(i); - } - } try { obj->SerializeUserVars(*this); @@ -922,38 +905,6 @@ void FSerializer::ReadObjects(bool hubtravel) } EndArray(); - // Special treatment for hub travel: We do not want the player pawn which exited the level when this snapshot was made - // but the one that got freshly created when the map was loaded in P_SetupLevel. - // The loop above marked all objects that represent a player, so now we go through them, and if a corresponding - // player had been spawned on map load it gets substituted here. - - // The substitution does not take place inside the above loop to ensure that no changes to the loaded objects - // can occur afterward. At this point everything has been loaded and the substitution is a simple matter of - // calling DObject::StaticPointerSubstitution. - - // If no corresponding player can be found among the freshly spawned ones, the one from the snapshot is kept. - if (hubtravel) - { - for (int i = 0; i < MAXPLAYERS; i++) - { - int pindex = r->mPlayers[i]; - if (pindex != -1) - { - if (players[i].mo != nullptr) - { - // Destroy the old pawn before substituting the pointer so that its inventory also gets properly destroyed. - r->mDObjects[pindex]->Destroy(); - DObject::StaticPointerSubstitution(r->mDObjects[pindex], players[i].mo); - r->mDObjects[pindex] = players[i].mo; - } - else - { - players[i].mo = static_cast(r->mDObjects[pindex]); - } - } - } - } - DThinker::bSerialOverride = false; assert(!founderrors); if (founderrors) From b7c822d20870b2be74eea07e725fc9ab7055774a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 23 Sep 2016 23:47:25 +0200 Subject: [PATCH 58/59] - fixed secplane_t serializer. It passed incorrect defaults to the sub-serializers and calculated negiC wrong. --- src/p_saveg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 80c55fecc..465760137 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -201,13 +201,13 @@ FSerializer &Serialize(FSerializer &arc, const char *key, secplane_t &p, secplan if (arc.BeginObject(key)) { - arc("normal", p.normal, def->normal) - ("d", p.D, def->D) - .EndObject(); + Serialize(arc, "normal", p.normal, def ? &def->normal : nullptr); + Serialize(arc, "d", p.D, def ? &def->D : nullptr); + arc.EndObject(); if (arc.isReading() && p.normal.Z != 0) { - p.negiC = 1 / p.normal.Z; + p.negiC = -1 / p.normal.Z; } } return arc; From ee2766d00be3dd9309309dde3151668cd39e3c30 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Sep 2016 01:47:44 +0200 Subject: [PATCH 59/59] - made adjustments for new savegame code. --- src/gl/dynlights/a_dynlight.cpp | 32 ++++++++++++++++++----------- src/gl/dynlights/gl_dynlight.h | 5 +++-- src/gl/renderer/gl_renderer.h | 2 +- src/gl/scene/gl_scene.cpp | 29 ++++++++++++++------------ src/gl/system/gl_framebuffer.cpp | 1 - src/gl/utility/gl_cycler.cpp | 21 ++++++++++++++----- src/gl/utility/gl_cycler.h | 35 +++++++++++--------------------- src/p_saveg.cpp | 2 ++ 8 files changed, 70 insertions(+), 57 deletions(-) diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index fcb945f01..36ef1b948 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -69,6 +69,7 @@ #include "r_utility.h" #include "portal.h" #include "doomstat.h" +#include "serializer.h" #include "gl/renderer/gl_renderer.h" @@ -154,24 +155,31 @@ static FRandom randLight; // // //========================================================================== -void ADynamicLight::Serialize(FArchive &arc) +void ADynamicLight::Serialize(FSerializer &arc) { Super::Serialize (arc); - arc << lightflags << lighttype; - arc << m_tickCount << m_currentRadius; - arc << m_Radius[0] << m_Radius[1]; + auto def = static_cast(GetDefault()); + arc("lightflags", lightflags, def->lightflags) + ("lighttype", lighttype, def->lighttype) + ("tickcount", m_tickCount, def->m_tickCount) + ("currentradius", m_currentRadius, def->m_currentRadius) + .Array("radius", m_Radius, def->m_Radius, 2); - if (lighttype == PulseLight) arc << m_lastUpdate << m_cycler; - if (arc.IsLoading()) - { - // The default constructor which is used for creating objects before deserialization will not set this variable. - // It needs to be true for all placed lights. - visibletoplayer = true; - LinkLight(); - } + if (lighttype == PulseLight) + arc("lastupdate", m_lastUpdate, def->m_lastUpdate) + ("cycler", m_cycler, def->m_cycler); } +void ADynamicLight::PostSerialize() +{ + Super::PostSerialize(); + // The default constructor which is used for creating objects before deserialization will not set this variable. + // It needs to be true for all placed lights. + visibletoplayer = true; + LinkLight(); +} + //========================================================================== // // [TS] diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h index d92bf35c6..84965e318 100644 --- a/src/gl/dynlights/gl_dynlight.h +++ b/src/gl/dynlights/gl_dynlight.h @@ -32,7 +32,7 @@ EXTERN_CVAR(Bool, gl_lights) EXTERN_CVAR(Bool, gl_attachedlights) class ADynamicLight; -class FArchive; +class FSerializer; @@ -93,7 +93,8 @@ class ADynamicLight : public AActor DECLARE_CLASS (ADynamicLight, AActor) public: virtual void Tick(); - void Serialize(FArchive &arc); + void Serialize(FSerializer &arc); + void PostSerialize(); BYTE GetRed() const { return args[LIGHT_RED]; } BYTE GetGreen() const { return args[LIGHT_GREEN]; } BYTE GetBlue() const { return args[LIGHT_BLUE]; } diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index b95408521..71c52474a 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -169,7 +169,7 @@ public: void RenderScreenQuad(); void SetFixedColormap (player_t *player); - void WriteSavePic (player_t *player, FILE *file, int width, int height); + void WriteSavePic (player_t *player, FileWriter *file, int width, int height); void EndDrawScene(sector_t * viewsector); void UpdateCameraExposure(); void BloomScene(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 528925b7a..0c76b4bb1 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -43,6 +43,7 @@ #include "a_hexenglobal.h" #include "p_local.h" #include "gl/gl_functions.h" +#include "serializer.h" #include "gl/dynlights/gl_lightbuffer.h" #include "gl/system/gl_interface.h" @@ -921,7 +922,7 @@ void FGLRenderer::RenderView (player_t* player) // //=========================================================================== -void FGLRenderer::WriteSavePic (player_t *player, FILE *file, int width, int height) +void FGLRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { GL_IRECT bounds; @@ -972,10 +973,10 @@ struct FGLInterface : public FRenderer void PrecacheSprite(FTexture *tex, SpriteHits &hits); void Precache(BYTE *texhitlist, TMap &actorhitlist) override; void RenderView(player_t *player) override; - void WriteSavePic (player_t *player, FILE *file, int width, int height) override; + void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; void StateChanged(AActor *actor) override; - void StartSerialize(FArchive &arc) override; - void EndSerialize(FArchive &arc) override; + void StartSerialize(FSerializer &arc) override; + void EndSerialize(FSerializer &arc) override; void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov) override; sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) override; @@ -1195,20 +1196,22 @@ void FGLInterface::StateChanged(AActor *actor) // //=========================================================================== -void FGLInterface::StartSerialize(FArchive &arc) +void FGLInterface::StartSerialize(FSerializer &arc) { gl_DeleteAllAttachedLights(); + if (arc.BeginObject("glinfo")) + { + arc("fogdensity", fogdensity) + ("outsidefogdensity", outsidefogdensity) + ("skyfog", skyfog) + .EndObject(); + } } -void gl_SerializeGlobals(FArchive &arc) -{ - arc << fogdensity << outsidefogdensity << skyfog; -} - -void FGLInterface::EndSerialize(FArchive &arc) +void FGLInterface::EndSerialize(FSerializer &arc) { gl_RecreateAllAttachedLights(); - if (arc.IsLoading()) gl_InitPortals(); + if (arc.isReading()) gl_InitPortals(); } //=========================================================================== @@ -1244,7 +1247,7 @@ void FGLInterface::ClearBuffer(int color) // //=========================================================================== -void FGLInterface::WriteSavePic (player_t *player, FILE *file, int width, int height) +void FGLInterface::WriteSavePic (player_t *player, FileWriter *file, int width, int height) { GLRenderer->WriteSavePic(player, file, width, height); } diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 1ca2f7edd..94eba0817 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -36,7 +36,6 @@ #include "vectors.h" #include "v_palette.h" #include "templates.h" -#include "farchive.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_framebuffer.h" diff --git a/src/gl/utility/gl_cycler.cpp b/src/gl/utility/gl_cycler.cpp index a12cb4851..b6d160925 100644 --- a/src/gl/utility/gl_cycler.cpp +++ b/src/gl/utility/gl_cycler.cpp @@ -34,19 +34,30 @@ */ #include +#include "serializer.h" #include "gl/utility/gl_cycler.h" //========================================================================== // -// +// This will never be called with a null-def, so don't bother with that case. // //========================================================================== -void FCycler::Serialize(FArchive & arc) +FSerializer &Serialize(FSerializer &arc, const char *key, FCycler &c, FCycler *def) { - arc << m_start << m_end << m_current - << m_time << m_cycle << m_increment << m_shouldCycle - << m_cycleType; + if (arc.BeginObject(key)) + { + arc("start", c.m_start, def->m_start) + ("end", c.m_end, def->m_end) + ("current", c.m_current, def->m_current) + ("time", c.m_time, def->m_time) + ("cycle", c.m_cycle, def->m_cycle) + ("increment", c.m_increment, def->m_increment) + ("shouldcycle", c.m_shouldCycle, def->m_shouldCycle) + .Enum("type", c.m_cycleType) + .EndObject(); + } + return arc; } //========================================================================== diff --git a/src/gl/utility/gl_cycler.h b/src/gl/utility/gl_cycler.h index 8a6b55581..42effce34 100644 --- a/src/gl/utility/gl_cycler.h +++ b/src/gl/utility/gl_cycler.h @@ -1,28 +1,24 @@ #ifndef __GL_CYCLER_H #define __GL_CYCLER_H -#include "farchive.h" +class FSerializer; -typedef enum +enum CycleType { - CYCLE_Linear, - CYCLE_Sin, - CYCLE_Cos, - CYCLE_SawTooth, - CYCLE_Square -} CycleType; - -inline FArchive &operator<< (FArchive &arc, CycleType &type) -{ - BYTE val = (BYTE)type; - arc << val; - type = (CycleType)val; - return arc; -} + CYCLE_Linear, + CYCLE_Sin, + CYCLE_Cos, + CYCLE_SawTooth, + CYCLE_Square +}; +class FCycler; +FSerializer &Serialize(FSerializer &arc, const char *key, FCycler &c, FCycler *def); class FCycler { + friend FSerializer &Serialize(FSerializer &arc, const char *key, FCycler &c, FCycler *def); + public: FCycler(); void Update(float diff); @@ -33,7 +29,6 @@ public: inline operator float () const { return m_current; } - void Serialize(FArchive & arc); protected: float m_start, m_end, m_current; float m_time, m_cycle; @@ -42,11 +37,5 @@ protected: CycleType m_cycleType; }; -inline FArchive &operator<< (FArchive &arc, FCycler &type) -{ - type.Serialize(arc); - return arc; -} - #endif diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 465760137..170273b43 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -271,6 +271,8 @@ FSerializer &Serialize(FSerializer &arc, const char *key, sector_t &p, sector_t .Terrain("floorterrain", p.terrainnum[0], &def->terrainnum[0]) .Terrain("ceilingterrain", p.terrainnum[1], &def->terrainnum[1]) ("scrolls", scroll, nul) + // GZDoom exclusive: + .Array("reflect", p.reflect, def->reflect, 2) .EndObject(); if (arc.isReading() && !scroll.isZero())