Resource Format Specification
This document describes the format of resources stored inside the .kfc_resources file.
Resources are typed binary objects. The concrete structure of types (fields, type hashes, field offsets, alignments, etc.) is defined in the types section.
General Notes
- Everything is little-endian unless otherwise noted.
- Make sure to zero out any padding bytes when serializing.
Primitive Types
| Name | Ordinal | Description |
|---|---|---|
none | 0x00 | No data |
bool | 0x01 | 1 byte, values: 0x00 (false), 0x01 (true) |
uint8 | 0x02 | 1 byte unsigned integer |
sint8 | 0x03 | 1 byte signed integer |
uint16 | 0x04 | 2 byte unsigned integer |
sint16 | 0x05 | 2 byte signed integer |
uint32 | 0x06 | 4 byte unsigned integer |
sint32 | 0x07 | 4 byte signed integer |
uint64 | 0x08 | 8 byte unsigned integer |
sint64 | 0x09 | 8 byte signed integer |
float32 | 0x0A | 4 byte IEEE 754 floating point number |
float64 | 0x0B | 8 byte IEEE 754 floating point number |
enum | 0x0C | stored using the enum's inner_type (see Enum) |
bitmask8 | 0x0D | 1 byte bitmask (up to 8 flags) |
bitmask16 | 0x0E | 2 byte bitmask (up to 16 flags) |
bitmask32 | 0x0F | 4 byte bitmask (up to 32 flags) |
bitmask64 | 0x10 | 8 byte bitmask (up to 64 flags) |
typedef | 0x11 | stored using the typedef's inner_type (see Typedef) |
struct | 0x12 | see Struct |
static_array | 0x13 | a fixed-size array (see Static Array) |
ds_array | 0x14 | unknown/not used |
ds_string | 0x15 | unknown/not used |
ds_optional | 0x16 | unknown/not used |
ds_variant | 0x17 | unknown/not used |
blob_array | 0x18 | a variable-size array (see Blob Array) |
blob_string | 0x19 | a variable-size string (see Blob String) |
blob_optional | 0x1A | an optional value (see Blob Optional) |
blob_variant | 0x1B | a variant of the base type (see Blob Variant) |
object_reference | 0x1C | 16 byte GUID referencing another resource |
guid | 0x1D | 16 byte ContentHash |
Enum
- An
enumis stored using itsinner_type(in the type metadata). - The
inner_typeis always a primitive integer type (uint8,sint8,uint16,sint16,uint32,sint32,uint64,sint64).
Struct
- A
structis a composite type consisting of multiple fields which is essentially a concatenation of its fields' serialized bytes. - If a struct inherits from a base struct (namely, has a
inner_type), the base struct's fields are serialized first (parents recursively up the chain). - Each field is serialized without its keys, just its value.
Note: Each field has a field_offset in the type metadata which can be used to locate the field's value inside the struct instead of recomputing padding/alignment yourself.
Typedef
- A
typedefis an alias for another type (theinner_type). - It can be resolved by simply serializing the
inner_typerecursively until a non-typedef type is reached.
Static Array
- A
static_arrayis a fixed-size array of elements of the same type. - The number of elements is
field_count(in the type metadata). - The element type is
inner_type(in the type metadata). - Elements are stored contiguously, directly inline.
Blob Array
- Out-of-line, data is stored as a blob.
- Layout:
- 4 byte
uint32relative offset (0 if empty) - 4 byte
uint32count (0 if empty)
- 4 byte
- The element type is
inner_type(in the type metadata). - Elements are stored contiguously, at the given blob offset.
- IMPORTANT: blob rules apply, see Blob Rules.
Blob String
- Out-of-line, data is stored as a blob (non-null-terminated).
- Layout:
- 4 byte
uint32relative offset (0 if empty) - 4 byte
uint32length in bytes (0 if empty)
- 4 byte
- Characters are stored as bytes at the given blob offset.
- IMPORTANT: blob rules apply, see Blob Rules.
Blob Optional
- Out-of-line, data is stored as a blob.
- Layout:
- 4 byte
uint32relative offset (0 if null)
- 4 byte
- The inner type is
inner_type(in the type metadata). - The value is stored at the given blob offset if not null.
- IMPORTANT: blob rules apply, see Blob Rules.
Blob Variant
- Out-of-line, data is stored as a blob.
- Layout:
- 4 byte
uint32qualified type hash of the stored variant (0 if no variant is specified) - 4 byte
uint32relative offset (0 if no variant is specified) - 4 byte
uint32blob size in bytes (0 if no variant is specified)
- 4 byte
- The base type is
inner_type(in the type metadata). - IMPORTANT: blob rules apply, see Blob Rules.
Blob Rules
Blob types (blob_array, blob_string, blob_optional, blob_variant) are placed out-of-line after the fixed-size base struct.
They need to be properly spaced and aligned according to their type metadata.
Because of this, when serializing a blob value you must manage this process yourself. Feel free to implement this in a way that makes sense for you, but here is how the game seems to do it:
- Set a
blob_offsetto the size of the base struct. - Serialize everything in order. For each blob field, do the following:
- (BlobVariant only) Write the qualified type hash.
- Align
blob_offsetto the blob data's alignment specified by the type metadata for the blob's data. - Compute
relative_offset = blob_offset - stream.position, where:stream.positionis the absolute position of therelative_offsetfield itself. (i.e. the position where therelative_offsetwill be written)
- Write the
relative_offset. - Write the
count/length/sizefield if applicable. (blob_array,blob_string,blob_variant) - Then write the blob data at the current
blob_offset. - After writing the blob data, increment
blob_offsetby the size of the written blob data. - And finally align
blob_offsetagain to the blob data's alignment.
- Continue with the next field until all fields are serialized.