- Final Fantasy VIII engine

[eng] Per aspera ad... resources :: by Demitsuri ::

FF8 exploring is hard but interesting. Last time I spoke about LZS decompression and now it's time to speak about the things what happens before. I think, this article is useless for resources format reversing and useful for understanding how it works. You should remember that described processes are right only for plain archive filesystems (archive filesystems(AFS) without other included AFS).

1. Resource loading
Each AFS has own loading subroutine. It's the simpliest way to implement AFS, but it means that all AFS names are hardcoded in FF8 binary. Let's look to menu loading. Loading subroutine consists of 3 commands repeated some times:
push offs_Filename
push addr_pBuffer
call loadFileFromMenu
Filenames are hardcoded in binary and each pointer to buffer storing in own global variable. It seems that source of that subroutine was:
//global variables
char* filename1 = "filename1";
char* filename2 = "filename2";
...
char* filenameN = "filenameN";

void* pBuffer1;
void* pBuffer2;
...
void* pBufferN;

function loadMenu()
{
    loadFileFromMenu(filename1, pBuffer1);
    loadFileFromMenu(filename2, pBuffer2);
    ....
    loadFileFromMenu(filenameN, pBufferN);
}
I don't have any idea why do they listed it all "as is", not by array processing.

2. Descriptors
While loading resources we need to have file descriptor to read from it.
At the begin global AFS descriptor is initializing. It has many unusable or temporary fata fields:
struct AFS_GLOBAL_DESCRIPTOR
{
DWORD unknown0;
DWORD unknown1;
DWORD unknown2;
DWORD unknown3;
DWORD unknown4; //isUnpacked flag? unpacking if 0
DWORD unknown5;
DWORD unknown6;
DWORD unknown7;
DWORD unknown8;
DWORD temporary0; //really read data size
DWORD temporary1; //uncompressed file size
DWORD temporary2; //data offset in fs-file
DWORD temporary3; //isCompressed flag from fi-file
PAFS_DESCRIPTOR lpAFSDescr; //pointer to AFS_DESCRIPTOR
}
Main field of this structure is lpAFSDescr. It initializes with null, but after descriptor creating, new pointer assigning to this field.

struct AFS_DESCRIPTOR
{
DWORD dwValid; // 0 - invalid descriptor, 1 - valid descriptor
DWORD dwElementCount; //number of files
PSYS_DESCRIPTOR lpSysDescr;
PLST_DESCRIPTOR lpLstDescr;
LPVOID lpIdxBuff; //pointer to buffer with fi data
};
First field of this and following descriptors are filled in processing functions. It's zero if error occurs during decompressing. Error may occur on each level of file processing: from memory management to decompression.

struct SYS_DESCRIPTOR
{
DWORD dwValid; // 0 - invalid descriptor, 1 - valid descriptor
LPCSTR lpFilePath;
DWORD dwFileDescriptor; //file descriptor for io
BYTE  unknown0[0x14]; 
DWORD unknown1[0x08];
};


struct LST_DESCRIPTOR
{
DWORD dwElementCount; //number of files
LPVOID lpFilenamePointerTable; //pointer to table with filename pointers to lpLstBuff
DWORD dwFileLength; //fl file length
LPVOID lpLstBuff; //pointer to buffer with fl data
DWORD unknown; //initialized with 0
}


None of these descriptors are used outside file loading function. And AFS files are opening and closing every time resource loading function called.

3. Loaded resources
So, here is list of loaded resources from menu AFS.

Function at 0x004a1a40 (I called it loadMenu) loads:
0x01D2BAF4 "m002.bin"
0x01D2BAF8 "mmag.bin"
0x01D2BAFC "pet_exp.bin"
0x01D2BB00 "m003.bin"
0x01D2BB04 "m001.msg"
0x01D2BB08 "shop.bin"
0x01D2BB0C "mthomas.bin"
0x01D2BB10 "mmagic.bin"
0x01D2BB14 "pet_exp.msg"
0x01D2BB18 "mngrphd.bin"
0x01D2BB1C "m000.bin"
0x01D2BB20 "m003.msg"
0x01D2BB24 "m000.msg"
0x01D2BB28 "mwepon.msg"
0x01D2BB2C "mitem.bin"
0x01D2BB30 "mitem.bin"
0x01D2BB34 "m004.msg"
0x01D2BB38 "m001.bin"
0x01D2BB3C "mtmag.bin"
0x01D2BB40 "areames.dc2"
0x01D2BB44 "m002.msg"
0x01D2BB48 "areames.dc1"
0x01D2BB4C "cardanm.sp2"
0x01D2BB50 "mwepon.bin"
0x01D2BB54 "m004.bin"
0x01D2BB58 "mwepon.bin"
0x01D2BB5C "magsort.bin"
0x01D2BB60 "mitem.bin"
0x01D2BB64 "price.bin"
0x01D2BB68 "face.sp2"
0x01D2BB6C "mmag.bin"
0x01D2BB70 "cyocobo.bin"
0x01D2BB74 "mmag2.bin"
0x01D2BB78 user sorted magic allocated memory


Function at 0x004a19a0 loads:
0x01D2BAE4 "vibrate.vib"
0x01D2BAE8 "icon.sp1"


Function at 0x004a1960 loads:
0x01D2BAE0 "vibrate.vib"
Again? Huh! But now we can search commands like "mov reg32, 0x01d2baf4" in disassembly and find subroutines, that processing game datafiles. It's cool, isn't it? But we'll do it a little bit later.