Gecko Patch File format, or GPF, is a feature of Gecko OS that the internet seems to have overlooked. (A google search yields just short of anything meaningful) This document aims to explain how to use this feature without the need to reverse engineer the source code.
GCT files, which you should be quite familiar with, contain code for Gecko OS to interpret and execute during gameplay. GPF files, on the other hand, simply contain chunks of data for Gecko OS to write out over specified places in memory, before the game boots. Here's how the format describes this information:
The file begins with a single byte that dictates how many patches are in this file. This means that a single GPF can only contain 255 patches of arbitrary size. In this case, 0 does in fact mean there are no patches to be applied.
After that follows the patches. Here's the format they follow:
struct patch
{
uint32 destination_addr;
uint32 patch_size;
uint8 patch_data[patch_size];
};
In English: Each patch has a small "header," consisting of:
- An unsigned 32-bit integer indicating where in memory this patch will be placed (AKA a pointer, in this case)
- A second unsigned 32-bit integer stating the length of the patch, in bytes.
Technically, this means that each patch can only be 2^32 bytes long (4GB), but, you know... details... Next comes the actual data of the patch. The next patch would come immediately after this data. Keep in mind that all integer values are in big endian.
To be clear, here's an example GPF that writes the string "NICE_MEME" to 0x80b326f3.
01 80 b3 26 f3 00 00 00 09 4E 49 43 45 5F 4D 45 4D 45
- 01: patch count
- 80b326f3: destination address
- 00000009: byte length of patch
- 4E 49 43 45 5F 4D 45 4D 45: "NICE_MEME" in hex
If you happen to know C, here's the code that actually applies the patches, from apploader.c in Gecko OS:
//---------------------------------------------------------------------------------
void app_apply_patch()
//---------------------------------------------------------------------------------
{
int i;
u8 *filebuff = (u8*)sdbuffer;
u8 *asmbuff;
no_patches = filebuff[0];
filebuff += 1;
for(i=0;i<no_patches;i++)
{
patch_dest = be32(filebuff);
patch_size = be32(filebuff+4);
memcpy((u8*)patch_dest, filebuff+8, patch_size);
DCFlushRange((u8*)patch_dest, patch_size);
filebuff = filebuff + patch_size + 8;
}
}
Where sdbuffer
is the data of the entire GPF.