Object Encoding & Decoding - JayhawkZombie/EECS581Project GitHub Wiki
All of the engine's game objects can be encoded to binary files and decoded from binary files (given the file was formatted properly and in the correct version format).
This is done in binary so as to avoid the overhead of reading in text and also avoid conversions from text to the underlying type.
The signature of the encoding (to a file) member method is:
void SerializeOut(std::ofstream &out);
The signature of the decoding (from a file) member method is:
void SerializeIn(std::ifstream &in);
When decoding/decoding an object, you MUST call the version of it for whatever class you derived from BEFORE decoding your data
ie
void MyDerivedClass::SerializeIn(std::ifstream &in)
{
BaseClass::SerializeIn(in);
//my stuff
}
void MyDerivedClass::SerializeOut(std::ofstream &out)
{
BaseClass::SerializeOut(out);
//my stuff
}
The data ultimately written to the file looks like nonsense if one attempts to open it in a text file, but that is because it is interpreting every byte as a single character.
Here is a small program demonstrating object encoding/decoding:
#include <string>
#include <fstream>
#include <iostream>
#include <vector>
struct vec2f {
vec2f() = default;
vec2f(const float &f1, const float &f2) : x(f1), y(f2) {}
float x = 0.44f, y = 0.27f;
};
struct mesh
{
std::vector<vec2f> points;
void Decode(std::ifstream &in);
void Encode(std::ofstream &out);
};
class TestBase
{
public:
TestBase() = default;
~TestBase() = default;
void Decode(std::ifstream &in);
void Encode(std::ofstream &out);
mesh mymesh;
private:
std::string str;
float f;
double d = 0.213423423f;
vec2f vec;
};
void mesh::Decode(std::ifstream &in)
{
std::size_t count{ 0 };
//read in the # of points we should expect to find
//this was encoded earlier by the Encode method
in.read((char *)(&count), sizeof(std::size_t));
std::cerr << "read " << count << " as # of points" << std::endl;
float f1{ 0.f }, f2{ 0.f };
//for each of those points, read in the values and push a new point into our vector of points
for (std::size_t i = 0; i < count; i++) {
in.read((char *)(&f1), sizeof(float));
in.read((char *)(&f2), sizeof(float));
std::cerr << "\tpt: (" << f1 << ", " << f2 << ")" << std::endl;
points.push_back({ f1, f2 });
}
}
void mesh::Encode(std::ofstream &out)
{
std::size_t amnt = points.size();
//write out the number of pts we are storing
out.write((char *)(&amnt), sizeof(std::size_t));
//for each of the points, write out the x value and then the y value
//they must be read in exacly the same order to properly decode
for (auto & pt : points) {
out.write((char *)(&(pt.x)), sizeof(float));
out.write((char *)(&(pt.y)), sizeof(float));
}
out.write("end_mesh", strlen("end_mesh"));
}
void TestBase::Decode(std::ifstream &in)
{
unsigned int amnt{ 0 };
in.read((char *)(&amnt), sizeof(unsigned int));
std::cerr << amnt << " read as indicator" << std::endl;
char *c = new char[amnt];
in.read(c, amnt);
str = std::string(c, amnt);
delete[] c;
std::cerr << "str: " << str << std::endl;
in.read((char*)(&f), sizeof(float));
std::cerr << "f: " << f << std::endl;
in.read((char *)(&d), sizeof(double));
std::cerr << "d: " << d << std::endl;
in.read((char *)(&vec.x), sizeof(float));
std::cerr << "vec.x: " << vec.x << std::endl;
in.read((char *)(&vec.y), sizeof(float));
std::cerr << "vec.y: " << vec.y << std::endl;
mymesh.Decode(in);
}
void TestBase::Encode(std::ofstream &out)
{
std::size_t amnt = str.size();
out.write((char *)(&amnt), sizeof(std::size_t));
out.write(str.c_str(), str.size());
out.write((char *)(&f), sizeof(float));
out.write((char *)(&d), sizeof(double));
out.write((char *)(&vec.x), sizeof(float));
out.write((char *)(&vec.y), sizeof(float));
mymesh.Encode(out);
out.write("end_base", strlen("end_base"));
}
int main()
{
std::ofstream file("TestOutBinary.sf", std::ios::out | std::ios::binary | std::ios::beg);
unsigned int writeamnt{ 10 };
char * testData = "test file";
float testfloat(0.0012302131f);
if (file.is_open()) {
file.write((char *)(&writeamnt), sizeof(unsigned int));
file.write(testData, 10);
file.write((char*)(&testfloat), sizeof(float));
file.close();
}
//try to read from the file
std::ifstream infile("TestEncoding.sf", std::ios::in | std::ios::binary | std::ios::beg);
TestBase obj;
obj.Decode(infile);
infile.close();
std::ofstream testencoding("TestEncoding.sf", std::ios::out | std::ios::binary | std::ios::beg);
obj.Encode(testencoding);
testencoding.close();
}
The encoded file looks like complete nonsense if opened in a text editor, but that should be expected:
" test file ?¡: `uQË?®Gá>q=Š>( ÍÌL>ÍÌÌ>ÍÌÌ>ÍÌL?š™?š™™?ÍÌL?ÍÌÌ? €? @š™™?š™@33³?333@ÍÌÌ?ÍÌL@gfæ?gff@ ÍÌL>ÍÌÌ>ÍÌÌ>ÍÌL?š™?š™™?ÍÌL?ÍÌÌ? €? @š™™?š™@33³?333@ÍÌÌ?ÍÌL@gfæ?gff@ ÍÌL>ÍÌÌ>ÍÌÌ>ÍÌL?š™?š™™?ÍÌL?ÍÌÌ? €? @š™™?š™@33³?333@ÍÌÌ?ÍÌL@gfæ?gff@ ÍÌL>ÍÌÌ>ÍÌÌ>ÍÌL?š™?š™™?ÍÌL?ÍÌÌ? €? @š™™?š™@33³?333@ÍÌÌ?ÍÌL@gfæ?gff@end_meshend_base"
The " at the beginning and end of the file are not in the file. I only put them there to prevent github from removing the extra spaces that appear when opening the file.