serializer.cpp
来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 556 行
CPP
556 行
/*
creg - Code compoment registration system
Copyright 2005 Jelmer Cnossen
Classes for serialization of registrated class instances
*/
#include "StdAfx.h"
#include "creg.h"
#include "Serializer.h"
#include "Platform/byteorder.h"
#include <fstream>
#include <assert.h>
#include <stdexcept>
#include <map>
#include <string.h>
//#include "LogOutput.h"
using namespace std;
using namespace creg;
#ifndef swabdword
#define swabdword(d) (d)
#endif
#ifndef swabword
#define swabword(d) (d)
#endif
#define CREG_PACKAGE_FILE_ID "CRPK"
// File format structures
#pragma pack(push,1)
struct PackageHeader
{
char magic[4];
int objDataOffset;
int objTableOffset;
int numObjects;
int objClassRefOffset; // a class ref is: zero-term class string + checksum DWORD
int numObjClassRefs;
unsigned int metadataChecksum;
void SwapBytes ()
{
objDataOffset = swabdword (objDataOffset);
objTableOffset = swabdword (objTableOffset);
objClassRefOffset = swabdword (objClassRefOffset);
numObjClassRefs = swabdword (numObjClassRefs);
numObjects = swabdword (numObjects);
metadataChecksum = swabdword (metadataChecksum);
}
};
struct PackageObject
{
unsigned short classRefIndex;
char isEmbedded;
void SwapBytes() {
classRefIndex = swabword (classRefIndex);
}
};
COMPILE_TIME_ASSERT(sizeof(PackageObject) == 3, test_size);
#pragma pack(pop)
static string ReadZStr(std::istream& f)
{
std::string s;
char c;
while(!f.eof ()) {
f >> c;
if (!c) break;
s += c;
}
return s;
}
static void WriteZStr(std::ostream& f, const string& s)
{
int c;
c = s.length ();
f.write (s.c_str(),c+1);
}
//-------------------------------------------------------------------------
// Base output serializer
//-------------------------------------------------------------------------
COutputStreamSerializer::COutputStreamSerializer ()
{
stream = 0;
}
bool COutputStreamSerializer::IsWriting ()
{
return true;
}
COutputStreamSerializer::ObjectRef* COutputStreamSerializer::FindObjectRef(void *inst, creg::Class *objClass, bool isEmbedded)
{
std::vector<ObjectRef*> *refs = &(ptrToId[inst]);
for (std::vector<ObjectRef*>::iterator i=refs->begin();i!=refs->end();i++) {
if ((*i)->isThisObject(inst,objClass,isEmbedded))
return *i;
}
return 0;
}
void COutputStreamSerializer::SerializeObjectInstance (void *inst, creg::Class *objClass)
{
// register the object, and mark it as embedded if a pointer was already referencing it
ObjectRef *obj = FindObjectRef(inst,objClass,true);
if (!obj) {
obj = &*objects.insert(objects.end(),ObjectRef(inst,objects.size (),true,objClass));
ptrToId[inst].push_back(obj);
} else if (obj->isEmbedded)
throw "Reserialization of embedded object";
else {
std::vector<ObjectRef*>::iterator pos;
for (pos=pendingObjects.begin();pos!=pendingObjects.end() && (*pos)!=obj;pos++) ;
if (pos==pendingObjects.end())
throw "Object pointer was serialized";
else {
pendingObjects.erase(pos);
}
}
obj->class_ = objClass;
obj->isEmbedded = true;
// printf ("writepos of %s (%d): %d\n", objClass->name.c_str(), obj->id,(int)stream->tellp());
// write an object ID
int id = swabdword (obj->id);
stream->write ((char*)&id, sizeof(int));
// write the object
objClass->SerializeInstance (this, inst);
}
void COutputStreamSerializer::SerializeObjectPtr (void **ptr, creg::Class *objClass)
{
if (*ptr) {
// valid pointer, write a one and the object ID
int id;
ObjectRef *obj = FindObjectRef(*ptr,objClass,false);
if (!obj) {
obj = &*objects.insert(objects.end(),ObjectRef(*ptr,objects.size (),false,objClass));
ptrToId[*ptr].push_back(obj);
id = obj->id;
pendingObjects.push_back (obj);
} else
id = obj->id;
*stream << (char)1;
id = swabdword(id);
stream->write ((char*)&id, sizeof(int));
} else {
// null pointer, write a zero
*stream << (char)0;
}
}
void COutputStreamSerializer::Serialize (void *data, int byteSize)
{
stream->write ((char*)data, byteSize);
}
struct COutputStreamSerializer::ClassRef
{
int index;
creg::Class* class_;
};
void COutputStreamSerializer::SavePackage (std::ostream *s, void *rootObj, Class *rootObjClass)
{
PackageHeader ph;
stream = s;
unsigned startOffset = stream->tellp();
stream->seekp (startOffset + sizeof (PackageHeader));
ph.objDataOffset = (int)stream->tellp();
// Insert the first object that will provide references to everything
ObjectRef *obj = &*objects.insert(objects.end(),ObjectRef(rootObj,objects.size(),false,rootObjClass));
ptrToId[rootObj].push_back(obj);
pendingObjects.push_back (obj);
map<creg::Class *,int> classSizes;
// Save until all the referenced objects have been stored
while (!pendingObjects.empty ())
{
std::vector <ObjectRef*> po = pendingObjects;
pendingObjects.clear ();
for (std::vector <ObjectRef*> ::iterator i=po.begin();i!=po.end();++i)
{
ObjectRef* obj = *i;
unsigned objstart = stream->tellp();
obj->class_->SerializeInstance (this, obj->ptr);
unsigned objend = stream->tellp();
int sz = objend-objstart;
classSizes[obj->class_]+=sz;
}
}
// Collect a set of all used classes
map<creg::Class *,ClassRef> classMap;
vector <ClassRef*> classRefs;
map<int,int> classObjects;
for (std::list <ObjectRef>::iterator i=objects.begin();i!=objects.end();i++) {
//printf ("Obj: %s\n", oi->second.class_->name.c_str());
map<creg::Class*,ClassRef>::iterator cr = classMap.find (i->class_);
if (cr == classMap.end()) {
ClassRef *pRef = &classMap[i->class_];
pRef->index = classRefs.size();
pRef->class_ = i->class_;
classRefs.push_back (pRef);
i->classIndex = pRef->index;
} else
i->classIndex = cr->second.index;
classObjects[i->classIndex]++;
}
// for (map<int,int>::iterator i=classObjects.begin();i!=classObjects.end();i++) {
// logOutput.Print("%20s %10u %10u",classRefs[i->first]->class_->name.c_str(),i->second,classSizes[classRefs[i->first]->class_]);
// }
// Write the class references
ph.numObjClassRefs = classRefs.size();
ph.objClassRefOffset = (int)stream->tellp();
for (uint a=0;a<classRefs.size();a++) {
WriteZStr (*stream, classRefs[a]->class_->name);
// write a checksum (unused atm)
int checksum = swabdword(0);
stream->write ((char*)&checksum, sizeof(int));
}
// Write object info
ph.objTableOffset = (int)stream->tellp();
ph.numObjects = objects.size();
for (std::list <ObjectRef>::iterator i=objects.begin();i!=objects.end();i++) {
PackageObject d;
d.classRefIndex = i->classIndex;
d.isEmbedded = i->isEmbedded ? 1 : 0;
d.SwapBytes ();
stream->write ((char*)&d, sizeof(PackageObject));
}
// Calculate a checksum for metadata verification
ph.metadataChecksum = 0;
for (uint a=0;a<classRefs.size();a++)
{
Class *c = classRefs[a]->class_;
c->CalculateChecksum (ph.metadataChecksum);
}
// printf("Checksum: %d\n", ph.metadataChecksum);
int endOffset = stream->tellp();
stream->seekp (startOffset);
memcpy(ph.magic, CREG_PACKAGE_FILE_ID, 4);
ph.SwapBytes ();
stream->write ((const char *)&ph, sizeof(PackageHeader));
// logOutput.Print("Number of objects saved: %d\nNumber of classes involved: %d\n", objects.size(), classRefs.size());
stream->seekp (endOffset);
ptrToId.clear();
pendingObjects.clear();
objects.clear();
}
//-------------------------------------------------------------------------
// CInputStreamSerializer
//-------------------------------------------------------------------------
CInputStreamSerializer::CInputStreamSerializer()
{
stream = 0;
}
CInputStreamSerializer::~CInputStreamSerializer()
{}
bool CInputStreamSerializer::IsWriting ()
{
return false;
}
void CInputStreamSerializer::Serialize (void *data, int byteSize)
{
stream->read ((char*)data, byteSize);
}
void CInputStreamSerializer::SerializeObjectPtr (void **ptr, creg::Class *cls)
{
char v;
// printf ("reading ptr %s* at %d\n", cls->name.c_str(), (int)stream->tellg());
*stream >> v;
if (v) {
int id;
stream->read ((char*)&id, sizeof(int));
id = swabdword (id);
StoredObject &o = objects [id];
if (o.obj) *ptr = o.obj;
else {
// The object is not yet avaiable, so it needs fixing afterwards
UnfixedPtr ufp;
ufp.objID = id;
ufp.ptrAddr = ptr;
unfixedPointers.push_back (ufp);
}
} else
*ptr = NULL;
}
// Serialize an instance of an object embedded into another object
void CInputStreamSerializer::SerializeObjectInstance (void *inst, creg::Class *cls)
{
int id;
stream->read((char*)&id, sizeof(int));
// printf ("readpos of embedded %s (%d): %d\n", cls->name.c_str(), id, ((int)stream->tellg())-4);
StoredObject& o = objects[swabdword(id)];
if (id==0 && o.obj) {
return; // this is old save game and it has not this object - skip it
}
assert (!o.obj);
assert (o.isEmbedded);
o.obj = inst;
cls->SerializeInstance (this, inst);
}
void CInputStreamSerializer::AddPostLoadCallback(void (*cb)(void*), void *ud)
{
PostLoadCallback plcb;
plcb.cb = cb;
plcb.userdata = ud;
callbacks.push_back (plcb);
}
void CInputStreamSerializer::LoadPackage (std::istream *s, void*& root, creg::Class *& rootCls)
{
PackageHeader ph;
stream = s;
s->read((char *)&ph, sizeof(PackageHeader));
if (memcmp (ph.magic, CREG_PACKAGE_FILE_ID, 4))
throw std::runtime_error ("Incorrect object package file ID");
// Load references
classRefs.resize (ph.numObjClassRefs);
s->seekg (ph.objClassRefOffset);
for (int a=0;a<ph.numObjClassRefs;a++)
{
string className = ReadZStr (*s);
int checksum;
s->read ((char*)&checksum, sizeof(int));
checksum = swabdword(checksum); // ignored for now
creg::Class *class_ = System::GetClass (className);
if (!class_)
throw std::runtime_error ("Package file contains reference to unknown class " + className);
classRefs[a] = class_;
}
// Calculate metadata checksum and compare with stored checksum
unsigned int checksum = 0;
for (uint a=0;a<classRefs.size();a++)
classRefs[a]->CalculateChecksum (checksum);
if (checksum != ph.metadataChecksum)
throw std::runtime_error ("Metadata checksum error: Package file was saved with a different version");
// Create all non-embedded objects
s->seekg (ph.objTableOffset);
objects.resize (ph.numObjects);
for (int a=0;a<ph.numObjects;a++)
{
PackageObject d;
s->read ((char*)&d, sizeof(PackageObject));
d.SwapBytes ();
if (!d.isEmbedded) {
// Allocate and construct
ClassBinder *binder = classRefs[d.classRefIndex]->binder;
void *inst = binder->class_->CreateInstance ();
objects [a].obj = inst;
} else objects[a].obj = 0;
objects [a].isEmbedded = !!d.isEmbedded;
objects [a].classRef = d.classRefIndex;
}
int endOffset = s->tellg();
// printf ("Loading %d objects (at %d)\n", objects.size(), (int)stream->tellg());
// Read the object data using serialization
s->seekg (ph.objDataOffset);
for (uint a=0;a<objects.size();a++)
{
if (!objects[a].isEmbedded) {
creg::Class *cls = classRefs[objects[a].classRef];
cls->SerializeInstance (this, objects[a].obj);
}
}
// Fix pointers to embedded objects
for (uint a=0;a<unfixedPointers.size();a++) {
void *p = objects [unfixedPointers[a].objID].obj;
*unfixedPointers[a].ptrAddr = p;
}
// Run all registered post load callbacks
for (uint a=0;a<callbacks.size();a++) {
callbacks[a].cb (callbacks[a].userdata);
}
// Run post load functions on all objects
for (uint a=0;a<objects.size();a++) {
StoredObject& o = objects[a];
Class *c = classRefs[objects[a].classRef];
std::vector<Class*> hierarchy;
for (Class *c2=c;c2;c2=c2->base)
hierarchy.insert(hierarchy.end(),c2);
for (std::vector<Class*>::reverse_iterator i=hierarchy.rbegin();i!=hierarchy.rend();i++) {
if ((*i)->postLoadProc) {
_DummyStruct *ds = (_DummyStruct*)o.obj;
(ds->*(*i)->postLoadProc)();
}
}
/*
if (c->postLoadProc) {
_DummyStruct *ds = (_DummyStruct*)o.obj;
(ds->*c->postLoadProc)();
}
*/
}
// The first object is the root object
root = objects[0].obj;
rootCls = classRefs[objects[0].classRef];
s->seekg (endOffset);
unfixedPointers.clear();
objects.clear();
}
ISerializer::~ISerializer() {
}
/* Testing..
class EmbeddedObj {
CR_DECLARE(EmbeddedObj);
int value;
};
CR_BIND(EmbeddedObj);
CR_REG_METADATA(EmbeddedObj, CR_MEMBER(value));
struct TestObj {
CR_DECLARE(TestObj);
TestObj() {
intvar=0;
for(int a=0;a<5;a++) sarray[a]=0;
childs[0]=childs[1]=0;
embeddedPtr=0;
}
~TestObj() {
if (childs[0]) delete childs[0];
if (childs[1]) delete childs[1];
}
int intvar;
string str;
int sarray[5];
EmbeddedObj *embeddedPtr;
vector<int> darray;
TestObj *childs[2];
EmbeddedObj embedded;
};
CR_BIND(TestObj);
CR_REG_METADATA(TestObj, (
CR_MEMBER(childs),
CR_MEMBER(intvar),
CR_MEMBER(str),
CR_MEMBER(sarray),
CR_MEMBER(embedded),
CR_MEMBER(embeddedPtr),
CR_MEMBER(darray)
));
static void savetest()
{
TestObj *o = SAFE_NEW TestObj;
o->darray.push_back(3);
o->intvar = 1;
o->str = "Hi!";
for (int a=0;a<5;a++) o->sarray[a]=a+10;
TestObj *c = SAFE_NEW TestObj;
c->intvar = 144;
o->childs [0] = c;
o->childs [1] = c;
c->embeddedPtr = &o->embedded;
o->embeddedPtr = &c->embedded;
creg::COutputStreamSerializer ss;
std::ofstream f ("test.p", ios::out | ios::binary);
ss.SavePackage (&f, o, o->GetClass());
}
static void loadtest()
{
creg::CInputStreamSerializer ss;
std::ifstream f ("test.p", ios::in | ios::binary);
void *root;
creg::Class *rootCls;
ss.LoadPackage (&f, root, rootCls);
assert (rootCls == TestObj::StaticClass());
TestObj *obj = (TestObj*)root;
assert (obj->childs[0]->embeddedPtr == &obj->embedded);
assert (obj->embeddedPtr == &obj->childs[0]->embedded);
assert (obj->childs[0]==obj->childs[1]);
assert (obj->childs[0]->intvar == 144);
assert (obj->darray.size()==1 && obj->darray[0]==3);
assert (obj->intvar == 1);
assert (obj->str == "Hi!");
for (int a=0;a<5;a++) printf("%d\n",obj->sarray[a]);
}
void test_creg_serializer()
{
savetest();
loadtest();
remove("test.p");
}
*/
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?