- update "common" subfolder.

# Conflicts:
#	source/CMakeLists.txt
This commit is contained in:
Christoph Oelckers 2020-04-07 18:04:35 +02:00
parent ada5ad5ec3
commit 091d90aba5
8 changed files with 751 additions and 308 deletions

View file

@ -713,7 +713,6 @@ set (PCH_SOURCES
core/savegamehelp.cpp
core/quotes.cpp
core/screenshot.cpp
core/serializer.cpp
core/raze_music.cpp
core/raze_sound.cpp
@ -786,9 +785,11 @@ set (PCH_SOURCES
common/engine/sc_man.cpp
common/engine/palettecontainer.cpp
common/engine/stringtable.cpp
common/engine/serializer.cpp
common/objects/dobject.cpp
common/objects/dobjgc.cpp
common/objects/dobjtype.cpp
common/scripting/core/dictionary.cpp
common/scripting/core/symbols.cpp
common/scripting/core/types.cpp
common/scripting/core/scopebarrier.cpp
@ -955,10 +956,9 @@ include_directories(
common/console
common/engine
common/objects
common/scripting/interface
common/scripting/core
common/scripting/vm
common/scripting/jit
common/scripting/core
${CMAKE_BINARY_DIR}/libraries/gdtoa
${SYSTEM_SOURCES_DIR}
@ -1074,11 +1074,11 @@ source_group("Common\\Engine" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/c
source_group("Common\\Objects" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/objects/.+")
source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/fonts/.+")
source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
source_group("Common\\File System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/filesystem/.+")
source_group("Common\\Scripting" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/.+")
source_group("Common\\Scripting\\Core" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/core/.+")
source_group("Common\\Scripting\\VM" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/vm/.+")
source_group("Common\\Scripting\\JIT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/jit/.+")
source_group("Common\\Scripting\\VM" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/scripting/vm/.+")
source_group("Common\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
source_group("Common\\Third Party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/.+")
source_group("Common\\Third Party\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/math/.+")
source_group("Common\\Third Party\\RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/rapidjson/.+")

View file

@ -0,0 +1,17 @@
#pragma once
// These are in a separate header because they require some rather 'dirty' headers to work which should not be part of serializer.h
template<class T>
FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<T> &value, TObjPtr<T> *)
{
Serialize(arc, key, value.o, nullptr);
return arc;
}
template<class T>
FSerializer &Serialize(FSerializer &arc, const char *key, TObjPtr<T> &value, T *)
{
Serialize(arc, key, value.o, nullptr);
return arc;
}

View file

@ -50,12 +50,15 @@
#include "cmdlib.h"
#include "utf8.h"
#include "printf.h"
#include "raze_sound.h"
#include "s_soundinternal.h"
#include "engineerrors.h"
#include "textures.h"
#include "texturemanager.h"
bool save_full = false;
extern DObject *WP_NOCHANGE;
bool save_full = false; // for testing. Should be removed afterward.
#include "serializer_internal.h"
//==========================================================================
//
@ -66,7 +69,7 @@ bool save_full = false;
//==========================================================================
static TArray<char> out;
static const char *StringToUnicode(const char *cc, int size = -1)
const char *StringToUnicode(const char *cc, int size)
{
int ch;
const uint8_t *c = (const uint8_t*)cc;
@ -94,7 +97,7 @@ static const char *StringToUnicode(const char *cc, int size = -1)
return &out[0];
}
static const char *UnicodeToString(const char *cc)
const char *UnicodeToString(const char *cc)
{
out.Resize((unsigned)strlen(cc) + 1);
int ndx = 0;
@ -110,225 +113,6 @@ static const char *UnicodeToString(const char *cc)
return &out[0];
}
//==========================================================================
//
//
//
//==========================================================================
struct FJSONObject
{
rapidjson::Value *mObject;
rapidjson::Value::MemberIterator mIterator;
int mIndex;
FJSONObject(rapidjson::Value *v)
{
mObject = v;
if (v->IsObject()) mIterator = v->MemberBegin();
else if (v->IsArray())
{
mIndex = 0;
}
}
};
//==========================================================================
//
// some wrapper stuff to keep the RapidJSON dependencies out of the global headers.
// FSerializer should not expose any of this.
//
//==========================================================================
struct FWriter
{
typedef rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<> > Writer;
typedef rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<> > PrettyWriter;
Writer *mWriter1;
PrettyWriter *mWriter2;
TArray<bool> mInObject;
rapidjson::StringBuffer mOutString;
TArray<DObject *> mDObjects;
TMap<DObject *, int> mObjectMap;
FWriter(bool pretty)
{
if (!pretty)
{
mWriter1 = new Writer(mOutString);
mWriter2 = nullptr;
}
else
{
mWriter1 = nullptr;
mWriter2 = new PrettyWriter(mOutString);
}
}
~FWriter()
{
if (mWriter1) delete mWriter1;
if (mWriter2) delete mWriter2;
}
bool inObject() const
{
return mInObject.Size() > 0 && mInObject.Last();
}
void StartObject()
{
if (mWriter1) mWriter1->StartObject();
else if (mWriter2) mWriter2->StartObject();
}
void EndObject()
{
if (mWriter1) mWriter1->EndObject();
else if (mWriter2) mWriter2->EndObject();
}
void StartArray()
{
if (mWriter1) mWriter1->StartArray();
else if (mWriter2) mWriter2->StartArray();
}
void EndArray()
{
if (mWriter1) mWriter1->EndArray();
else if (mWriter2) mWriter2->EndArray();
}
void Key(const char *k)
{
if (mWriter1) mWriter1->Key(k);
else if (mWriter2) mWriter2->Key(k);
}
void Null()
{
if (mWriter1) mWriter1->Null();
else if (mWriter2) mWriter2->Null();
}
void StringU(const char *k, bool encode)
{
if (encode) k = StringToUnicode(k);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void String(const char *k)
{
k = StringToUnicode(k);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void String(const char *k, int size)
{
k = StringToUnicode(k, size);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void Bool(bool k)
{
if (mWriter1) mWriter1->Bool(k);
else if (mWriter2) mWriter2->Bool(k);
}
void Int(int32_t k)
{
if (mWriter1) mWriter1->Int(k);
else if (mWriter2) mWriter2->Int(k);
}
void Int64(int64_t k)
{
if (mWriter1) mWriter1->Int64(k);
else if (mWriter2) mWriter2->Int64(k);
}
void Uint(uint32_t k)
{
if (mWriter1) mWriter1->Uint(k);
else if (mWriter2) mWriter2->Uint(k);
}
void Uint64(int64_t k)
{
if (mWriter1) mWriter1->Uint64(k);
else if (mWriter2) mWriter2->Uint64(k);
}
void Double(double k)
{
if (mWriter1)
{
mWriter1->Double(k);
}
else if (mWriter2)
{
mWriter2->Double(k);
}
}
};
//==========================================================================
//
//
//
//==========================================================================
struct FReader
{
TArray<FJSONObject> mObjects;
rapidjson::Document mDoc;
TArray<DObject *> mDObjects;
rapidjson::Value *mKeyValue = nullptr;
bool mObjectsRead = false;
FReader(const char *buffer, size_t length)
{
mDoc.Parse(buffer, length);
mObjects.Push(FJSONObject(&mDoc));
}
rapidjson::Value *FindKey(const char *key)
{
FJSONObject &obj = mObjects.Last();
if (obj.mObject->IsObject())
{
if (key == nullptr)
{
// we are performing an iteration of the object through GetKey.
auto p = mKeyValue;
mKeyValue = nullptr;
return p;
}
else
{
// Find the given key by name;
auto it = obj.mObject->FindMember(key);
if (it == obj.mObject->MemberEnd()) return nullptr;
return &it->value;
}
}
else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size())
{
return &(*obj.mObject)[obj.mIndex++];
}
return nullptr;
}
};
//==========================================================================
//
//
@ -402,6 +186,7 @@ void FSerializer::Close()
}
if (r != nullptr)
{
CloseReaderCustom();
delete r;
r = nullptr;
}
@ -635,6 +420,43 @@ FSerializer &FSerializer::ScriptNum(const char *key, int &num)
return *this;
}
//==========================================================================
//
// this is merely a placeholder to satisfy the VM's spriteid type.
//
//==========================================================================
FSerializer &FSerializer::Sprite(const char *key, int32_t &spritenum, int32_t *def)
{
if (isWriting())
{
if (w->inObject() && def != nullptr && *def == spritenum) return *this;
WriteKey(key);
w->Int(spritenum);
}
else
{
auto val = r->FindKey(key);
if (val != nullptr && val->IsInt())
{
spritenum = val->GetInt();
}
}
return *this;
}
//==========================================================================
//
// only here so that it can be virtually overridden. Without reference this cannot save anything.
//
//==========================================================================
FSerializer& FSerializer::StatePointer(const char* key, void* ptraddr, bool *res)
{
if (res) *res = false;
return *this;
}
//==========================================================================
//
//
@ -835,7 +657,7 @@ void FSerializer::ReadObjects(bool hubtravel)
obj->SerializeUserVars(*this);
obj->Serialize(*this);
}
catch (CEngineError &err)
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\n", err.GetMessage(), obj ? obj->GetClass()->TypeName.GetChars() : "invalid object");
@ -1136,6 +958,15 @@ FSerializer &Serialize(FSerializer &arc, const char *key, uint32_t &value, uint3
//
//==========================================================================
FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* 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, int8_t &value, int8_t *defval)
{
int32_t vv = value;
@ -1229,25 +1060,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, float &value, float *d
//
//==========================================================================
template<class T>
FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base)
{
assert(base != nullptr);
if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval)
{
int64_t vv = value == nullptr ? -1 : value - base;
Serialize(arc, key, vv, nullptr);
value = vv < 0 ? nullptr : base + vv;
}
return arc;
}
//==========================================================================
//
//
//
//==========================================================================
FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval)
{
if (arc.isWriting())
@ -1344,13 +1156,11 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje
if (value != nullptr && !(value->ObjectFlags & (OF_EuthanizeMe | OF_Transient)))
{
int ndx;
/*
if (value == WP_NOCHANGE)
{
ndx = -1;
}
else
*/
{
int *pndx = arc.w->mObjectMap.CheckKey(value);
if (pndx != nullptr)
@ -1391,7 +1201,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje
int index = val->GetInt();
if (index == -1)
{
value = nullptr;// WP_NOCHANGE;
value = WP_NOCHANGE;
}
else
{
@ -1463,19 +1273,20 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FName &value, FName *d
//==========================================================================
//
// Note that the sound name here is not reliable because it's a file name, not a unique alias.
// Currently the index is the only means to retrieve the sound on loading.
//
//
//==========================================================================
FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundID *def)
{
#if 1
int id = sid;
Serialize(arc, key, id, nullptr);
if (arc.isReading()) sid = FSoundID(id);
#else
if (arc.isWriting())
if (!arc.soundNamesAreUnique)
{
//If sound name here is not reliable, we need to save by index instead.
int id = sid;
Serialize(arc, key, id, nullptr);
if (arc.isReading()) sid = FSoundID(id);
}
else if (arc.isWriting())
{
if (!arc.w->inObject() || def == nullptr || sid != *def)
{
@ -1507,7 +1318,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI
}
}
}
#endif
return arc;
}
@ -1625,6 +1435,86 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&fon
}
//==========================================================================
//
// Dictionary
//
//==========================================================================
FString DictionaryToString(const Dictionary &dict)
{
Dictionary::ConstPair *pair;
Dictionary::ConstIterator i { dict.Map };
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
writer.StartObject();
while (i.NextPair(pair))
{
writer.Key(pair->Key);
writer.String(pair->Value);
}
writer.EndObject();
FString contents { buffer.GetString(), buffer.GetSize() };
return contents;
}
Dictionary *DictionaryFromString(const FString &string)
{
if (string.Compare("null") == 0)
{
return nullptr;
}
Dictionary *const dict = Create<Dictionary>();
if (string.IsEmpty())
{
return dict;
}
rapidjson::Document doc;
doc.Parse(string.GetChars(), string.Len());
if (doc.GetType() != rapidjson::Type::kObjectType)
{
I_Error("Dictionary is expected to be a JSON object.");
return dict;
}
for (auto i = doc.MemberBegin(); i != doc.MemberEnd(); ++i)
{
if (i->value.GetType() != rapidjson::Type::kStringType)
{
I_Error("Dictionary value is expected to be a JSON string.");
return dict;
}
dict->Map.Insert(i->name.GetString(), i->value.GetString());
}
return dict;
}
template<> FSerializer &Serialize(FSerializer &arc, const char *key, Dictionary *&dict, Dictionary **)
{
if (arc.isWriting())
{
FString contents { dict ? DictionaryToString(*dict) : "null" };
return arc(key, contents);
}
else
{
FString contents;
arc(key, contents);
dict = DictionaryFromString(contents);
return arc;
}
}
//==========================================================================
//
// Handler to retrieve a numeric value of any kind.

View file

@ -9,6 +9,7 @@
#include "vectors.h"
#include "palentry.h"
#include "name.h"
#include "dictionary.h"
extern bool save_full;
@ -61,11 +62,14 @@ class FSerializer
public:
FWriter *w = nullptr;
FReader *r = nullptr;
bool soundNamesAreUnique = false; // While in GZDoom, sound names are unique, that isn't universally true - let the serializer handle both cases with a flag.
unsigned ArraySize();
void WriteKey(const char *key);
void WriteObjects();
private:
virtual void CloseReaderCustom() {}
public:
~FSerializer()
@ -73,6 +77,7 @@ public:
mErrors = 0; // The destructor may not throw an exception so silence the error checker.
Close();
}
void SetUniqueSoundNames() { soundNamesAreUnique = true; }
bool OpenWriter(bool pretty = true);
bool OpenReader(const char *buffer, size_t length);
bool OpenReader(FCompressedBuffer *input);
@ -86,6 +91,11 @@ public:
const char *GetKey();
const char *GetOutput(unsigned *len = nullptr);
FCompressedBuffer GetCompressedOutput();
// The sprite serializer is a special case because it is needed by the VM to handle its 'spriteid' type.
virtual FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def);
// This is only needed by the type system.
virtual FSerializer& StatePointer(const char* key, void* ptraddr, bool *res);
FSerializer &StringPtr(const char *key, const char *&charptr); // This only retrieves the address but creates no permanent copy of the string unlike the regular char* serializer.
FSerializer &AddString(const char *key, const char *charptr);
const char *GetString(const char *key);
@ -173,6 +183,8 @@ public:
int mErrors = 0;
};
FSerializer& Serialize(FSerializer& arc, const char* key, char& value, char* defval);
FSerializer &Serialize(FSerializer &arc, const char *key, bool &value, bool *defval);
FSerializer &Serialize(FSerializer &arc, const char *key, int64_t &value, int64_t *defval);
FSerializer &Serialize(FSerializer &arc, const char *key, uint64_t &value, uint64_t *defval);
@ -191,7 +203,6 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI
FSerializer &Serialize(FSerializer &arc, const char *key, FString &sid, FString *def);
FSerializer &Serialize(FSerializer &arc, const char *key, NumericValue &sid, NumericValue *def);
template<class T>
FSerializer &Serialize(FSerializer &arc, const char *key, T *&value, T **)
{
@ -229,6 +240,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, TArray<T, TT> &value,
template<> FSerializer& Serialize(FSerializer& arc, const char* key, PClass*& clst, PClass** def);
template<> FSerializer& Serialize(FSerializer& arc, const char* key, FFont*& font, FFont** def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, Dictionary *&dict, Dictionary **def);
inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DVector3 *def)
{
@ -298,4 +310,7 @@ inline SaveRecord::SaveRecord(const char* nm, void (*handler)(FSerializer& arc))
saveRecords.records.Push(this);
}
FString DictionaryToString(const Dictionary &dict);
Dictionary *DictionaryFromString(const FString &string);
#endif

View file

@ -0,0 +1,240 @@
const char* UnicodeToString(const char* cc);
const char* StringToUnicode(const char* cc, int size = -1);
//==========================================================================
//
//
//
//==========================================================================
struct FJSONObject
{
rapidjson::Value* mObject;
rapidjson::Value::MemberIterator mIterator;
int mIndex;
FJSONObject(rapidjson::Value* v)
{
mObject = v;
if (v->IsObject()) mIterator = v->MemberBegin();
else if (v->IsArray())
{
mIndex = 0;
}
}
};
//==========================================================================
//
// some wrapper stuff to keep the RapidJSON dependencies out of the global headers.
// FSerializer should not expose any of this, although it is needed by special serializers.
//
//==========================================================================
struct FWriter
{
typedef rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<> > Writer;
typedef rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<> > PrettyWriter;
Writer *mWriter1;
PrettyWriter *mWriter2;
TArray<bool> mInObject;
rapidjson::StringBuffer mOutString;
TArray<DObject *> mDObjects;
TMap<DObject *, int> mObjectMap;
FWriter(bool pretty)
{
if (!pretty)
{
mWriter1 = new Writer(mOutString);
mWriter2 = nullptr;
}
else
{
mWriter1 = nullptr;
mWriter2 = new PrettyWriter(mOutString);
}
}
~FWriter()
{
if (mWriter1) delete mWriter1;
if (mWriter2) delete mWriter2;
}
bool inObject() const
{
return mInObject.Size() > 0 && mInObject.Last();
}
void StartObject()
{
if (mWriter1) mWriter1->StartObject();
else if (mWriter2) mWriter2->StartObject();
}
void EndObject()
{
if (mWriter1) mWriter1->EndObject();
else if (mWriter2) mWriter2->EndObject();
}
void StartArray()
{
if (mWriter1) mWriter1->StartArray();
else if (mWriter2) mWriter2->StartArray();
}
void EndArray()
{
if (mWriter1) mWriter1->EndArray();
else if (mWriter2) mWriter2->EndArray();
}
void Key(const char *k)
{
if (mWriter1) mWriter1->Key(k);
else if (mWriter2) mWriter2->Key(k);
}
void Null()
{
if (mWriter1) mWriter1->Null();
else if (mWriter2) mWriter2->Null();
}
void StringU(const char *k, bool encode)
{
if (encode) k = StringToUnicode(k);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void String(const char *k)
{
k = StringToUnicode(k);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void String(const char *k, int size)
{
k = StringToUnicode(k, size);
if (mWriter1) mWriter1->String(k);
else if (mWriter2) mWriter2->String(k);
}
void Bool(bool k)
{
if (mWriter1) mWriter1->Bool(k);
else if (mWriter2) mWriter2->Bool(k);
}
void Int(int32_t k)
{
if (mWriter1) mWriter1->Int(k);
else if (mWriter2) mWriter2->Int(k);
}
void Int64(int64_t k)
{
if (mWriter1) mWriter1->Int64(k);
else if (mWriter2) mWriter2->Int64(k);
}
void Uint(uint32_t k)
{
if (mWriter1) mWriter1->Uint(k);
else if (mWriter2) mWriter2->Uint(k);
}
void Uint64(int64_t k)
{
if (mWriter1) mWriter1->Uint64(k);
else if (mWriter2) mWriter2->Uint64(k);
}
void Double(double k)
{
if (mWriter1)
{
mWriter1->Double(k);
}
else if (mWriter2)
{
mWriter2->Double(k);
}
}
};
//==========================================================================
//
//
//
//==========================================================================
struct FReader
{
TArray<FJSONObject> mObjects;
rapidjson::Document mDoc;
TArray<DObject *> mDObjects;
rapidjson::Value *mKeyValue = nullptr;
bool mObjectsRead = false;
FReader(const char *buffer, size_t length)
{
mDoc.Parse(buffer, length);
mObjects.Push(FJSONObject(&mDoc));
}
rapidjson::Value *FindKey(const char *key)
{
FJSONObject &obj = mObjects.Last();
if (obj.mObject->IsObject())
{
if (key == nullptr)
{
// we are performing an iteration of the object through GetKey.
auto p = mKeyValue;
mKeyValue = nullptr;
return p;
}
else
{
// Find the given key by name;
auto it = obj.mObject->FindMember(key);
if (it == obj.mObject->MemberEnd()) return nullptr;
return &it->value;
}
}
else if (obj.mObject->IsArray() && (unsigned)obj.mIndex < obj.mObject->Size())
{
return &(*obj.mObject)[obj.mIndex++];
}
return nullptr;
}
};
//==========================================================================
//
//
//
//==========================================================================
template<class T>
FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **defval, T *base)
{
assert(base != nullptr);
if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval)
{
int64_t vv = value == nullptr ? -1 : value - base;
Serialize(arc, key, vv, nullptr);
value = vv < 0 ? nullptr : base + vv;
}
return arc;
}

View file

@ -0,0 +1,218 @@
#include "dictionary.h"
#include "vm.h"
#include "serializer.h"
#include <cassert>
//=====================================================================================
//
// DObject implementations for Dictionary and DictionaryIterator
//
//=====================================================================================
IMPLEMENT_CLASS(Dictionary, false, false);
IMPLEMENT_CLASS(DictionaryIterator, false, true);
IMPLEMENT_POINTERS_START(DictionaryIterator)
IMPLEMENT_POINTER(Dict)
IMPLEMENT_POINTERS_END
//=====================================================================================
//
// Dictionary functions
//
//=====================================================================================
void Dictionary::Serialize(FSerializer &arc)
{
Super::Serialize(arc);
constexpr char key[] { "dictionary" };
if (arc.isWriting())
{
// Pass this instance to serializer.
Dictionary *pointerToThis = this;
arc(key, pointerToThis);
}
else
{
// Receive new Dictionary, copy contents, clean up.
Dictionary *pointerToDeserializedDictionary;
arc(key, pointerToDeserializedDictionary);
Map.TransferFrom(pointerToDeserializedDictionary->Map);
delete pointerToDeserializedDictionary;
}
}
static Dictionary *DictCreate()
{
Dictionary *dict { Create<Dictionary>() };
return dict;
}
static void DictInsert(Dictionary *dict, const FString &key, const FString &value)
{
dict->Map.Insert(key, value);
}
static void DictAt(const Dictionary *dict, const FString &key, FString *result)
{
const FString *value = dict->Map.CheckKey(key);
*result = value ? *value : "";
}
static void DictToString(const Dictionary *dict, FString *result)
{
*result = DictionaryToString(*dict);
}
static void DictRemove(Dictionary *dict, const FString &key)
{
dict->Map.Remove(key);
}
//=====================================================================================
//
// Dictionary exports
//
//=====================================================================================
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, Create, DictCreate)
{
ACTION_RETURN_POINTER(DictCreate());
}
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, Insert, DictInsert)
{
PARAM_SELF_STRUCT_PROLOGUE(Dictionary);
PARAM_STRING(key);
PARAM_STRING(value);
DictInsert(self, key, value);
return 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, At, DictAt)
{
PARAM_SELF_STRUCT_PROLOGUE(Dictionary);
PARAM_STRING(key);
FString result;
DictAt(self, key, &result);
ACTION_RETURN_STRING(result);
}
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, ToString, DictToString)
{
PARAM_SELF_STRUCT_PROLOGUE(Dictionary);
FString result;
DictToString(self, &result);
ACTION_RETURN_STRING(result);
}
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, FromString, DictionaryFromString)
{
PARAM_PROLOGUE;
PARAM_STRING(string);
ACTION_RETURN_POINTER(DictionaryFromString(string));
}
DEFINE_ACTION_FUNCTION_NATIVE(_Dictionary, Remove, DictRemove)
{
PARAM_SELF_STRUCT_PROLOGUE(Dictionary);
PARAM_STRING(key);
DictRemove(self, key);
return 0;
}
//=====================================================================================
//
// DictionaryIterator functions
//
//=====================================================================================
DictionaryIterator::DictionaryIterator()
: Iterator(nullptr)
, Pair(nullptr)
, Dict(nullptr)
{
}
void DictionaryIterator::Serialize(FSerializer &arc)
{
if (arc.isWriting())
{
I_Error("Attempt to save pointer to unhandled type DictionaryIterator");
}
}
void DictionaryIterator::init(Dictionary *dict)
{
Iterator = std::make_unique<Dictionary::ConstIterator>(dict->Map);
Dict = dict;
GC::WriteBarrier(this, Dict);
}
static DictionaryIterator *DictIteratorCreate(Dictionary *dict)
{
DictionaryIterator *iterator = Create<DictionaryIterator>();
iterator->init(dict);
return iterator;
}
static int DictIteratorNext(DictionaryIterator *self)
{
assert(self->Iterator != nullptr);
const bool hasNext { self->Iterator->NextPair(self->Pair) };
return hasNext;
}
static void DictIteratorKey(const DictionaryIterator *self, FString *result)
{
*result = self->Pair ? self->Pair->Key : FString {};
}
static void DictIteratorValue(const DictionaryIterator *self, FString *result)
{
*result = self->Pair ? self->Pair->Value : FString {};
}
//=====================================================================================
//
// DictionaryIterator exports
//
//=====================================================================================
DEFINE_ACTION_FUNCTION_NATIVE(_DictionaryIterator, Create, DictIteratorCreate)
{
PARAM_PROLOGUE;
PARAM_POINTER(dict, Dictionary);
ACTION_RETURN_POINTER(DictIteratorCreate(dict));
}
DEFINE_ACTION_FUNCTION_NATIVE(_DictionaryIterator, Next, DictIteratorNext)
{
PARAM_SELF_STRUCT_PROLOGUE(DictionaryIterator);
ACTION_RETURN_BOOL(DictIteratorNext(self));
}
DEFINE_ACTION_FUNCTION_NATIVE(_DictionaryIterator, Key, DictIteratorKey)
{
PARAM_SELF_STRUCT_PROLOGUE(DictionaryIterator);
FString result;
DictIteratorKey(self, &result);
ACTION_RETURN_STRING(result);
}
DEFINE_ACTION_FUNCTION_NATIVE(_DictionaryIterator, Value, DictIteratorValue)
{
PARAM_SELF_STRUCT_PROLOGUE(DictionaryIterator);
FString result;
DictIteratorValue(self, &result);
ACTION_RETURN_STRING(result);
}

