Skip to main content

GRF

This document describes the GRF file format used in the Ragnarok Online client.

Contents

GRF files are virtual file system containers not unlike ZIP archives. The main features are:

  • By streaming game assets more efficiently they may help reduce loading times
  • Optional support for multiple data compression and encryption schemes
  • Compressing files greatly reduces the amount of storage space on disk

The GRF format is closely related to PAK files used in Arcturus, which are an earlier implementation of the same concept.

Layout

Arcturus (PAK)

The PAK file format used in Arcturus is nearly identical to early versions of the GRF format.

FieldOffsetLengthTypeDescription
StorageBytes0variableblobThe compressed file records (contents of the archive)
FileRecordsvariablevariablearrayA list of file records describing the compressed byte storage
Footervariable4ArchiveMetadata"Header" containing the location and size of the file records list

Due to the large size, this structure shouldn't be read into memory. It's listed purely for illustration purposes.

Archive Metadata

The metadata required for reading file records, located at the end of the file (at offset EOF - sizeof(ArchiveMetadata)).

FieldOffsetLengthTypeDescription
StartOffset04uintRelative offset (from the start of the file) of the first record
NumRecords44uintHow many records there are in this archive
VersionTag81byteAlways 0x12 (decimal: 18)

File Records

Each record describes where in the archive a given file can be found, and what options to pass to the decompressor.

FieldOffsetLengthTypeDescription
PathStringSize01byteSize of the entry's path name (excluding the null terminator)
Type01byte1 for "file" and 2 for "directory" (?) - needs more research
Offset04intRelative offset (from the start of the archive)
CompressedSize04intSize of the compressed buffer area for this file
DecompressedSize04intSize of the entry after decompressing the buffer
FilePath0variablestringNull-terminated C string of size PathStringSize + 1

Version 1.2

This version has been used in at least the fRO client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 1.3

This version has been used in at least the 2003 iRO Beta client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 2.0

This is the GRF version used in the latest kRO client, as well as regional clients dating back to at least 2004.

FieldOffsetLengthTypeDescription
Signature015stringAlways "Master of Magic" (fixed-size, without a null-terminator)
EncryptionKey1515stringArray of byte values; all zeroes if encryption isn't used (default)
FileTableOffset304uintWhere the file table is stored (relative to the start of the file)
ScramblingSeed344uintSeemingly used for obfuscating the contents of the archive
ScrambledFileCount384uintSubtract ScramblingSeed and 7 to compute the real FileCount
Version424uintEncodes the version in major.minor format (two bytes each)

File Table

This is the manifest of the archive, stored at an arbitrary position in the file (defined by FileTableOffset).

FieldOffsetLengthTypeDescription
CompressedSize04uintCompressed size of the file table (list of file entries)
DecompressedSize44uintSize of the file table after decompressing it
CompressedRecordHeaders8variableblobCompressed buffer (use zlib's inflate to decompress)

These parameters all have to be passed to the decompressor in order to unpack the list of file records.

File Entries

After unpacking the CompressedRecordHeaders buffer, you'll get a list of these file records ("headers").

FieldOffsetLengthTypeDescription
CompressedSize04uintHow large the compressed file contents are (zlib parameter)
ByteAlignedSize44uintHow many bytes to actually read (padded to next 8-byte boundary)
DecompressedSize84uintSize of the actual file contents (another zlib parameter)
Type121byteWhether the file is stored raw, compressed, or encrypted (?)
Offset134uintWhere in the archive the compressed file contents can be found

You can pass the compressed/aligned buffer to zlib's inflate to unpack a given file.

References