Joel Verhagen

a computer programming blog

Resource File Manager in C++

I have recently been spending a lot of time learning about game programming. One function I would like my game to have is to store all the resource or data files it uses into one big data file. There are a couple of reasons for this:

  • Raw resources are hidden from the user, which provides a little bit of privacy.
  • The is a reduced chance that the user will inadvertently delete a file necessary for the game to run.
  • It's fun to do!

There are a couple people out there that have already taken a hack at it and provided perfectly usable implementation (one on SFML's wiki and one on GameDev.net). However, I wrote my own because neither handles the problem of big-endian vs. little-endian memory and I wanted the experience of some hardcore C++ file I/O action.

To solve the big-endian vs. little-endian problem, I decided on how integers will be stored in my file (I chose little-endian because that's much more common) and I found a way to convert a C++ int to little-endian bytes. Someone on StackOverflow created a compact header file to determine the endianness of the host machine.

From there, it was pretty easy to get things going. My finished product is called Resource File Manager. Generic enough for you? The current version is version 1.0. You can currently pack files into a larger file using ResourceFileWriter and read them back out again using ResourceFileReader.

You can download the whole package from this link.

Also, here's a GitHub repository for the project.

I have successfully tested the classes in MingW and Visual Studio 2010.

Here's a short example of how the Resource File Manager is used.

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;

#include "ResourceFile.h"

int main()
{
    {
        // write test
        cout << "Write Test:" << endl;

        ResourceFileWriter writeTest("writeTest.dat");
        writeTest.addEntry("ball.png", "files/ball.png");
        writeTest.addEntry("audio.mp3", "files/audio.mp3");
        writeTest.addEntry("Text Document.txt", "files/Text Document.txt");
        writeTest.write();

        vector<string> entryNamesList = writeTest.getEntryNames();

        cout << "The following files were added to writeTest.dat." << endl;
        for(vector<string>::iterator i = entryNamesList.begin(); i != entryNamesList.end(); i++)
        {
            string entryName = (*i);
            cout << entryName << endl;
        }
    }

    cout << endl << endl;

    {
        // read test
        cout << "Read Test:" << endl;

        ResourceFileReader readTest("writeTest.dat");
        readTest.read();

        vector<string> entryNamesList = readTest.getEntryNames();

        cout << "The following files are in writeTest.dat." << endl;
        for(vector<string>::iterator i = entryNamesList.begin(); i != entryNamesList.end(); i++)
        {
            string entryName = (*i);
            cout << entryName << " (" << readTest.getEntryFileSize(entryName) << " bytes)" << endl;
        }
    }

    return 0;
}