- 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.
This commit is contained in:
Christoph Oelckers 2016-09-23 00:45:41 +02:00
parent 604b2b316b
commit c17da32dbd
8 changed files with 149 additions and 28 deletions

View file

@ -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 T> class TObjPtr;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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<FJSONObject> mObjects;
rapidjson::Document mDoc;
TArray<DObject *> 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)

View file

@ -16,7 +16,16 @@ struct FReader;
extern TArray<sector_t> loadsectors;
extern TArray<line_t> loadlines;
extern TArray<side_t> 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<class T>
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;
}

View file

@ -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