View file

@ -0,0 +1,70 @@
#pragma once
#include "tarray.h"
#include "zstring.h"
#include "dobject.h"
#include <memory>
/**
* @brief The Dictionary class exists to be exported to ZScript.
*
* It is a string-to-string map.
*
* It is derived from DObject to be a part of normal GC process.
*/
class Dictionary final : public DObject
{
DECLARE_CLASS(Dictionary, DObject)
public:
using StringMap = TMap<FString, FString>;
using ConstIterator = StringMap::ConstIterator;
using ConstPair = StringMap::ConstPair;
void Serialize(FSerializer &arc) override;
StringMap Map;
};
/**
* @brief The DictionaryIterator class exists to be exported to ZScript.
*
* It provides iterating over a Dictionary. The order is not specified.
*
* It is derived from DObject to be a part of normal GC process.
*/
class DictionaryIterator final : public DObject
{
DECLARE_CLASS(DictionaryIterator, DObject)
HAS_OBJECT_POINTERS
public:
~DictionaryIterator() override = default;
/**
* IMPLEMENT_CLASS macro needs a constructor without parameters.
*
* @see init().
*/
DictionaryIterator();
void Serialize(FSerializer &arc) override;
/**
* @brief init function complements constructor.
* @attention always call init after constructing DictionaryIterator.
*/
void init(Dictionary *dict);
std::unique_ptr<Dictionary::ConstIterator> Iterator;
Dictionary::ConstPair *Pair;
/**
* @brief Dictionary attribute exists for holding a pointer to iterated
* dictionary, so it is known by GC.
*/
Dictionary *Dict;
};

