GRF

Created

Files are saved in little-endian.

Header

Label Bytes Type Description
signature 16 char 'Master of Magic\0'
encryption 14 ubyte Determines if encryption is allowed. If set to [0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e] then encryption is on. If all 14 bytes are null then it is not.
offset 4 uint32 Offset of the Filetable after the header (offset + 46).
seed 4 uint32 Used as some dummy count for the actual filecount.
nFiles 4 uint32 Number of files in the GRF (inaccurate). The correct filecount is nFiles - seed - 7.
version 4 uint32 Version of the GRF. Known so far: 0x102, 0x103 and 0x200.

After the header comes the actual data. Somewhere at the bottom at the offset offset + 46 starts the Filetable. Depending on the version the files and or the filetable are either compressed and or encrypted.

Filetable

Version 0x100 - 0x103

Repeat the following structure nFiles times:

Label Bytes Type Description
length 4 uint32 Length that is used for the filename (actual filename length may be shorter).
filename length char Nibble swapped and possibly encrypted filename. On version >=0x101 the first two bytes are skipped and the filename is encrypted.
compressed_size 4 uint32 The compressed size. Actual value is compressed_size - size - 715. The size value is two entries below.
compressed_size_aligned 4 uint32 Compressed and encrypted size. Actual value is compressed_size_aligned - 37579.
size 4 uint32 The decrypted and uncompressed size.
flags 1 ubyte Bit flags with the following values: File/Compressed (0x01), Encrypted (0x02) and EncryptedHeader (0x04).
If the file has the extension .gnd, .gat, .act or .str then the flag EncryptedHeader must be set, otherwise Encrypted must be set (regardless of the value in the flags field).
offset 4 uint32 Offset of the filedata in the GRF after the header (offset + 46).

Version 0x200

In this version the filetable is compressed (zlib).

Label Bytes Type Description
compressed_size 4 uint32 Compressed size of the filetable.
uncompressed_size 4 uint32 Uncompressed size of the filetable
files variable FileEntry See the file entries below.

Repeat the following structure nFiles times:

Label Bytes Type Description
filename variable char The null terminated filename.
compressed_size 4 uint32 The compressed size.
compressed_size_aligned 4 uint32 Compressed and encrypted size.
size 4 uint32 The decrypted and uncompressed size.
flags 1 ubyte Bit flags with the following values: File/Compressed (0x01), Encrypted (0x02) and EncryptedHeader (0x04).
offset 4 uint32 Offset of the filedata in the GRF after the header (offset + 46).

Encryption and flags

The possible file flags are:

Community edits

LZMA compression instead of deflate (zlib)

rAthena user named curiosity released a patched cps.dll called "LessGRF" (Source code) that uses the LZMA compression algorithm from the 7zip LZMA SDK to compress the files instead of zlib. To know whether the files have been compressed using LZMA an additional null byte is prepended to the file data. That means after you decrypted a file (if it was neccessary) you check if the first byte is null. If it is then use the LZMA decompression algorithm on the rest of the bytes (the null byte is not part of the data) otherwise you use zlib.