RPK
RPK is an acronym for Rayform Package, and can be found inside the game directory with the game EXE. The RPK format is a package meant for storing the game's assets into one file. These formats can store thousands of assets in just one package which would amount to thousands of files on the file system without it. Packages can be nested within another package.
Under the hood, there is a table of entries containing information about each asset. An entry in this context is a game asset, and the words entry/asset will be used interchangeably.
Layout
The figure below depicts the layout of the RPK format.
block-beta columns 4 block:header:2 magic["magic<br/>(<span class='type'>u32</span>)"] table_size_bytes["table_size_bytes<br/>(<span class='type'>u32</span>)"] end table_entries["table_entries<br/>(<span class='type'>Vec<TableEntry></span>)"] chunks["chunks<br/>(<span class='type'>Vec<u8></span>)"] space:4 block:entry_layout:4 name["name<br/>(<span class='type'>[u8; 16]</span>)"] offset["offset<br/>(<span class='type'>u32</span>)"] entry_size["size<br/>(<span class='type'>u32</span>)"] padding["padding<br/>(<span class='type'>[u32; 2]</span>)"] end table_entries --> entry_layout
Header
The header consists of only eight bytes.
#![allow(unused)] fn main() { struct RpkHeader { magic: u32, table_size_bytes: u32 } }
magic
MUST always be 0xAFBF0C01
. There are in total four Exanima formats
with the same magic: FDS, FLB, RML, and RPK. The layout is the same across all
of them.
table_size_bytes
is the total amount of bytes in the table. Each entry in the table
is 32 bytes long which means this can be divided by 32 to get the total amount of
entries. An entry is correlated to an asset that can be dumped into a file. As an
example, in Exanima 0.9, the Apparel.rpk has a size of 54688 bytes in it's table
which is 1709 when divided by 32. That is 1,709 assets just in that one RPK file.
Table Entry
Each entry has the following structure:
#![allow(unused)] fn main() { struct TableEntry { name: [u8; 16], offset: u32, size: u32, padding: [u32; 2], } }
name
is NOT null-terminated
and MUST be 16 bytes long. If the name is not 16 characters long, the
remaining bytes will all be 0.
offset
is used to find the starting position of the asset's chunk when the reader
position is at the end of the table.
size
indicates how big the asset's chunk is in bytes.
padding
is always 0.