Silkroad Online – A brief insight to GFXFileManager

A few days ago, I completed the first major part of my research to the GFXFileManager. The interface and a reference implementation of the FileManager is complete. Only a few unknown functions are left.

TL;DR; Full Source Code on GitHub: https://github.com/florian0/GFXFileManager

The gameclient uses the GFXFileManager.dll to access the *.pk2-containers. There is no pk2 related code in the client itself. If you ever had a look at the leaked vsro serverfiles, you might have noticed that the server uses it, too, eventho the gameserver uses no pk2-files at all. In fact, the Dll can be used to access files inside pk2-containers and files on the plain hard disk. The gameclient uses the Dll to access SOME files on the hard disk.

Looking at the exports

The Dll exports 3 different functions:

GFXDllCreateObject

extern "C" __declspec(dllexport) int __stdcall  GFXDllCreateObject(int mode, IFileManager** object, int version) {

    if (version != TARGET_VERSION) {
        char message[100];
        sprintf(message, "Dll Version(%x)\nNecessary Version (%x)", version, TARGET_VERSION);
        MessageBox(0, message, "Invalid Version(GFXFileManager.dll)", MB_OK|MB_APPLMODAL);
        return 0;
    }

    if (mode == MODE_ARCHIVE) {
        *object = new CPFileManager();
    } else if (mode == MODE_FILESYSTEM) {
        *object = new CWFileManager();
    } else {
        *object = 0;
    }

    return 0;
}

A function that basically switches between two different objects. CPFileManager, which is used for pk2-containers, and CWFileManager, which is used for plain disk access. Since they are returned by the same function, they must have a common base interface. Well, they have. Its called IFileManager.

GFXDllDeleteObject

extern "C" __declspec(dllexport) void __stdcall GFXDllReleaseObject(IFileManager* object) {
    delete object;
}

The counterpart to the create-function: A generic deleting function.

GFXFMInfo

extern "C" __declspec(dllexport) int __stdcall GFXFMInfo(gfxinfo_t *pInfo, int index) {

    if ( g_gfx_infos[index].in_use )
    {
        memcpy(pInfo, &g_gfx_infos[index], sizeof(gfxinfo_t));
    }
    else
    {
        memset(pInfo, 0, sizeof(gfxinfo_t));
    }

    return 0;
}

And a function that delivers information about an instance of CWFileManager or CPFileManager. It’s most likely for debugging purposes. The FMInfo structure pointer is linked to the to FMInstance. But the IFileManager-interface does not offer a function to access this nor is there are codepath that delivers the index of the FMinfo in the array. As of current state of reversing, there is no code for reusing fields of the array. So after sizeof(gfxinfo_t) open containers (which is 500), something might fail. This should, hopefully, never happen.

 

About IFileManager

This is an abstract base for all classes that implement any kind of container. It provides 55 methods in total, where 4 are not abstract. Overall, the IFileManager interface is designed to act as a wrapper around the WinAPI. Creating and removing directory, reading and writing files, accessing file-times and searching are possible. My reverse engineered class interface is based on the CWFileManager implementation. Most of the functions were simple wrappers around their original WinAPI, so figuring out most the names wasn’t that hard. On the unknown ones, I used the Joymax Pk2 Tool. It helped, especially, on the search-related stuff.

Note, that there are two Open and two Create functions. Either one that simply returned an identifier, which can basically be any number that fits 32 bit, and another, that works basically the same, but fills an object called CJArchiveFm with information.

About CJArchiveFm

This class is not a part of the GFXFileManager. It acts as a buffered reader/writer. The buffersize is, for all binaries I checked, 4096 bytes. If you take a look at the insides, it really just comes down to using the GFXFileManager Read/Write methods for accessing the file. So its nothing  special, just another wrapper around a wrapper. Joymax loves wrappers.

Final thoughts on CWFileManager and CPFileManager

While these two implementations share the same base, they don’t behave the same. Some functions are stubs in CWFileManager, while they are present in CPFileManager. Some functions just behave different, like opening a container using CPFileManager will virtually change directory into the archive, while opening a folder using CWFileManager will exactly do nothing. Also the behaviour of the search-functions is different. CWFileManager only provides the original searchresult-data supplied by FindFirstFile and FindNextFile of the WinAPI. CPFileManager will populate a wrapper-struct around these information (who ever had this idea …).

 

Next steps

Only a few methods are left unknown. Some are known to be named incorrect. First of all, it really requires some in-depth testing. My short tests succeeded, but that doesn’t necessarily mean that it is flawless. Another interesting step might be reversing the CPFileManager-implementation.

Feel free to test and report bugs. Also feel free to fork and implement your own container, if you are able to.