Search code examples
c++bmpfile-type

Creating a custom file-type in C++


I am making a level editor that needs to output a custom map file for use with source code that I will write for a graphics library.

My question is: What do I need to bear in mind when deciding how to structure a custom file type? Also how can I encode a standard bitmap image into my file (the tile set) so that it can all be contained in a single file rather two files; the map file and tile set (.bmp file).

Thanks.


Solution

  • First you need to design your file layer, you need fixed size data to determine how to decode your variable data, for example, the first four bytes you read could tell how many tiles there are, the next 4 bytes could tell how many map files there are, after that you for example have the first file, you have to define your file information, eg which file and how many bytes it is, then you know how many bytes you're away from the next file entry, etc...

    You can also use structures and overload the ofstream operators << and >> for it to create your own filetype, I did this myself when creating a replay system for Need For Speed Underground 2.

    struct Point4D { float x, y, z, w; };
    struct Point3D { float x, y, z; };
    struct Point2D { float x, y; };
    
    
    struct QuatRot
    {
        Point4D Front;//frontvector
        Point4D Right;//rightvector
        Point4D Up;//upvector
    };
    
    #define NFS_MAX_VEHICLES (14)//seriously just 14.. (including the player)
    
    
    struct VehicleInfo
    {
        //type      name                                    array   offset
        float       unknown_01                              [8];    //0x0
        Point3D     Pos;                                            //0x20
        float       unknown_02;                                     //0x2C
        QuatRot     Rotation;                                       //0x30
        Point3D     unknown_03;                                     //0x60
        float       unknown_04;                                     //0x6C
        Point3D     Velocity;                                       //0x70
        float       unknown_05;                                     //0x7C
        Point3D     TurnSpeed;                                      //0x80
        float       unknown_06;                                     //0x8C
        float       SpeedAndBrakeAccelerator;                       //0x90
        float       unknown_07                              [3];    //0x94
        Point3D     unknown_08;                                     //0xA0
        float       unknown_09;                                     //0xAC
        Point4D     unknown_10                              [5];    //0xB0
        float       unknown_11                              [20];   //0x100
        float       unknown_12_is_zero_when_accelerating;           //0x150
        //end of structure...?
    };
    
    std::ostream& operator<<(std::ostream& stream, const VehicleInfo &info);
    //overload << and >> operators on "VehicleInfo" type
    std::ofstream& operator<<(std::ofstream& stream, VehicleInfo &info);
    std::ifstream& operator>>(std::ifstream& stream, VehicleInfo &info);
    //overload << and >> operators on "FrameInfo" type
    std::ofstream& operator<<(std::ofstream& stream, Recorder::FrameInfo &info);
    std::ifstream& operator>>(std::ifstream& stream, Recorder::FrameInfo &info);
    
    namespace Recorder
    {
        struct FrameInfo//1392 bytes / frame | max 167040 bytes @ 120 fps | 9.56 MB / min max
        {
            std::chrono::high_resolution_clock::duration time;
            VehicleInfo Vehicle;
            int Nitro;
            float RPM;
            float TURBO;
            int CurrentGear;
            KeyManager Keys[256];
        };
    };
    
    std::ofstream& operator<<(std::ofstream& stream, VehicleInfo &info)
    {
        stream.write(reinterpret_cast<char*>(&info.unknown_01[0]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_01[1]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_01[2]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_01[3]), sizeof(float));
            //...
        stream.write(reinterpret_cast<char*>(&info.unknown_11[18]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_11[19]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_11[20]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_12_is_zero_when_accelerating), sizeof(float));
        return stream;
    }
    
    std::ifstream& operator>>(std::ifstream& stream, VehicleInfo &info)
    {
        stream.read(reinterpret_cast<char*>(&info.unknown_01[0]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_01[1]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_01[2]), sizeof(float));
                //.....
        stream.read(reinterpret_cast<char*>(&info.unknown_11[16]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_11[17]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_11[18]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_11[19]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_11[20]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_12_is_zero_when_accelerating), sizeof(float));
        return stream;
    }
    
    std::ofstream& operator<<(std::ofstream& stream, Recorder::FrameInfo &info)
    {
        stream.write(reinterpret_cast<char*>(&info.time), sizeof(std::chrono::high_resolution_clock::duration));
        stream << info.Vehicle;
        stream.write(reinterpret_cast<char*>(&info.Nitro), sizeof(int));
        stream.write(reinterpret_cast<char*>(&info.RPM), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.CurrentGear), sizeof(int));
        stream.write(reinterpret_cast<char*>(&info.TURBO), sizeof(int));
        for(int i = 0; i < 256; ++i)
        {
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Pressed), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Released), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Down), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Up), sizeof(bool));
        }
        return stream;
    }
    
    std::ifstream& operator>>(std::ifstream& stream, Recorder::FrameInfo &info)
    {
        stream.read(reinterpret_cast<char*>(&info.time), sizeof(std::chrono::high_resolution_clock::duration));
        stream >> info.Vehicle;
        stream.read(reinterpret_cast<char*>(&info.Nitro), sizeof(int));
        stream.read(reinterpret_cast<char*>(&info.RPM), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.CurrentGear), sizeof(int));
        stream.read(reinterpret_cast<char*>(&info.TURBO), sizeof(int));
        for(int i = 0; i < 256; ++i)
        {
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Pressed), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Released), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Down), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Up), sizeof(bool));
        }
        return stream;
    }
    
    ///
    
    std::stringstream& operator<<(std::stringstream& stream, VehicleInfo &info)
    {
        stream.write(reinterpret_cast<char*>(&info.unknown_01[0]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_01[1]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_01[2]), sizeof(float));
                //...
        stream.write(reinterpret_cast<char*>(&info.unknown_11[19]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_11[20]), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.unknown_12_is_zero_when_accelerating), sizeof(float));
        return stream;
    }
    
    std::stringstream& operator>>(std::stringstream& stream, VehicleInfo &info)
    {
        stream.read(reinterpret_cast<char*>(&info.unknown_01[0]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_01[1]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_01[2]), sizeof(float));
                //....
        stream.read(reinterpret_cast<char*>(&info.unknown_11[20]), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.unknown_12_is_zero_when_accelerating), sizeof(float));
        return stream;
    }
    
    std::stringstream& operator<<(std::stringstream& stream, Recorder::FrameInfo &info)
    {
        stream.write(reinterpret_cast<char*>(&info.time), sizeof(std::chrono::high_resolution_clock::duration));
        stream << info.Vehicle;
        stream.write(reinterpret_cast<char*>(&info.Nitro), sizeof(int));
        stream.write(reinterpret_cast<char*>(&info.RPM), sizeof(float));
        stream.write(reinterpret_cast<char*>(&info.CurrentGear), sizeof(int));
        stream.write(reinterpret_cast<char*>(&info.TURBO), sizeof(int));
        for(int i = 0; i < 256; ++i)
        {
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Pressed), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Released), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Down), sizeof(bool));
            stream.write(reinterpret_cast<char*>(&info.Keys[i].Up), sizeof(bool));
        }
        return stream;
    }
    
    std::stringstream& operator>>(std::stringstream& stream, Recorder::FrameInfo &info)
    {
        stream.read(reinterpret_cast<char*>(&info.time), sizeof(std::chrono::high_resolution_clock::duration));
        stream >> info.Vehicle;
        stream.read(reinterpret_cast<char*>(&info.Nitro), sizeof(int));
        stream.read(reinterpret_cast<char*>(&info.RPM), sizeof(float));
        stream.read(reinterpret_cast<char*>(&info.CurrentGear), sizeof(int));
        stream.read(reinterpret_cast<char*>(&info.TURBO), sizeof(int));
        for(int i = 0; i < 256; ++i)
        {
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Pressed), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Released), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Down), sizeof(bool));
            stream.read(reinterpret_cast<char*>(&info.Keys[i].Up), sizeof(bool));
        }
        return stream;
    }