By Turbo Pascal, Tegucigalpa, Honduras, Central AmericaDocument Version 1.0
December 11 - 2001
This document describe the file format used in a Tomb Raider WAD file
used for the oficial level editor, currently i am only describing the sections
that are used for my program Strpix3, so severals other section
are not full described yet.
The file format is explained in sequential way, the red field show
the data, next it come the size in bytes, also i am including a explanation
on what it mean and how to use the field.
WAD file format
* Version : (4 bytes).
if 129 then this is a valid tomb Raider WAD file format.
//next come the texture table, first the amount texture pieces in this
wad and later each texture piece definition.
* Amount_textures: (4 Bytes), amount
textures pieces defined.
* Texture_records: (amount_textures*8
sizes bytes);
// all textures pieces are stored in severals 256x256 pixels
pages size .
// The texture record tell to the engine in wich page each texture
piece is located and wich size in pixels they have.
// Each texture piece record is 8 bytes size defined in the follow way:
- X1 : (1 Byte);
- Y1 : (1 Byte);
- PAGE : (2 bytes); //in which page the texture
piece is stored.
- flipx: ( 1 Byte) ; // Normaly -1 or 0.
- width: (1 Byte); // Width for this texture
(X2=X1+WIDTH)
- flipy: (1 Byte); // Normaly -1 or 0
- Height: (1 Byte); // Height for this texture
(Y2=Y1+HEIGHT)
// So each texture is a rectangular piece located at especified PAGE, in the cordinates (X1, Y1) - (X1+width, Y1+height).
//Next you have to load the raw texture data in 24 bits RGB pixel format, first you read the size in bytes for the whole raw data and then it come the raw texture data.
Note: I don;t have clear where is defined if a pixel is transparent, i guess that using the 24th bit, but i am not sure.
* Raw_data_size: (4 Bytes)
* Raw_Data: (Raw_data_size
bytes sizes).
// For find out the total textures pages (256x256 each) availible in
the WAd:: Total_pages:=Raw_data_size / 196608.
//Next come the Mesh pointers table, this table is used as un mesh index table for quick find where in the file begin the data for a especific mesh.
* Amount_pointers_records : (4 bytes)
* Pointer_records : ( Amount_pointers_records*4
bytes)
//Each record is a 4 bytes value used as offset from the begining mesh section. The movables and statics structures point to a index in this table, the reader then use the offset value stored in this entry and from the begining mesh section it skip offset bytes to find out where in the file begin the data for the Mesh pointed for the Movable or for the static structure.
//Is not clear why some entries in this table are unused, so you will
found just the "0" value in severals entries, so Amount_pointers_records
WILL NOT GIVE YOU the amount meshes availibles in the wad.
In Originals WADs from CD counting the pointers_records different
that "0" will give you the amount meshes+1 availibles but i found
in WADs built with TRWEST duplicated mesh pointers entris
even severals times, so is not safe to find the total mesh availibles using
the mesh pointer table, better find out this using the info found in the
mesh
section.
//Next come the mesh section.
// All objects in a wad is built s using one or more individual pieces
called meshes , each mesh is a bunch of Polygons that will
draw a object piece.
Normally all ornaments are just one mesh, and the movables (the objects
that will have some animation) will use severals pieces, then each piece
can be animated independientily at realtime for the engine, the wad store
un bunch of cordinates offsets and angles values that will tell to the
engine how to move and rotate at realtime each piece, so all objects are
drawn for the user using a CAD program ONLY ONCE and at ONLY
ONE position, later the engine will redraw in game for
his self all animations using the bunch of offsets and angles values.
* Amount_mesh_data : (4 Bytes)
//will give you the amount bytes that was necessary to store the info for all the mesh availibles in the WAD. This value is interpreted as amount words ( word=2 bytes), so you have to multiply this value by 2 for get the total bytes that is currently needed for hold the whole mesh section.
//Next come each Mesh data record, at this point you don't know yet how much records you will found next.
//At this point is a good idea to save the current file position to a variable called begining_mesh_section, then you can use in conbination with a mesh pointers data found in the mesh pointer table to locate a especific mesh, just do :
mesh_position:=begining_mesh_section+mesh_pointers[x]
"x" is a mesh pointer index referenced in the statics or movables records
//Before start reading each mesh record also is good idea to calc where is the file position where the mesh data will end:
Mesh_data_end:= begining_mesh_section+(amount_mesh_data*2) //mesh_data end will hold where finished the mesh section.
//Now read a mesh record in the fallow way:
// Mesh record
==========
* sphere_X : (2 bytes)
* sphere_Y : (2 bytes)
* sphere_Z : (2 bytes)
* spehere_radius: (4 bytes)
//These seem to be cordinates and the radius data for draw a invicible
sphere used for colisional testing in the game. Only mesh pieces that are
used for Movables will use these data, mesh used for ornaments (statics)
will have "0" values.
// the x, y, z, cordinates realy point to the center of the mesh, but
for me is not clear how the sphere_radius is interpreted, but i know that
if you put "0" to the radius then this piece will be unsolid.
//next come the table vertices, this is a optomized table where is store all points (vertices) used for the polygons (faces)
* Amount_vertices: (2 bytes)
//amount vertex records to follow.
* Vertices
: (amount_vertices*6 bytes ) //all vertices records.
//Each record is 6 bytes long and it have to be readed as:
- X (2 bytes) //x cordinate
- Y (2 bytes) //y cordinate
- Z (2 bytes) //z cordinate
//These values have to be interpreted as signed 16 bits from -32768..32767.
// These values are 1000/1 scaled, so the values will be so big for
normals Directx, Opengl or CAD renders, so it's good
idea to make smaller these values when you are going to use
them. Just divide each value for 1000 or for 100
//Next come the Normal/Lights vertices tables, this table hold values that tell to the engine how the faces will be affected for externals lights. There are two ways for calc how the light is reflected in the faces: Using Normals values or using Lighting values. The meshes used for movables are using the "normal" method and the statics mesh are using the "lighting" method.
* Amount_records:
(2 bytes)
//amount records to fallow, this field have to
be interpreted as signed 16 bits, if this is a positive value then next
come records with "Normals" values, if negative then next come records
with "lighting" values.
* Normals or Lighths values:
//If amount_records is positive then this is
amount_records*6
bytes sizes, if negative then it is abs(amount_records)*2
bytes size.
Normals records have to be interpreted as:
- normal_x : (2 bytes)
- normal_y: (2 Bytes)
- normal_z: (2 Bytes)
For Lighting records:
- Light : (2 Bytes)
// Currently i don;t have idea how to re-calc the "lighting" method used for the statics mesh.
//Next come each face definition that will draw this mesh.
* Amount_polys
: (2 bytes)
//how much polys records come next for draw the
mesh, there are two kind polys used, Reactangles and Triangles.
//Each poly record have to be readed in the follow way:
* Type_poly:
(2 bytes) // If this is value "9" then the this is a rectangular face else
it's triangular face.
* p1
(2 bytes)
* p2
(2 bytes)
* p3
(2 bytes)
* p4
(2 bytes) // Read this field only if type_poly = 9.
//Those are the points corners for the face,
they are indexes to the Vertice table, this is a smart way to define the
faces because duplicated vertex are stored just once in the vertice table,
then the engine only have to do the calcs over the table vertice for rotate
or move (anim) the meshes.
* Texture : (2
bytes) //texture index and texture flag to apply in this face.
* Attrib : (2
bytes) //atribute for the face, 0= normal, 1=semi-transparent, maybe 2=transparent
but i am not sure.
................................................................................................................................................................................
The Texture field is a tricky one, and
have to be interpreted well for apply corectly the texture map over the
faces.
you have to interpret this as un 16 bits ( from
0..15 bits) and interpret the bits in the follow way:
When used in rectangular faces:
Bits
------
- 0..11 = Texture index, (pointing
to the texture table), interpreted as signed value, if negative then the
texture is "X" fliped.
- 12..15 = These bits will be the same
as the Bit 11.
When used in Triangular faces:
Bits
-----
- 0..10 = Texture Index
(pointing to the texture table) always a positive value.
- 11
= Unused, always 0?
- 12..14 = Triangle type, there are
4 different posibles triangles values: 0, 2, 4, 6.
- 15
= Flip flag, 0=normal, 1 = X fliped.
.........................................................................................................................................................................
// Then it come the next poly record, it;s is necesary to count how much rectangles are you founding when you are reading the total polys records and store this count in a variable called total_rectangles.
When all polys for this mesh are done you sometimes will need to read a padded 2 bytes, before start reading the next mesh record, if the total rectangles used for draw this mesh is a odd value ( (total_rectangles mod 0)<>0 ) then you will found the 2 padded bytes else just start reading the next mesh record.
This 2 padded bytes has not any usefull meaning, but if you are going to build WADs files please include this rule.
* Padd_bytes: (2 bytes), read this field only if total rectangles is a odd number.
//After reading a complete mesh record you have to compare the current file position with the mesh_data_end variable if the current file position is equal or higher then there is no more mesh records to read, otherwhise read the next mesh record.
Also keep count how much Meshes records are you reading because is the only way to know how much mesh are availibles at the end in the WAD.
//After the Mesh section it come more sections that i am not going to enter in detail yet because i have not working with them directly, but a more detailid explanation about these section can be found at the roseta stone document.
//anims
* Num_anims: (4 bytes)
* Anim_Data : (num_anims*
40 bytes size)
// The anims structure define what animations each movable has,
for example wallking, running, attacking, dying etc.
It's hardcoded in the engine how much animation a especific movable
has, each movable is marked with a Object ID, the engine espect a fixed
amount of animation based in tghe object ID, also each # animation already
have
a especific meaning for the engine for each Object ID.
for example, if a object ID is hardcoded to expect this animation order:
Anim # Meaning
----------------------------
- 1 Stay
- 2 Start walking
- 3 walking
- 4 stop walking
- 5 start runing
- 6 Running
- 7 Attack.
- 8 Die
When you import a movable from another TR game version over thise slot, then new movable could have more animations and in a different order, for this slot the engine will use ONLY the first 8 anims no matter how much anims you imported over this slot, also the engine will still using the anim# 2 for start walking, #3 for walking... etc. so you have to fix correctly the anims in your new movable in the same order that the slot that you are using. There is not way to tell to the engine to use more #anims in the slot.
//States
* Num_states: (4 bytes)
* states_data : (num_states*
6 bytes size)
//anim dispatch
* Num_dispatch: (4 bytes)
* dispacth_data : (num_dispatch*
8 bytes size)
// These are a auxiliar data that tell to the engine how to change
between anims. for example from change from the stay anims to
the start-walking anim and then to the wallking anim intead to change
from stay to walk directly.
//anim commands
* Num_commands: (4 bytes)
* commands_data : (num_commands*
2 bytes size)
//this is a serie Opcodes (commands) that will be execute for the engine
when a especific #anim is played, like play the running steps sounds when
the #running animation is played.
//meshtree
* Num_meshtreedata:: (4 bytes)
* meshtree_data : (num_meshtreedata*
4 bytes size)
// Here you will found how each mesh piece will be joined togeter for
build a complete movable, in this structure you will found a mesh value
that is a index to the mesh pointer table, then using the mesh pointer
table you found the actual mesh.
//frames
* Num_framesdata:: (4 bytes)
* frames_data : (num_framesdata*
2 bytes size)
//These are a bounch of offsets and angles values that will be aplied
to the meshtree for re-draw at realtime each frame animation.
//movables
* Num_movables:: (4 bytes)
* movables_data : (num_movables*
18 bytes size)
// Here you will found how much animated objects are availibles in
the wad, here you will found the Item ID for each movable.
// Each record in this structure is a complete movable and point to
the meshtree strutcture for tell to the engine how to draw this object.
This method of drawing the movable indirectly using the meshtree structure
is very smart because this way each mesh piece can be re-used in
the drawing of another movable, a Torso mesh for example can be used
in two different movables.
So be warned that editing a mesh can affect more that one object.
Also that is related to the problem of dependency objetcs where somtimes you are obligated to export a object for be able to use sucessful a desired object. The solution will be to draw a separated mesh and fix the meshtreee structure for un-link some dependencies.
//statics mesh
* Num_statics: (4 bytes)
* static_data : (num_statics*
32 bytes size)
//this is the structure how much statics mesh (ornaments) are availibles
in the wad. each record is 32 bytes long and have to be read in the follow
way:
- object_id : (4 bytes)
statics object ID matched in the Items table when the level is built.
- mesh
: (2 bytes) index to the mesh pointer table.
- vx1,vx2,vy1 : ( 2 bytes each) These are cordinates
values interpeted as signed 16 bits and are used for draw a invisible
- vy2,vz1,vz2 : (2 bytes each) bounding box
used for visivility and colisional test when the game is runing.
- cx1,cx2,cy1 : (2 bytes each);
- cy2,cz1,cz2 : (2 bytes each);
- flag
: (2 bytes) // flag, normalmente 1 o 2, desconosco el uso de este
flag.
********************* END OF DOCUMENT *********************************************