OBD:Instance file format: Difference between revisions

added note about unused field in template descriptor
(wording; I may be keeping the intro here after all, so I removed the unneeded definitions; merged the tiny content from OBD:File types/Empty so I can delete /Empty)
(added note about unused field in template descriptor)
(43 intermediate revisions by 2 users not shown)
Line 1: Line 1:
:''For other files ending in ".dat", see [[Oni (folder)]].''
{{UpdatedForOniX|1.0.0}}
Files in GameDataFolder/ named "level[0-19]_Final.dat", together with ".raw" and sometimes ".sep" counterparts, contain the game data for Oni. The [[Raw|documentation for raw and separate files]] can be read after this page.
{{OBD Home}}
{{Hatnote|".dat" redirects here; for other files ending in ".dat", see [[Oni (folder)]].<br>
:''You should read the [[Game data terminology]] page before this one.''<br>
:''The [[Raw|Raw and separate file formats]] page should be read after this one.''}}
Files in GameDataFolder/ named "level[0-19]_Final.dat", together with ".raw" and sometimes ".sep" counterparts, contain the game data for Oni.


The same format was used for the tools files, named level0_Tools.dat/.raw/.sep, however the retail Oni game application does not load tools files; for the story behind the tools files, see [[Big Blue Box|HERE]].
The same format was used for the tool files, named level0_Tools.dat/.raw/.sep, however the retail Oni game application does not load tool files; for the story behind the tool files, see [[level0_Tools]].
 
The level 0 files do not actually contain a level, but instances (resources) shared across all levels. Level 0 is loaded when the game starts, and never unloaded. All other level files are only loaded when the corresponding level starts and unloaded when it ends.
 
==Terminology==
;Level data files
The data for each level is found in two files in Windows retail Oni, ending in ".dat" and ".raw". On Macs and in the Windows demo, a third file type ending in ".sep" is used. These two/three files are collectively called level data files.
 
;Instance file
Any file ending in ".dat" found in GameDataFolder/. Instance files are the main type of data file in the sense that, when loading a level, Oni reads the instance file first, and this file serves as an index that allows it to find resources which are packed into the level data files.
 
;Instance, resource, file
This one can be confusing. An instance is often called a file on this wiki, but an instance is not the same as an "instance file". An instance is an individual resource, such as a texture. They are called files because they are individual pieces of data stored and cataloged in a "file system" composed of a set of .dat/.raw[/.sep] files. In at least some cases they were indeed actual, separate files on the developer's computer before being packed into the level data files.
 
When OniSplit is used to split level data files into individual files for each resource, in effect it is creating thousands of "single-instance instance files", at which point "instance", "file", and "instance file" become fully synonymous.
 
;Template, file type
A template represents a type of resource. Templates are identified with four-letter codes, often called tags, such as "SUBT" for subtitle files. Just as instances are often called files on the wiki, templates are often called file types.
 
;.oni
Generated by [[OniSplit]], these files are Windows-format .dat files which basically contain a single instance extracted from an instance file, with all the instance data that was contained by the .raw/.sep files appended at the end.


The level 0 files do not actually contain a level, but instances (resources) shared across all levels. Level 0 is loaded when the game starts, and never unloaded. All other level files, 1-19, are only loaded when their corresponding level starts, and unloaded when it ends. Since Oni can only hold these two levels in memory concurrently, resources have to be duplicated on disk whenever a character class, sound effect, etc. occurs in more than one level. For instance, although there are only 2,380 unique sounds in the game, there are 7,386 sounds stored across all level data files.
{{TOClimit}}
==Backwards and garbage data==
==Backwards and garbage data==
During development, Oni had in-game editing tools. These tools presented a GUI for things like placing AIs and setting their attributes, editing particles, etc. When a developer saved his work, the contents of the level, stored in RAM, were written directly to disk. The structure of the .dat/.raw/.sep files reflects the way in which Bungie West chose to store levels in memory, and thus when when we read the data in the files with a hex editor, we can see various eccentricities such as blank space and garbage data that represented various RAM contents from the developer's PC.
During development, Oni had an [[level0_Tools|in-game editor]] which presented a GUI for manipulating AIs, particles, etc. in a level. When a developer saved his work, the contents of the level, stored in his PC's RAM, were flushed directly to disk. Thus the structure of the .dat/.raw/.sep files reflects the way in which Bungie West chose to store levels in memory. So when we read the data in the files with a hex editor, we can see eccentricities such as blank space (coming from unused fields and byte-alignment padding) and garbage data (such as now-meaningless pointer values). [[OBD:Raw and separate file formats#Gaps|Further gaps]], mostly representing orphaned obsolete resources, add up to about 25 MB for the whole game.
 
Additionally, because the levels were built on Intel-based machines, which use a little-endian architecture, sequences of bytes which represent numbers were written from least-significant to most-significant byte, which looks "backwards" from the standpoint of a culture that reads left-to-right. When Macs, which were big-endian at the time due to their PowerPC architecture, read these files, they then had to flip each sequence of bytes in memory before they could be understood.


An exception to this backwards-writing rule is when strings of ASCII characters were written to disk. These are not numbers and thus are not subject to endianness, so they retain their left-to-right order. Now, this may not seem to be the case as you continue reading below. The first two strings of characters which you'll see are "13RV" and "TBUS", which are meant to be read "VR31" and "SUBT". The reason these four-character strings are backwards is that Oni stored them as a 32-bit integer. Any sequence of four characters can be represented as such a number. Writing the integer 1,448,227,633 to disk results in the bytes 0x31, 0x33, 0x52, and 0x56, which produce the ASCII codes for '1', '3', 'R' and 'V' (the computer would have had to be big-endian to be able to naturally write them in the left-to-right order we would prefer to see). This practice of Bungie's provided a combination of convenient storage of a tag in memory as a number, and human-readability when organizing game assets by tag.
Additionally, because the levels were built on Intel-based machines, which use a little-endian architecture, sequences of bytes which represent numbers were written from least-significant to most-significant byte. [[wp:FourCC|FourCCs]] in the data are stored "backwards", such as "13RV" which is meant to be read "VR31", because Bungie defined those four bytes as a 32-bit integer, not a string, causing them to be written to disk in little-endian order.


==File limits==
==File limits==
Line 43: Line 27:
{{Table}}
{{Table}}
{{OBD_Table_Header}}
{{OBD_Table_Header}}
{{OBDtr| 0x00 | int64  | | 1F 27 DC 33 DF BC 03 00 | 0x0003BCDF33DC271F | Windows level file template checksum; Windows demo and Mac retail/demo use 0x0003BCDF23C13061 instead }}
{{OBDtr| 0x00 | int64  | | 1F 27 DC 33 DF BC 03 00 | 0x0003BCDF33DC271F | Total template checksum (main indicator of engine compatibility):
{{OBDtr| 0x08 | int32  | | 31 33 52 56            | 'VR31'            | .dat version; .oni files use 'VR32' instead }}
*0x0003BCDF33DC271F ("PC") - templates compatible with Windows retail engine(s)
{{OBDtr| 0x0C | int64   | | 40 00 14 00 10 00 08 00 | 0x0008001000140040 | signature }}
*0x0003BCDF23C13061 ("Mac") - templates compatible with Windows demo and Mac engines
*0x0003BA70A8D8AE11 ("PS2") - templates compatible with PlayStation 2 engine(s)
*0x0000000000000000 (blank) - for use with [[OniX]] engine(s) (VR33), which handle data versioning using the 0x3C field below
OniSplit's .oni files (VR32) use PC checksum by default and Mac/PS2 checksums where required (SNDD, TXMP, AGQG, M3GM, IGSt, TSFT/TSGA, TRAM/TREX) }}
{{OBDtr| 0x08 | int32  | | 31 33 52 56            | '13RV'            | .dat version (meant to be read as "VR31")<br>OniSplit's .oni files use '23RV' ("VR32") instead<br>OniX's [[Oni (folder)|GDFX]] uses '33RV' ("VR33") to signify that the new data versioning system is in use }}
{{OBDtr| 0x0C | int16   | | 40 00 | 64 | size of this header }}
{{OBDtr| 0x0E | int16  | | 14 00 | 20 | size of instance descriptor }}
{{OBDtr| 0x10 | int16  | | 10 00 | 16 | size of template descriptor }}
{{OBDtr| 0x12 | int16  | | 08 00 | | size of name descriptor }}
{{OBDtr| 0x14 | int32  | | 83 24 00 00 | 9347      | instance descriptor count  }}
{{OBDtr| 0x14 | int32  | | 83 24 00 00 | 9347      | instance descriptor count  }}
{{OBDtr| 0x18 | int32  | | D4 1B 00 00 | 7124      | name descriptor count }}
{{OBDtr| 0x18 | int32  | | D4 1B 00 00 | 7124      | name descriptor count }}
Line 53: Line 45:
{{OBDtr| 0x28 | int32  | | 40 F2 28 00 | 0x28F240  | name table offset }}
{{OBDtr| 0x28 | int32  | | 40 F2 28 00 | 0x28F240  | name table offset }}
{{OBDtr| 0x2C | int32  | | 04 4F 02 00 | 151300    | name table size }}
{{OBDtr| 0x2C | int32  | | 04 4F 02 00 | 151300    | name table size }}
{{OBDtr| 0x30 | int32  | | 00 00 00 00 |           | used by OniSplit only: raw table offset }}
{{OBDtr| 0x30 | int32  | | 99 CF 40 00 | (garbage) | used by OniSplit for raw table offset }}
{{OBDtr| 0x34 | int32  | | 00 00 00 00 |           | used by OniSplit only: raw table size }}
{{OBDtr| 0x34 | int32  | | 90 4F 63 00 | (garbage) | used by OniSplit for raw table size }}
{{OBDtr| 0x38 | int32  | | 00 00 00 00 |           | unused }}
{{OBDtr| 0x38 | int32  | | F4 55 5F 00 | (garbage) | unused }}
{{OBDtr| 0x3C | int32  | | 00 00 00 00 |           | unused }}
{{OBDtr| 0x3C | int32  | | 90 4F 63 00 | (garbage) | used by OniX (three high bytes) for data versioning; contains the highest data version (timestamp) found in any instance in this .dat; see instance descriptor table's 0x10 for format }}
|}
|}


The file's '''template checksum''' tells us that this level data is in the .dat/.raw file scheme, as opposed to the .dat/.raw/.sep file scheme used by Mac Oni and the Windows demo of Oni.
The file's '''total template checksum''' is the sum of all the template checksums (see "Template descriptors" below). Oni looks at this number in order to validate that it can read this version of the game data format. In practical terms, the total checksum value given for Windows above tells us that this level data is in the .dat/.raw file scheme, and the value given for Mac Oni and the Windows demo tells us that the level data uses the .dat/.raw/.sep file scheme.


The '''version''' of the instance file is the format version. Reading it backwards, as discussed under "Introduction", we get "VR31", which is probably "version 31". This is the format version of all instance files in all releases of Oni.
The '''version''' of the instance file is the format version. Reading it backwards, as discussed under the "Backwards and garbage data" section, we get "VR31", which probably means "version 3.1". This is the format version of all instance files in all releases of Oni, regardless of file scheme.


The '''signature''' is identical in all instance files.
The '''descriptor sizes''' are the sizes of the instance, template, and name descriptors which are coming up in this file (see breakdowns in later sections). For instance, each instance descriptor will be 0x14, or 20 bytes, in length.


The '''descriptor counts''' are the sizes of some arrays which are coming up soon in this file: the instance, name and template descriptors. For instance, the size of the instance descriptor array will be 0x2483, or 9,347 items, in length.
The '''descriptor counts''' are the sizes of arrays which are coming up in this file: the instance, name and template descriptors. For instance, the size of the instance descriptor array will be 0x2483, or 9,347 items, in length.


Next we are told the addresses and sizes of the '''data and name tables''' in the instance file. The name table simply follows the data table, as you'll see if you add the data table offset plus the data table size, but that doesn't mean the name table offset is redundant; if its start was not 32-bit-aligned, it probably would be moved down to start at the next 32-bit word, but this is unnecessary because it happens to be aligned already.
Next we are told the addresses and sizes of the '''data and name tables''' in the instance file. The name table simply follows the data table, as you'll see if you add the data table offset plus the data table size, but that doesn't mean the name table offset is redundant; if its start was not 32-bit-aligned, it probably would be moved down to start at the next 32-bit word, but this is unnecessary because it happens to be aligned already.


After this comes four "int"s of '''zeroes'''. Empty space like this is common in the data files, and indicates that something stored in memory at this relative position was not written to disk (probably pointers, or sometimes a space reserved for possible future use in a resource type).
After this comes four "int"s of '''garbage'''. Space occupied by random values like this is common in the data files, and indicates that something stored in memory at this relative position was written to disk even though it wouldn't be meaningful on disk (probably pointers or uninitialized memory in a space that was being reserved for possible future use). The first two 32-bit fields are, however, used in .oni files generated by OniSplit, and the last 32-bit field is partly used by OniX for a new form of template versioning. Future usage of these fields by OniSplit and/or OniX may change (hopefully not too much).


That concludes the header of the instance file. Immediately after this header, we find the instance descriptors array.
That concludes the header of the instance file. Immediately after this header, we find the instance descriptors array.
Line 76: Line 68:
The instance descriptor array tells Oni where to find the data and the name of every instance (resource) indexed by the .dat file. The descriptors start at 0x40 in the .dat file, but below is a descriptor found at 0x017B50 in the file which makes a better example. In the table below, we use offsets relative to the start of this descriptor.
The instance descriptor array tells Oni where to find the data and the name of every instance (resource) indexed by the .dat file. The descriptors start at 0x40 in the .dat file, but below is a descriptor found at 0x017B50 in the file which makes a better example. In the table below, we use offsets relative to the start of this descriptor.


{{Table}}
{| class="wikitable"
{{OBD_Table_Header}}
|- bgcolor="#E9E9E9"
{{OBDtr| 0x00 | tag     | | 54 42 55 53 | 'SUBT'   | template tag }}
! width=5% | Offset
{{OBDtr| 0x04 | int32   | | C8 30 22 00 | 0x2230C8 | data offset (relative to data table) }}
! width=5% | Type
{{OBDtr| 0x08 | int32   | | 01 CB 00 00 | 0xCB01   | name offset (relative to name table) }}
! width=10% | Raw Hex
{{OBDtr| 0x0C | int32   | | C0 09 00 00 | 2496     | data size }}
! width=10% | Value
{{OBDtr| 0x10 | int32   | | 00 00 00 00 | 0        | flags; possible values:
! width=35% | Description (vanilla)
! width=35% | Description (GDFX)
|- align=center
| 0x00
| tag
| 54 42 55 53
| 'SUBT'
|colspan="2" align=left | template tag
|- align=center
| 0x04
| int32
| C8 30 22 00
| 0x2230C8
|colspan="2" align=left | data offset (relative to data table)
|- align=center
| 0x08
| int32
| 01 CB 00 00
| 0xCB01
|colspan="2" align=left | name offset (relative to name table)
|- align=center
| 0x0C
| int32
| C0 09 00 00
| 2496
|colspan="2" align=left | data size
|- align=center valign=top
| 0x10
| int32
| 00 00 00 00
| 0
| align=left | flags; possible values:
:0x'''01''' 00 00 00 - unnamed
:0x'''02''' 00 00 00 - empty
:0x'''04''' 00 00 00 - never used; intended to mark instance as pointing to duplicate data rather than its own data
:0x'''08''' 00 00 00 - instance's data is being used by duplicate instances as a source
The first two of the following bits occur throughout the original .dat files. However these bits are ignored by the engine when loading data because they only have relevance during runtime, when Oni is in Tool mode:
:0x00 00 '''10''' 00 - touched (unsaved data)
:0x00 00 '''20''' 00 - "in batch file"
:0x00 00 '''40''' 00 - delete upon next save
| align=left | flags; possible values:
:0x'''01''' 00 00 00 - unnamed
:0x'''01''' 00 00 00 - unnamed
:0x'''02''' 00 00 00 - empty
:0x'''02''' 00 00 00 - empty
:0x'''04''' 00 00 00 - never used; appears to mean "big-endian" data
:0x'''04''' 00 00 00 - never used; intended to mark instance as pointing to duplicate data rather than its own data
:0x'''08''' 00 00 00 - shared }}
:0x'''08''' 00 00 00 - instance's data is being used by duplicate instances as a source
The Tool mode bits have been moved to the upper half of the flags byte (they are cleared altogether in the GDFX data, but this is their location in memory):
:0x'''10''' 00 00 00 - touched (unsaved data)
:0x'''20''' 00 00 00 - "in batch file"
:0x'''40''' 00 00 00 - delete upon next save
This frees up the three higher bytes for the data versioning timestamp which is in YY/MM/DD format, stored thusly:
:0x00 '''00''' 00 00 - versioning timestamp – day
:0x00 00 '''00''' 00 - versioning timestamp – month
:0x00 00 00 '''00''' - versioning timestamp – year
|}
|}


Line 92: Line 132:


===Unnamed and empty resources===
===Unnamed and empty resources===
You'll notice that the level file header lists fewer names (7,124) than instances (9,347). That's because there are 3 types of instance descriptors:
You'll notice that the level file header lists fewer names (7,124) than instances (9,347). That's because there are 3 types of instance:
*Unnamed and not empty - they are only referenced by other instances in the same file, generally as child data (e.g., 3D geometry elements like ABNA are "contained" by AKEV, a level's environment).
*Unnamed and not empty - they are only referenced by other instances in the same file, generally as child data (e.g., 3D geometry elements like ABNA are "contained" by AKEV, a level's environment).
*:In vanilla Oni .dats there are some rare occurrences of unnamed non-empty ''orphan'' instances (e.g., [[OBD:File types/Naming#TRCM|TRCM]]). These are a form of garbage and are discarded by OniSplit when unpacking a level.
*Named and not empty - they can be referenced by other instances in any file and the engine can use their name or template tag to find them.
*Named and not empty - they can be referenced by other instances in any file and the engine can use their name or template tag to find them.
*Named and empty - "empty" instances are used in level-specific instance files (i.e. not in '''level0_Final.dat''') to associate an instance ID with a name. For every empty resource, there's another one with a matching name in '''level0_Final.dat''' that has data in it. The empty resource in the instance file is (usually) looked up by ID, then the engine searches all the loaded files for a non-empty instance with the same name, causing it to find the actual file in the global data in '''level0_Final.dat'''.
*Named and empty - "empty" instances are used in level-specific instance files (i.e. not in level0_Final.dat) to associate an instance ID with a name. For every empty resource, there's another one with a matching name in level0_Final.dat that has data in it. The empty resource in the instance file is (usually) looked up by ID, then the engine searches all the loaded files for a non-empty instance with the same name, causing it to find the actual file in the global data in level0_Final.dat.


===Peeking at instance name===
===Peeking at instance name===
If you want to see the name of this resource, let's look at address 0xCB01 + 0x28F240 (the file header's address for the name table) = 0x29BD41. There we find the string "SUBTsubtitles". The actual subtitle data should be found at the address 0x2230C8 + 0x03BCA0 (the file header's address for the data table) = 0x25ED68. Let's go there now....
Before we talk about the name table in depth, we can peek ahead at the name of this resource using the offset we've just been given. Let's add the offset 0xCB01 to 0x28F240, the file header's address for the name table. This gives us the address 0x29BD41. There we find the string "SUBTsubtitles".


===Peeking at instance data===
===Peeking at instance data===
For some reason, the addresses we calculate from the descriptor data offsets are all off by eight bytes, so we need to subtract 8 from 0x25ED68 and go to 0x25ED60. Compare what you see here to the documentation for the [[SUBT]] type. Below is the data you should actually see for the English Oni SUBT file at this address. Note that we still haven't found the actual subtitle data, because SUBT stores its data in the raw file. The princess is in another castle:
The actual subtitle data should be found by adding the offset 0x2230C8 to 0x03BCA0, the file header's address for the data table, to get 0x25ED68. We're going to leave the full details of the data table for later, but below is the data you should actually see for the English Oni SUBT file at this address. You have to consult the [[SUBT]] page to know how to read this data.


{{Table}}
{{Table}}
{{OBDth}}
{{OBDth}}
{{OBDtr| 0x00 | res_id  | | 01 F4 12 00 | 4852        | 04852-subtitles.SUBT }}
{{OBDtr| 0x04 | lev_id  | | 01 00 00 00 | 0          | level 0 }}
{{OBDtr| 0x08 | char[16] | | AD DE      | dead        | unused }}
{{OBDtr| 0x08 | char[16] | | AD DE      | dead        | unused }}
{{OBDtr| 0x18 | offset  | | 80 44 44 01 | 0x01444480  | raw file data address }}
{{OBDtr| 0x18 | offset  | | 80 44 44 01 | 0x01444480  | raw file data address }}
Line 112: Line 151:
|}
|}


The first two words, or 32-bit sequences, are the standard resource header. The second and third bytes of the first word are the '''resource ID'''. The second and third bytes of the second word are the '''level number''' where this resource is found.
After '''padding''' of 16 unused bytes, we find that, instead of data, there's an address of the actual data: it's in the level's raw file. Open level0_Final.raw and jump to address 0x01444480, and you should see "01_01_01 Griffin: Give me another reading.", and the rest of some very familiar dialogue continuing from there.


After a '''buffer''' of 16 unused bytes, we find the address of the actual data: it's in the level's raw file. Open level0_Final.raw and jump to address 0x01444480, and you should see "01_01_01 Griffin: Give me another reading.", and the rest of some very familiar dialogue continuing from there.
The '''array size''' of 609 tells the part of the engine that reads SUBT data to expect a chunk of 609 subtitled lines of dialogue.
 
The '''array size''' of 609 tells us that this is a chunk of 609 subtitled lines of dialogue.


==Name descriptors==
==Name descriptors==
The name descriptor array starts immediately after the instance descriptors array. To find the end of the instance descriptors, we can simply take the size of an instance descriptor, 20 bytes, and multiply it by the number of instance descriptors in the file header. In level 0 there are 9347 instance descriptors, so 20 * 9347 = 186940. In hex, that's 0x02DA3C. Adding that to 0x40 (the start of the instance descriptors) takes us to address 0x02DA7C. Voila, the start of the name descriptors.
The name descriptor array starts immediately after the instance descriptors array. To find the end of the instance descriptors, we can simply take the size of an instance descriptor, 20 bytes, and multiply it by the number of instance descriptors in the file header. In this case, that means 20 * 9347 = 186940, or 0x02DA3C. Adding that to 0x40 (the start of the instance descriptors) takes us to address 0x02DA7C. Voila, the start of the name descriptors.


The name descriptor array stores the numbers of all named instances in alphabetical order. This allows the engine to do a binary search to quickly find instances by name. It is also used when finding instances by type. However the addresses of these instances in memory cannot be known until the file is loaded into RAM, so a space of 32 bits is reserved for that runtime pointer.
The name descriptor array stores the numbers of all named instances in alphabetical order. This allows the engine to do a binary search to quickly find instances by name. It is also used when finding instances by type. However the addresses of these instances in memory cannot be known until the file is loaded into RAM, so a space of 32 bits is reserved for that runtime pointer.
Line 130: Line 167:


==Template descriptors==
==Template descriptors==
Likewise, the template descriptor array starts directly after the name descriptors. Since name descriptors are 8 bytes, 8 * 7124 (taken from the header) = 56992, or 0xDEA0, and adding that to the name descriptor array's start address (0x02DA7C) gives us 0x03B91C: the start of the template descriptors.
Likewise, the template descriptor array starts directly after the name descriptors. Since name descriptors are 8 bytes, 8 * 7124 (taken from the header) = 56992, or 0xDEA0, and adding that to the name descriptor array's start address (0x02DA7C) gives us 0x03B91C as the start of the template descriptors.


The template descriptor array contains information about all templates (that is, resource types, AKA tags), used in the file (56 in this case). So any resource occurring in this instance file has to have its type listed here.
The template descriptor array contains information about all templates (that is, resource types, aka tags), used in the file (56 in this case, as we learned from the file header). Any resource occurring in this instance file has to have its type listed here. Here is the template descriptor at 0x3B9FC:
 
The template checksum is used to prevent loading of instance files that are not compatible with the current engine version. The number of resources is self-explanatory. Note that "TBUS" has a usage number of 2, which corresponds to what we learned earlier about Oni having only two subtitle files, "SUBTsubtitles" and "SUBTmessages".


{{Table}}
{{Table}}
{{OBD_Table_Header}}
{{OBD_Table_Header}}
{{OBDtr| 0x00 | int64  | | A0 6D 12 00 00 00 00 00 | 0x126DA0    | template checksum }}
{{OBDtr| 0x00 | int64  | | 3C B9 A6 71 08 00 00 00 | 0x871A6B93C | template checksum }}
{{OBDtr| 0x0C | tag    | | 41 4E 42 41             | 'ABNA'      | template tag }}
{{OBDtr| 0x08 | tag    | | 45 47 52 54             | 'EGRT'      | template tag }}
{{OBDtr| 0x08 | int32  | | 01 00 00 00            | 1          | number of resources in file that use this template }}
{{OBDtr| 0x0C | int32  | | 01 00 00 00            | 1          | unused: number of resources in file that use this template }}
|}
|}


;Template checksum
The '''template checksum''' is used to prevent loading of instance files that are not compatible with the current engine version. The '''tag''' is the same kind of number-written-as-backwards-ASCII that we discussed in the "Backwards and garbage data" section; in this case, 'EGRT' means [[TRGE]]. The field for the '''number of resources''' using this template is unused. The number should be correct for each template, but Oni never uses it for anything.
An instance can have pointers to other related instances, but since pointers are only valid in memory, they cannot be stored on disk. They must be set when the level data is loaded into memory. To be able to do this, the engine must know where pointers are kept in an instance's data, and this is done using "templates". This template info is hard-coded into the game:
 
*a checksum of the data contained by the template (the checksum algorithm is unknown, but the checksum stored in Oni's code for a given tag must match the one in the template descriptors array for that tag)
You might wonder how Oni knows how to read each type of data, such as a SUBT or an ABNA. The simple answer is that this information is hard-coded into Oni. In fact, the information on each instance type, as stored in Oni's code, is actually the real "template". The file data only gives the tag and checksum that refer to a certain template. Which types of data fields are encountered in which order is already known by Oni. These hardcoded templates also tell Oni which parts of the file data are reserved for pointers.
*a 4-letter tag used to identify the template (ABNA, ONCC, WMDD, etc.)
 
*a short description of the data structure, e.g. "BSP Tree Node Array"
That's because an instance may have pointers to other related instances, but pointers are only valid in memory; they cannot be stored on disk. They must be set when the level data is loaded into memory and the address in RAM has been determined. So one type of data field in Oni's templates is a raw pointer; on Macs and the Windows demo, there is an additional "separate offset" type. The pointer and offset are 32 bits in length, as one must expect since Oni was compiled for 32-bit PCs.
*a list of all the instance's data fields and their types (see [[OBD:Data types]])
 
*other data that appears to be unused, like the size of the fixed part and the size of an array element for data structures that contain variable-length arrays
Incidentally, the templates in Oni's code have not just the familiar four-character tags attached to them, but also a descriptive string, e.g. "BSP Tree Node Array". This is the source of the names on the [[OBD:File types|File types]] page.


==Data table==
==Data table==
The data table stores all the instance data. We peeked at this before when we looked at the instance descriptor for SUBTsubtitles.
The data table stores all the instance data (or points to its actual location in a raw/separate file). We peeked at this before when we looked at the instance descriptor for SUBTsubtitles.


The start of each instance's record, the ID number, is always 32 byte-aligned. Thus, even though the template descriptors ended at 0x03BC9C, there are four empty bytes here so that the data table can begin at 0x03BCA0, which divides evenly by 32. This alignment also means that the instance-specific data will always be found at an offset like 0x0008, 0x0028, 0x0148 etc.
The start of each instance's record, the ID number, is always 32 byte-aligned. Thus, even though the template descriptors ended at 0x03BC9C, there are four empty bytes here so that the data table can begin at 0x03BCA0, which divides evenly by 32. This alignment also means that the instance-specific data will always be found at an offset like 0x0008, 0x0028, 0x0148 etc.


The instance ID and file ID are not actually part of the instance data. The engine always keeps pointers to the start of the type-specific data, and the instance ID and file ID are accessed using negative offsets when needed (usually to find the name or template tag of an instance, given a pointer to it).
The instance ID and file ID are not actually part of the instance data, but are considered to be the resource header. The engine always keeps pointers to the start of the type-specific data itself; we saw this before when we jumped to 0x25ED68 and saw the data for the SUBT rather than the header for this data. The instance ID and file ID are accessed using negative offsets when needed (usually to find the name or template tag of an instance, given a pointer to it).


{{Table}}
{{Table}}
Line 165: Line 200:
|}
|}


;Instance ID
The '''instance's ID''' is computed as:
The ID of an instance is computed as:
  (instance_descriptor_index << 8) <nowiki>|</nowiki> 1
  (instance_descriptor_index << 8) <nowiki>|</nowiki> 1.
The 1 allows the engine to know which IDs have already been converted to pointers (an instance pointer will always be 8 byte-aligned, so it will never have the zero bit already set).
The 1 allows the engine to know which IDs have already been converted to pointers (a instance pointer will always be 8 byte aligned so it can never have the bit 0 set).
 


;File ID
The '''file ID''' is computed from the name of the instance file. For "_Final" files the file ID is computed as:
The file ID is computed from the name of the instance file. For "_Final" files the file ID is computed as:
  (level_number << 25) <nowiki>|</nowiki> 1
  (level_number << 25) <nowiki>|</nowiki> 1
Again the 1 allows the engine to know which file IDs have already been converted to pointers.
Again, the 1 allows the engine to know which file IDs have already been converted to pointers.


As you can see, the size of a given instance's data can be almost anything. Thus, we cannot compute the end of this table in any simple way. That's why the instance file header explicitly gives us the address of the name table.
As you can see, after the header, the size of the actual instance data can be almost anything. Thus, we cannot compute the end of the data table in any simple way. That's why the instance file header explicitly gives us the address of the name table that comes after this.


By the way, how do we know which resource's data we're looking at in the table? Let's look at the very first data, at 0x03BCA0. Noting that the first two numbers, the instance and file ID, do not count as data, and knowing that the instance descriptor gives the offset into the data table for the start of each instance's data, that means that there must be a resource with a data offset of 0x08, the lowest offset possible into the table. We can find this right at the start of the instance descriptor array:
By the way, how do we know which resource's data we're looking at in the data table? Let's look at the very first data, at 0x03BCA0. Noting that the first two numbers, the instance and file ID, do not count as data, there must be a resource with a data offset of 0x08, the lowest offset possible into the table. We can find this offset listed right at the start of the instance descriptor array:


{{Table}}
{{Table}}
Line 186: Line 218:
{{OBDtr| 0x08 | int32  | | 00 00 00 00 | 0x00    | name offset (relative to name table) }}
{{OBDtr| 0x08 | int32  | | 00 00 00 00 | 0x00    | name offset (relative to name table) }}
{{OBDtr| 0x0C | int32  | | 60 0F 00 00 | 3936    | data size }}
{{OBDtr| 0x0C | int32  | | 60 0F 00 00 | 3936    | data size }}
{{OBDtr| 0x10 | int32  | | 00 00 00 00 | 0      | flags
{{OBDtr| 0x10 | int32  | | 00 00 00 00 | 0      | flags }}
|}
|}


Line 199: Line 231:
|}
|}


These names can be up to 63 characters long, counting the tag.
These names can be up to 63 characters long, counting the tag. The instance file concludes with the end of the name table.


{{OBD}}
{{OBD}}