AnvilNext `.forge` file format - GenuineAster/broadside GitHub Wiki

Forge File Format

The file format used by Assassin Creed IV's AnvilNext engine (previously named Anvil and Scimitar) to store resources has the .forge filename extension. In AC4, these files all fit the DataPC_*\.forge regex. This article attempts to shed light on the workings of this file format.

Data Description

This is the breakdown of the format. Type are as listed below:

(u|i)(8,16,32,64)(LE,BE,)
  ^       ^         ^^
  |       |          `- Little Endian or Big Endian (default is Little Endian) 
  |       `------------ The size of the type, in bits
  `-------------------- The signedness of the type. 'u' is for unsigned, 'i' is for signed

Special types:
char   : u8;
wchar  : u16;

Special values:

$  : address of current data
$$ : address of current chunk
$? : address of current table/list item
?? : current index in list

The File Header Chunk

0x00000000: file_hdr:
file_hdr+0x00000000: .scimitar:        char[8] = 'scimitar'   // File identifier
file_hdr+0x00000008: .null_byte:       char    = 0x00         // Possibly null terminator?
file_hdr+0x00000009: .unknown:         u32                    // Possibly file format version?
file_hdr+0x0000000d: .rsc_idx_hdr_ptr: u32                    // Pointer to Resource Indexes Header
file_hdr+0x00000011: .unknown:         u32[4]
file_hdr+0x00000021: .padding:         char[rsc_idx_hdr_ptr-$]
file_hdr_end:

The Resource Index Chunk

The Resource Index Header

file_hdr.rsc_idx_hdr_ptr: rsc_idx_hdr:
rsc_idx_hdr+0x00000000: .record_count:     u32     // Number of resources in this file
rsc_idx_hdr+0x00000004: .unknown:          u32[10]
rsc_idx_hdr+0x0000002b: .unknown:          u32     // Seems to be the same as .record_count
rsc_idx_hdr+0x00000030: .unknown:          u32[7]
rsc_idx_hdr+0x0000004c: .rsc_desc_ptr:     u32     // Pointer to the Resource Descriptions Chunk
rsc_idx_hdr+0x00000050: .unknown:          u32
rsc_idx_hdr+0x00000054: .useless_chk_ptr:  u32     // Pointer to the Useless Chunk
rsc_idx_hdr+0x00000058: .unknown:          u32
rsc_idx_hdr+0x0000006c: .rsc_data_chk_ptr: u32     // Pointer to the Resource Data Section
rsc_idx_hdr_end:

The Resource Index List

Located at file_hdr.rsc_idx_hdr_ptr. A list that is rsc_idx_hdr.record_count elements large, containing:

resource_record_??:
resource_record_??+0x00000000: .resource_ptr:  u32     // A pointer to the resource data
resource_record_??+0x00000004: .unknown:       u32[3]
resource_record_??+0x00000010: .resource_size: u32     // The size of the resource data
resource_record_??_end

Resource Descriptions Chunk

Located at rsc_idx_hdr.rsc_desc_ptr. A list that is rsc_idx_hdr.record_count elements large, containing:

resource_desc_??:
resource_desc_??+0x00000000: .resource_size: u32 
resource_desc_??+0x00000004: .unknown:       u32[6]
resource_desc_??+0x0000001c: .next_index:    u32 = ?? + 1 // or ~0 if ?? is rsc_idx_hdr.record_count-1
resource_desc_??+0x00000020: .prev_index:    u32 = ?? - 1 // or ~0 if ?? is 0
resource_desc_??+0x00000024: .unknown:       u32
resource_desc_??+0x00000028: .timestamp:     u32          // UNIX timestamp
resource_desc_??+0x0000002c: .name:          char[128]    // The name of the file, padded with 0x0
resource_desc_??+0x00000078: .unknown:       u32[5]

Resource Data Chunk

This chunk starts at file_hdr..rsc_data_chk_ptr. It contains some unknown values and then a UUID in ASCII. The rest of the data in this chunk corresponds to each resource_record_??.resource_ptr. That pointer leads to the following sequence of bytes every time, but in a different location:

04 00 00 00 : 0x4
00 00 00 00 : 0x0
33 AA FB 57
99 FA 04 10
01 00 00 00 : 0x1
80 00 80 01

The next time this sequence occurs is resource_record_??.resource_size + 1 bytes later, which is the start of the ?? + 1th resource.