View file

@ -1078,6 +1078,46 @@ bool PName::ReadValue(FSerializer &ar, const char *key, void *addr) const
}
}
/* PStatePointer **********************************************************/
//==========================================================================
//
// PStatePointer Default Constructor
//
//==========================================================================
PStatePointer::PStatePointer()
{
mDescriptiveName = "Pointer<State>";
PointedType = NewStruct(NAME_State, nullptr, true);
IsConst = true;
}
//==========================================================================
//
// PStatePointer :: WriteValue
//
//==========================================================================
void PStatePointer::WriteValue(FSerializer& ar, const char* key, const void* addr) const
{
ar.StatePointer(key, const_cast<void*>(addr), nullptr);
}
//==========================================================================
//
// PStatePointer :: ReadValue
//
//==========================================================================
bool PStatePointer::ReadValue(FSerializer& ar, const char* key, void* addr) const
{
bool res = false;
ar.StatePointer(key, addr, &res);
return res;
}
/* PSpriteID ******************************************************************/
//==========================================================================
@ -1102,9 +1142,7 @@ PSpriteID::PSpriteID()
void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
int32_t val = *(int*)addr;
#ifdef GZDOOM
ar.Sprite(key, val, nullptr);
#endif
}
//==========================================================================
@ -1116,9 +1154,7 @@ void PSpriteID::WriteValue(FSerializer &ar, const char *key, const void *addr) c
bool PSpriteID::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
int32_t val = 0;
#ifdef GZDOOM
ar.Sprite(key, val, nullptr);
#endif
*(int*)addr = val;
return true;
}
@ -1440,49 +1476,6 @@ PPointer *NewPointer(PClass *cls, bool isconst)
return static_cast<PPointer *>(ptype);
}
/* PStatePointer **********************************************************/
//==========================================================================
//
// PStatePointer Default Constructor
//
//==========================================================================
PStatePointer::PStatePointer()
{
mDescriptiveName = "Pointer<State>";
PointedType = NewStruct(NAME_State, nullptr, true);
IsConst = true;
}
//==========================================================================
//
// PStatePointer :: WriteValue
//
//==========================================================================
void PStatePointer::WriteValue(FSerializer &ar, const char *key, const void *addr) const
{
#ifdef GZDOOM
ar(key, *(FState **)addr);
#endif
}
//==========================================================================
//
// PStatePointer :: ReadValue
//
//==========================================================================
bool PStatePointer::ReadValue(FSerializer &ar, const char *key, void *addr) const
{
bool res = false;
#ifdef GZDOOM
::Serialize(ar, key, *(FState **)addr, nullptr, &res);
#endif
return res;
}
/* PClassPointer **********************************************************/