The purpose to design a serializer class is to ensure data can be saved to and read from binary files in the exact same way.
With fstream, a file can be opened in either read mode or write mode. Once the file is opened, all operations will depend on the mode user chose.
The first function is to ensure file header. The function will write a given header string into file, or read header from file and check if it matches the one provided by user.
bool EnsureHeader(const char* header, UINT size)
{
if (m_Mode == SM_Write)
{
m_FileStream.write(header, size);
}
else
{
assert(!m_FileStream.eof());
char* pBuf = new char[size];
m_FileStream.read(pBuf, size);
for (UINT i = 0; i < size; i++)
{
if (pBuf[i] != header[i])
{
delete[] pBuf;
return false;
}
}
Then, I made a template function to write to and read from file.
template<typename T>
void SerializeData(T& data)
{
if (m_Mode == SM_Write)
{
m_FileStream.write((char*)&data, sizeof(T));
}
else
{
assert(!m_FileStream.eof());
m_FileStream.read((char*)&data, sizeof(T));
}
}
The template function can take a simple type of data as parameter and save to or read from a binary file. However, it’s not good enough for container classes such as std::string. So I have a specialization template function with std::string.
template<>
void SerializeData<string>(string& str)
{
if (m_Mode == SM_Write)
{
UINT size = (UINT)str.size();
m_FileStream.write((char*)&size, sizeof(size));
if (size)
m_FileStream.write((char*)str.data(), size);
}
else
{
assert(!m_FileStream.eof());
UINT size;
m_FileStream.read((char*)&size, sizeof(size));
str.resize(size);
if (size)
m_FileStream.read((char*)str.data(), size);
}
}
With the same idea, I added vector and array serialization. Just to make sure read and write behave in the same way.
Here is a sample of how to use my serializer:
void RMesh::Serialize(RSerializer& serializer)
{
if (!serializer.EnsureHeader("RMSH", 4))
return;
serializer.SerializeVector(m_MeshElements, &RSerializer::SerializeObject);
serializer.SerializeVector(m_Materials, &RSerializer::SerializeObject);
serializer.SerializeObjectPtr(&m_Animation);
serializer.SerializeVector(m_BoneInitInvMatrices);
serializer.SerializeVector(m_BoneIdToName, &RSerializer::SerializeData);
And finally to call serialize on mesh class.
serializer.Open(”mesh.data”, SM_Write);
mesh.Serialize(serializer);