简体   繁体   中英

Storing, retrieving binary data or bytes to and from a file for a std::deque?

I am writing a "replay system" for a game and I'm wondering how I should store the recorded frames?

As for now I have this code / structures (note: this code is shortened):

struct Point4D { float x, y, z, w; };
struct Point3D { float x, y, z; };
struct Point2D { float x, y; };
struct QuatRot { Point4D Front,Right,Up; };
struct VehicleInfo
{
    Point3D     Pos,Velocity,TurnSpeed;
    QuatRot     Rotation;
};
namespace Recorder
{
    struct FrameInfo
    //~380 bytes / frame| max 45600 bytes @ 120 fps
    //max 3.7 GB raw data for 24 hours of recording, not bad..
    {
        std::chrono::high_resolution_clock::duration time;
        VehicleInfo Vehicle;
        int Nitro;
        float RPM;
        int CurrentGear;
    };

    std::deque<FrameInfo> frames;
    FrameInfo g_Temp;

    void ProcessRecord()
    {
        //record vehicle data here to g_Temp
        frames.push_back(g_Temp);
        return;
    }
    //other recording code.......
};

What I was thinking of is, making a raw array of bytes, allocate it to the size of the deque container, copy them with memcpy from the deque to the array and then write all the array bytes to the file..

Then if I would like to read my recording data I would just read the bytes of the file and assign them to a new array, and use memcpy to copy the array contents to a deque..

and this is much like.. well.. the C way? There has to be some other way to do this, store the data in a file , then read it back into the deque (maybe using some C++11 features?).

How would I accomplish this?

Which approach do you recommend?

I am using windows if that matters.

memcpy is premature optimization.

When reading stuff from disk, your bottleneck is disk IO, not copying it from one part of memory to another.

Fix your data structure so it uses fixed size data structures (instead of int , use a 32 bit int, etc).

Don't write std::chrono::high_resolution_clock::duration in binary -- a library update could completely change the size of that without them blinking an eye or shedding a tear, let alone it's meaning. Write out in ms or something, so the meaning always remains the same, in a (say) 64 bit integer. You can then read it back into your std::chrono::high_resolution_clock::duration .

Always write out a version number and structure size when serializing, so deserialization can handle even rudimentary versioning.

I'd write a "to stream" and "from stream". "to stream" write out the version number and the size. "from stream" reads the version number and size, loads each field that is in both the current version and the stream version, clears the remaining, then reads the remaining data off of the stream.

If you find you need more performance, you'll note that the position and angle of your car will change far more often than the gears. In addition, dropping frames that are reasonably interpolated between existing frames would massively decrease the size of your replay format (it isn't as if you are running physics in the replay, given your description). Finally, if you have a consistent physics model, only storing user input and replaying based on that could be possible (but that is hard to pull off).

Other insanities: SRECORDASSIGN can be replaced by just calling operator= on the structures in question. Magic numbers like 0x4C applied to pointers are silly, and are almost always replaceable with simple struct member access.

If I interpret your question correctly (I'm tired so please just leave a comment if I'm wrong), you want to write and read your recording to and from file.

This can easily be done with any struct:

struct Foo
{
   float bar;
   int baz;
};
std::ostream& operator<<(std::ostream& stream, const Foo &foo)
{
   stream << foo.bar << " " << foo.baz;
}
std::ofstream& operator<<(std::ofstream& stream, Foo &foo)
{
   stream.write(reinterpret_cast<char*>(&foo.bar), sizeof(bar));
   stream.write(reinterpret_cast<char*>(&foo.baz), sizeof(baz));
}
std::ifstream& operator>>(std::ifstream& stream, Foo &foo)
{
   stream.read(reinterpret_cast<char*>(&foo.bar), sizeof(bar));
   stream.read(reinterpret_cast<char*>(&foo.baz), sizeof(baz));
}

You can test this with

#include <fstream>
#include <iostream>

int main()
{
   Foo foo;
   foo.bar = -1.2f;
   foo.baz = 37;
    std::cout << foo << "\n";
   std::ofstream output;
   output.open("myfile", std::ios::binary);
   output << foo;
   output.close();
   std::ifstream input;
   input.open("myfile", std::ios::binary);
   input >> foo;
   input.close();
   std::cout << foo << "\n";
}

For further information about std::basic_ostream::write and std::basic::istream::read , I recommend having a look at cppreference.com

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM