Tomb Raider I/II/III/IV Data File Format
|
This document is aimed at superseding the respectable TRosettaStone document which is dated of late 99 ! It was (and is yet) an invaluable source of information when it comes to work with the file format used by the Tomb Raider engines. This document owes a lot to TRosettaStone and is not a complete rewrite: it retains the majority of the original text. But I felt it was time to correct / update the document because since the last 4 years a lot of work from different people shed some new lights on the TR file formats.
This document only describes the level file format, not the .DAT file nor the ID / animations of the different items found in the game.Those may be the subject of other documents, that have yet to be written. You can have a look to the original TRosettaStone document as it has a lot of information about these subjects.
Note on the screenshots: the screenshots you can see in this document are taken either from the game or from TRPlayer, a tool which attempts to display Tomb Raider levels and whose sources will be available soon here. The source of the screenshot is specified when needed, but basically if you can see Lara, it's from the game, else it's from TRPlayer :). When the screenshot quality does not matter, I tried to lower it as much as possible to lighten the weight of the images.
Several people have been involved in the (difficult) task of deciphering the TR
file formats (not only the level file format). Here's an alphabetical list (if
you have contributed in a way or another and are not in this list, please
contact me and I will update it) , but before that I have to
give a special thanks to the TRosettaStone writer and historically first
contributor, R.Stone (or is he M.Bartoli ?): without you, nothing
could exist in the "Unofficial TR Community", as it is known. You deserve
much of the greetings.
Popov, Rgbold, TRWad, Turbo Pascal, Dr. Willard, Yuri Zhivago
Most of them can be reached at the unofficial community forum
here.
If you have some corrections to make on this document or some new information to add, feel free to contact me (Popov), as I will try to maintain this document up to date as much as I can. Please, don't spread modified copy of this document yourself, instead send me a mail so that I can apply the modifications myself: it will be safer in the long run because we know modifications can only be issued from one source, not from any people around the world. Of course, you can still spread this document itself (but as-is) !
English is not my native language (far from it !), so this document would need a thorough review and lots of corrections to make it acceptable. So, if yourself are an english guy (or even just better than me) and have some free hours to waste...
Abbreviation | Description |
---|---|
TR | Tomb Raider |
TRSDoc | The original TRosettaStone document |
TR1 | Tomb Raider 1. It also includes TR1 unfinished business if not otherwise stated |
TR2 | Tomb Raider 2. It also includes TR2 Golden Mask if not otherwise stated |
TR3 | Tomb Raider 3 - Adventures of Lara Croft. It also includes TR3 Lost Artifact if not otherwise stated |
TR4 | Tomb Raider 4 - The last revelation |
TRLE | Tomb Raider Level Editor, shipped with Tomb Raider Chronicles (TR5) |
When something is only relevant to a specific TR version, these icons will be used:
TR1 only: | |
TR2 only: | |
TR3 only: | |
TR4 only: |
These icons can be combined if something applies to several TR versions but not all (for eg. would mean that whatever follows the icons is applicable only to TR3 and TR4). When a block of text is specific to some given TR versions, it is showed like that:
Here some specifities of TR3/4 are explained...
Using the following combo-box, you can filter this document to only show information relevant to a specific TR version. By default, this document show all information from all TR versions. Try to filter on TR1 or TR2: the above text should disappear...
Other icons have been used throughout this text:
indicates that's something is unknown and has to be worked out. If you think you can help on those ones, don't be shy and drop me a line ! |
Thanks to clipart-graphics.net
for providing the icons found in this text.
Some pictures were also taken from the official
Tomb Raider web site.
Here's the list of current unknowns:
This document contains detailed descriptions of the Tomb Raider I / II / III / IV data file formats. It is assumed that the reader has knowledge and experience programming in C or C++, and has at least a passing familiarity with graphics programming. All information in this document was derived from different people (see the foreword ), without the aid or assistance of anyone at Core Design or Eidos (though, the TRLE has been used to help working out some part of the file format). As such, the information in this document may contain errors or omissions, and all structure and variable names were deduced from the interpretation of the data (and therefore could be misleading or completely wrong). All the information in this document was tested and is therefore plausible, but could also be a misinterpretation. All information herein is provided as is - you get what you pay for, and this one's free. This was a spare-time project that set out to document the Tomb Raider 2 (.TR2) file format; along the way, additional information about Tomb Raider 1 / Gold (.PHD, .TUB) and Tomb Raider 3 (.TR2) files became available, and that information is provided in context. Tomb Raider 4 (.TR4) file format description is now also integral part of this document.
Because of Core/Eidos' position on Tomb Raider level editing tools, it is suggested that any tools that you develop be released in source code form, anonymously, using Usenet newsgroups. Anonymity can protect you from legal action, wide distribution via Usenet prevents Core/Eidos from attempting to recall or control the distribution of your software, and distributing source code both allows multi-platform development (e.g. let others port it to the Mac or Linux for you) and encourages others to write utilities, since they can learn and benefit from your source code. Also, Linux has taught us that 40,000 people debugging a single application makes for a clean final product ;-)
Tomb Raider, Tomb Raider Gold, Unfinished Business, Tomb Raider II, Tomb Raider III, Tomb Raider IV, Lara Croft, and all images and data within the data files and game engine are Copyright © Core Design and/or Eidos PLC. Modification and/or distribution of any part of a Tomb Raider data file (any version) is almost certainly a copyright violation.
This document was composed at a screen resolution of 1024x768, and is best viewed at that resolution. This document can be viewed offline (not connected to the Internet), provided that you download the whole package here to get all the additional images. If you do not, images will be broken links, but the text can still be read. Use your browser's BACK button to return from a link, e.g. if you click on a structure declaration to see its definition, clicking BACK will return you to your point of origin after you've examined the structure definition.
Tomb Raider is driven by two sets of files. The script file, TOMBPC.DAT, contains all the text strings describing the various elements in the game (e.g. the game engine knows about "Key 1"; it looks in TOMBPC.DAT to determine the name to be displayed in Lara's inventory, such as "Rusty Key" or "Taste rostige" or "Clé Rouillée"), the level and cut-scene filenames (e.g. WALL.TR2, CUT3.TR2), the order in which they are to be played, and various per-level and per-game configuration options (e.g. what weapons and objects Lara starts the level with, whether or not the "cheat" codes work, etc.). The level files, {level-name}.PHD / TUB / TR2 / TR4, contain everything about the level, including the geographical geometry, the geometry (meshes) of all animate and inanimate objects in the level, all the textures and colour data, all animation data, index information (and, in TR1 and TR4, the actual sound sample data) for all sounds, accessibility maps - everything necessary to run the game. For whatever reason, Core has included everything in one file instead of breaking it up into logical groupings; this means that every level contains all the meshes, textures, sound information, and animation data for Lara and all of her weapons. There are a fair number of other redundancies, too.
Note: this document does not deal with the TOMBPC.DAT ! See foreword .
For the purposes of further discussion, the following
are assumed:
Data alignment is something one has to be careful about. When some entity gets an address that is a multiple of n, it is said to be n-byte aligned. The reason it is important here is that some systems prefer multibyte alignment for multibyte quantities, and compilers for such systems may pad the data to get the "correct" alignments, thus making the in-memory structures out of sync with their file counterparts. However, a compiler may be commanded to use a lower level of alignment, one that will not cause padding. And for TR's data structures, 2-byte alignment should be successful in nearly all cases, with exceptions noted below.
To set single-byte alignment in Microsoft Visual C++, use the following compiler directive:
#pragma pack(push, tr2, 1)To return to the project's default alignment, use the following directive:
#pragma pack(pop, tr2)
To achieve 2-byte alignment in Metrowerks CodeWarrior, widely used in the MacOS, use this compiler directive:
#pragma options align=mac68kTo return to the project's default alignment, use this directive:
#pragma options align=resetSimilar options exist for other compilers.
The world coordinate system is oriented with the X-Z plane horizontal and Y vertical, with -Y being "up" (e.g. decreasing Y values indicate increasing altitude). The world coordinate system is right-handed (+X goes right, +Y goes bottom and +Z goes 'into' the screen) and is specified using bit32 values; however, the geography is limited to the +X/+Z quadrant for reasons that are explained below. Mesh coordinates are relative and are specified using bit16 s. There are some additional coordinate values used, such as "the number of 1024-unit blocks between points A and B"; these are simply scaled versions of more conventional coordinates.
All colours in TR are specified either explicitly (using either the tr_colour structure, described below, or the 16-bit ARGB structure) or implicitly, by indexing one of the palettes (only in TR1/2/3 - there's no palette in TR4).
If, for some reason, 16-bit textures are turned off, all
colours and textures use an 8-bit palette that is stored in the level
file. This palette consists of a 256-element array of tr_colour structures, each designating
some colour; textures and other elements that need to reference a colour
specify an index (0..255) into the Palette[] array. There is also a 16-bit
palette, which is used for identifying colours of solid polygons. The
16-bit palette contains up to 256 four-byte entries; the first three bytes
are a tr_colour, while the last byte is ignored
(set to 0).
The 16-bit textile array, which contains tr_textile16 structures, specifies colours using 16-bit ARGB, where the highest bit (0x8000) is a crude alpha channel (really just simple transparency - 0 ::= transparent, 1 ::= opaque). The next 5 bits (0x7c00) specify the red channel, the next 5 bits (0x03e0) specify the green channel, and the last 5 bits (0x001f) specify the blue channel, each on a scale from 0..31.
The 32-bit textile array, which contains
tr_textile32 structures, specifies
colours using 32-bit ARGB, where the highest byte is the alpha channel (255
means the texel is fully opaque, 0 the texel is fully transparent). The
next bytes specify (in this order) the red / green / blue channels.
The 16 and 32-bit textile arrays depict the same graphics data, but of course
the 32-bit array has a better color resolution. It's the one used if you select
a 32 bits A8R8G8B8 texture format in the setup menu from
TR4.
There are two basic types of objects in TR - meshes and sprites. Meshes are collections of textured or coloured polygons (only in TR1/2/3 - there are no coloured polys in TR4) that are assembled to form a three-dimensional object (such as a tree, a tiger, or Lara herself). The "rooms" themselves are also composed of meshes. Mesh objects may contain more than one mesh; though these meshes are moved relative to each other, each mesh is rigid. Sprites are two-dimensional images that are inserted into three-dimensional space, such as the "secret" dragons, ammunition, medi-packs, etc. There are also animated sprite sequences, such as the fire at the end of "The Great Wall." Core had presumably used this method to reduce CPU utilization on the PlayStation and/or the earlier PCs. Sprites become less and less abundant; TR2 has very few scenery sprites, and TR3's pickups are models instead of sprites. Objects are referenced in one of two ways - as an offset into an array (e.g. Moveables[i]) or using an identifying tag (ObjectID). In the latter case, the related array (Items[], Moveables[], etc.) is searched until a matching ObjectID is found.
There are three basic types of animations in TR, two corresponding directly with meshes and sprites, and a third type, animated textures. Sprite animation (sprite sequences) consists simply of a series of sprites that are to be displayed one after another, e.g. grenade explosions. Mesh animations are much more complex, done by what is essentially a skeletal-modeling scheme. These involve some arrays (Frames[] and MeshTree[]) of offsets and rotations for each element of a composite mesh. Frames are then grouped into an array (Animations[]) that describes discrete "movements," e.g. Lara taking a step or a tiger striking with its paw. The animations are "sewn together" by a state change array and an animation dispatch array, which, together with state information about the character, ensure that the animation is fluid (e.g. if Lara is running and the player releases the RUN key, she will stop; depending upon which of her feet was down at the time, either her left or right foot will strike the floor as part of the "stop" animation. The correct animation (left foot stop vs. right foot stop) is selected using these structures and the state information). Animated textures are simply a list of textures that are cycled through in an endless loop; they are normally used as geographic elements of the levels (e.g. water surface, bubbling lava, Atlantean plasma walls).
TR4 uses a new scheme to cycle through the list of
textures in an animated texture: you can see it in action in angkor1.tr4, room #
76:
The
animated texture here is made of two textures and we can see in the game
that the textures scroll in the v coordinate direction.
We
still have to work out how this new scheme is selected over the old one (a
bit somewhere would tell it ?) and how in the end the animation is rendered (are
the two textures modulated before rendering ? is it possible to have more
than two textures ?).
There are two main types of lighting in Tomb Raider, constant and vertex. Constant lighting means that all parts of an object have the same illumination, while in vertex lighting, each polygon vertex has its own light value, and the illumination of the polygon interiors is interpolated from the vertex values. Furthermore, lighting can be either internal or external. Internal lighting is specified in an object's data, external lighting is calculated using the room's light sources (ambient light, point light sources, spot lights (TR4), flares, gunshots). When available, external lighting also uses the vertex normals to calculate the incoming light at each vertex. Light intensities are described either with a single value or with a 16 bits color value (you can see it more like a 'color filter'), depending mainly on the TR version.
All mesh surfaces are either coloured or textured (only textured in TR4) . Coloured surfaces are "painted" with a single colour that is either specified explicitly or using an index into the palette. Textured surfaces map textures (bitmapped images) from the texture tiles (textiles) to each point on the mesh surface. This is done using conventional UV mapping, which is specified in "Object Textures" below; each object texture specifies a mapping from a set of vertices to locations in the textile, and these texture vertices are associated with position vertices specified here. Each textile is a 256x256 pixels wide area.
There are several sorts of sounds, which can be classified as either continuous or triggered. Continuous sounds are level-background sounds (such as the blowing wind in "The Great Wall") and sound sources (such as waterfalls; these are in SoundSources[]). Triggered sounds are sounds played when some event happens, such as at certain animation frames (footsteps and other Lara sounds), when doors open and close, and when weapons are fired. Sounds are stored in two places: the game-data files and the CD audio tracks (the latter are separate soundfiles in the MacOS version). The latter kind of sound is referred to straightforwardly, by index, while the former kind of sound is referred to using a three-layer indexing scheme, to provide a maximum amount of abstraction. An internal sound index references SoundMap[], which points to a SoundDetails[] record, which in turn points to a SampleIndices[] entry, which in turn points to a sound sample. SoundDetails[] contains such features as sound intensity, how many sound samples to choose from, among others. The sound samples themselves are in Microsoft WAVE format, and they are embedded either in the data files (TR1 / TR4) or in a separate file (MAIN.SFX) in TR2 and TR3.
(Some compilers, like CodeWarrior, will pad this structure to make 4 bytes; one must either read and write 3 bytes explicitly, or else use a simple array of bytes instead of this structure.)
And as mentioned earlier, the 16-bit palette uses a similar structure:
If the rectangle is a coloured polygon (not textured), the .Texture element contains two indices: the low byte (.Texture & 0xff) is an index into the 256-colour palette, while the high byte (.Texture >> 8) is in index into the 16-bit palette, when present.
The Texture field can have the bit 15 set : when it is, the face is two-sided (ie. visible from both sides)
The extra field LightingEffect exists only for tris / quads making meshes (used by moveables and static meshes), not for tris / quads making rooms ! It has this layout:
Bit 0 not set Bit 0 set Bit 1-7 all set to 0 Bit 1-7 all set to 1
A room in TR2 is simply a rectangular three-dimensional area. A room may be "indoors" or "outdoors," may or may not be enclosed, may be accessible or inaccessible to Lara, may or may not contain doors or objects. All rooms have "portals," called "doors" in some documentation, which are pathways to adjacent rooms. There are two kinds of portals, visibility portals and collisional portals. Visibility portals are for determining how much of a room (if any) is visible from another room, while collisional portals are for enabling an object to travel from one room to another. The visibility portals are most likely for doing "portal rendering", which is a visibility-calculation scheme that goes as follows: The viewpoint is a member of some room, which is then listed as visible from it. This room's (visibility) portals are checked for visibility from that viewpoint, and visible portals have their opposite-side rooms marked as visible. These rooms are then checked for portals that are visible from the viewpoint through the viewpoint's room's portals, and visible ones have their opposite-side rooms marked as visible. This operation is repeated, with viewing through intermediate portals, until all visible portals have been found. The result is a tree of rooms, starting from the viewpoint's room; only those rooms and their contents need be rendered. It is clear that both visibility and collision calculations require that objects have room memberships given for them, and indeed we shall find that most map objects have room memberships. Try to give a look here for in-depth information about Portal rendering.
Rooms may overlap; as we shall see, this is involved in how horizontal collisional portals are implemented. However, different rooms may overlap without either being directly accessible from the other; there are several inadvertent examples of such "5D space" in the Tomb Raider series. The only possibly deliberate example I know of is the flying saucer in "Area 51" in TR3, whose interior is bigger than its exterior.
A room can have an "alternate room" specified for it; that means that that room can be replaced by that alternate as the game is running. This trick is used to produce such tricks as empty rooms vs. rooms full of water, scenery rearrangements (for example, the dynamited house in "Bartoli's Hideout" in TR2), and so forth. An empty room is first created, and then a full room is created at its location from a copy of it. The empty room then has that full room set as its alternate, and when that room is made to alternate, one sees a full room rather than an empty one.
The rooms are stored sequentially in an array, and "Room Numbers" are simply indices into this array (e.g. "Room Number 5" is simply Rooms[5]; the first room is Rooms[0]).
Rooms are divided into Sectors, which are 1024x1024 unit squares that form a grid on the X-Z plane. Sectors are the defining area for floor/ceiling height and various actions (e.g. a tiger appears and attacks when Lara steps on a given square); the various attributes of each sector are stored in the Sector Data (described in this section) and the FloorData . As an aside, Sectors correspond to the "squares," easily visible in all of the Tomb Raider games, that experienced players count when gauging jumps; they also account for some of the game's less-appealing graphic artifacts. Careful tiling and texture construction can make these "squares" almost invisible.
Rooms have two kinds of surfaces, rendered and collisional, much like the two kinds of portals. The former are what is seen, while the latter control how objects interact with the world geometry. Furthermore, these two types are specified separately in the room data.
Rooms are defined with a complex structure, which is described below "inside-out," meaning that the smaller component structures are described first, followed by the larger structures that are built using the smaller structures.
BoxIndex is more complicated:
Furthermore, there is a special value of the "real" index, 2047, or 0x7ff.
So, there's the need to fully understand this field when used by TR3/4...
Intensity1 ranges from 0 (dark) to 0x1FFF (bright). However, some rooms occasionally have some lights with intensity greater than 0x1FFF (look at room #9, 2nd light in level1.phd for eg).
Fade1 is the distance max the light shines on and ranges from 0 to 0x7FFF.
Intensity1 ranges from 0 (dark) to 0x1FFF (bright). However, some rooms have some lights with intensity greater than 0x1FFF (look at room #17, 2nd light in xian.tr2 for eg). Really, the range is from 0 to 0x7FFF (ultra bright). Above 0x7FFF, it seems the light is always black...
Fade1 is the distance max the light shines on and ranges from 0 to 0x7FFF.
Intensity2 and Fade2 seem not to be used...
Intensity1+Intensity2 is light color. Format is 0x00BBGGRR (Intensity1 is 0xGGRR and Intensity2 is 0x00BB).
Fade1 is the power of the light and ranges mainly from 0 (low power) to 0x1FFF (high power). Though, values greater than 0x1FFF do exist and their meanings are unknown.
Fade2 is the distance max the light can shine on. Range is mainly from 0 to 0x7FFF, but negative values do exist and their meanings are unknown.
Need to work out accurately the Fade1 and Fade2 fields...
TR4 has a rather different room light structure:
Have to describe precisely the fields of the tr4_room_light structure...
Lighting1 ranges from 0 (bright) to 0x1FFF (dark)
Changing the value of Lighting1 seems to do nothing...
Lighting2 ranges from 0 (bright) to 0x1FFF (dark)
Changing the value of Lighting1 seems to do nothing...
Lighting2 is the per-vertex color, in this format (16 bits): 0RRRRRGGGGGBBBBB
Attributes is a set of flags:
The lowest 5 bits are used in combination with the LightMode field of the tr_room structure. See below.
Changing the value of Intensity2 seems to do nothing...
Intensity1 ranges from 0 (bright) to 0x1FFF (dark). There's not a -1 special value for Intensity1 as stated in TRSDoc: mesh lighting is always used
Changing the value of Intensity2 seems to do nothing...
Intensity1 is constant color with format (16 bits): 0BBBBBGGGGGRRRRR
Flags have these meanings:
AmbientIntensity1 ranges from 0 (bright) to 0x1FFF (dark).
AmbientIntensity2 seems not to be used in TR2/3 (should use 0 for TR3 though, else strange effects...).
AmbientIntensity1 + AmbientIntensity2 is the ambient color. R = low 8 bits of AmbientIntensity2, G = high 8 bits of AmbientIntensity1 and B = low 8 bits of AmbientIntensity1.
LightMode can have these values (LightMode is not a bitfield !):
When LightMode is 1 or 2, it works in conjunction with the 5 lowest bits of vertex attributes:
- if lightmode = 2, values from 0 to 16 for the lowest 5 bits of vertex attributes make the lighting fades from current vertex lighting to brighter vertex ligthing (the lower the value is, the quicker the fade to full vertex lighting is - no effect if the value is 16). Values from 16 to 30 (not 31 !) for the lowest 5 bits of vertex attributes make the lighting fades from current vertex lighting to darker vertex lighting (the higher the value is, the quicker the fade to black is - no effect if the value is 16).
- if lightmode = 1, same working than when lightmode=2 but with a flickering effect, not a fading
The FloorData defines special sector attributes such as floor and ceiling slopes, collisional portals to other rooms, climbability of walls, and all the various types of triggering. It is referenced by the sectors as an array of 16-bit unsigned integers, e.g. the current sector is calculated as (((CurrentX - tr_room_info.x) / 1024) * tr_room.NumZsectors) + ((CurrentZ - tr_room_info.z) / 1024), which is then used as an offset into tr_room:: SectorList[]; tr_room_sector::FDindex is an offset into the FloorData[] array.
The FloorData consists of opcodes and operands. Opcodes are 16 bits, as
follows:
Function:
bits 0..7 (0x00FF)
SubFunction:
bits 8..14 (0x7F00)
EndData:
bit 15 (0x8000)
If EndData
is set, there are no more opcodes (after the current one) in this section of
FloorData; otherwise, the next opcode in FloorData should be interpreted
after the current one.
Some functions reference an FDlist, which is a separate list of opcodes
and operands that immediately follows the current FloorData opcode.
FDlist opcodes and operands are different from the base FloorData opcodes and
operands:
FDfunction: bits 10-13
(0x3C00)
Operands: bits 0-9
(0x03FF) vary, depending on FDfunction
FDcontinue:
bit 15 (0x8000)
Several of the functions indicate adjustments to the sector's floor and ceiling heights; these are specified by adjusting the corner heights. The corners will be denoted as 00, 01, 10, and 11; the first is the corner's X coordinate and the second is the corner's Z coordinate, with both given as multiples of 1024.
When parsing functions for TR3, use only the lower 5 bits to find the function value, because some of TR3's functions use the upper 3 bits of the lower byte as part of the operand. However, this will also work correctly in TR1 and TR2.
FloorData Functions are described below.
Function 0x01: Portal Sector
SubFunction 0x00: Room Portal: the
next FloorData element (the operand) is the number of the room that this sector
is a collisional portal to.
An entity that arrives in a sector
with this function present will gets its room membership changed to this
function's operand, without any change in position.
Function 0x02: Floor Slant
SubFunction 0x00: Floor Slant: The
next FloorData element contains the slant values for the floor of this
sector. Slant values are specified in increments of 256 units. The high
byte (bit8) is the Z slope, while the low byte (bit8) is the X slope. If the X
slope is greater than zero, then its value is added to the floor heights of
corners 00 and 01. If it is less than zero, then its value is subtracted from
the floor heights of corners 10 and 11. If the Z slope is greater than zero,
then its value is added to the floor heights of corners 00 and 10. If it is
less than zero, then its value is subtracted from the floor heights of corners
01 and 11.
Function 0x03: Ceiling Slant
SubFunction 0x00: Ceiling Slant: The
next FloorData element contains the slant values for the ceiling of this
sector. Slant values are specified in increments of 256 units. The high
byte (bit8) is the Z slope, while the low byte (bit8) is the X slope. If the X
slope is greater than zero, then its value is subtracted from the ceiling
heights of corners 10 and 11. If it is less than zero, then its value is added
to the ceiling heights of corners 00 and 01. If the Z slope is greater than
zero, then its value is subtracted from the ceiling heights of corners 00 and
10. If it is less than zero, then its value is added to the ceiling heights of
corners 01 and 11.
Function 0x04: Trigger items, switch cameras, end the
level and much more.
As used below, "run FDlist(activate or deactivate)" means go through each
element in FDlist and perform its function ("run FDlist+1" just means start at
FDlist[1] rather than FDlist[0]). Activate/deactivate is only used for
the activate/deactivate item function.
There are two states for each item, active/inactive (the meaning depends on the
item, e.g. a tiger must be active to be seen, if a door is active it is open,
if it is inactive it is closed, etc.) and on/off (keyholes and switches).
The bitu16 immediately following the 0x04 FloorData opcode contains
flags; the bits at 0x3e00 are the Activation Mask (which is XORed with any
appropriate item flags), the bit at 0x0100 indicates "state change occurs only
once". A good example of activation-mask use is the multiple-switch room of
"Palace Midas" in TR1.
SubFunction 0x00: Run
FDlist(activate)
SubFunction 0x01: If Lara is on the
ground, run FDlist(activate)
SubFunction 0x02: If item at
FDlist[0] is on, run FDlist+1(activate), else run FDlist+1(deactivate)
SubFunction 0x03: If item at
FDlist[0] is on, run FDlist+1(activate)
SubFunction 0x04: If item at
FDlist[0] is picked up, run FDlist+1(activate)
SubFunction 0x05: If item at
FDlist[0] is in this sector, run FDlist+1(activate), else run
FDlist+1(deactivate)
SubFunction 0x06: If Lara is on the
ground, run FDlist(deactivate)
SubFunction 0x07: unknown
SubFunction 0x08: If Lara is not on
the ground, run FDlist(activate)
(mainly used
for activating collision detection with such objects as footbridges)
SubFunction 0x09: Run
FDlist(deactivate)
Function 0x05: Kills Lara
Any SubFunction: If Lara is on the
ground, it kills Lara with fire.
Function 0x06: Climbable Walls
This subfunction indicates
climbability of walls; its value is the bitwise OR of the values associated
with all the climbable-wall directions (0x01 ::= +Z, 0x02 ::= +X, 0x04 ::= -Z,
0x08 ::= -X), e.g. SubFunction 0x09 indicates that the walls on both the +Z and
-X sides of this sector are climbable.
Functions 0x07 to 0x12: (only in TR3) These specify the floor and ceiling slopes, which are more complicated here, since these functions specify dividing up the floors and ceilings into triangles along either of the two diagonals. Also, one of the triangles may be a collisional portal to the room above (if in the ceiling) or to the room below (if in the floor). The function word must be parsed as follows:
Bit 15: Continuation bit
Bits 10-14: value t01
Bits 5-9: value t00
Bits 0-4 function value
where t00 and t01 are signed.
It is followed by one operand, to be parsed as follows:
Bits 12-15: value t13
Bits 8-11: value t12
Bits 4-7: value t11
Bits 0-3: value t10
where t10, t11, t12, and t13 are unsigned.
Here are the triangulations and vertex adjustments; for some of the functions, one of the triangles is a portal to another room:
Functions 0x07, 0x0b, 0x0c:
Triangle 1: 00-01-10 (function 0x0b: is a portal)
Triangle 2: 11-10-01 (function 0x0c: is a portal)
Overall adjustment: adj = t00 + t01 + t10 + t12
Add these quantities to these vertex floor heights:
00: (adj - t11)
01: (adj - t12)
10: (adj - t10)
11: (adj - t13)
Functions 0x08, 0x0d, 0x0e:
Triangle 1: 01-11-00 (function 0x0d: is a portal)
Triangle 2: 10-00-11 (function 0x0e: is a portal)
Overall adjustment: adj = t00 + t01 + t11 + t13
Add these quantities to these vertex floor heights:
00: (adj - t11)
01: (adj - t12)
10: (adj - t10)
11: (adj - t13)
Functions 0x09, 0x0f, 0x10:
Triangle 1: 00-10-01 (function 0x0f: is a portal)
Triangle 2: 11-01-10 (function 0x10: is a portal)
Overall adjustment: adj = t10 + t12
Subtract these quantities from these vertex ceiling heights:
00: (adj - t12)
01: (adj - t11)
10: (adj - t13)
11: (adj - t10)
Functions 0x0a, 0x10, 0x11:
Triangle 1: 01-00-11 (function 0x11: is a portal)
Triangle 2: 10-11-00 (function 0x12: is a portal)
Overall adjustment: adj = t11 + t13
Subtract these quantities from these vertex ceiling heights:
00: (adj - t12)
01: (adj - t11)
10: (adj - t13)
11: (adj - t10)
Function 0x13: has subfunction
0x00 and no operand. Unknown, but is possibly monkey-swingability of the
ceiling.
FloorData FDlist functions are described below:
FDfunction 0x00: Activate or deactivate item
Operand (bits 0..9): Item index
FDfunction 0x01: Switch to camera (also uses the
bitu16 immediately following)
Operand (bits 0..6): Index in
Cameras[]
The bitu16 immediately following specifies delay and repeatability
for switched camera
Operand (bits 0..7 (0xff)): Camera
Delay
Number of seconds
to wait before automatically switching back to the normal camera.
0x00 never
switches back to the normal camera.
Operand (bit 8 (0x100)): If set,
only switch to camera once; otherwise, switch to
camera every time
FDfunction 0x02: Underwater Current
Operand (bits 0..9 (0x3ff)):
direction and intensity of flow
0, 1,
2 -Z direction in
decreasing intensity (0 is strongest)
3, 4,
5 -X direction in
decreasing intensity
6, 7,
8 +Z direction in
decreasing intensity
9, 10,
11 +X direction in decreasing intensity
FDfunction 0x03:: Set AlternateRoom Variable
Operand (bit 0 (0x01)):
AlternateRoom Flag value (0/1)
FDfunction 0x04: Alter Room Flags this affects
(enhances/negates) roomflags (always paired with 0x05)
Operand: not sure, range 0 - 5
FDfunction 0x05: Alter Room Flags this affects
(enhances/negates) roomflags (always paired with 0x04)
Operand: not sure, range 0 - 5
FDfunction 0x06: Look at Item (if a camera change is
also desired, this should come first)
Operand (bits 0..9 (0x3ff)): Item
index
FDfunction 0x07: End Level
FDfunction 0x08: Play CD Track
Operand (bits 0..9 (0x3ff)): CD
track ID (TR1: Internal Sound Index
)
FDfunction 0x09: Assault Course Clock Control
Operand (bits 0..9 (0x3ff)):
0x1c ::= clear clock
0x1d ::= stop clock
0x1e ::= clock reset and displayed
This opcode is also associated with
switches; other values of its operand appear to indicate switch sounds.
FDfunction 0x0a: Play "Found Secret" Sound
Operand (bits 0..9 (0x3ff)): Which
secret (0..NumSecrets-1)
FDfunction 0x0b: Unknown
While FloorData index 0 means the sector does not use floordata, there is still a "dummy" entry for index 0. This dummy entry doesn't contain any useful information.
Overview: Nearly all of the non-geographic visual elements in TR2 (as well as a few parts of the landscape) are specified as meshes. A mesh is simply a list of vertices and how they're arranged. The TR2 mesh structure includes a list of vertices as relative coordinates (which allows meshes to easily be placed anywhere in the world geometry), a list of normals (to indicate which side of each face is visible), and lists of Rectangles and Triangles, both Textured and Coloured. The elements of each tr_face4 or tr_face3 structure (Rectangles and Triangles) contain an offset into the Vertices[] array for the mesh. Other arrays (Moveables[], StaticMeshes[]) do not reference the array Meshes[] directly, but instead reference the array MeshPointers[], which points to locations inside of Meshes[], inside of which the meshes are stored in packed fashion.
Meshes:
The sign of the number of normals specifies
which sort of lighting to use. If the sign is positive, then external vertex
lighting is used, with the lighting calculated from the room's ambient and
point-source lighting values. The latter appears to use a simple Lambert law
for directionality: intensity is proportional to max((normal
direction).(direction to source), 0). If the sign is negative, then internal
vertex lighting is used, using the data included with the mesh.NOTE
that this is not a "real" C/C++ structure, in that the arrays are sized by the
NumXXX elements that precede them.
typedef struct {
tr_vertex Centre; // This is
usually close to the mesh's centroid, and appears to be the center of a sphere
used for collision testing.
bit32 CollisionSize; // This
appears to be the radius of that aforementioned collisional sphere.
bit16 NumVertices; // number of
vertices in this mesh
tr_vertex Vertices[NumVertices];
// list of vertices (relative coordinates)
bit16 NumNormals; // If positive,
number of normals in this mesh.
// If negative, number of vertex lighting elements (* (-1))
tr_vertex Normals[NumNormals];
// list of normals (if NumNormals is positive)
bit16 Lights[-NumNormals]; // list
of light values (if NumNormals is negative)
bit16 NumTexturedRectangles; //
number of textured rectangles in this mesh
tr_face4 TexturedRectangles[NumTexturedRectangles];
// list of textured rectangles
bit16 NumTexturedTriangles; //
number of textured triangles in this mesh
tr_face3 TexturedTriangles[NumTexturedTriangles];
// list of textured triangles
bit16 NumColouredRectangles; //
number of coloured rectangles in this mesh
tr_face4 ColouredRectangles[NumColouredRectangles];
// list of coloured rectangles
bit16 NumColouredTriangles; //
number of coloured triangles in this mesh
tr_face3 ColouredTriangles[NumColouredTriangles];
// list of coloured triangles
} tr_mesh;
Static Meshes:
/*
* StaticMesh structure. This defines meshes that don't move (e.g.
skeletons
* lying on the floor, spiderwebs, trees, statues, etc.)
* StaticMeshes have two bounding boxes; it is not clear why they have
more than
* one. One could be the visibililty box, and one could be the collisional
* box, for instance; the former being used for visibility testing, and
the
* latter for collision testing.
*/
typedef struct { // 32 bytes
bitu32 ObjectID; // Object
Identifier (matched in Items[])
bitu16 Mesh; // mesh (offset into
MeshPointers[])
tr_vertex BoundingBox[2][2]; //
First index is which one; second index is opposite corners
bitu16 Flags; // Meaning
uncertain; it is usually 2, and is 3 for objects Lara can travel through,
// like TR2's skeletons and underwater vegetation
} tr_staticmesh;
Moveables:
/*
* Moveable structure. This defines a list of contiguous meshes that
* comprise one object.
* This structure also points to the hierarchy and offsets of the meshes
* (MeshTree), and also to the animations used (Animation); these will be
* described in detail below. If the Animation index is -1, that means
that
* the entity's animations are all generated by the engine; an example is
* Lara's ponytail. Some movables are really stationary, such as locks and
* the sky, and some are not rendered, such as "look at me" points to aim
* the camera at.
*/
typedef struct { // 18 bytes
bitu32 ObjectID; // Item
Identifier (matched in Items[])
bitu16 NumMeshes; // number of
meshes in this object
bitu16 StartingMesh; // stating
mesh (offset into MeshPointers[])
bitu32 MeshTree; // offset into
MeshTree[]
bitu32 FrameOffset; // byte offset
into Frames[] (divide by 2 for Frames[i])
bitu16 Animation; // offset into
Animations[]
} tr_moveable;
Items: Items are instances of objects, which can be sprite sequences or movables. For an object to appear in a level, it must be referenced in the Items[] array. Multiple instances are possible (e.g. two identical tigers in different rooms are represented using two entries in Items[], one for each). The object ID is used to locate the appropriate sprite sequence or movable for the item.
typedef struct { // 24
bytes [TR1: 22 bytes]
bit16 ObjectID; // Object
Identifier (matched in Moveables[], or SpriteSequences[], as appropriate)
bit16 Room; // which room contains
this item
bit32 x; // item position in world
coordinates
bit32 y;
bit32 z;
bit16 Angle; // ((0xc000 >>
14) * 90) degrees
bit16 Intensity1; // (constant
lighting; -1 means use mesh lighting)
bit16 Intensity2; // Like
Intensity1, and almost always with the same value. [absent from TR1 data files]
bitu16 Flags; // 0x0100 indicates
"initially invisible", 0x3e00 is Activation Mask
// 0x3e00 indicates "open" or "activated"; these can be XORed with
//
related FloorData::FDlist fields (e.g. for switches)
} tr_item
;
Sprites:
These are "billboard" objects that are always rendered perpendicular to the view direction. These are used for text and explosion effects and similar things; they are also used for some scenery objects and pickup items, though this use gets less as one goes from TR1 to TR3. The various "Sides" below are the positions of the sprite sides relative to the sprite's overall position, measured in TR's world-coordinate units.
typedef struct { // 16 bytes
bitu16 Tile;
bitu8 x;
bitu8 y;
bitu16 Width;
// actually (Width * 256) + 255
bitu16 Height;
// actually (Height * 256) + 255
bit16 LeftSide;
bit16 TopSide;
bit16 RightSide;
bit16 BottomSide;
} tr_sprite_texture;
Sprite Sequences:
These are collections of sprites that are referred to as a group. The members
of this group can be cycled through (animated sprites such as flames) or
selected in other ways (text). Some sequences have only one member; this is
done so as to access all the sprites in the same way.
typedef struct { // 8 bytes
bit32 ObjectID; // Item identifier
(matched in Items[])
bit16 NegativeLength; // negative
of "how many sprites are in this sequence"
bit16 Offset; // where (in sprite
texture list) this sequence starts
} tr_sprite_sequence
;
Overview:
The animated mesh objects in the Tomb Raider series are sets of meshes that are
moved relative to each other, as defined by Moveables[] entries. Each entry
describes which meshes to be used (a contiguous set of them referred to in
MeshPointers[]), what hierarchy and relative offsets they have (contents of
MeshTree[] pointed to), and what animations are to be used (contents of
Animations[] pointed to).
The hierarchy used is a branching one, with the meshes being at the nodes, and with the first mesh being the root node. The MeshTree[] values, called "Bone2" in some documentation, are applied to each of the child meshes in sequence; they are sets of four bit32's, the first being a hierarchy operator, and the remaining three being the coordinates in the parent mesh's system. A hierarchy example is that for the Lara meshes:
Hips Left thigh Left shin Left foot Right thigh Right shin Right foot Torso Left inner arm Left outer arm Left hand Right inner arm Right outer arm Right hand Head (Ponytail is a separate object)This is implemented by using a stack of meshes and "push" and "pop" operations in MeshTree[]. Normally, each mesh's parent is the previous mesh in series. But such meshes can be "remembered" by adding them to a stack of meshes with a "push" operation. This remembered mesh can then be used as the parent mesh with a "pop" operation. It is not clear what the maximum stack depth is; most TR mesh stacks do not extend beyond 2 or 3 meshes.
The animations for each mesh object are selected with some ingenious techniques. Which animations to use are not hardcoded; instead, each entity has some states it can be in, and these states are used to select which animation. For example, locks have only one state (they just sit there), doors have two states (open and closed), and Lara has numerous states, such as standing, walking, running, jumping, falling, being hurt, dying, etc. Each animation has a state ID, which can be used to select it; however, state transitions might seem to require a large number of intermediate states (opening, closing, starting to jump, landing, etc.). The alternative used in the Tomb Raider engine is for each animation to have bridge animations to other states' animations, which are selected using the ID of which state to change to. These bridge animations then lead to the animation with the appropriate state. Thus, a closed door will run a looped closed-door animation as long as its state stays "closed", but when its state becomes "open", it will change to an opening-door bridge animation, which will end in a looped open-door animation. Likewise, closing a door will make it use a closing-door bridge animation. Some bridge animations are chosen with a finer grain of selectivity, however, such as using one for left foot forward and one for right foot forward.
Thus, each animation references a set of StateChange structures (called simply a "structure" in some documentation), each one of which references an AnimDispatch structure (called a "range" in some documentation). Each StateChange structure contains a new state and which AnimDispatch structures to use. When an entity goes into a new state, the StateChange structures are scanned for that state's ID, and if one matches, then that StateChange's AnimDispatches are then scanned for a range of frames that contains the ID of the current frame. If such an AnimDispatch is found, the animation and the frame are changed to those listed in it.
The ultimate unit of animation is, of course, the frame, and each frame consists of a bounding box, the offset of the root mesh, and rotation angles for all the meshes with respect to their parent meshes. The root mesh is also rotated, but relative to the object's overall coordinates. All rotations are performed around the meshes' origins, and are in order Y, X, Z (yaw, pitch, roll). The reason for the root mesh's displacement is because entities traveling on solid surfaces are likely tracked by having their locations be at ground level, and Lara's hips, for example, are well above the ground. Finally, some of the angles are not specified explicitly, when they are not, they are zero.
Frames are referenced in two ways, either by an offset into the Frames[] array that contains them, or by frame index. The values of the latter appear to be unique to each kind of entity, but not between entities; the first frame for each kind is numbered 0. This is likely a convenience when constructing the animations, since the list of animation frames for each entity can be constructed separately. However, using these indices is fairly simple. Each Animation structure has a first-frame index; this index is subtracted from the index of the desired frame in order to find out its index relative to the animation's first frame.
There are also some special AnimCommands (called "Bone1" in some documentation) for doing various additional things. Some of them are for setting reference points; these may either be 3D ones, for example for grab locations, or 2D ones, for jumps from surface. Some others define actions per frame, like playing sounds, emitting bubbles, and so forth.
Finally, some entities appear to have very incomplete animations; their complete animations are "borrowed" from similar entities. One example of this is the various goons in TR2's Venice levels -- some of them have a full set of animations, while some others have only the standing animation. The ones with only the standing animation borrow their other animations from the fully-animated ones.
Data Structures:
/*
* MeshTree structure
*
* MeshTree[] is actually groups of four bit32s. The first one is a
* "flags" word;
* bit 1 (0x0002) indicates "put the parent mesh on the
mesh stack";
* bit 0 (0x0001) indicates "take the top mesh off of
the mesh stack and use as the parent mesh"
* when set, otherwise "use the previous mesh are the parent mesh".
* When both are present, the bit-0 operation is always done before the
bit-1 operation; in effect, read the stack but do not change it.
* The next three bit32s are X, Y, Z offsets of the mesh's origin from the
parent mesh's origin.
*/
typedef struct { // 4 bytes
bit32 Coord;
} tr_meshtree;
/*
* Animation structure.
* This describes each individual animation; these may be looped by
specifying
* the next animation to be itself. In TR2 and TR3, one must be careful
when
* parsing frames using the FrameSize value as the size of each frame,
since
* an animation's frame range may extend into the next animation's frame
range,
* and that may have a different FrameSize value.
*/
typedef struct { // 32 bytes
bitu32 FrameOffset; // byte offset
into Frames[] (divide by 2 for Frames[i])
bitu8 FrameRate;
// Engine ticks per frame
bitu8 FrameSize; // number of
bit16's in Frames[] used by this animation
bitu16 StateID;
bitu8 Unknown2[8];
bitu16 FrameStart; // first frame
in this animation
bitu16 FrameEnd; // last frame in
this animation (numframes = (End - Start) + 1)
bitu16 NextAnimation;
bitu16 NextFrame;
bitu16 NumStateChanges;
bitu16 StateChangeOffset; //
offset into StateChanges[]
bitu16 NumAnimCommands; // How
many of them to use.
bitu16 AnimCommand; // offset into
AnimCommand[]
} tr_animation
;
/*
* State Change structure
* Each one contains the state to change to and which animation dispatches
* to use; there may be more than one, with each separate one covering a
different
* range of frames.
*/
typedef struct { // 6 bytes
bitu16 StateID;
bitu16 NumAnimDispatches; //
number of ranges (seems to always be 1..5)
bitu16 AnimDispatch; // Offset
into AnimDispatches[]
} tr_state_change
;
/*
* Animation Dispatch structure
* This specifies the next animation and frame to use; these are
associated
* with some range of frames. This makes possible such specificity as one
* animation for left foot forward and another animation for right foot
forward.
*/
typedef struct { // 8 bytes
bit16 Low;
// Lowest frame that uses this range
bit16 High;
// Highest frame (+1?) that uses this range
bit16 NextAnimation;
// Animation to dispatch to
bit16 NextFrame;
// Frame offset to dispatch to
} tr_anim_dispatch
;
/*
* AnimCommand structure
* These are various commands associated with each animation; they are
* called "Bone1" in some documentation. They are varying numbers of
bit16's
* packed into an array; the first of each set is the opcode, which
determines
* how operand bit16's follow it. Some of them refer to the whole
animation
* (jump and grab points, etc.), while others of them are associated with
* specific frames (sound, bubbles, etc.).
*/
typedef struct { // 2 bytes
bit16 Value;
} tr_anim_command
;
Here are all the AnimCommand opcodes and their operands:
// 1: 3 operands. Position reference: (x,y,z); found in grab and block-move
animations
// 2: 2 operands. Position reference on surface for jumping: (x,z) for
horizontal and (y,z) for vertical surfaces(?)
// 3: No operands. Not clear; occurs in animations that are "slaved" to other
animations, such as Lara throwing switches or moving blocks.
// 4: No operands. Not clear; occurs in some death and settling-down
animations, but not all.
// 5: 2 operands. The first one is a frame number, and the second one is the ID
of the sound to play at that frame (internal sound index).
In TR2 and TR3, one of the sound indices two highest bits may be set; when they
are, their meanings are
0x4000 -- play this sound when on dry land (example: footsteps)
0x8000 -- play this sound when in water (example: running through shallow
water)
// 6: 2 operands. The first one is a frame number, and the second one is some
miscellaneous action.
// 0: Occurs in flipping-over animations; freeze camera
at current position until end of animation?
// 3: Make bubble
// 12: Temporarily stop responding to controls?
// etc.
14 and 15: Some kind of camera control?
18: ?
19: ?
20: Lara changing clothes (using a different Lara model)
21: ?
22: ?
23: Hide object
24: Show object
26: Some kind of camera control?
TR3 has additional ones, such as
-32736 = 0x8000 + 32
32
16416 = 0x4000 + 32
/*
* Frame structure.
*
* Frames indicate how composite meshes are positioned and rotated. They
work
* in conjunction with Animations[] and MeshTree[]. A given frame has the
following
* format:
* bit16 BB1x, BB1y, BB1z // bounding box (low)
* bit16 BB2x, BB2y, BB2z // bounding box (high)
* bit16 OffsetX, OffsetY, OffsetZ // starting offset for this moveable
* (TR1 ONLY: bit16 NumValues // number of angle sets to
follow; these start with the first mesh, and meshes without angles get zero
angles.)
* (TR2/3: NumValues is implicitly NumMeshes (from moveable))
* What follows next is a list of angle sets. In TR2/3, an angle set can
* specify either one or three axes of rotation. If either of the high two
* bits (0xc000) of the first angle bitu16 are set, it's one axis: only
one
* bitu16, low 10 bits (0x03ff), scale is 0x100 ::= 90 degrees; the high
two
* bits are interpreted as follows: 0x4000 ::= X only, 0x8000 ::= Y only,
* 0xC000 ::= Z only.
* If neither of the high bits are set, it's a three-axis rotation. The
next
* 10 bits (0x3ff0) are the X rotation, the next 10 (including the
following
* bitu16) (0x000f, 0xfc00) are the Y rotation, the next 10 (0x03ff) are
the
* Z rotation, same scale as before (0x100 ::= 90 degrees).
* Rotations are performed in Y, X, Z order.
* TR1 ONLY: All angle sets are two words and
interpreted like the two-word
* sets in TR2/3, EXCEPT that the word order is reversed.
*/
Overview:
All the Tomb Raider game physics and entity behavior appears to be hardcoded,
with each type ID being associated with some specific sort of behavior (as
Lara, as a boat, as a tiger, as a door, as a boulder, as a lock, etc.). There
is no sign of the sorts of schemes used by some other game engines for
specifying this behavior in data files. One scheme is to use generic
characters, generic projectiles, and so forth, and to specialize them by
reading in appropriate records from data files. Another scheme is to use
interpreted pseudocode; this is used by id's Quake. This hardcoding makes it
difficult to port the earlier Tomb Raider scenarios to the engines of the later
games, which could be desirable with their improved 3D-card and sound-card
support. While textures, models, and animations can be ported, behavior cannot
be.
However, there is a hint that TR3 may have some such information. Some of its characters are hostile in some levels, and not in others (the India-level monkeys, the Antarctica-level flamethrower wielders); there may be some flag in Items[] that determines whether a character is hostile or not. But the hostile and non-hostile versions of these characters may have separate type ID's.
Despite that lack, the Tomb Raider series does have navigation hints for the Non-Player Characters; those entities that move freely across the maps under the command of the game AI. One of the NPC's is the camera, since only Lara (and the vehicles she rides) is under the direct control of the player; the game AI makes the camera follow Lara. The camera uses the navigation hints used by the flying NPC's; these can be constructed so as to help the camera out of tight spots.
The navigation hints are three data structures: boxes, overlaps, and zones. Most sectors point to some box, the main exceptions being horizontal-portal sectors. Several neighbring sectors may point to the same box. A box is a horizontal rectangle, with corners and height specified; each box also has a pointer into the list of overlaps. Each segment in that list is the list of accessible neighboring boxes for some box; the NPC's apparently select from this list to decide where to go next. This selection is done with the help of the zones. These structures of 6 (TR1) or 10 (TR2, TR3) bit16 's that act as zone ID's; their overall indexing is the same as the boxes, meaning that each box will have an associated set of zone ID's. An NPC will select one of this set to use, and will prefer to go into the overlaps-list boxes that have the same zone value as the box it is currently in. For example, one can create guard paths by making chains of zone-ID-sharing boxes, with their overlaps pointing to the next boxes in those chains.
Data Structures:
Boxes:
typedef struct { // 8 bytes [TR1: 20 bytes]
In TR1, the first four are bit32's instead of bitu8's, and are not scaled.
bitu8 Zmin;
// sectors (* 1024 units)
bitu8 Zmax;
bitu8 Xmin;
bitu8 Xmax;
bit16 TrueFloor; // Y value (no
scaling)
bit16 OverlapIndex; // index into
Overlaps[]. The high bit is sometimes set; this
// occurs in front of swinging doors and the like.
} tr_box
;
Overlaps:
This is a set of lists of neighboring boxes for each box, each member being a bitu16; the highest bit being set marks the end of each list. NPC's apparently use this list to decide where to go next.
Zones:
This is a set of bit16's, 6 for TR1 and 10 for TR2 and TR3. NPCs prefer to travel to a box with the same zone ID as the one they are currently at. Which of these zone ID's it uses depends on the kind of the NPC and its current state. The first half of the Zones structure is for the "normal" room state, and the second half is for the "alternate" room state. TR1, for example, has 2 sets of ground zones and 1 set of fly zones; its zones are
ground zone 1 (normal)
ground zone 2 (normal)
fly zone (normal)
ground zone 1 (alternate)
ground zone 2 (alternate)
fly zone (alternate)
The ground zones are for NPC's that travel on the ground, while the fly zones
are for flying or swimming NPC's. TR2 and TR3 have similar breakdowns, though
they have 4 ground zones.
Overview:
The Tomb Raider series makes abundant use of sound, which appears in a variety
of contexts. Sounds can be either continuous or triggered. Continuous ones can
be for the whole level or produced by some sound-source object. The whole-level
sound is a CD-track sound, which is played continuously, thus the blowing-wind
sounds in the underground parts of "The Great Wall". Sound-source objects make
sound in a range around some specific point. Likewise, triggered ones can be
triggered by a variety of events. The triggering can be hardcoded in the engine
(gunshots, switch pulls) or by reaching some animation frame (footsteps, Lara's
somewhat unladylike sounds). Switch pulls and/or door sounds may be specified
with operand of FDFunction 0x09; operand values lower than those used for
assault-course clock control may specify which sounds to use.
Though CD-track sounds are referred to by track index, game-data sounds are referred to by an internal sound index; this is translated into which sound sample with the help of three layers of indexing, to allow for a suitable degree of abstraction. Internal sound indices for various sounds appear to be consistent across all the level files in a game; a gunshot or a passport opening in one level file will have the same internal sound index as in all the others. The highest level of these is the SoundMap[] array, which translates the internal sound index into an index into SoundDetails[]. Each SoundDetails record contains such details as the sound intensity, how many samples to select from, and an index into SampleIndices[]. This allows for selecting among multiple samples to produce variety; that index is the index to the SampleIndices[] value of first of these, with the rest of them being having the next indices in series of that array. Thus, if the number of samples is 4, then the TR engine looks in SampleIndices[] locations Index, Index+1, Index+2, and Index+3. Finally, the SampleIndices[] array references some arrays of sound samples. In TR1, these samples are embedded in the level files, and SampleIndices[] contains the displacements of each one in bytes from the beginning of that embedded block. In TR2 and TR3, these samples are concatenated in the file "MAIN.SFX" with no additional information; SampleIndices[] contains sequence numbers (0, 1, 2, 3, ...) in MAIN.SFX. Finally , the samples themselves are all in Microsoft WAVE format.
The CD-audio tracks are stored in different fashions in the various versions of
the TR series. In the PC version of TR3, they are all stored
in the file CDAUDIO.WAD, which has the format (source: Sven, BachmannS@gmx.net,
http://wotsit.org/cgi-bin/download.cgi?tr3audio): a series of header records
with this format:
{ // 0x108 bytes
bit32 SampleLength; // how many bytes
bit32 SampleOffset; // offset in file
bit8 Name[256]; // C string; the length is a guess, because Sven's sizes are inconsistent.
};
followed by embedded samples in the Microsoft WAVE format.
In the Macintosh versions of TR1 and TR2, the CD audio tracks are separate files in AIFF format, while in the Macintosh version of TR3, these tracks are separate files in Microsoft WAVE format. The Macintosh version of TR3 contains an additional file, CDAudio.db, which contains the names of all the track files as 32-byte zero-padded C strings with no extra contents.
Data Structures:
/*
* SoundSource structure
* This structure contains the details of continuous-sound sources.
Although
* a SoundSource object has a position, it has no room membership; the
sound
* seems to propagate omnidirectionally for about 10 horizontal-grid sizes
* without regard for the presence of walls.
*/
typedef struct {
bit32 x;
// absolute X position of sound source (world coordinates)
bit32 y;
// absolute Y position of sound source (world coordinates)
bit32 z;
// absolute Z position of sound source (world coordinates)
bitu16 SoundID; // internal sound
index
bitu16 Flags; // 0x40, 0x80, or
0xc0
} tr_sound_source
;
SoundMap is for mapping from internal-sound index to SoundDetails index; it is 370 bit16s in TR2 and TR3 and 256 bit16s in TR1 . A value of -1 indicates "none".
/*
* Sound-sample details (SoundDetails)
*/
typedef struct { // 8 bytes
bit16 Sample; // (index into
SampleIndices)
bit16 Volume;
bit16 Unknown1; // sound range?
(distance at which this sound can be heard?)
bit16 Unknown2; // Bits 8-15:
priority?, Bits 2-7: number of sound
// samples in this group, Bits 0-1: channel number?
} tr_sound_details
;
SampleIndices: In TR1, this is a list of indices into
the embedded sound-samples object, which precedes this object in the level
file. In TR2 and TR3, this is a list of indices into the file
"MAIN.SFX"; the indices are the index numbers of that file's embedded sound
samples, rather than the samples' starting locations. That file itself is a set
of concatenated soundfiles with no catalogue info present. In all the TR
series, the sound format used is Microsoft WAVE (.wav).
These are various odds and ends that do not fit into the earlier categories.
Version: Every level file (.PHD, .TUB, .TR2) begins with a bitu32 version number. This seems to be used by the engine to guarantee compatibility between various level editor versions and the game engine version. More generally, it can be used to determine what sort of level is being read. Here are the known (observed) values for the version header:
0x00000020 Tomb
Raider 1, Gold, Unfinished Business
0x0000002d Tomb
Raider 2
0xFF080038 Tomb
Raider 3
0xFF180038 Tomb
Raider 3
Palette: This consists of 256 tr_colour structs, one for each palette entry. However, the individual colour values range from 0 to 63; they must be multiplied by 4 to get the correct values.
This used for all 8-bit colour, such as 8-bit textures.
Object Textures:
/*
* Object-texture vertex structure. It specifies a vertex location in
textile coordinates.
* The Xpixel and Ypixel are the actual coordinates of the vertex's pixel.
* The Xcoordinate and Ycoordinate values depend on where the other
vertices
* are in the object texture. And if the object texture is used to specify
* a triangle, then the fourth vertex's values will all be zero.
*/
typedef struct { // 4 bytes
bitu8 Xcoordinate; // 1 if Xpixel
is the low value, 255 if Xpixel is the high value in the object texture
bitu8 Xpixel;
bitu8 Ycoordinate; // 1 if Ypixel
is the low value, 255 if Ypixel is the high value in the object texture
bitu8 Ypixel;
} tr_object_texture_vert
;
/*
* Object texture structure.
* These, thee contents of ObjectTextures[], are used for specifying
texture
* mapping for the world geometry and for mesh objects.
*/
typedef struct { // 20 bytes
bitu16 Attribute; // 0 means
that a texture is all-opaque, and that transparency
// information is ignored.
//
1 means that transparency information is used. In 8-bit colour,
//
index 0 is the transparent colour, while in 16-bit colour, the
//
top bit (0x8000) is the alpha channel (1 = opaque, 0 = transparent).
//
2 (only in TR3) means that the opacity (alpha) is equal to
the intensity;
//
the brighter the colour, the more opaque it is. The intensity
is probably calculated
//
as the maximum of the individual color values.
bitu16 Tile; // index into textile
list
tr_object_texture_vert
Vertices[4]; // the four corners of the texture
} tr_object_texture
;
Animated Textures:
Animated textures describe sets of object textures that are cycled through to produce texture animations; they are a set of bit16's with the following format (not a "real" C/C++ structure):
bit16 NumAnimatedTextures
struct {
bit16 NumTextureIDs; // Actually,
this is the number of texture ID's - 1.
bit16 TextureIDs[NumTextureIDs +
1]; // offsets into ObjectTextures[], in animation order.
} AnimatedTextures[NumAnimatedTextures];
If a texture belongs to an animated-texture group, it will automatically be
animated by the engine. The animation framerate is most likely hardcoded.
Cameras:
These are positions to switch the camera to; the camera gets switched to one of
these as specified in the floordata, which also specify what to look at, how
long to switch, and whether to do so only once.
typedef struct {
bit32 x;
bit32 y;
bit32 z;
bit16 Room;
bitu16 Unknown1; // correlates to
Boxes[]? Zones[]?
} tr_camera;
Cinematic Frames:
These are camera positionings for cutscenes. All the entity animations are
specified separately, and it is not clear where there is any syncing between
these frames and any of the animations.
typedef struct {
bit16 rotY; //
rotation about Y axis, +/- 32767 == +/- 180 degrees
bit16 rotZ; //
rotation about Z axis, +/- 32767 == +/- 180 degrees
bit16 rotZ2; // seems to
work a lot like rotZ; I haven't yet been able to
// differentiate them
bit16 posZ; // camera
position relative to something (target? Lara? room
// origin?). pos* are _not_ in world coordinates.
bit16 posY; // camera
position relative to something (see posZ)
bit16 posX; // camera
position relative to something (see posZ)
bit16 unknown; // changing this
can cause a runtime error
bit16 rotX; //
rotation about X axis, +/- 32767 == +/- 180 degrees
} tr_cinematic_frame;
LightMap:
A 32*256 array of bitu8
's which is apparently for applying light to 8-bit colour, in some
documentation called "ColourMap". The current palette index and lighting value
are used to calcuate an index to this table, which is a table of palette
indices.
The Tomb Raider series' software rendering, like that of most real-time-3D games, uses 8-bit colour for speed and low bulk; however, there is the serious problem of how to do lighting with 8-bit colour, because doing it directly is computationally expensive. The usual solution is to arrange the palettes' colours in ramps, which the engine then follows in the appropriate directions. However, the TR series' palettes generally lack such neat ramps.
But the TR series has a more general solution, one that does not require palettes to have colour ramps. It uses precalculated lighting tables, the "ColourMap" objects. These contain translations of a colour value and a lighting value, listed by palette index. The translation goes as follows:
n = ColourMap[256 * k + i];
where i is the original palette index, k is determined from the lighting value,
and n is the new palette index. The lighting index k varies from 0 to 31, and
the corresponding lighting value is, for TR1,
2 - k / 16
and for TR2 and TR3,
2 - (k + 1) / 16
This may be associated with the curious fact of the lighting values in the data
files increasing in the "wrong" direction in TR1 and TR2, with 0 being full
brightness and greater values being darker.