TR2IO.C
He never admited but all we suspect that Yury Zivago and the Rosetta
Stone ahutor are the same person, after some weeks that the rosseta stone
document was released this guy came with a program called TRueview, a program
that render the whole level in similar way used in game, however you need
a fast machine with a fast videocard to be able to run the program smootly.
The Whole executable y source code can be found somwhere at the Epp2 web site, i am posting here just the module where the TR file is opened. This is in "C" code but it can be useful to learn about the TR file format and implement your own reader in the language programming that you are using, you will found in this source code a lot a usefull info about the TR game. Enjoy!.
Turbo Pascal.
/*
* tr2io.c By Yuri Zivago.
*
* Tomb Raider II input/output routines for TRueView
*
*** CURRENTLY LACKING:
*** Better error control (specifically,
any malloc() or fread() error
***
just unceremoniously exits the program)
*** Correct handling for unknowns (obviously,
I can't correctly
***
handle what I don't understand)
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <memory.h>
#include <string.h>
#include "config.h"
#ifdef INCLUDE_TR4_SUPPORT
// ZLIB #defines and #include for TR4 compression support
#define _WINDOWS 1
#define ZLIB_DLL 1
#include "zlib.h"
#endif // INCLUDE_TR4_SUPPORT
#include "tr2port.h"
#include "tr2.h"
/*
* definitions, declarations, prototypes, etc.
*/
// prototype of routine to extract the mesh structures from the raw
mesh data
static void ioExtractMeshes(bitu8 *MeshData,
bitu32 NumMeshPointers,
bitu32 *MeshPointers,
TR2_Level *Level);
// prototype of hex dump routine
void HexDump(unsigned char *buffer, int len, int offset, char *prefix,
FILE *fp);
/***
*** Code and Macros to handle Big Endian and Little Endian systems
***
*** Users with big-endian CPUs (e.g. Mac) need to
*** #define BIG_ENDIAN_CPU before this point (see TR2PORT.H).
***/
#ifdef BIG_ENDIAN_CPU // don't include this code if we're on an Intel CPU
/*
* Swap16(bitu16 *value)
*
* Inverts the bytes of a 16-bit value (to handle big-endian CPUs)
*/
static void Swap16(bitu16 *value)
{
union {
bitu16 u16;
bitu8 u8[2];
} swap;
bitu8 ch;
swap.u16 = *value;
ch = swap.u8[0];
swap.u8[0] = swap.u8[1];
swap.u8[1] = ch;
*value = swap.u16;
}
/*
* Swap32(bitu32 *value)
*
* Inverts the bytes of a 32-bit value (to handle big-endian CPUs)
*/
static void Swap32(bitu32 *value)
{
union {
bitu32 u32;
bitu8 u8[4];
} swap;
bitu8 ch;
swap.u32 = *value;
ch = swap.u8[0];
swap.u8[0] = swap.u8[3];
swap.u8[3] = ch;
ch = swap.u8[1];
swap.u8[1] = swap.u8[2];
swap.u8[2] = ch;
*value = swap.u32;
}
/*
* Generic byte swapper (calls Swap16 or Swap32 based on <size>)
*/
static void Swap16_32(bitu32 *value, int size)
{
if (size == sizeof(bitu16))
Swap16((bitu16 *)value);
else // size == sizeof(bitu32)
Swap32(value);
}
#endif // BIG_ENDIAN_CPU
/*
* Macros for handling endian-ness.
* Note that if we're on a LITTLE_ENDIAN_CPU (e.g. Intel), this
expands
* to nothing (thus introducing no overhead).
* Mac users need to #define BIG_ENDIAN_CPU so that all integral
values
* longer than 8 bits are handled correctly.
*/
#ifdef BIG_ENDIAN_CPU
#define CONVERT_ENDIAN(x, size) Swap16_32((bitu32 *)(x), (size))
#else // LITTLE_ENDIAN_CPU (e.g. Intel) (default
- don't do anything)
#define CONVERT_ENDIAN(x, size)
#endif // BIG_ENDIAN_CPU
/*
* TR2alloc()
*
* Wrapper for malloc() - if malloc() returns NULL, we can handle
* it here (all I do right now is exit).
*/
void *TR2alloc(int size)
{
void *ReturnValue;
if (size == 0)
return(NULL);
//
// I've been told that MacOS has problems here, and that doubling
// the allocation size (e.g. ReturnValue = malloc(size * 2)) "fixes"
// the problem (or at least masks it well)...
//
#ifdef MACINTOSHPC
size *= 2;
#endif
if ((ReturnValue = malloc(size)) == NULL) {
fprintf(stderr, "malloc(%d) failed,
exiting...\n", size);
size = 1 / (int)ReturnValue; //
do a divide-by-zero so we can get into the debugger here
exit(1);
}
memset(ReturnValue, 0x00, size); // initialise it to NULL
(better safe than sorry)
return(ReturnValue);
}
/*
* TR2_Version_type TR2getVersion(bitu32 version)
*
* Returns TombRaider_1..TombRaider_3 or TombRaider_VersionUnknown
based on <version>
*/
TR2_Version_type TR2getVersion(bitu32 version)
{
int i;
static struct {
bitu32 LongVersion;
TR2_Version_type Version;
} TR2_Versions[] = {
{ 0x00000020, TombRaider_1
},
{ 0x0000002d, TombRaider_2
},
{ 0xff080038, TombRaider_3
},
{ 0xff180038, TombRaider_3
},
{ 0xfffffff0, TombRaider_4
}, // bogus
{ 0x00345254, TombRaider_4
}, // "TR4\0"
{ 0x00000000, TombRaider_UnknownVersion
}
};
for (i = 0; TR2_Versions[i].LongVersion != 0; ++i) {
if (TR2_Versions[i].LongVersion == version)
break;
}
return(TR2_Versions[i].Version);
}
static bitu8 *TR4_LevelData;
static bitu32 TR4_LevelDataOffset = 0;
static bitu32 TR4_LevelDataSize;
static TR2_Version_type LevelVersion = TombRaider_2;
/*
* TR2fread()
*
* Wrapper for fread() - if fread() fails to return the number
of
* bytes requested, print a message and exit.
*/
static int TR2fread(void *buffer, size_t size, size_t count, FILE *fp)
{
int NumRead;
if (LevelVersion == TombRaider_4) {
NumRead = count;
NumRead *= size;
if ((TR4_LevelDataOffset + NumRead)
<= TR4_LevelDataSize) {
memcpy(buffer, &TR4_LevelData[TR4_LevelDataOffset],
NumRead);
TR4_LevelDataOffset
+= NumRead;
return(count);
}
else {
fprintf(stderr, "(TR4)
fread(buffer, %d, %d, fp) returned %d\n", size, count, NumRead);
exit(2);
}
}
NumRead = fread(buffer, size, count, fp);
if ((size_t)NumRead != count) {
fprintf(stderr, "fread(buffer, %d, %d,
fp) returned %d\n", size, count, NumRead);
exit(2);
}
return(NumRead);
}
/*
* MapTR1ItemToTR2Item()
*
* Does what its name implies - maps TR1 ItemIDs to TR2 ItemIDs
*/
static int MapTR1ItemToTR2Item(int ItemID)
{
int ReturnValue;
static int TR1toTR2map[300] = {
0, //
0:0 Lara
1, //
1:1 Lara pistol animation
3, //
2:3 Lara shotgun animation
4, //
3:4 Lara magnum animation
5, //
4:5 Lara Uzi animation
0, //
5:? Lara / Lara's home appearance / Lara wounded / Lara turned to gold
0, //
6:? Lara's evil twin mutant
36, // 7:36
(15?) Wolf
37, // 8:37
Bear
38, // 9:38?
Bat
0, // 10: Crocodile
(on land)
0, // 11: Crocodile
(in water)
39, // 12:39? Lion
(male)
39, // 13:39? Lion
(female)
39, // 14:39? Panther
45, // 15:45? Gorilla
21, // 16:21? Giant
Rat (on land)
21, // 17:21? Giant
Rat (in water)
214, // 18:214 Tyrannosaur
0, // 19:? Raptor
0, // 20: Winged
mummy (unused) / Winged mutant
0, // 21: Lara's
hips
0, // 22: Lara's
hips
0, // 23: Centaur
mutant
0, // 24: Mummy
0, // 25: ?
0, // 26: ?
0, // 27: Larson
0, // 28:? (can't
die until later) Pierre
0, // 29: Skateboard
0, // 30: Skateboard
kid
0, // 31: Cowboy
0, // 32: "Mr.
T"
0, // 33: Winged
Natla (actually, Natla with a winged mutant)
0, // 34: Giant
mutant
55, // 35:55 Collapsible
floor
0, // 36: Swinging
blade
59, // 37:59 Spikes
60, // 38:60 Boulder
61, // 39:61 Dart
62, // 40:62 Wall-mounted
dartgun
63, // 41:63? Door
(opens upward)
64, // 42:64 Slamming
doors
0, // 43: Sword
of Damocles
0, // 44: Thor's
hammer's handle
0, // 45: Thor's
hammer's block
0, // 46: Hanging
ball? / Some kind of box?
0, // 47: Metal
rod? / Powered mining cart
67, // 48:67 Movable
cubical block (pushable)
68, // 49:68 Movable
cubical block (pushable)
69, // 50:69 Movable
cubical block (pushable)
70, // 51:70 Movable
cubical block (pushable)
0, // 52: Movable
tall block
0, // 53: Pieces
of something?
0, // 54: Sword
of Damocles
103, // 55:103 Above-water
switch
105, // 56:105 Underwater
switch
106, // 57:106 Door
107, // 58:107 Door
108, // 59:108 Door
109, // 60:109 Door
110, // 61:110 Door
111, // 62:111 Door
112, // 63:112 Door
113, // 64:113 Door
0, // 65: Trapdoor
(opens downward)
0, // 66: Trapdoor
(opens downward)
0, // 67: ?
117, // 68:117 Bridge (flat)
118, // 69:118 Bridge (slope
= 1)
119, // 70:119 Bridge (slope
= 2)
120, // 71:120 Passport
(opening up)
121, // 72:121? Compass
0, // 73: ?
0, // 74: Cogs
(animated)
0, // 75: Cogs
(animated)
0, // 76: Cogs
(animated)
0, // 77: Lara
in CS / Scion holder in CS
0, // 78: Larson
in CS / Natla in CS / Scion holder in CS
0, // 79: Larson's
gun in CS / Scion in CS / Natla in CS
0, // 80: Scion
in CS
133, // 81:133 Passport
(closed)
134, // 82:134 N-thingy
(Playstation memory card?)
0, // 83: Save
crystal
135, // 84:135 * Pistols
136, // 85:136 * Shotgun
137, // 86:137 * Magnums
138, // 87:138 * Uzis
0, // 88:? *
Pistol ammo(?)
143, // 89:143 * Shotgun
ammo
144, // 90:144 * Magnum
ammo
145, // 91:145 * Uzi ammo
0, // 92: ?
149, // 93:149 * Small medipack
150, // 94:150 * Large medipack
153, // 95:153 Sunglasses
154, // 96:154 Cassette
player and headphones
155, // 97:155 Direction
keys
0, // 98: ?
157, // 99:157 Pistol
158, // 100:158 Shotgun
159, // 101:159 Magnum
160, // 102:160 Uzi
164, // 103:164 Pistol ammo(?)
165, // 104:165 Shotgun ammo
166, // 105:166 Magnum ammo
167, // 106:167 Uzi ammo
0, // 107: ?
171, // 108:171 Small medipack
172, // 109:172 Large medipack
174, // 110:174 * Key 1
175, // 111:175 * Key 2
176, // 112:176 * Key 3
177, // 113:177 * Key 4
178, // 114:178 Key 1
179, // 115:179 Key 2
180, // 116:180 Key 3
181, // 117:181 Key 4
182, // 118:182 Lock 1 empty
183, // 119:183 Lock 2 empty
184, // 120:184 Lock 3 empty
185, // 121:185 Lock 4 empty
186, // 122:186 Lock 1 full
187, // 123:187 Lock 2 full
188, // 124:188 Lock 3 full
189, // 125:189 Lock 4 full
205, // 126:205 * Key 5
207, // 127:207 Key 5
0, // 128: Lara's
hips
206, // 129:206 * Key 6
193, // 130:193 * Key 7
194, // 131:194 * Key 8
195, // 132:195 * Key 9
208, // 133:208 Key 6
197, // 134:197 Key 7
198, // 135:198 Key 8
199, // 136:199 Key 9
0, // 137:? Lock 6
201, // 138:201 Lock 7
202, // 139:202 Lock 8
203, // 140:203 Lock 9
0, // 141: ?
0, // 142: ?
0, // 143: * Scion
Piece
0, // 144: ?
0, // 145: ?
0, // 146: Complete
Scion
0, // 147: Scion Holder
0, // 148: ?
0, // 149: ?
0, // 150: Scion Piece
229, // 151:229 * Flare(?) / Explosion
0, // 152: ?
230, // 153:230 * Splash
0, // 154: ?
231, // 155:231 * Bubbles
231, // 156:231? * Bubbles
0, // 157: ?
233, // 158:233 * Blood splatter
0, // 159: ?
234, // 160:234? * Flying disk
0, // 161: Centaur
statue
0, // 162: Shack suspended
from wire rope
0, // 163: Mutant
egg and holder (normal size)
238, // 164:238 * Bullet hit
239, // 165:239 * Sparkle
240, // 166:240 Gunflare
0, // 167: ?
0, // 168: ?
0, // 169: Lara's
hips
0, // 170: Lara's
hips
0, // 171: ?
0, // 172: Mutant
bullet
0, // 173: Mutant
grenade
0, // 174: ?
0, // 175: ?
0, // 176: * Splatter
0, // 177: Lara's
hips
252, // 178:252 * Fire
0, // 179: Lara's
hips
0, // 180: Flowing
Atlantean lava
0, // 181: Mutant
egg and holder (big)
14, // 182:14? Motorboat
0, // 183: Lara's
hips
0, // 184: ?
0, // 185: ?
0, // 186: ?
0, // 187: ?
0, // 188: ?
0, // 189: Shrinking
wedge?
255, // 190:255 * Standard symbols
0, // 191: * Plant
1
0, // 192: * Plant
2
0, // 193: * Plant
3
0, // 194: * Plant
4
0, // 195: * Plant
5
0, // 196: ?
0, // 197: ?
0, // 198: ?
0, // 199: ?
0, // 200: * Bag 1
0, // 201: ?
0, // 202: ?
0, // 203: ?
0, // 204: * Bag 2
0, // 205: ?
0, // 206: ?
241, // 207:241 Gunflare
0, // 208: ?
0, // 209: ?
0, // 210: ?
0, // 211: ?
0, // 212: * Rock
1
0, // 213: * Rock
2
0, // 214: * Rock
3
0, // 215: * Bag 3
0, // 216: * Pottery
1
0, // 217: * Pottery
2
0, // 218: ?
0, // 219: ?
0, // 220: ?
0, // 221: ?
0, // 222: ?
0, // 223: ?
0, // 224: ?
0, // 225: ?
0, // 226: ?
0, // 227: ?
0, // 228: ?
0, // 229: ?
0, // 230: ?
0, // 231: * Painted
pot
0, // 232: ?
0, // 233: * Inca
mummy
0, // 234: ?
0, // 235: ?
0, // 236: * Pottery
3
0, // 237: * Pottery
4
0, // 238: * Pottery
5
0, // 239: * Pottery
6
};
ReturnValue = TR1toTR2map[ItemID];
return((ReturnValue == 0 && ItemID != 0) ? ItemID
: ReturnValue);
}
/*
* The level reader. Takes a filename (presumably SOMETHING.TR2),
* reads and parses the file into dynamically allocated structures.
* Returns a pointer to the whole parsed level.
*/
TR2_Level *ReadTR2level(char *FileName)
{
TR2_Level *Level;
FILE *fp;
int i,
j;
#ifdef BIG_ENDIAN_CPU
int k; // index variable for endian conversion
#endif // BIG_ENDIAN_CPU
bitu32 NumMeshDataWords,
NumMeshPointers,
*MeshPointerList,
DataOffset,
DataSize;
bitu8 *RawMeshData;
/* try to open the file */
if ((fp = fopen(FileName, READBINARY)) == NULL) {
perror(FileName);
return(NULL);
}
/* allocate the base structure */
Level = (TR2_Level *)TR2alloc(sizeof(TR2_Level));
/* initialise the filename field */
/* (unfortunately, strdup() isn't universally available...)
*/
Level->FileName = (char *)TR2alloc(strlen(FileName) +
1);
strcpy(Level->FileName, FileName);
/* read the version */
TR2fread(&Level->Version, sizeof(Level->Version),
1, fp);
CONVERT_ENDIAN(&Level->Version, sizeof(Level->Version));
Level->EngineVersion = TR2getVersion(Level->Version);
// TombRaider_1, TombRaider_2, TombRaider_3
LevelVersion = Level->EngineVersion;
if (Level->EngineVersion == TombRaider_4) {
#ifdef INCLUDE_TR4_SUPPORT
bitu16 tmp16;
bitu32 CompressedSize,
UncompressedSize;
bitu8 *CompressedData;
int zerr;
// Allow TR2fread() to really read the
file
LevelVersion = TombRaider_2;
// read three unknown bitu16s
TR2fread(&tmp16, sizeof(tmp16),
1, fp);
TR2fread(&tmp16, sizeof(tmp16),
1, fp);
TR2fread(&tmp16, sizeof(tmp16),
1, fp);
// read the sizes of the 32-bit textures
TR2fread(&UncompressedSize, sizeof(UncompressedSize),
1, fp);
TR2fread(&CompressedSize, sizeof(CompressedSize),
1, fp);
// skip the 32-bit textures
fseek(fp, CompressedSize, SEEK_CUR);
// read in the 16-bit textures, set NumTextiles
TR2fread(&UncompressedSize, sizeof(UncompressedSize),
1, fp);
TR2fread(&CompressedSize, sizeof(CompressedSize),
1, fp);
Level->NumTextiles = UncompressedSize
/ sizeof(tr2_textile16);
Level->Textile16 = (struct tr2_textile16_struct
*)TR2alloc(sizeof(tr2_textile16) * Level->NumTextiles);
// allocate a temporary buffer for decompression
CompressedData = (bitu8 *)TR2alloc(CompressedSize);
TR2fread(CompressedData, CompressedSize,
1, fp);
// decompress the textures
zerr = uncompress((bitu8 *)Level->Textile16,
&UncompressedSize, CompressedData, CompressedSize);
// free the temporary buffer
free((char *)CompressedData);
// read the sizes of the remaining textures
TR2fread(&UncompressedSize, sizeof(UncompressedSize),
1, fp);
TR2fread(&CompressedSize, sizeof(CompressedSize),
1, fp);
// skip the remaining textures
fseek(fp, CompressedSize, SEEK_CUR);
// read the sizes of the level data
TR2fread(&UncompressedSize, sizeof(UncompressedSize),
1, fp);
TR2fread(&CompressedSize, sizeof(CompressedSize),
1, fp);
// allocate a temporary buffer for decompression
CompressedData = (bitu8 *)TR2alloc(CompressedSize);
TR2fread(CompressedData, CompressedSize,
1, fp);
TR4_LevelData = (bitu8 *)TR2alloc(UncompressedSize);
// decompress the level data
zerr = uncompress(TR4_LevelData, &UncompressedSize,
CompressedData, CompressedSize);
TR4_LevelDataOffset = 0;
TR4_LevelDataSize = UncompressedSize;
// Force TR2fread() to read the decompressed
data
LevelVersion = TombRaider_4;
#else // !INCLUDE_TR4_SUPPORT
fprintf(stderr, "Sorry, TR4 support
was not compiled into this viewer.\n");
return(NULL);
#endif // INCLUDE_TR4_SUPPORT
}
if (Level->EngineVersion == TombRaider_2 || Level->EngineVersion
== TombRaider_3) {
/* read the 8-bit palette */
#ifdef MACINTOSHPC
bitu8 MacColour3[3];
for (i = 0; i < 256; ++i) {
TR2fread(MacColour3,
3, 1, fp);
Level->Palette8[i].Red
= MacColour3[0];
Level->Palette8[i].Green
= MacColour3[1];
Level->Palette8[i].Blue
= MacColour3[2];
}
#else
TR2fread(Level->Palette8, sizeof(tr2_colour),
256, fp);
#endif
/* read 16-bit palette */
TR2fread(Level->Palette16, sizeof(Level->Palette16),
1, fp);
}
if (Level->EngineVersion != TombRaider_4) {
/* read the textiles */
TR2fread(&Level->NumTextiles, sizeof(Level->NumTextiles),
1, fp);
CONVERT_ENDIAN(&Level->NumTextiles,
sizeof(Level->NumTextiles));
/* 8-bit textiles come first */
Level->Textile8 = (struct tr2_textile8_struct
*)TR2alloc(sizeof(tr2_textile8) * Level->NumTextiles);
TR2fread(Level->Textile8, sizeof(tr2_textile8),
Level->NumTextiles, fp);
/* 16-bit textiles come second */
Level->Textile16 = (struct tr2_textile16_struct
*)TR2alloc(sizeof(tr2_textile16) * Level->NumTextiles);
if (Level->EngineVersion != TombRaider_1)
{
TR2fread(Level->Textile16,
sizeof(tr2_textile16), Level->NumTextiles, fp);
#ifdef BIG_ENDIAN_CPU
/* convert 16-bit
textiles to big-endian format, if appropriate */
for (i = 0; i <
(int)Level->NumTextiles; ++i) {
for (j = 0; j < (256 * 256); ++j) {
CONVERT_ENDIAN(&Level->Textile16[i].Tile[j], sizeof(Level->Textile16[0].Tile[0]));
}
}
#endif
}
}
/* 32-bit unknown - seems to always be 0 */
TR2fread(&Level->UnknownT, sizeof(Level->UnknownT),
1, fp);
CONVERT_ENDIAN(&Level->UnknownT, sizeof(Level->UnknownT));
/* read raw room data */
TR2fread(&Level->NumRooms, sizeof(Level->NumRooms),
1, fp);
CONVERT_ENDIAN(&Level->NumRooms, sizeof(Level->NumRooms));
DataSize = Level->NumRooms * sizeof(tr2_room);
Level->Rooms = (struct tr2_room_struct *)TR2alloc(DataSize);
/* extract room details */
for (i = 0; i < Level->NumRooms; ++i) {
/* read RoomInfo */
TR2fread(&Level->Rooms[i].info,
sizeof(tr2_room_info), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].info.x,
sizeof(Level->Rooms[0].info.x));
CONVERT_ENDIAN(&Level->Rooms[i].info.z,
sizeof(Level->Rooms[0].info.z));
CONVERT_ENDIAN(&Level->Rooms[i].info.yBottom,
sizeof(Level->Rooms[0].info.yBottom));
CONVERT_ENDIAN(&Level->Rooms[i].info.yTop,
sizeof(Level->Rooms[0].info.yTop));
/* read raw data for rest of room */
TR2fread(&Level->Rooms[i].NumDataWords,
sizeof(Level->Rooms[0].NumDataWords), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumDataWords,
sizeof(Level->Rooms[0].NumDataWords));
Level->Rooms[i].Data = (bitu8 *)TR2alloc(sizeof(bitu16)
* Level->Rooms[i].NumDataWords);
TR2fread(Level->Rooms[i].Data, sizeof(bitu16),
Level->Rooms[i].NumDataWords, fp);
/* identify vertices */
DataOffset = 0;
Level->Rooms[i].RoomData.NumVertices
= *(bit16 *)(Level->Rooms[i].Data);
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.NumVertices,
sizeof(Level->Rooms[0].RoomData.NumVertices));
DataOffset += sizeof(Level->Rooms[0].RoomData.NumVertices);
DataSize = Level->Rooms[i].RoomData.NumVertices
* sizeof(tr2_vertex_room);
if (Level->Rooms[i].RoomData.NumVertices
> 0) {
Level->Rooms[i].RoomData.Vertices
= (tr2_vertex_room *)TR2alloc(DataSize);
if (Level->EngineVersion
== TombRaider_1) {
DataSize = Level->Rooms[i].RoomData.NumVertices * (sizeof(tr2_vertex_room)
- 4);
for (j = 0; j < Level->Rooms[i].RoomData.NumVertices; ++j) {
memcpy(&Level->Rooms[i].RoomData.Vertices[j], Level->Rooms[i].Data
+ DataOffset + (j * (sizeof(tr2_vertex_room) - 4)), sizeof(tr2_vertex_room)
- 4);
// ??? Adjust for what's missing?
Level->Rooms[i].RoomData.Vertices[j].Lighting2 = Level->Rooms[i].RoomData.Vertices[j].Lighting1;
Level->Rooms[i].RoomData.Vertices[j].Attributes = 0;
}
}
else {
memcpy(Level->Rooms[i].RoomData.Vertices, Level->Rooms[i].Data + DataOffset,
DataSize);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].RoomData.NumVertices; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Vertex.x, sizeof(Level->Rooms[0].RoomData.Vertices[0].Vertex.x));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Vertex.y, sizeof(Level->Rooms[0].RoomData.Vertices[0].Vertex.y));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Vertex.z, sizeof(Level->Rooms[0].RoomData.Vertices[0].Vertex.z));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Lighting1, sizeof(Level->Rooms[0].RoomData.Vertices[0].Lighting1));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Attributes, sizeof(Level->Rooms[0].RoomData.Vertices[0].Attributes));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Vertices[j].Lighting2, sizeof(Level->Rooms[0].RoomData.Vertices[0].Lighting2));
}
#endif // BIG_ENDIAN_CPU
}
DataOffset += DataSize;
/* identify rectangles */
Level->Rooms[i].RoomData.NumRectangles
= *(bit16 *)(Level->Rooms[i].Data + DataOffset);
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.NumRectangles,
sizeof(Level->Rooms[0].RoomData.NumRectangles));
DataOffset += sizeof(Level->Rooms[0].RoomData.NumRectangles);
DataSize = Level->Rooms[i].RoomData.NumRectangles
* sizeof(tr2_face4);
if (Level->Rooms[i].RoomData.NumRectangles
> 0) {
Level->Rooms[i].RoomData.Rectangles
= (tr2_face4 *)TR2alloc(DataSize);
memcpy(Level->Rooms[i].RoomData.Rectangles,
Level->Rooms[i].Data + DataOffset, DataSize);
if (Level->EngineVersion
>= TombRaider_3) {
int j;
for (j = 0; j < Level->Rooms[i].RoomData.NumRectangles; ++j) {
Level->Rooms[i].RoomData.Rectangles[j].Texture &= 0x7fff;
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].RoomData.NumRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Rectangles[j].Vertices[k],
sizeof(Level->Rooms[0].RoomData.Rectangles[0].Vertices[0]));
}
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Rectangles[j].Texture, sizeof(Level->Rooms[0].RoomData.Rectangles[0].Texture));
}
#endif // BIG_ENDIAN_CPU
}
DataOffset += DataSize;
/* identify triangles */
Level->Rooms[i].RoomData.NumTriangles
= *(bit16 *)(Level->Rooms[i].Data + DataOffset);
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.NumTriangles,
sizeof(Level->Rooms[0].RoomData.NumTriangles));
DataOffset += sizeof(Level->Rooms[0].RoomData.NumTriangles);
DataSize = Level->Rooms[i].RoomData.NumTriangles
* sizeof(tr2_face3);
if (Level->Rooms[i].RoomData.NumTriangles
> 0) {
Level->Rooms[i].RoomData.Triangles
= (tr2_face3 *)TR2alloc(DataSize);
memcpy(Level->Rooms[i].RoomData.Triangles,
Level->Rooms[i].Data + DataOffset, DataSize);
if (Level->EngineVersion
>= TombRaider_3) {
int j;
for (j = 0; j < Level->Rooms[i].RoomData.NumTriangles; ++j) {
Level->Rooms[i].RoomData.Triangles[j].Texture &= 0x7fff;
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].RoomData.NumTriangles; ++j) {
for (k = 0; k < 3; ++k) {
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Triangles[j].Vertices[k],
sizeof(Level->Rooms[0].RoomData.Triangles[0].Vertices[0]));
}
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Triangles[j].Texture, sizeof(Level->Rooms[0].RoomData.Triangles[0].Texture));
}
#endif // BIG_ENDIAN_CPU
}
DataOffset += DataSize;
/* identify sprites */
Level->Rooms[i].RoomData.NumSprites
= *(bit16 *)(Level->Rooms[i].Data + DataOffset);
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.NumSprites,
sizeof(Level->Rooms[0].RoomData.NumSprites));
DataOffset += sizeof(Level->Rooms[0].RoomData.NumSprites);
DataSize = Level->Rooms[i].RoomData.NumSprites
* sizeof(tr2_room_sprite);
if (Level->Rooms[i].RoomData.NumSprites
> 0) {
Level->Rooms[i].RoomData.Sprites
= (tr2_room_sprite *)TR2alloc(DataSize);
memcpy(Level->Rooms[i].RoomData.Sprites,
Level->Rooms[i].Data + DataOffset, DataSize);
if (Level->EngineVersion
>= TombRaider_3) {
int j;
for (j = 0; j < Level->Rooms[i].RoomData.NumSprites; ++j) {
Level->Rooms[i].RoomData.Sprites[j].Texture &= 0x7fff;
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].RoomData.NumSprites; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Sprites[j].Vertex, sizeof(Level->Rooms[0].RoomData.Sprites[0].Vertex));
CONVERT_ENDIAN(&Level->Rooms[i].RoomData.Sprites[j].Texture, sizeof(Level->Rooms[0].RoomData.Sprites[0].Texture));
}
#endif // BIG_ENDIAN_CPU
}
/* free the raw room data */
free(Level->Rooms[i].Data);
Level->Rooms[i].Data = NULL; //
mark it so we don't accidentally free it again later
/* read door info */
TR2fread(&Level->Rooms[i].NumPortals,
sizeof(Level->Rooms[0].NumPortals), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumPortals,
sizeof(Level->Rooms[0].NumPortals));
Level->Rooms[i].Portals = (struct tr2_room_portal_struct
*)TR2alloc(sizeof(tr2_room_portal) * Level->Rooms[i].NumPortals);
TR2fread(Level->Rooms[i].Portals, sizeof(tr2_room_portal),
Level->Rooms[i].NumPortals, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumPortals; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].AdjoiningRoom, sizeof(Level->Rooms[0].Portals[0].AdjoiningRoom));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.x, sizeof(Level->Rooms[0].Portals[0].Normal.x));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.y, sizeof(Level->Rooms[0].Portals[0].Normal.y));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.z, sizeof(Level->Rooms[0].Portals[0].Normal.z));
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].x, sizeof(Level->Rooms[0].Portals[0].Vertices[0].x));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].y, sizeof(Level->Rooms[0].Portals[0].Vertices[0].y));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].z, sizeof(Level->Rooms[0].Portals[0].Vertices[0].z));
}
}
#endif // BIG_ENDIAN_CPU
/* read sector info */
TR2fread(&Level->Rooms[i].NumZsectors,
sizeof(Level->Rooms[0].NumZsectors), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumZsectors,
sizeof(Level->Rooms[0].NumZsectors));
TR2fread(&Level->Rooms[i].NumXsectors,
sizeof(Level->Rooms[0].NumXsectors), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumXsectors,
sizeof(Level->Rooms[0].NumXsectors));
Level->Rooms[i].SectorList = (struct
tr2_room_sector_struct *)TR2alloc(sizeof(tr2_room_sector) * Level->Rooms[i].NumZsectors
* Level->Rooms[i].NumXsectors);
TR2fread(Level->Rooms[i].SectorList,
sizeof(tr2_room_sector), Level->Rooms[i].NumZsectors * Level->Rooms[i].NumXsectors,
fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (Level->Rooms[i].NumZsectors
* Level->Rooms[i].NumXsectors); ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].FDindex,
sizeof(Level->Rooms[0].SectorList[0].FDindex));
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].BoxIndex,
sizeof(Level->Rooms[0].SectorList[0].BoxIndex));
}
#endif // BIG_ENDIAN_CPU
/* read room lighting & mode */
if (Level->EngineVersion >= TombRaider_3)
{
TR2fread(&Level->Rooms[i].Intensity1,
4, 1, fp);
// Fake TR2 record:
Level->Rooms[i].LightMode
= 0;
}
else if (Level->EngineVersion == TombRaider_1)
{
TR2fread(&Level->Rooms[i].Intensity1,
2, 1, fp); // Is this intensity or LightMode?
Level->Rooms[i].Intensity2
= Level->Rooms[i].Intensity1;
Level->Rooms[i].LightMode
= 0;
}
else { // TR2
TR2fread(&Level->Rooms[i].Intensity1,
6, 1, fp);
}
/* read room lighting info */
TR2fread(&Level->Rooms[i].NumLights,
sizeof(Level->Rooms[0].NumLights), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumLights,
sizeof(Level->Rooms[0].NumLights));
if (Level->Rooms[i].NumLights > 0) {
// Level->Rooms[i].Lights
= (struct tr2_room_light_struct *)TR2alloc(sizeof(tr2_room_light) * Level->Rooms[i].NumLights);
Level->Rooms[i].Lights = (struct tr2_room_light_struct *)TR2alloc(48
* Level->Rooms[i].NumLights);
if (Level->EngineVersion
== TombRaider_1) {
for (j = 0; j < Level->Rooms[i].NumLights; ++j) {
TR2fread(&Level->Rooms[i].Lights[j].x, sizeof(Level->Rooms[0].Lights[0].x),
3, fp); // x, y, z
TR2fread(&Level->Rooms[i].Lights[j].Intensity1, sizeof(bitu16), 1,
fp); // Intensity1
Level->Rooms[i].Lights[j].Intensity2 = Level->Rooms[i].Lights[j].Intensity1;
TR2fread(&Level->Rooms[i].Lights[j].Fade1, sizeof(bitu32), 1, fp);
// Fade1
Level->Rooms[i].Lights[j].Fade2 = Level->Rooms[i].Lights[j].Fade1;
}
}
else if (Level->EngineVersion
== TombRaider_4) {
TR2fread(Level->Rooms[i].Lights, 46, Level->Rooms[i].NumLights, fp);
}
else {
TR2fread(Level->Rooms[i].Lights, sizeof(tr2_room_light), Level->Rooms[i].NumLights,
fp);
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].NumLights;
++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].x,
sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].y,
sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].z,
sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity1,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity2,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade1,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade2,
sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
/* read Static Mesh Data */
TR2fread(&Level->Rooms[i].NumStaticMeshes,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumStaticMeshes,
sizeof(bitu16));
if (Level->Rooms[i].NumStaticMeshes
> 0) {
Level->Rooms[i].StaticMeshes
= (tr2_room_staticmesh *)TR2alloc(Level->Rooms[i].NumStaticMeshes * sizeof(tr2_room_staticmesh));
if (Level->EngineVersion
== TombRaider_1) {
for (j = 0; j < Level->Rooms[i].NumStaticMeshes; ++j) {
TR2fread(&Level->Rooms[i].StaticMeshes[j], 18, 1, fp); // account for
the missing .Intensity2
Level->Rooms[i].StaticMeshes[j].ObjectID = Level->Rooms[i].StaticMeshes[j].Intensity2;
Level->Rooms[i].StaticMeshes[j].Intensity2 = Level->Rooms[i].StaticMeshes[j].Intensity1;
}
}
else {
TR2fread(Level->Rooms[i].StaticMeshes, sizeof(tr2_room_staticmesh), Level->Rooms[i].NumStaticMeshes,
fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumStaticMeshes; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].x, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].y, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].z, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].ObjectID, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Rotation, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity1, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity2, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
TR2fread(&Level->Rooms[i].AlternateRoom,
sizeof(bit16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].AlternateRoom,
sizeof(bit16));
TR2fread(&Level->Rooms[i].Flags,
sizeof(bit16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].Flags,
sizeof(bit16));
/* read TR3 room light colour */
if (Level->EngineVersion >= TombRaider_3)
{
// we force this to
be 3 bytes (instead of just sizeof(RoomLightColour)) for Macs and others
that can't handle odd-length structures...
TR2fread(&Level->Rooms[i].RoomLightColour,
3 /* sizeof(Level->Rooms[i].RoomLightColour) */, 1, fp);
}
}
/* read floor data */
/*
* Really, FloorData should be a per-sector dynamic
allocation; however,
* that requires a parser that can accurately determine
where one sector's
* FloorData ends and another's begins. Until
we have that, we'll stick to
* this crude (but effective) method...
*/
TR2fread(&Level->NumFloorData, sizeof(Level->NumFloorData),
1, fp);
CONVERT_ENDIAN(&Level->NumFloorData, sizeof(Level->NumFloorData));
if (Level->NumFloorData > 0) {
Level->FloorData = (bitu16 *)TR2alloc(sizeof(bitu16)
* Level->NumFloorData);
TR2fread(Level->FloorData, sizeof(bitu16),
Level->NumFloorData, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumFloorData;
++j) {
CONVERT_ENDIAN(&Level->FloorData[j],
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* read mesh data */
TR2fread(&NumMeshDataWords, sizeof(NumMeshDataWords),
1, fp);
CONVERT_ENDIAN(&NumMeshDataWords, sizeof(NumMeshDataWords));
RawMeshData = (bitu8 *)TR2alloc(sizeof(bitu16) * NumMeshDataWords);
TR2fread(RawMeshData, sizeof(bitu16), NumMeshDataWords,
fp);
// Endian-conversion of this data occurs in ioExtractMeshes()
/* read mesh pointers */
TR2fread(&NumMeshPointers, sizeof(NumMeshPointers),
1, fp);
CONVERT_ENDIAN(&NumMeshPointers, sizeof(NumMeshPointers));
MeshPointerList = (bitu32 *)TR2alloc(sizeof(bitu32) *
NumMeshPointers);
TR2fread(MeshPointerList, sizeof(bitu32), NumMeshPointers,
fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)NumMeshPointers; ++j) {
CONVERT_ENDIAN(&MeshPointerList[j],
sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
/* extract meshes */
ioExtractMeshes(RawMeshData, NumMeshPointers, MeshPointerList,
Level);
free(RawMeshData);
free(MeshPointerList);
/* read animations */
TR2fread(&Level->NumAnimations, sizeof(Level->NumAnimations),
1, fp);
CONVERT_ENDIAN(&Level->NumAnimations, sizeof(Level->NumAnimations));
if (Level->NumAnimations > 0) {
// Level->Animations = (struct tr2_animation_struct
*)TR2alloc(sizeof(tr2_animation) * Level->NumAnimations);
Level->Animations = (struct tr2_animation_struct *)TR2alloc(40 * Level->NumAnimations);
if (Level->EngineVersion == TombRaider_4)
{
for (i = 0; i <
(int)Level->NumAnimations; ++i) {
bitu8 junk[16];
TR2fread(&Level->Animations[i].FrameOffset, 16, 1, fp);
TR2fread(junk, 8, 1, fp);
TR2fread(&Level->Animations[i].FrameStart, 16, 1, fp);
}
}
else {
TR2fread(Level->Animations,
sizeof(tr2_animation), Level->NumAnimations, fp);
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumAnimations; ++j) {
CONVERT_ENDIAN(&Level->Animations[j].FrameOffset,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Animations[j].FrameStart,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].FrameEnd,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateID,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextAnimation,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextFrame,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumStateChanges,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateChangeOffset,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumAnimCommands,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].AnimCommand,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read state changes */
TR2fread(&Level->NumStateChanges, sizeof(Level->NumStateChanges),
1, fp);
CONVERT_ENDIAN(&Level->NumStateChanges, sizeof(Level->NumStateChanges));
if (Level->NumStateChanges > 0) {
Level->StateChanges = (struct tr2_state_change_struct
*)TR2alloc(sizeof(tr2_state_change) * Level->NumStateChanges);
TR2fread(Level->StateChanges, sizeof(tr2_state_change),
Level->NumStateChanges, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumStateChanges; ++j) {
CONVERT_ENDIAN(&Level->StateChanges[j].StateID,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].NumAnimDispatches,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].AnimDispatch,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read AnimDispatches */
TR2fread(&Level->NumAnimDispatches, sizeof(Level->NumAnimDispatches),
1, fp);
CONVERT_ENDIAN(&Level->NumAnimDispatches, sizeof(Level->NumAnimDispatches));
if (Level->NumAnimDispatches > 0) {
Level->AnimDispatches = (struct tr2_anim_dispatch_struct
*)TR2alloc(sizeof(tr2_anim_dispatch) * Level->NumAnimDispatches);
TR2fread(Level->AnimDispatches, sizeof(tr2_anim_dispatch),
Level->NumAnimDispatches, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumAnimDispatches; ++j)
{
CONVERT_ENDIAN(&Level->AnimDispatches[j].Low,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].High,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextAnimation,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextFrame,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read anim commands */
TR2fread(&Level->NumAnimCommands, sizeof(Level->NumAnimCommands),
1, fp);
CONVERT_ENDIAN(&Level->NumAnimCommands, sizeof(Level->NumAnimCommands));
if (Level->NumAnimCommands > 0) {
Level->AnimCommands = (struct tr2_anim_command_struct
*)TR2alloc(sizeof(tr2_anim_command) * Level->NumAnimCommands);
TR2fread(Level->AnimCommands, sizeof(tr2_anim_command),
Level->NumAnimCommands, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumAnimCommands; ++j) {
CONVERT_ENDIAN(&Level->AnimCommands[j].Value,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read MeshTrees */
TR2fread(&Level->NumMeshTrees, sizeof(Level->NumMeshTrees),
1, fp);
CONVERT_ENDIAN(&Level->NumMeshTrees, sizeof(Level->NumMeshTrees));
if (Level->NumMeshTrees > 0) {
Level->MeshTrees = (struct tr2_meshtree_struct
*)TR2alloc(Level->NumMeshTrees * sizeof(bitu32));
TR2fread(Level->MeshTrees, sizeof(bit32),
Level->NumMeshTrees, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)(Level->NumMeshTrees / sizeof(tr2_meshtree));
++j) {
CONVERT_ENDIAN(&Level->MeshTrees[j].Flags,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->MeshTrees[j].x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->MeshTrees[j].y,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->MeshTrees[j].z,
sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
/* read frames */
TR2fread(&Level->NumFrames, sizeof(Level->NumFrames),
1, fp);
CONVERT_ENDIAN(&Level->NumFrames, sizeof(Level->NumFrames));
if (Level->NumFrames > 0) {
Level->Frames = (bitu16 *)TR2alloc(sizeof(bitu16)
* Level->NumFrames);
TR2fread(Level->Frames, sizeof(bitu16),
Level->NumFrames, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumFrames; ++j) {
CONVERT_ENDIAN(&Level->Frames[j],
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
if (Level->EngineVersion == TombRaider_1)
{
// re-format the Frames[]
to look like TR2 frames
int NumFrames;
for (j = 0; j <
(int)Level->NumAnimations; ++j) {
int fo = Level->Animations[j].FrameOffset / 2;
Level->Animations[j].FrameSize = (Level->Frames[fo + 9] * 2) + 10;
}
for (i = 0; i <
(int)Level->NumFrames; ) {
i += 9; // point to NumFrames;
j = i; // get rid of (overwrite) NumFrames
NumFrames = Level->Frames[i++];
while (NumFrames--) {
Level->Frames[j++] = Level->Frames[i + 1]; // reverse the words
as we go
Level->Frames[j++] = Level->Frames[i];
i += 2;
}
}
}
}
/* read moveables */
TR2fread(&Level->NumMoveables, sizeof(Level->NumMoveables),
1, fp);
CONVERT_ENDIAN(&Level->NumMoveables, sizeof(Level->NumMoveables));
if (Level->NumMoveables > 0) {
Level->Moveables = (struct tr2_moveable_struct
*)TR2alloc(sizeof(tr2_moveable) * Level->NumMoveables);
TR2fread(Level->Moveables, sizeof(tr2_moveable),
Level->NumMoveables, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumMoveables; ++j) {
CONVERT_ENDIAN(&Level->Moveables[j].ObjectID,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].NumMeshes,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].StartingMesh,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].MeshTree,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].FrameOffset,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].Animation,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fread(&Level->NumStaticMeshes, sizeof(Level->NumStaticMeshes),
1, fp);
CONVERT_ENDIAN(&Level->NumStaticMeshes, sizeof(Level->NumStaticMeshes));
if (Level->NumStaticMeshes > 0) {
Level->StaticMeshes = (tr2_staticmesh
*)TR2alloc(Level->NumStaticMeshes * sizeof(tr2_staticmesh));
TR2fread(Level->StaticMeshes, sizeof(tr2_staticmesh),
Level->NumStaticMeshes, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumStaticMeshes;
++j) {
int j1, j2;
CONVERT_ENDIAN(&Level->StaticMeshes[j].ObjectID,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->StaticMeshes[j].StartingMesh,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].Flags,
sizeof(bitu16));
for (j1 = 0; j1 <
2; ++j1) {
for (j2 = 0; j2 < 2; ++j2) {
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].z, sizeof(bitu16));
}
}
}
#endif
}
if (Level->EngineVersion < TombRaider_3) {
/* read object textures */
TR2fread(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures), 1, fp);
CONVERT_ENDIAN(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures));
if (Level->NumObjectTextures > 0) {
Level->ObjectTextures
= (struct tr2_object_texture_struct *)TR2alloc(sizeof(tr2_object_texture)
* Level->NumObjectTextures);
TR2fread(Level->ObjectTextures,
sizeof(tr2_object_texture), Level->NumObjectTextures, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumObjectTextures;
++j) {
CONVERT_ENDIAN(&Level->ObjectTextures[j].TransparencyFlags,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->ObjectTextures[j].Tile,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
if (Level->EngineVersion == TombRaider_4) {
bitu8 zzbuf[4];
TR2fread(zzbuf, 1, 3, fp); // skip "SPR"
}
/* read sprite textures */
TR2fread(&Level->NumSpriteTextures, sizeof(Level->NumSpriteTextures),
1, fp);
CONVERT_ENDIAN(&Level->NumSpriteTextures, sizeof(Level->NumSpriteTextures));
if (Level->NumSpriteTextures > 0) {
Level->SpriteTextures = (struct tr2_sprite_texture_struct
*)TR2alloc(sizeof(tr2_sprite_texture) * Level->NumSpriteTextures);
TR2fread(Level->SpriteTextures, sizeof(tr2_sprite_texture),
Level->NumSpriteTextures, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumSpriteTextures; ++j)
{
CONVERT_ENDIAN(&Level->SpriteTextures[j].Tile,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].LeftSide,
sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].TopSide,
sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].RightSide,
sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].BottomSide,
sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
/* read sprite texture data (?) */
TR2fread(&Level->NumSpriteSequences, sizeof(Level->NumSpriteSequences),
1, fp);
CONVERT_ENDIAN(&Level->NumSpriteSequences, sizeof(Level->NumSpriteSequences));
if (Level->NumSpriteSequences > 0) {
Level->SpriteSequences = (struct tr2_sprite_sequence_struct
*)TR2alloc(Level->NumSpriteSequences * sizeof(tr2_sprite_sequence));
TR2fread(Level->SpriteSequences, sizeof(tr2_sprite_sequence),
Level->NumSpriteSequences, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumSpriteSequences; ++j)
{
CONVERT_ENDIAN(&Level->SpriteSequences[j].ObjectID,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->SpriteSequences[j].NegativeLength,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteSequences[j].Offset,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read cameras */
TR2fread(&Level->NumCameras, sizeof(Level->NumCameras),
1, fp);
CONVERT_ENDIAN(&Level->NumCameras, sizeof(Level->NumCameras));
if (Level->NumCameras > 0) {
Level->Cameras = (tr2_camera *)TR2alloc(sizeof(tr2_camera)
* Level->NumCameras);
TR2fread(Level->Cameras, sizeof(tr2_camera),
Level->NumCameras, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumCameras;
++j) {
CONVERT_ENDIAN(&Level->Cameras[j].x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].y,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].z,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].Room,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Cameras[j].Unknown1,
sizeof(bitu16));
}
#endif
}
/* read sound effects (?) */
TR2fread(&Level->NumSoundSources,
sizeof(Level->NumSoundSources), 1, fp);
CONVERT_ENDIAN(&Level->NumSoundSources, sizeof(Level->NumSoundSources));
if (Level->NumSoundSources > 0) {
// Level->SoundSources = (tr2_sound_source
*)TR2alloc(sizeof(tr2_sound_source) * Level->NumSoundSources);
Level->SoundSources = (tr2_sound_source *)TR2alloc(40 * Level->NumSoundSources);
if (Level->EngineVersion == TombRaider_4) {
TR2fread(Level->SoundSources, 40, Level->NumSoundSources,
fp);
}
else {
TR2fread(Level->SoundSources, sizeof(tr2_sound_source),
Level->NumSoundSources, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumSoundSources;
++j) {
CONVERT_ENDIAN(&Level->SoundSources[j].x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].y,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].z,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].SoundID,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundSources[j].Flags,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
if (Level->EngineVersion == TombRaider_4) { ////////////////////////////////////////
bitu32 NumZZ;
bitu8 zzbuf[16];
TR2fread(&NumZZ, 1, sizeof(NumZZ),
fp);
while (NumZZ--) {
TR2fread(zzbuf, 1,
16, fp);
}
}
/* read boxes */
TR2fread(&Level->NumBoxes, sizeof(Level->NumBoxes),
1, fp);
CONVERT_ENDIAN(&Level->NumBoxes, sizeof(Level->NumBoxes));
if (Level->NumBoxes > 0) {
Level->Boxes = (tr2_box *)TR2alloc(sizeof(tr2_box)
* Level->NumBoxes);
if (Level->EngineVersion == TombRaider_1)
{
#ifdef MACINTOSHPC
#pragma options align=mac68k
#endif
#ifdef INTELPC // this is actually a Microsoft Visual C++ thing...
#pragma pack(push,foo,1)
#endif
struct tr1_box {
bit32 Zmin, Zmax, Xmin, Xmax;
bit16 TrueFloor, OverlapIndex;
} *tr1box;
tr1box = (struct tr1_box
*)TR2alloc(sizeof(struct tr1_box) * Level->NumBoxes);
TR2fread(tr1box, sizeof(struct
tr1_box), Level->NumBoxes, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumBoxes;
++j) {
CONVERT_ENDIAN(&tr1box[j].Zmin,
sizeof(bitu32));
CONVERT_ENDIAN(&tr1box[j].Zmax,
sizeof(bitu32));
CONVERT_ENDIAN(&tr1box[j].Xmin,
sizeof(bitu32));
CONVERT_ENDIAN(&tr1box[j].Xmax,
sizeof(bitu32));
}
#endif
for (j = 0; j <
Level->NumBoxes; ++j) {
Level->Boxes[j].Zmin = tr1box[j].Zmin / 1024;
Level->Boxes[j].Zmax = tr1box[j].Zmax / 1024;
Level->Boxes[j].Xmin = tr1box[j].Xmin / 1024;
Level->Boxes[j].Xmax = tr1box[j].Xmax / 1024;
Level->Boxes[j].TrueFloor = tr1box[j].TrueFloor;
Level->Boxes[j].OverlapIndex = tr1box[j].OverlapIndex;
}
free(tr1box);
#ifdef MACINTOSHPC
#pragma options align=reset
#endif
#ifdef INTELPC // this is actually a Microsoft Visual C++ thing...
#pragma pack(pop,foo)
#endif
}
else {
TR2fread(Level->Boxes,
sizeof(tr2_box), Level->NumBoxes, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumBoxes;
++j) {
CONVERT_ENDIAN(&Level->Boxes[j].TrueFloor,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Boxes[j].OverlapIndex,
sizeof(bitu16));
}
#endif
}
/* read overlaps (?) */
TR2fread(&Level->NumOverlaps, sizeof(Level->NumOverlaps),
1, fp);
CONVERT_ENDIAN(&Level->NumOverlaps, sizeof(Level->NumOverlaps));
if (Level->NumOverlaps > 0) {
Level->Overlaps = (bit16 *)TR2alloc(sizeof(bitu16)
* Level->NumOverlaps);
TR2fread(Level->Overlaps, sizeof(bitu16),
Level->NumOverlaps, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumOverlaps;
++j) {
CONVERT_ENDIAN(&Level->Overlaps[j],
sizeof(bitu16));
}
#endif
}
/* read Zones */
if (Level->NumBoxes > 0) {
Level->Zones = (bit16 *)TR2alloc(20
* Level->NumBoxes);
if (Level->EngineVersion == TombRaider_1)
{
TR2fread(Level->Zones,
12, Level->NumBoxes, fp);
}
else {
TR2fread(Level->Zones,
20, Level->NumBoxes, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (Level->NumBoxes
* 20); ++j) {
CONVERT_ENDIAN(&Level->Zones[j],
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* read animation textures (?) */
TR2fread(&Level->NumAnimatedTextures, sizeof(Level->NumAnimatedTextures),
1, fp);
CONVERT_ENDIAN(&Level->NumAnimatedTextures, sizeof(Level->NumAnimatedTextures));
if (Level->NumAnimatedTextures > 0) {
Level->AnimatedTextures = (bit16 *)TR2alloc(sizeof(bit16)
* Level->NumAnimatedTextures);
TR2fread(Level->AnimatedTextures, sizeof(bit16),
Level->NumAnimatedTextures, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumAnimatedTextures;
++j) {
CONVERT_ENDIAN(&Level->AnimatedTextures[j],
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
if (Level->EngineVersion >= TombRaider_3) {
/* read object textures */
if (Level->EngineVersion == TombRaider_4)
{
bitu8 zzbuf[4];
TR2fread(zzbuf, 1,
4, fp); // skip "TEX" //!! this should be 3, but we have a bug...
}
TR2fread(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures), 1, fp);
CONVERT_ENDIAN(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures));
if (Level->NumObjectTextures > 0) {
// Level->ObjectTextures
= (struct tr2_object_texture_struct *)TR2alloc(sizeof(tr2_object_texture)
* Level->NumObjectTextures);
Level->ObjectTextures = (struct tr2_object_texture_struct *)TR2alloc(38
* Level->NumObjectTextures);
if (Level->EngineVersion
== TombRaider_4) {
TR2fread(Level->ObjectTextures, 38, Level->NumObjectTextures, fp);
{
int jjj, kkk;
struct tr4ot {
bitu16 Unknown1;
bitu32 Tile;
struct tr2_object_texture_vert_struct Vertices[4];
bitu8 filler[16];
} *tr4ot_ptr = (struct tr4ot *)Level->ObjectTextures;
for (jjj = 0; jjj < (int)Level->NumObjectTextures; ++jjj) {
Level->ObjectTextures[jjj].TransparencyFlags = tr4ot_ptr[jjj].Unknown1;
Level->ObjectTextures[jjj].Tile = (bitu16)tr4ot_ptr[jjj].Tile & 0x7fff;
for (kkk = 0; kkk < 4; ++kkk) {
Level->ObjectTextures[jjj].Vertices[kkk].Xcoordinate = tr4ot_ptr[jjj].Vertices[kkk].Xcoordinate;
Level->ObjectTextures[jjj].Vertices[kkk].Xpixel = tr4ot_ptr[jjj].Vertices[kkk].Xpixel;
Level->ObjectTextures[jjj].Vertices[kkk].Ycoordinate = tr4ot_ptr[jjj].Vertices[kkk].Ycoordinate;
Level->ObjectTextures[jjj].Vertices[kkk].Ypixel = tr4ot_ptr[jjj].Vertices[kkk].Ypixel;
}
}
}
}
else {
TR2fread(Level->ObjectTextures, sizeof(tr2_object_texture), Level->NumObjectTextures,
fp);
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)Level->NumObjectTextures;
++j) {
CONVERT_ENDIAN(&Level->ObjectTextures[j].TransparencyFlags,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->ObjectTextures[j].Tile,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* read items */
TR2fread(&Level->NumItems, sizeof(Level->NumItems),
1, fp);
CONVERT_ENDIAN(&Level->NumItems, sizeof(Level->NumItems));
if (Level->NumItems > 0) {
Level->Items = (struct tr2_item_struct
*)TR2alloc(sizeof(tr2_item) * Level->NumItems);
if (Level->EngineVersion == TombRaider_1)
{
for (i = 0; i <
Level->NumItems; ++i) {
TR2fread(&Level->Items[i], sizeof(tr2_item) - 2, 1, fp);
Level->Items[i].Flags = Level->Items[i].Intensity2;
Level->Items[i].Intensity2 = Level->Items[i].Intensity1;
}
}
else {
TR2fread(Level->Items,
sizeof(tr2_item), Level->NumItems, fp);
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumItems; ++j) {
CONVERT_ENDIAN(&Level->Items[j].ObjectID,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Room,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].y,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].z,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].Angle,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity1,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity2,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Flags,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* read LightMaps */
Level->LightMap = (bitu8 *)TR2alloc(32 * 256);
if (Level->EngineVersion != TombRaider_4) {
TR2fread(Level->LightMap, 32, 256, fp);
}
if (Level->EngineVersion == TombRaider_1) {
/* read the 8-bit palette */
#ifdef MACINTOSHPC
bitu8 MacColour3[3];
for (i = 0; i < 256; ++i) {
TR2fread(MacColour3,
3, 1, fp);
Level->Palette8[i].Red
= MacColour3[0];
Level->Palette8[i].Green
= MacColour3[1];
Level->Palette8[i].Blue
= MacColour3[2];
}
#else
TR2fread(Level->Palette8, sizeof(tr2_colour),
256, fp);
#endif
// build 16-bit textiles from 8-bit
(no better colours, but creates consistent .TR2 file)
for (i = 0; i < (int)Level->NumTextiles;
++i) {
bitu16 argb;
double colour_tmp;
for (j = 0; j <
(256 * 256); ++j) {
colour_tmp = Level->Palette8[Level->Textile8[i].Tile[j]].Red & 0x3f;
colour_tmp = colour_tmp * 31.0 / 63.0;
argb = ((int)colour_tmp) << 10;
colour_tmp = Level->Palette8[Level->Textile8[i].Tile[j]].Green & 0x3f;
colour_tmp = colour_tmp * 31.0 / 63.0;
argb |= ((int)colour_tmp) << 5;
colour_tmp = Level->Palette8[Level->Textile8[i].Tile[j]].Blue & 0x3f;
colour_tmp = colour_tmp * 31.0 / 63.0;
argb |= ((int)colour_tmp);
argb &= 0x7fff; // ???
if (Level->Textile8[i].Tile[j] != 0)
argb |= 0x8000;
Level->Textile16[i].Tile[j] = argb;
}
}
}
/* read cinematic frames */
if (Level->EngineVersion == TombRaider_4) {
bitu32 NumCinematicFrames; // it's now
bitu32
TR2fread(&NumCinematicFrames, sizeof(NumCinematicFrames),
1, fp);
Level->NumCinematicFrames = (bitu16)NumCinematicFrames;
CONVERT_ENDIAN(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames));
if (Level->NumCinematicFrames > 0) {
Level->CinematicFrames
= (tr2_cinematic_frame *)TR2alloc(24 * Level->NumCinematicFrames);
// now 24 bytes
TR2fread(Level->CinematicFrames,
24, Level->NumCinematicFrames, fp); // now 24 bytes
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
}
else {
TR2fread(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames), 1, fp);
CONVERT_ENDIAN(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames));
if (Level->NumCinematicFrames > 0) {
Level->CinematicFrames
= (tr2_cinematic_frame *)TR2alloc(sizeof(tr2_cinematic_frame) * Level->NumCinematicFrames);
TR2fread(Level->CinematicFrames,
sizeof(tr2_cinematic_frame), Level->NumCinematicFrames, fp);
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
}
/* read demodata (?) */
TR2fread(&Level->NumDemoData, sizeof(Level->NumDemoData),
1, fp);
CONVERT_ENDIAN(&Level->NumDemoData, sizeof(Level->NumDemoData));
if (Level->NumDemoData > 0) {
Level->DemoData = (bitu8 *)TR2alloc(1
* Level->NumDemoData);
TR2fread(Level->DemoData, 1, Level->NumDemoData,
fp);
// There may or may not be endian conversion
required here - I have
// no idea what this data is.
}
/* read SoundMap */
Level->SoundMap = (bit16 *)TR2alloc(370 * sizeof(bit16));
if (Level->EngineVersion == TombRaider_1) {
TR2fread(Level->SoundMap, sizeof(bit16),
256, fp);
memset(Level->SoundMap, 0, 370 * sizeof(bit16));
////////////////////// KLUDGE!!!
}
else {
if (Level->EngineVersion == TombRaider_4)
{
TR2fread(Level->SoundMap,
sizeof(bit16), 370, fp);
}
else {
TR2fread(Level->SoundMap,
sizeof(bit16), 370, fp);
}
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < 370; ++j) {
CONVERT_ENDIAN(&Level->SoundMap[j],
sizeof(bitu16));
}
#endif
/* read SoundDetails */
TR2fread(&Level->NumSoundDetails, sizeof(Level->NumSoundDetails),
1, fp);
CONVERT_ENDIAN(&Level->NumSoundDetails, sizeof(Level->NumSoundDetails));
if (Level->NumSoundDetails > 0) {
Level->SoundDetails = (struct tr2_sound_details_struct
*)TR2alloc(sizeof(tr2_sound_details) * Level->NumSoundDetails);
TR2fread(Level->SoundDetails, sizeof(tr2_sound_details),
Level->NumSoundDetails, fp);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->NumSoundDetails; ++j) {
CONVERT_ENDIAN(&Level->SoundDetails[j].Sample,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Volume,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].SoundRange,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Flags,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
#ifdef NEVER_COMPILE_THIS
if (Level->EngineVersion == TombRaider_1) {
bitu32 AIFFsize;
/* read (skip) sound samples (AIFF)
*/
TR2fread(&AIFFsize, sizeof(AIFFsize),
1, fp);
CONVERT_ENDIAN(&AIFFsize, sizeof(AIFFsize));
fseek(fp, SEEK_CUR, AIFFsize);
}
#endif
if (Level->EngineVersion != TombRaider_1) { // KLUDGE!
/* read sampleindices */
TR2fread(&Level->NumSampleIndices,
sizeof(Level->NumSampleIndices), 1, fp);
CONVERT_ENDIAN(&Level->NumSampleIndices,
sizeof(Level->NumSampleIndices));
if (Level->NumSampleIndices > 0) {
Level->SampleIndices
= (bit32 *)TR2alloc(sizeof(bitu32) * Level->NumSampleIndices);
TR2fread(Level->SampleIndices,
sizeof(bitu32), Level->NumSampleIndices, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSampleIndices; ++j) {
CONVERT_ENDIAN(&Level->SampleIndices[j], sizeof(bitu32));
}
#endif
}
}
if (Level->EngineVersion == TombRaider_4) {
free(TR4_LevelData);
}
#ifdef NEVER_COMPILE_THIS
// convert ItemIDs (for writing a .PHD file as a .TR2
file - doesn't usually work)
if (Level->EngineVersion == TombRaider_1) {
for (i = 0; i < (int)Level->NumItems;
++i) {
Level->Items[i].ObjectID
= MapTR1ItemToTR2Item(Level->Items[i].ObjectID);
}
for (i = 0; i < (int)Level->NumMoveables;
++i) {
Level->Moveables[i].ObjectID
= MapTR1ItemToTR2Item(Level->Moveables[i].ObjectID);
}
for (i = 0; i < (int)Level->NumStaticMeshes;
++i) {
Level->StaticMeshes[i].ObjectID
= MapTR1ItemToTR2Item(Level->StaticMeshes[i].ObjectID);
}
for (i = 0; i < (int)Level->NumSpriteSequences;
++i) {
Level->SpriteSequences[i].ObjectID
= MapTR1ItemToTR2Item(Level->SpriteSequences[i].ObjectID);
}
}
#endif
fclose(fp);
return(Level);
}
/*
* ioExtractMeshes()
*
* Reads through raw mesh data and extracts the details of each
mesh,
* allocating memory as needed.
*/
static void ioExtractMeshes(bitu8 *MeshData,
bitu32 NumMeshPointers,
bitu32 *MeshPointers,
TR2_Level *Level)
{
bitu32 size,
i;
#ifdef BIG_ENDIAN_CPU
bitu32 j; // index variable for endian conversion
int k;
#endif // BIG_ENDIAN_CPU
bitu8 *MeshPointer;
int NegativeSize;
/* alloc space for mesh */
Level->NumMeshes = NumMeshPointers;
Level->Meshes = (struct tr2_mesh_struct *)TR2alloc(sizeof(tr2_mesh)
* Level->NumMeshes);
for (i = 0; i < NumMeshPointers; ++i) {
/* get mesh start */
MeshPointer = &MeshData[MeshPointers[i]];
/* get Centre + Unknowns */
memcpy(&Level->Meshes[i].Centre.x,
MeshPointer, 10);
CONVERT_ENDIAN(&Level->Meshes[i].Centre.x,
sizeof(Level->Meshes[i].Centre.x));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.y,
sizeof(Level->Meshes[i].Centre.y));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.z,
sizeof(Level->Meshes[i].Centre.z));
// depending on the interpretation of
the unknowns that follow the Centre
// element, more endian conversion may
be necessary
MeshPointer += 10;
/* get number of vertices */
memcpy(&Level->Meshes[i].NumVertices,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumVertices,
sizeof(Level->Meshes[i].NumVertices));
MeshPointer += sizeof(bitu16);
Level->Meshes[i].NumVertices = (bit16)abs(Level->Meshes[i].NumVertices);
/* get vertex list */
size = sizeof(tr2_vertex) * Level->Meshes[i].NumVertices;
Level->Meshes[i].Vertices = (tr2_vertex
*)TR2alloc(size);
memcpy(Level->Meshes[i].Vertices, MeshPointer,
size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (bitu32)Level->Meshes[i].NumVertices;
++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].x,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].y,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].z,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
MeshPointer += size;
/* get number of normals */
memcpy(&Level->Meshes[i].NumNormals,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumNormals,
sizeof(Level->Meshes[i].NumNormals));
MeshPointer += sizeof(bitu16);
NegativeSize = (Level->Meshes[i].NumNormals
< 0);
Level->Meshes[i].NumNormals = (bit16)abs(Level->Meshes[i].NumNormals);
/* get normal list */
if (NegativeSize) {
NegativeSize = 0;
size = Level->Meshes[i].NumNormals
* sizeof(bitu16);
Level->Meshes[i].MeshLights
= (bit16 *)TR2alloc(size);
memcpy(Level->Meshes[i].MeshLights,
MeshPointer, size);
}
else {
size = sizeof(tr2_vertex)
* Level->Meshes[i].NumNormals;
Level->Meshes[i].Normals
= (tr2_vertex *)TR2alloc(size);
memcpy(Level->Meshes[i].Normals,
MeshPointer, size);
}
#ifdef BIG_ENDIAN_CPU
if (Level->Meshes[i].MeshLights == NULL)
{
for (j = 0; j <
(bitu32)Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].z, sizeof(bitu16));
}
}
else {
for (j = 0; j <
(bitu32)Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].MeshLights[j], sizeof(bitu16));
}
}
#endif // BIG_ENDIAN_CPU
MeshPointer += size;
/* get number of textured rectangles
*/
memcpy(&Level->Meshes[i].NumTexturedRectangles,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedRectangles,
sizeof(Level->Meshes[i].NumTexturedRectangles));
MeshPointer += sizeof(bitu16);
Level->Meshes[i].NumTexturedRectangles
= (bit16)abs(Level->Meshes[i].NumTexturedRectangles);
size = sizeof(tr2_face4) * Level->Meshes[i].NumTexturedRectangles;
Level->Meshes[i].TexturedRectangles
= (tr2_face4 *)TR2alloc(size);
/* get list of textured rectangles */
if (Level->Meshes[i].NumTexturedRectangles
> 0) {
if (Level->EngineVersion
== TombRaider_4) {
int j;
for (j = 0; j < Level->Meshes[i].NumTexturedRectangles; ++j) {
memcpy(&Level->Meshes[i].TexturedRectangles[j], MeshPointer, sizeof(tr2_face4));
MeshPointer += sizeof(tr2_face4) + sizeof(bitu16);
}
}
else {
memcpy(Level->Meshes[i].TexturedRectangles, MeshPointer, size);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(bitu32)Level->Meshes[i].NumTexturedRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
if (Level->EngineVersion
!= TombRaider_4)
MeshPointer += size;
}
/* get number of textured triangles */
memcpy(&Level->Meshes[i].NumTexturedTriangles,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedTriangles,
sizeof(Level->Meshes[i].NumTexturedTriangles));
MeshPointer += sizeof(bitu16);
Level->Meshes[i].NumTexturedTriangles
= (bit16)abs(Level->Meshes[i].NumTexturedTriangles);
size = sizeof(tr2_face3) * Level->Meshes[i].NumTexturedTriangles;
Level->Meshes[i].TexturedTriangles =
(tr2_face3 *)TR2alloc(size);
/* get list of textured triangles */
if (Level->Meshes[i].NumTexturedTriangles
> 0) {
if (Level->EngineVersion
== TombRaider_4) {
int j;
for (j = 0; j < Level->Meshes[i].NumTexturedTriangles; ++j) {
memcpy(&Level->Meshes[i].TexturedTriangles[j], MeshPointer, sizeof(tr2_face3));
MeshPointer += sizeof(tr2_face3) + sizeof(bitu16);
}
}
else {
memcpy(Level->Meshes[i].TexturedTriangles, MeshPointer, size);
}
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(bitu32)Level->Meshes[i].NumTexturedTriangles; ++j) {
for (k = 0; k < 3; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
if (Level->EngineVersion
!= TombRaider_4)
MeshPointer += size;
}
if (Level->EngineVersion == TombRaider_4)
{ ////////////////////////////////
Level->Meshes[i].NumColouredRectangles
= 0;
Level->Meshes[i].NumColouredTriangles
= 0;
MeshPointer += 2;
continue;
}
/* get number of coloured rectangles
*/
memcpy(&Level->Meshes[i].NumColouredRectangles,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredRectangles,
sizeof(Level->Meshes[i].NumColouredRectangles));
MeshPointer += sizeof(bitu16);
Level->Meshes[i].NumColouredRectangles
= (bit16)abs(Level->Meshes[i].NumColouredRectangles);
size = sizeof(tr2_face4) * Level->Meshes[i].NumColouredRectangles;
Level->Meshes[i].ColouredRectangles
= (tr2_face4 *)TR2alloc(size);
/* get list of coloured rectangles */
if (Level->Meshes[i].NumColouredRectangles
> 0) {
memcpy(Level->Meshes[i].ColouredRectangles,
MeshPointer, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(bitu32)Level->Meshes[i].NumColouredRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
MeshPointer += size;
}
/* get number of coloured triangles */
memcpy(&Level->Meshes[i].NumColouredTriangles,
MeshPointer, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredTriangles,
sizeof(Level->Meshes[i].NumColouredTriangles));
MeshPointer += sizeof(bitu16);
Level->Meshes[i].NumColouredTriangles
= (bit16)abs(Level->Meshes[i].NumColouredTriangles);
size = sizeof(tr2_face3) * Level->Meshes[i].NumColouredTriangles;
Level->Meshes[i].ColouredTriangles =
(tr2_face3 *)TR2alloc(size);
/* get list of coloured triangles */
if (Level->Meshes[i].NumColouredTriangles
> 0) {
memcpy(Level->Meshes[i].ColouredTriangles,
MeshPointer, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(bitu32)Level->Meshes[i].NumColouredTriangles; ++j) {
for (k = 0; k < 3; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
MeshPointer += size;
}
}
}
/*
* FreeIfUsed()
*
* Frees a block of memory if the pointer is non-NULL.
* (worker-bee for FreeTR2level)
*/
void FreeIfUsed(void *mem)
{
if (mem != NULL)
free(mem);
}
/*
* FreeTR2level()
*
* Frees a TR2_Level structure in its entirety, including the
* base structure.
*/
void FreeTR2level(TR2_Level *Level)
{
int i;
FreeIfUsed(Level->FileName);
FreeIfUsed(Level->Textile8);
FreeIfUsed(Level->Textile16);
/* Rooms have internal allocated structures...*/
for (i = 0; i < Level->NumRooms; ++i) {
FreeIfUsed(Level->Rooms[i].RoomData.Vertices);
FreeIfUsed(Level->Rooms[i].RoomData.Rectangles);
FreeIfUsed(Level->Rooms[i].RoomData.Triangles);
FreeIfUsed(Level->Rooms[i].RoomData.Sprites);
FreeIfUsed(Level->Rooms[i].Data);
// this _should_ already be free (NULL)
FreeIfUsed(Level->Rooms[i].Portals);
FreeIfUsed(Level->Rooms[i].SectorList);
FreeIfUsed(Level->Rooms[i].Lights);
FreeIfUsed(Level->Rooms[i].StaticMeshes);
}
FreeIfUsed(Level->Rooms);
FreeIfUsed(Level->FloorData);
/* Meshes have internal allocated structures...*/
for (i = 0; i < Level->NumMeshes; ++i) {
FreeIfUsed(Level->Meshes[i].Vertices);
FreeIfUsed(Level->Meshes[i].Normals);
FreeIfUsed(Level->Meshes[i].MeshLights);
FreeIfUsed(Level->Meshes[i].TexturedRectangles);
FreeIfUsed(Level->Meshes[i].TexturedTriangles);
FreeIfUsed(Level->Meshes[i].ColouredRectangles);
FreeIfUsed(Level->Meshes[i].ColouredTriangles);
}
FreeIfUsed(Level->Meshes);
FreeIfUsed(Level->Animations);
FreeIfUsed(Level->StateChanges);
FreeIfUsed(Level->AnimDispatches);
FreeIfUsed(Level->AnimCommands);
FreeIfUsed(Level->MeshTrees);
FreeIfUsed(Level->Frames);
FreeIfUsed(Level->Moveables);
FreeIfUsed(Level->StaticMeshes);
FreeIfUsed(Level->ObjectTextures);
FreeIfUsed(Level->SpriteTextures);
FreeIfUsed(Level->SpriteSequences);
FreeIfUsed(Level->Cameras);
FreeIfUsed(Level->SoundSources);
FreeIfUsed(Level->Boxes);
FreeIfUsed(Level->Overlaps);
FreeIfUsed(Level->Zones);
FreeIfUsed(Level->AnimatedTextures);
FreeIfUsed(Level->Items);
FreeIfUsed(Level->LightMap);
FreeIfUsed(Level->CinematicFrames);
FreeIfUsed(Level->DemoData);
FreeIfUsed(Level->SoundMap);
FreeIfUsed(Level->SoundDetails);
FreeIfUsed(Level->SampleIndices);
FreeIfUsed(Level);
}
/*
* ioLocateDuplicateMesh(int which)
*
* Scans Level->Meshes[] for an exact match of Level->Meshes[which]
* (occurring prior to <which>). Returns index of match,
or -1 if
* no match was found.
*/
static int ioLocateDuplicateMesh(TR2_Level *Level, int which)
{
int i,
size;
for (i = 0; i < which; ++i) {
if (Level->Meshes[i].NumVertices ==
Level->Meshes[which].NumVertices &&
Level->Meshes[i].NumNormals
== Level->Meshes[which].NumNormals &&
Level->Meshes[i].NumTexturedRectangles
== Level->Meshes[which].NumTexturedRectangles &&
Level->Meshes[i].NumTexturedTriangles
== Level->Meshes[which].NumTexturedTriangles &&
Level->Meshes[i].NumColouredRectangles
== Level->Meshes[which].NumColouredRectangles &&
Level->Meshes[i].NumColouredTriangles
== Level->Meshes[which].NumColouredTriangles) {
/* counts match, see
if data matches */
if (memcmp(&Level->Meshes[i].Centre.x,
&Level->Meshes[which].Centre.x, 10))
return(-1);
size = Level->Meshes[i].NumVertices
* sizeof(tr2_vertex);
if (memcmp(Level->Meshes[i].Vertices,
Level->Meshes[which].Vertices, size))
return(-1);
if (Level->Meshes[i].MeshLights
!= NULL || Level->Meshes[which].MeshLights != NULL) {
if (Level->Meshes[i].MeshLights == NULL || Level->Meshes[which].MeshLights
== NULL)
return(-1);
size = Level->Meshes[i].NumNormals * 2;
if (memcmp(Level->Meshes[i].MeshLights, Level->Meshes[which].MeshLights,
size))
return(-1);
}
else {
if (Level->Meshes[i].Normals == NULL || Level->Meshes[which].Normals ==
NULL)
return(-1);
size = Level->Meshes[i].NumNormals * sizeof(tr2_vertex);
if (memcmp(Level->Meshes[i].Normals, Level->Meshes[which].Normals, size))
return(-1);
}
size = Level->Meshes[i].NumTexturedRectangles
* sizeof(tr2_face4);
if (size > 0 &&
memcmp(Level->Meshes[i].TexturedRectangles, Level->Meshes[which].TexturedRectangles,
size))
return(-1);
size = Level->Meshes[i].NumTexturedTriangles
* sizeof(tr2_face3);
if (size > 0 &&
memcmp(Level->Meshes[i].TexturedTriangles, Level->Meshes[which].TexturedTriangles,
size))
return(-1);
size = Level->Meshes[i].NumColouredRectangles
* sizeof(tr2_face4);
if (size > 0 &&
memcmp(Level->Meshes[i].ColouredRectangles, Level->Meshes[which].ColouredRectangles,
size))
return(-1);
size = Level->Meshes[i].NumColouredTriangles
* sizeof(tr2_face3);
if (size > 0 &&
memcmp(Level->Meshes[i].ColouredTriangles, Level->Meshes[which].ColouredTriangles,
size))
return(-1);
return(i);
}
}
return(-1);
}
/*
* ioGenerateRawMeshData()
*
* Creates raw mesh data from meshes in *Level.
* Returns number of words in MeshData; also, as parameters,
returns
* a pointer to an (allocated) array of raw mesh data, and a pointer
to
* an (allocated) array of mesh pointers. Caller is responsible
for
* freeing these arrays.
*
* One odd note - sometimes, one of the counter fields (NumVertices,
* NumNormals, NumTexturedRectangles, NumTexturedTriangles,
* NumColouredRectangles, NumColouredTriangles) is NEGATIVE.
However,
* simply taking the absolute value of the number seems to work
just
* fine (with the exception of NumNormals - see comment below).
Surely
* this is being used to indicate something, but what? This
routine
* always uses the positive value.
*
* Seems there's an opportunity to optimise here - if two given
sets of
* mesh data are identical, we can simply write them once and
point both
* associated MeshPointers at the single set of data...
* Could use a "SameAs()" function that scans all meshes BEFORE
this one;
* if this one is identical to another, just set this mesh pointer
to that
* data; if it's unique, write it out.
*/
static bitu32 ioGenerateRawMeshData(TR2_Level *Level, bitu8 **MeshData,
bitu32 **MeshPointers)
{
bitu32 NumMeshWords;
bitu32 size,
i;
bitu8 *TempPointer;
bitu16 Temp16;
int j;
#ifdef BIG_ENDIAN_CPU
int k;
#endif
*MeshPointers = (bitu32 *)TR2alloc(Level->NumMeshes * sizeof(bitu32));
/* figure out the size of the raw data */
for (i = 0, size = 0; (long)i < Level->NumMeshes; ++i)
{
size += 10 + (6 * sizeof(bitu16));
// size of unknowns and counts (e.g. NumVertices, NumTexturedTriangles,
etc.)
size += Level->Meshes[i].NumVertices
* sizeof(tr2_vertex);
if (Level->Meshes[i].MeshLights != NULL)
size += Level->Meshes[i].NumNormals
* sizeof(bitu16);
else
size += Level->Meshes[i].NumNormals
* sizeof(tr2_vertex);
size += Level->Meshes[i].NumTexturedRectangles
* sizeof(tr2_face4);
size += Level->Meshes[i].NumTexturedTriangles
* sizeof(tr2_face3);
size += Level->Meshes[i].NumColouredRectangles
* sizeof(tr2_face4);
size += Level->Meshes[i].NumColouredTriangles
* sizeof(tr2_face3);
size += 8; // fudge for 4-byte
alignment
}
*MeshData = (bitu8 *)TR2alloc(size);
NumMeshWords = size / sizeof(bitu16);
/* build the raw data array */
for (i = 0, TempPointer = *MeshData; (long)i < Level->NumMeshes;
++i) {
/* set current mesh pointer */
if ((j = ioLocateDuplicateMesh(Level,
i)) != -1) {
(*MeshPointers)[i]
= (*MeshPointers)[j];
continue;
}
else
(*MeshPointers)[i]
= TempPointer - *MeshData;
/* handle Centre + unknowns */
CONVERT_ENDIAN(&Level->Meshes[i].Centre.x,
sizeof(Level->Meshes[i].Centre.x));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.y,
sizeof(Level->Meshes[i].Centre.y));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.z,
sizeof(Level->Meshes[i].Centre.z));
// depending on the interpretation of
the unknowns that follow the Centre
// element, more endian conversion may
be necessary
memcpy(TempPointer, &Level->Meshes[i].Centre.x,
10);
CONVERT_ENDIAN(&Level->Meshes[i].Centre.x,
sizeof(Level->Meshes[i].Centre.x));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.y,
sizeof(Level->Meshes[i].Centre.y));
CONVERT_ENDIAN(&Level->Meshes[i].Centre.z,
sizeof(Level->Meshes[i].Centre.z));
// depending on the interpretation of
the unknowns that follow the Centre
// element, more endian conversion may
be necessary
TempPointer += 10;
/* Handle Vertices */
CONVERT_ENDIAN(&Level->Meshes[i].NumVertices,
sizeof(Level->Meshes[i].NumVertices));
memcpy(TempPointer, &Level->Meshes[i].NumVertices,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumVertices,
sizeof(Level->Meshes[i].NumVertices));
TempPointer += sizeof(bitu16);
size = sizeof(tr2_vertex) * Level->Meshes[i].NumVertices;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Meshes[i].NumVertices;
++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].x,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].y,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].z,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer, Level->Meshes[i].Vertices,
size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Meshes[i].NumVertices;
++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].x,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].y,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Vertices[j].z,
sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TempPointer += size;
/* Handle Normals */
Temp16 = Level->Meshes[i].NumNormals;
if (Level->Meshes[i].MeshLights != NULL)
Temp16 *= -1;
CONVERT_ENDIAN(&Temp16, sizeof(Temp16));
memcpy(TempPointer, &Temp16, sizeof(bitu16));
TempPointer += sizeof(bitu16);
if (Level->Meshes[i].MeshLights != NULL)
{
size = sizeof(bit16)
* Level->Meshes[i].NumNormals;
// don't know if there
is any endian conversion here or not...
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].MeshLights[j], sizeof(bitu16));
}
#endif
memcpy(TempPointer,
Level->Meshes[i].MeshLights, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].MeshLights[j], sizeof(bitu16));
}
#endif
}
else { // "normal" Normals
size = sizeof(tr2_vertex)
* Level->Meshes[i].NumNormals;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].z, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer,
Level->Meshes[i].Normals, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumNormals; ++j) {
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].Normals[j].z, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
TempPointer += size;
/* Handle Textured Rectangles */
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedRectangles,
sizeof(Level->Meshes[i].NumTexturedRectangles));
memcpy(TempPointer, &Level->Meshes[i].NumTexturedRectangles,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedRectangles,
sizeof(Level->Meshes[i].NumTexturedRectangles));
TempPointer += sizeof(bitu16);
if (Level->Meshes[i].NumTexturedRectangles
> 0) {
size = sizeof(tr2_face4)
* Level->Meshes[i].NumTexturedRectangles;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumTexturedRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer,
Level->Meshes[i].TexturedRectangles, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumTexturedRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TempPointer += size;
}
/* Handle Textured Triangles */
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedTriangles,
sizeof(Level->Meshes[i].NumTexturedTriangles));
memcpy(TempPointer, &Level->Meshes[i].NumTexturedTriangles,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumTexturedTriangles,
sizeof(Level->Meshes[i].NumTexturedTriangles));
TempPointer += sizeof(bitu16);
if (Level->Meshes[i].NumTexturedTriangles
> 0) {
size = sizeof(tr2_face3)
* Level->Meshes[i].NumTexturedTriangles;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumTexturedTriangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer,
Level->Meshes[i].TexturedTriangles, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumTexturedTriangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].TexturedTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TempPointer += size;
}
/* Handle Coloured Rectangles */
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredRectangles,
sizeof(Level->Meshes[i].NumColouredRectangles));
memcpy(TempPointer, &Level->Meshes[i].NumColouredRectangles,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredRectangles,
sizeof(Level->Meshes[i].NumColouredRectangles));
TempPointer += sizeof(bitu16);
if (Level->Meshes[i].NumColouredRectangles
> 0) {
size = sizeof(tr2_face4)
* Level->Meshes[i].NumColouredRectangles;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumColouredRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer,
Level->Meshes[i].ColouredRectangles, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumColouredRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredRectangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TempPointer += size;
}
/* Handle Coloured Triangles */
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredTriangles,
sizeof(Level->Meshes[i].NumColouredTriangles));
memcpy(TempPointer, &Level->Meshes[i].NumColouredTriangles,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Meshes[i].NumColouredTriangles,
sizeof(Level->Meshes[i].NumColouredTriangles));
TempPointer += sizeof(bitu16);
if (Level->Meshes[i].NumColouredTriangles
> 0) {
size = sizeof(tr2_face3)
* Level->Meshes[i].NumColouredTriangles;
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumColouredTriangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
memcpy(TempPointer,
Level->Meshes[i].ColouredTriangles, size);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Meshes[i].NumColouredTriangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Vertices[k],
sizeof(bitu16));
}
CONVERT_ENDIAN(&Level->Meshes[i].ColouredTriangles[j].Texture, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TempPointer += size;
}
size = (TempPointer - *MeshData) &
0x03;
if (size) { // round up
TempPointer += (4
- size);
}
}
NumMeshWords = (TempPointer - *MeshData) / sizeof(bitu16);
return(NumMeshWords);
}
/*
* TR2fwrite()
*
* Wrapper for fwrite() - if fwrite() fails to return the number
of
* bytes requested, print a message and exit.
*/
static int TR2fwrite(void *buffer, size_t size, size_t count, FILE
*fp)
{
int ReturnValue;
ReturnValue = fwrite(buffer, size, count, fp);
if ((size_t)ReturnValue != count) {
perror("TR2fwrite");
fprintf(stderr, "fwrite(buffer, %d,
%d, fp) failed.\n", size, count);
exit(3);
}
return(ReturnValue);
}
/*
* The level writer. Takes a level and filename (presumably
SOMETHING.TR2),
* writes the level to the file in TR2 format.
* Note that endian-ness conversion takes place twice: once to
fix the values
* before writing, and once to put them back again afterward (assuming
you
* want to keep manipulating the same level after a save).
*
* Returns 0 if all went well
* -1 if
there were any errors (writes to stderr in that case)
*/
int WriteTR2level(TR2_Level *Level, char *FileName)
{
FILE *fp;
int i,
#ifdef BIG_ENDIAN_CPU
j,
k,
#endif // BIG_ENDIAN_CPU
len,
ReturnValue;
bitu8 *TempBuffer,
*TempPointer;
bitu32 NumMeshDataWords,
NumMeshPointers,
*MeshPointers;
if ((fp = fopen(FileName, WRITEBINARY)) != NULL) {
/* write the version */
// CONVERT_ENDIAN(&Level->Version,
sizeof(Level->Version));
// TR2fwrite(&Level->Version, sizeof(Level->Version),
1, fp);
// CONVERT_ENDIAN(&Level->Version,
sizeof(Level->Version));
// temporary hack - always write "TR2 versions"
NumMeshDataWords = 0x0000002d;
TR2fwrite(&NumMeshDataWords, sizeof(bitu32), 1, fp);
/* write the 8-bit palette */
#ifdef MACINTOSHPC
for (i = 0; i < 256; ++i) {
TR2fwrite(&Level->Palette8[i].Red,
1, 1, fp);
TR2fwrite(&Level->Palette8[i].Green,
1, 1, fp);
TR2fwrite(&Level->Palette8[i].Blue,
1, 1, fp);
}
#else
TR2fwrite(Level->Palette8, sizeof(tr2_colour),
256, fp);
#endif
/* write 16-bit palette */
TR2fwrite(Level->Palette16, sizeof(Level->Palette16),
1, fp);
/* write the textiles */
CONVERT_ENDIAN(&Level->NumTextiles,
sizeof(Level->NumTextiles));
TR2fwrite(&Level->NumTextiles, sizeof(Level->NumTextiles),
1, fp);
CONVERT_ENDIAN(&Level->NumTextiles,
sizeof(Level->NumTextiles));
/* 8-bit textiles come first */
TR2fwrite(Level->Textile8, sizeof(tr2_textile8),
Level->NumTextiles, fp);
/* 16-bit textiles come second */
#ifdef BIG_ENDIAN_CPU
/* convert 16-bit textiles to big-endian
format, if appropriate */
for (i = 0; i < (int)Level->NumTextiles;
++i) {
for (j = 0; j <
(256 * 256); ++j) {
CONVERT_ENDIAN(&Level->Textile16[i].Tile[j], sizeof(bitu16));
}
}
#endif
TR2fwrite(Level->Textile16, sizeof(tr2_textile16),
Level->NumTextiles, fp);
#ifdef BIG_ENDIAN_CPU
/* convert 16-bit textiles to big-endian
format, if appropriate */
for (i = 0; i < (int)Level->NumTextiles;
++i) {
for (j = 0; j <
(256 * 256); ++j) {
CONVERT_ENDIAN(&Level->Textile16[i].Tile[j], sizeof(bitu16));
}
}
#endif
/* write 32-bit unknown */
CONVERT_ENDIAN(&Level->UnknownT,
sizeof(Level->UnknownT));
TR2fwrite(&Level->UnknownT, sizeof(Level->UnknownT),
1, fp);
CONVERT_ENDIAN(&Level->UnknownT,
sizeof(Level->UnknownT));
/* write room count */
CONVERT_ENDIAN(&Level->NumRooms,
sizeof(Level->NumRooms));
TR2fwrite(&Level->NumRooms, sizeof(Level->NumRooms),
1, fp);
CONVERT_ENDIAN(&Level->NumRooms,
sizeof(Level->NumRooms));
/* write room details */
for (i = 0; i < Level->NumRooms;
++i) {
/* write RoomInfo
*/
CONVERT_ENDIAN(&Level->Rooms[i].info.x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.z,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.yBottom,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.yTop,
sizeof(bitu32));
TR2fwrite(&Level->Rooms[i].info,
sizeof(tr2_room_info), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].info.x,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.z,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.yBottom,
sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].info.yTop,
sizeof(bitu32));
/* Build raw data for
rest of room */
len = sizeof(bitu16)
+
(Level->Rooms[i].RoomData.NumVertices * sizeof(tr2_vertex_room)) +
sizeof(bitu16) +
(Level->Rooms[i].RoomData.NumRectangles * sizeof(tr2_face4)) +
sizeof(bitu16) +
(Level->Rooms[i].RoomData.NumTriangles * sizeof(tr2_face3)) +
sizeof(bitu16) +
(Level->Rooms[i].RoomData.NumSprites * sizeof(tr2_room_sprite));
TempBuffer = (bitu8
*)TR2alloc(len); // allocate space for the raw data
/* write out the number
of data words to follow */
Level->Rooms[i].NumDataWords
= len / sizeof(bitu16);
CONVERT_ENDIAN(&Level->Rooms[i].NumDataWords,
sizeof(bitu32));
TR2fwrite(&Level->Rooms[i].NumDataWords,
sizeof(bitu32), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumDataWords,
sizeof(bitu32));
TempPointer = TempBuffer;
/* copy vertices */
len = sizeof(bitu16);
*(bitu16 *)TempPointer
= Level->Rooms[i].RoomData.NumVertices;
CONVERT_ENDIAN(TempPointer,
len);
TempPointer += len;
len = Level->Rooms[i].RoomData.NumVertices
* sizeof(tr2_vertex_room);
if (Level->Rooms[i].RoomData.NumVertices
> 0) {
memcpy(TempPointer, Level->Rooms[i].RoomData.Vertices, len);
#ifdef BIG_ENDIAN_CPU
for (i = 0; i < Level->Rooms[i].RoomData.NumVertices; ++i) {
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Vertex.x, sizeof(bit16));
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Vertex.y, sizeof(bit16));
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Vertex.z, sizeof(bit16));
// the following might be 6*bitu8, meaning that conversion is inappropriate
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Lighting1, sizeof(bit16));
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Attributes, sizeof(bitu16));
CONVERT_ENDIAN(((tr2_vertex_room *)TempPointer)[i].Lighting2, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
TempPointer += len;
/* copy rectangles
*/
len = sizeof(bitu16);
*(bitu16 *)TempPointer
= Level->Rooms[i].RoomData.NumRectangles;
CONVERT_ENDIAN(TempPointer,
len);
TempPointer += len;
len = Level->Rooms[i].RoomData.NumRectangles
* sizeof(tr2_face4);
if (Level->Rooms[i].RoomData.NumRectangles
> 0) {
memcpy(TempPointer, Level->Rooms[i].RoomData.Rectangles, len);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].RoomData.NumRectangles; ++j) {
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(((tr2_face4 *)TempPointer)[j].Vertices[k], sizeof(bit16));
}
CONVERT_ENDIAN(((tr2_face4 *)TempPointer)[j].Texture, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
TempPointer += len;
/* copy triangles */
len = sizeof(bitu16);
*(bitu16 *)TempPointer
= Level->Rooms[i].RoomData.NumTriangles;
CONVERT_ENDIAN(TempPointer,
len);
TempPointer += len;
len = Level->Rooms[i].RoomData.NumTriangles
* sizeof(tr2_face3);
if (Level->Rooms[i].RoomData.NumTriangles
> 0) {
memcpy(TempPointer, Level->Rooms[i].RoomData.Triangles, len);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].RoomData.NumTriangles; ++j) {
for (k = 0; k < 3; ++k) {
CONVERT_ENDIAN(((tr2_face3 *)TempPointer)[j].Vertices[k], sizeof(bit16));
}
CONVERT_ENDIAN(((tr2_face3 *)TempPointer)[j].Texture, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
TempPointer += len;
/* copy sprites */
len = sizeof(bitu16);
*(bitu16 *)TempPointer
= Level->Rooms[i].RoomData.NumSprites;
CONVERT_ENDIAN(TempPointer,
len);
TempPointer += len;
len = Level->Rooms[i].RoomData.NumSprites
* sizeof(tr2_room_sprite);
if (Level->Rooms[i].RoomData.NumSprites
> 0) {
memcpy(TempPointer, Level->Rooms[i].RoomData.Sprites, len);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].RoomData.NumSprites; ++j) {
CONVERT_ENDIAN(((tr2_room_sprite *)TempPointer)[j].Vertex, sizeof(bit16));
CONVERT_ENDIAN(((tr2_room_sprite *)TempPointer)[j].Texture, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
/* write out the raw
data */
TR2fwrite(TempBuffer,
Level->Rooms[i].NumDataWords, sizeof(bitu16), fp);
/* get rid of our temporary
buffer */
free(TempBuffer);
TempBuffer = NULL;
/* write Portal info
*/
CONVERT_ENDIAN(&Level->Rooms[i].NumPortals,
sizeof(bitu16));
TR2fwrite(&Level->Rooms[i].NumPortals,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumPortals,
sizeof(bitu16));
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumPortals; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].AdjoiningRoom, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.x, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.y, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.z, sizeof(bit16));
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].x, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].y, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].z, sizeof(bit16));
}
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Rooms[i].Portals,
sizeof(tr2_room_portal), Level->Rooms[i].NumPortals, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumPortals; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].AdjoiningRoom, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.x, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.y, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Normal.z, sizeof(bit16));
for (k = 0; k < 4; ++k) {
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].x, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].y, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Portals[j].Vertices[k].z, sizeof(bit16));
}
}
#endif // BIG_ENDIAN_CPU
/* write sector info
*/
CONVERT_ENDIAN(&Level->Rooms[i].NumZsectors,
sizeof(bitu16));
TR2fwrite(&Level->Rooms[i].NumZsectors,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumZsectors,
sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].NumXsectors,
sizeof(bitu16));
TR2fwrite(&Level->Rooms[i].NumXsectors,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumXsectors,
sizeof(bitu16));
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(Level->Rooms[i].NumZsectors * Level->Rooms[i].NumXsectors); ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].FDindex, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].BoxIndex, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Rooms[i].SectorList,
sizeof(tr2_room_sector), Level->Rooms[i].NumZsectors * Level->Rooms[i].NumXsectors,
fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(Level->Rooms[i].NumZsectors * Level->Rooms[i].NumXsectors); ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].FDindex, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].SectorList[j].BoxIndex, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
/* write Intensity1,
Intensity2, LightMode */
TR2fwrite(&Level->Rooms[i].Intensity1,
6, 1, fp);
/* write room lighting
info */
CONVERT_ENDIAN(&Level->Rooms[i].NumLights,
sizeof(bitu16));
TR2fwrite(&Level->Rooms[i].NumLights,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumLights,
sizeof(bitu16));
if (Level->Rooms[i].NumLights
> 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].NumLights; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].x, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].y, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].z, sizeof(bit32));
// Depending on the interpretation of the following elements, this endian
// conversion may not be correct.
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity1, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity2, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade1, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade2, sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Rooms[i].Lights, sizeof(tr2_room_light), Level->Rooms[i].NumLights,
fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < Level->Rooms[i].NumLights; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].x, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].y, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].z, sizeof(bit32));
// Depending on the interpretation of the following elements, this endian
// conversion may not be correct.
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity1, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Intensity2, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade1, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Rooms[i].Lights[j].Fade2, sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
}
/* write Static Mesh
Data */
CONVERT_ENDIAN(&Level->Rooms[i].NumStaticMeshes,
sizeof(bitu16));
TR2fwrite(&Level->Rooms[i].NumStaticMeshes,
sizeof(bitu16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].NumStaticMeshes,
sizeof(bitu16));
if (Level->Rooms[i].NumStaticMeshes
> 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumStaticMeshes; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].x, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].y, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].z, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].ObjectID, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Rotation, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity1, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity2, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Rooms[i].StaticMeshes, sizeof(tr2_room_staticmesh), Level->Rooms[i].NumStaticMeshes,
fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->Rooms[i].NumStaticMeshes; ++j) {
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].x, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].y, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].z, sizeof(bit32));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].ObjectID, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Rotation, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity1, sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].StaticMeshes[j].Intensity2, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
/* write miscellaneous
unknowns (thought to be lighting-related) */
CONVERT_ENDIAN(&Level->Rooms[i].AlternateRoom,
sizeof(bit16));
TR2fwrite(&Level->Rooms[i].AlternateRoom,
sizeof(bit16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].AlternateRoom,
sizeof(bit16));
CONVERT_ENDIAN(&Level->Rooms[i].Flags,
sizeof(bit16));
TR2fwrite(&Level->Rooms[i].Flags,
sizeof(bit16), 1, fp);
CONVERT_ENDIAN(&Level->Rooms[i].Flags,
sizeof(bit16));
}
/* write floor data */
/*
* Really, FloorData should be
a per-sector dynamic allocation; however,
* that requires a parser that
can accurately determine where one sector's
* FloorData ends and another's
begins. Until we have that, we'll stick to
* this crude (but effective) method...
*/
CONVERT_ENDIAN(&Level->NumFloorData,
sizeof(Level->NumFloorData));
TR2fwrite(&Level->NumFloorData,
sizeof(Level->NumFloorData), 1, fp);
CONVERT_ENDIAN(&Level->NumFloorData,
sizeof(Level->NumFloorData));
if (Level->NumFloorData > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumFloorData; ++j) {
CONVERT_ENDIAN(&Level->FloorData[j], sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->FloorData,
sizeof(bitu16), Level->NumFloorData, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumFloorData; ++j) {
CONVERT_ENDIAN(&Level->FloorData[j], sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* generate raw mesh data */
NumMeshDataWords = ioGenerateRawMeshData(Level,
&TempBuffer, &MeshPointers);
NumMeshPointers = Level->NumMeshes;
/* write mesh data */
CONVERT_ENDIAN(&NumMeshDataWords,
sizeof(NumMeshDataWords));
TR2fwrite(&NumMeshDataWords, sizeof(NumMeshDataWords),
1, fp);
CONVERT_ENDIAN(&NumMeshDataWords,
sizeof(NumMeshDataWords));
TR2fwrite(TempBuffer, sizeof(bitu16),
NumMeshDataWords, fp);
/* write mesh pointers */
CONVERT_ENDIAN(&NumMeshPointers,
sizeof(NumMeshPointers));
TR2fwrite(&NumMeshPointers, sizeof(NumMeshPointers),
1, fp);
CONVERT_ENDIAN(&NumMeshPointers,
sizeof(NumMeshPointers));
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)NumMeshPointers;
++j) {
CONVERT_ENDIAN(&MeshPointers[j],
sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(MeshPointers, sizeof(bitu32),
NumMeshPointers, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < (int)NumMeshPointers;
++j) {
CONVERT_ENDIAN(&MeshPointers[j],
sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
/* free temporary arrays */
free(TempBuffer);
TempBuffer = NULL;
free(MeshPointers);
MeshPointers = NULL;
/* write animations */
CONVERT_ENDIAN(&Level->NumAnimations,
sizeof(Level->NumAnimations));
TR2fwrite(&Level->NumAnimations,
sizeof(Level->NumAnimations), 1, fp);
CONVERT_ENDIAN(&Level->NumAnimations,
sizeof(Level->NumAnimations));
if (Level->NumAnimations > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimations; ++j) {
CONVERT_ENDIAN(&Level->Animations[j].FrameOffset, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Animations[j].FrameStart, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].FrameEnd, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextAnimation, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextFrame, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumStateChanges, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateChangeOffset, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumAnimCommands, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].AnimCommand, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Animations,
sizeof(tr2_animation), Level->NumAnimations, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimations; ++j) {
CONVERT_ENDIAN(&Level->Animations[j].FrameOffset, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Animations[j].FrameStart, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].FrameEnd, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextAnimation, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NextFrame, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumStateChanges, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].StateChangeOffset, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].NumAnimCommands, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Animations[j].AnimCommand, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write state changes */
CONVERT_ENDIAN(&Level->NumStateChanges,
sizeof(Level->NumStateChanges));
TR2fwrite(&Level->NumStateChanges,
sizeof(Level->NumStateChanges), 1, fp);
CONVERT_ENDIAN(&Level->NumStateChanges,
sizeof(Level->NumStateChanges));
if (Level->NumStateChanges > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumStateChanges; ++j) {
CONVERT_ENDIAN(&Level->StateChanges[j].StateID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].NumAnimDispatches, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].AnimDispatch, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->StateChanges,
sizeof(tr2_state_change), Level->NumStateChanges, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumStateChanges; ++j) {
CONVERT_ENDIAN(&Level->StateChanges[j].StateID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].NumAnimDispatches, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StateChanges[j].AnimDispatch, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write anim dispatches */
CONVERT_ENDIAN(&Level->NumAnimDispatches,
sizeof(Level->NumAnimDispatches));
TR2fwrite(&Level->NumAnimDispatches,
sizeof(Level->NumAnimDispatches), 1, fp);
CONVERT_ENDIAN(&Level->NumAnimDispatches,
sizeof(Level->NumAnimDispatches));
if (Level->NumAnimDispatches > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimDispatches; ++j) {
CONVERT_ENDIAN(&Level->AnimDispatches[j].Low, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].High, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextAnimation, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextFrame, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->AnimDispatches,
sizeof(tr2_anim_dispatch), Level->NumAnimDispatches, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimDispatches; ++j) {
CONVERT_ENDIAN(&Level->AnimDispatches[j].Low, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].High, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextAnimation, sizeof(bitu16));
CONVERT_ENDIAN(&Level->AnimDispatches[j].NextFrame, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write AnimCommands */
CONVERT_ENDIAN(&Level->NumAnimCommands,
sizeof(Level->NumAnimCommands));
TR2fwrite(&Level->NumAnimCommands,
sizeof(Level->NumAnimCommands), 1, fp);
CONVERT_ENDIAN(&Level->NumAnimCommands,
sizeof(Level->NumAnimCommands));
if (Level->NumAnimCommands > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimCommands; ++j) {
CONVERT_ENDIAN(&Level->AnimCommands[j].Value, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->AnimCommands,
sizeof(tr2_anim_command), Level->NumAnimCommands, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumAnimCommands; ++j) {
CONVERT_ENDIAN(&Level->AnimCommands[j].Value, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write MeshTrees */
CONVERT_ENDIAN(&Level->NumMeshTrees,
sizeof(Level->NumMeshTrees));
TR2fwrite(&Level->NumMeshTrees,
sizeof(Level->NumMeshTrees), 1, fp);
CONVERT_ENDIAN(&Level->NumMeshTrees,
sizeof(Level->NumMeshTrees));
if (Level->NumMeshTrees > 0) {
#ifdef BIG_ENDIAN_CPU
bitu32 *u32ptr = (bitu32
*)&Level->MeshTrees[0];
for (j = 0; j <
(int)Level->NumMeshTrees; ++j) {
CONVERT_ENDIAN(&u32ptr[j], sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->MeshTrees,
sizeof(bitu32), Level->NumMeshTrees, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumMeshTrees; ++j) {
CONVERT_ENDIAN(&u32ptr[j], sizeof(bitu32));
}
#endif // BIG_ENDIAN_CPU
}
/* write frames */
CONVERT_ENDIAN(&Level->NumFrames,
sizeof(Level->NumFrames));
TR2fwrite(&Level->NumFrames, sizeof(Level->NumFrames),
1, fp);
CONVERT_ENDIAN(&Level->NumFrames,
sizeof(Level->NumFrames));
if (Level->NumFrames > 0) {
TR2fwrite(Level->Frames,
sizeof(bitu16), Level->NumFrames, fp);
// there is probably
some endian correction needed here, but I don't
// currently know
the frame structure...
}
/* write moveables */
CONVERT_ENDIAN(&Level->NumMoveables,
sizeof(Level->NumMoveables));
TR2fwrite(&Level->NumMoveables,
sizeof(Level->NumMoveables), 1, fp);
CONVERT_ENDIAN(&Level->NumMoveables,
sizeof(Level->NumMoveables));
if (Level->NumMoveables > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumMoveables; ++j) {
CONVERT_ENDIAN(&Level->Moveables[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].NumMeshes, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].StartingMesh, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].MeshTree, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].FrameOffset, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].Animation, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Moveables,
sizeof(tr2_moveable), Level->NumMoveables, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumMoveables; ++j) {
CONVERT_ENDIAN(&Level->Moveables[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].NumMeshes, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].StartingMesh, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Moveables[j].MeshTree, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].FrameOffset, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Moveables[j].Animation, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
CONVERT_ENDIAN(&Level->NumStaticMeshes,
sizeof(Level->NumStaticMeshes));
TR2fwrite(&Level->NumStaticMeshes,
sizeof(Level->NumStaticMeshes), 1, fp);
CONVERT_ENDIAN(&Level->NumStaticMeshes,
sizeof(Level->NumStaticMeshes));
if (Level->NumStaticMeshes > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumStaticMeshes; ++j) {
int j1, j2;
CONVERT_ENDIAN(&Level->StaticMeshes[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->StaticMeshes[j].StartingMesh, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].Flags, sizeof(bitu16));
for (j1 = 0; j1 < 2; ++j1) {
for (j2 = 0; j2 < 2; ++j2) {
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].z, sizeof(bitu16));
}
}
}
#endif
TR2fwrite(Level->StaticMeshes,
sizeof(tr2_staticmesh), Level->NumStaticMeshes, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumStaticMeshes; ++j) {
int j1, j2;
CONVERT_ENDIAN(&Level->StaticMeshes[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->StaticMeshes[j].StartingMesh, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].Flags, sizeof(bitu16));
for (j1 = 0; j1 < 2; ++j1) {
for (j2 = 0; j2 < 2; ++j2) {
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].x, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].y, sizeof(bitu16));
CONVERT_ENDIAN(&Level->StaticMeshes[j].BoundingBox[j1][j2].z, sizeof(bitu16));
}
}
}
#endif
}
/* write object textures */
CONVERT_ENDIAN(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures));
TR2fwrite(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures), 1, fp);
CONVERT_ENDIAN(&Level->NumObjectTextures,
sizeof(Level->NumObjectTextures));
if (Level->NumObjectTextures > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumObjectTextures; ++j) {
CONVERT_ENDIAN(&Level->ObjectTextures[j].TransparencyFlags, sizeof(bitu16));
CONVERT_ENDIAN(&Level->ObjectTextures[j].Tile, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->ObjectTextures,
sizeof(tr2_object_texture), Level->NumObjectTextures, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumObjectTextures; ++j) {
CONVERT_ENDIAN(&Level->ObjectTextures[j].TransparencyFlags, sizeof(bitu16));
CONVERT_ENDIAN(&Level->ObjectTextures[j].Tile, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write sprite textures */
CONVERT_ENDIAN(&Level->NumSpriteTextures,
sizeof(Level->NumSpriteTextures));
TR2fwrite(&Level->NumSpriteTextures,
sizeof(Level->NumSpriteTextures), 1, fp);
CONVERT_ENDIAN(&Level->NumSpriteTextures,
sizeof(Level->NumSpriteTextures));
if (Level->NumSpriteTextures > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumSpriteTextures; ++j) {
CONVERT_ENDIAN(&Level->SpriteTextures[j].Tile, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].LeftSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].TopSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].RightSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].BottomSide, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->SpriteTextures,
sizeof(tr2_sprite_texture), Level->NumSpriteTextures, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumSpriteTextures; ++j) {
CONVERT_ENDIAN(&Level->SpriteTextures[j].Tile, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].LeftSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].TopSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].RightSide, sizeof(bit16));
CONVERT_ENDIAN(&Level->SpriteTextures[j].BottomSide, sizeof(bit16));
}
#endif // BIG_ENDIAN_CPU
}
/* write sprite texture data (?) */
CONVERT_ENDIAN(&Level->NumSpriteSequences,
sizeof(Level->NumSpriteSequences));
TR2fwrite(&Level->NumSpriteSequences,
sizeof(Level->NumSpriteSequences), 1, fp);
CONVERT_ENDIAN(&Level->NumSpriteSequences,
sizeof(Level->NumSpriteSequences));
if (Level->NumSpriteSequences > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumSpriteSequences; ++j) {
CONVERT_ENDIAN(&Level->SpriteSequences[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SpriteSequences[j].NegativeLength, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteSequences[j].Offset, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->SpriteSequences,
sizeof(tr2_sprite_sequence), Level->NumSpriteSequences, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumSpriteSequences; ++j) {
CONVERT_ENDIAN(&Level->SpriteSequences[j].ObjectID, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SpriteSequences[j].NegativeLength, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SpriteSequences[j].Offset, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write cameras (?) */
CONVERT_ENDIAN(&Level->NumCameras,
sizeof(Level->NumCameras));
TR2fwrite(&Level->NumCameras, sizeof(Level->NumCameras),
1, fp);
CONVERT_ENDIAN(&Level->NumCameras,
sizeof(Level->NumCameras));
if (Level->NumCameras > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
(int)Level->NumCameras; ++j) {
CONVERT_ENDIAN(&Level->Cameras[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].Room, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Cameras[j].Unknown1, sizeof(bitu16));
}
#endif
TR2fwrite(Level->Cameras,
sizeof(tr2_camera), Level->NumCameras, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumCameras; ++j) {
CONVERT_ENDIAN(&Level->Cameras[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Cameras[j].Room, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Cameras[j].Unknown1, sizeof(bitu16));
}
#endif
}
/* write sound effects */
CONVERT_ENDIAN(&Level->NumSoundSources,
sizeof(Level->NumSoundSources));
TR2fwrite(&Level->NumSoundSources,
sizeof(Level->NumSoundSources), 1, fp);
CONVERT_ENDIAN(&Level->NumSoundSources,
sizeof(Level->NumSoundSources));
if (Level->NumSoundSources > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSoundSources; ++j) {
CONVERT_ENDIAN(&Level->SoundSources[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].SoundID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundSources[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->SoundSources,
sizeof(tr2_sound_source), Level->NumSoundSources, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSoundSources; ++j) {
CONVERT_ENDIAN(&Level->SoundSources[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->SoundSources[j].SoundID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundSources[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write boxes (?) */
CONVERT_ENDIAN(&Level->NumBoxes,
sizeof(Level->NumBoxes));
TR2fwrite(&Level->NumBoxes, sizeof(Level->NumBoxes),
1, fp);
CONVERT_ENDIAN(&Level->NumBoxes,
sizeof(Level->NumBoxes));
if (Level->NumBoxes > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumBoxes; ++j) {
CONVERT_ENDIAN(&Level->Boxes[j].TrueFloor, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Boxes[j].OverlapIndex, sizeof(bitu16));
}
#endif
TR2fwrite(Level->Boxes,
sizeof(tr2_box), Level->NumBoxes, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumBoxes; ++j) {
CONVERT_ENDIAN(&Level->Boxes[j].TrueFloor, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Boxes[j].OverlapIndex, sizeof(bitu16));
}
#endif
}
/* write overlaps (?) */
CONVERT_ENDIAN(&Level->NumOverlaps,
sizeof(Level->NumOverlaps));
TR2fwrite(&Level->NumOverlaps, sizeof(Level->NumOverlaps),
1, fp);
CONVERT_ENDIAN(&Level->NumOverlaps,
sizeof(Level->NumOverlaps));
if (Level->NumOverlaps > 0) {
TR2fwrite(Level->Overlaps,
2, Level->NumOverlaps, fp);
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
/* write Zones (?) */
if (Level->NumBoxes > 0) {
TR2fwrite(Level->Zones,
20, Level->NumBoxes, fp);
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
/* write animation textures (?) */
CONVERT_ENDIAN(&Level->NumAnimatedTextures,
sizeof(Level->NumAnimatedTextures));
TR2fwrite(&Level->NumAnimatedTextures,
sizeof(Level->NumAnimatedTextures), 1, fp);
CONVERT_ENDIAN(&Level->NumAnimatedTextures,
sizeof(Level->NumAnimatedTextures));
if (Level->NumAnimatedTextures > 0)
{
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumAnimatedTextures; ++j) {
CONVERT_ENDIAN(&Level->AnimatedTextures[j], sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->AnimatedTextures,
sizeof(bit16), Level->NumAnimatedTextures, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumAnimatedTextures; ++j) {
CONVERT_ENDIAN(&Level->AnimatedTextures[j], sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write items (?) */
CONVERT_ENDIAN(&Level->NumItems,
sizeof(Level->NumItems));
TR2fwrite(&Level->NumItems, sizeof(Level->NumItems),
1, fp);
CONVERT_ENDIAN(&Level->NumItems,
sizeof(Level->NumItems));
if (Level->NumItems > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumItems; ++j) {
CONVERT_ENDIAN(&Level->Items[j].ObjectID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Room, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].Angle, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity1, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity2, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->Items,
sizeof(tr2_item), Level->NumItems, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumItems; ++j) {
CONVERT_ENDIAN(&Level->Items[j].ObjectID, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Room, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].x, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].y, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].z, sizeof(bitu32));
CONVERT_ENDIAN(&Level->Items[j].Angle, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity1, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Intensity2, sizeof(bitu16));
CONVERT_ENDIAN(&Level->Items[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write LightMaps */
TR2fwrite(Level->LightMap, 32, 256,
fp);
/* write cinematic frames */
CONVERT_ENDIAN(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames));
TR2fwrite(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames), 1, fp);
CONVERT_ENDIAN(&Level->NumCinematicFrames,
sizeof(Level->NumCinematicFrames));
if (Level->NumCinematicFrames > 0) {
TR2fwrite(Level->CinematicFrames,
16, Level->NumCinematicFrames, fp);
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
/* write demodata (?) */
CONVERT_ENDIAN(&Level->NumDemoData,
sizeof(Level->NumDemoData));
TR2fwrite(&Level->NumDemoData, sizeof(Level->NumDemoData),
1, fp);
CONVERT_ENDIAN(&Level->NumDemoData,
sizeof(Level->NumDemoData));
if (Level->NumDemoData > 0) {
TR2fwrite(Level->DemoData,
1, Level->NumDemoData, fp);
// There may or may
not be endian conversion required here - I have
// no idea what this
data is.
}
/* write SoundMap */
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < 370; ++j) {
CONVERT_ENDIAN(&Level->SoundMap[j],
sizeof(bitu16));
}
#endif
TR2fwrite(Level->SoundMap, sizeof(bit16),
370, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j < 370; ++j) {
CONVERT_ENDIAN(&Level->SoundMap[j],
sizeof(bitu16));
}
#endif
/* write SoundDetails */
CONVERT_ENDIAN(&Level->NumSoundDetails,
sizeof(Level->NumSoundDetails));
TR2fwrite(&Level->NumSoundDetails,
sizeof(Level->NumSoundDetails), 1, fp);
CONVERT_ENDIAN(&Level->NumSoundDetails,
sizeof(Level->NumSoundDetails));
if (Level->NumSoundDetails > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSoundDetails; ++j) {
CONVERT_ENDIAN(&Level->SoundDetails[j].Sample, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Volume, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].SoundRange, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
TR2fwrite(Level->SoundDetails,
sizeof(tr2_sound_details), Level->NumSoundDetails, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSoundDetails; ++j) {
CONVERT_ENDIAN(&Level->SoundDetails[j].Sample, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Volume, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].SoundRange, sizeof(bitu16));
CONVERT_ENDIAN(&Level->SoundDetails[j].Flags, sizeof(bitu16));
}
#endif // BIG_ENDIAN_CPU
}
/* write sampleindices */
CONVERT_ENDIAN(&Level->NumSampleIndices,
sizeof(Level->NumSampleIndices));
TR2fwrite(&Level->NumSampleIndices,
sizeof(Level->NumSampleIndices), 1, fp);
CONVERT_ENDIAN(&Level->NumSampleIndices,
sizeof(Level->NumSampleIndices));
if (Level->NumSampleIndices > 0) {
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSampleIndices; ++j) {
CONVERT_ENDIAN(&Level->SampleIndices[j], sizeof(bitu32));
}
#endif
TR2fwrite(Level->SampleIndices,
sizeof(bitu32), Level->NumSampleIndices, fp);
#ifdef BIG_ENDIAN_CPU
for (j = 0; j <
Level->NumSampleIndices; ++j) {
CONVERT_ENDIAN(&Level->SampleIndices[j], sizeof(bitu32));
}
#endif
}
fclose(fp);
ReturnValue = 0;
}
else {
perror(FileName);
ReturnValue = -1;
}
return(ReturnValue);
}
/*
* HexDump() does what its name implies - a 16-byte-wide dump,
with offsets
* starting at the specified value.
*/
void HexDump(unsigned char *buffer, int len, int offset, char *prefix,
FILE *fp)
{
int i;
if (len != 0) { // don't display anything if
it's zero-length
do {
fprintf(fp, "%s%04x
", prefix, offset);
offset += 16;
for (i = 0; i <
16 && len--; ++i) {
fprintf(fp, "%02x ", *buffer++);
}
fprintf(fp, "\n");
} while (len > 0);
}
}
/*
* DumpTR2level(TR2_Level *Level, char *FileName)
*
* Creates <FileName> and dumps readable ASCII level data to
it.
* Print an easy-to-find marker (e.g. "-=-=-=-=-=" at every heading.
*/
int DumpTR2level(TR2_Level *Level, char *FileName)
{
FILE *fp;
int ReturnValue,
i,
j,
k,
l;
if ((fp = fopen(FileName, WRITETEXT)) != NULL) {
fprintf(fp, "Dump of data for '%s'\n\n",
Level->FileName);
fprintf(fp, "Version = %ld (0x%08lx)\n",
Level->Version, Level->Version);
fprintf(fp, "8-bit palette:\n");
for (i = 0; i < 256; ++i) {
fprintf(fp, "
Palette8[%3d]: Red %3d Green %3d Blue %3d\n", i, Level->Palette8[i].Red,
Level->Palette8[i].Green, Level->Palette8[i].Blue);
}
fprintf(fp, "16-bit palette: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
HexDump((bitu8 *)Level->Palette16, sizeof(Level->Palette16),
0, "", fp);
// skip textiles - hex dumps of graphic bitmaps aren't much fun to
look at...
fprintf(fp, "(Skipped %d 8-bit Textiles) -=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumTextiles);
fprintf(fp, "(Skipped %d 16-bit Textiles) -=-=-=-=-=-=-=-=-=-=-=\n",
Level->NumTextiles);
fprintf(fp, "UnknownT = %ld (0x%08lx)\n",
Level->UnknownT, Level->UnknownT);
fprintf(fp, "NumRooms: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumRooms);
for (i = 0; i < Level->NumRooms;
++i) {
fprintf(fp, "Room
%d: ---===---\n", i);
fprintf(fp, "
Info: X %d Z %d yBottom %d yTop %d\n", Level->Rooms[i].info.x,
Level->Rooms[i].info.z, Level->Rooms[i].info.yBottom, Level->Rooms[i].info.yTop);
fprintf(fp, "
NumVertices: %d\n", Level->Rooms[i].RoomData.NumVertices);
for (j = 0; j <
Level->Rooms[i].RoomData.NumVertices; ++j) {
fprintf(fp, " Vertex[%3d]: (%5d, %5d, %5d)
Lighting1 %d Attributes 0x%04x Lighting2 %d\n", j,
Level->Rooms[i].RoomData.Vertices[j].Vertex.x,
Level->Rooms[i].RoomData.Vertices[j].Vertex.y,
Level->Rooms[i].RoomData.Vertices[j].Vertex.z,
Level->Rooms[i].RoomData.Vertices[j].Lighting1,
Level->Rooms[i].RoomData.Vertices[j].Attributes,
Level->Rooms[i].RoomData.Vertices[j].Lighting2);
}
fprintf(fp, "
NumRectangles: %d\n", Level->Rooms[i].RoomData.NumRectangles);
for (j = 0; j <
Level->Rooms[i].RoomData.NumRectangles; ++j) {
fprintf(fp, " Rectangles[%3d]: Vertices (%3d
%3d %3d %3d) Texture %d\n", j,
Level->Rooms[i].RoomData.Rectangles[j].Vertices[0],
Level->Rooms[i].RoomData.Rectangles[j].Vertices[1],
Level->Rooms[i].RoomData.Rectangles[j].Vertices[2],
Level->Rooms[i].RoomData.Rectangles[j].Vertices[3],
Level->Rooms[i].RoomData.Rectangles[j].Texture);
}
fprintf(fp, "
NumTriangles: %d\n", Level->Rooms[i].RoomData.NumTriangles);
for (j = 0; j <
Level->Rooms[i].RoomData.NumTriangles; ++j) {
fprintf(fp, " Triangles[%3d]: Vertices (%3d
%3d %3d %3d) Texture %d\n", j,
Level->Rooms[i].RoomData.Triangles[j].Vertices[0],
Level->Rooms[i].RoomData.Triangles[j].Vertices[1],
Level->Rooms[i].RoomData.Triangles[j].Vertices[2],
Level->Rooms[i].RoomData.Triangles[j].Texture);
}
fprintf(fp, "
NumSprites: %d\n", Level->Rooms[i].RoomData.NumSprites);
for (j = 0; j <
Level->Rooms[i].RoomData.NumSprites; ++j) {
fprintf(fp, " Sprites[%3d]: Vertex %3d
Texture %d\n", j,
Level->Rooms[i].RoomData.Sprites[j].Vertex,
Level->Rooms[i].RoomData.Sprites[j].Texture);
}
fprintf(fp, "
NumPortals: %d\n", Level->Rooms[i].NumPortals);
for (j = 0; j <
Level->Rooms[i].NumPortals; ++j) {
fprintf(fp, " Portals[%2d]: Adjoining Room
%2d Normal (%3d, %3d, %3d)\n", j,
Level->Rooms[i].Portals[j].AdjoiningRoom,
Level->Rooms[i].Portals[j].Normal.x,
Level->Rooms[i].Portals[j].Normal.y,
Level->Rooms[i].Portals[j].Normal.z);
fprintf(fp, "
Vertices (%5d, %5d, %5d) (%5d, %5d, %5d) (%5d, %5d, %5d) (%5d, %5d, %5d)\n",
Level->Rooms[i].Portals[j].Vertices[0].x,
Level->Rooms[i].Portals[j].Vertices[0].y,
Level->Rooms[i].Portals[j].Vertices[0].z,
Level->Rooms[i].Portals[j].Vertices[1].x,
Level->Rooms[i].Portals[j].Vertices[1].y,
Level->Rooms[i].Portals[j].Vertices[1].z,
Level->Rooms[i].Portals[j].Vertices[2].x,
Level->Rooms[i].Portals[j].Vertices[2].y,
Level->Rooms[i].Portals[j].Vertices[2].z,
Level->Rooms[i].Portals[j].Vertices[3].x,
Level->Rooms[i].Portals[j].Vertices[3].y,
Level->Rooms[i].Portals[j].Vertices[3].z);
}
fprintf(fp, "
NumZsectors: %d NumXsectors: %d\n", Level->Rooms[i].NumZsectors,
Level->Rooms[i].NumXsectors);
for (j = 0; j <
(Level->Rooms[i].NumZsectors * Level->Rooms[i].NumXsectors); ++j) {
fprintf(fp, " Sector[%3d]: FDindex %5d
BoxIndex %d RoomBelow %3d Floor %4d RoomAbove %3d
Ceiling %4d\n", j,
Level->Rooms[i].SectorList[j].FDindex,
Level->Rooms[i].SectorList[j].BoxIndex,
Level->Rooms[i].SectorList[j].RoomBelow,
Level->Rooms[i].SectorList[j].Floor,
Level->Rooms[i].SectorList[j].RoomAbove,
Level->Rooms[i].SectorList[j].Ceiling);
}
fprintf(fp, "
SectorData (6):\n");
HexDump((bitu8 *)&Level->Rooms[i].Intensity1,
6, 0, " ", fp);
fprintf(fp, "
NumLights: %d\n", Level->Rooms[i].NumLights);
for (j = 0; j <
Level->Rooms[i].NumLights; ++j) {
fprintf(fp, " Light %2d: pos (%5d, %5d, %5d)
Intensity1 %d Intensity2 %d Fade1 %d Fade2 %d\n", j,
Level->Rooms[i].Lights[j].x, Level->Rooms[i].Lights[j].y, Level->Rooms[i].Lights[j].z,
Level->Rooms[i].Lights[j].Intensity1, Level->Rooms[i].Lights[j].Intensity2,
Level->Rooms[i].Lights[j].Fade1, Level->Rooms[i].Lights[j].Fade2);
}
fprintf(fp, "
NumStaticMeshes: %d\n", Level->Rooms[i].NumStaticMeshes);
for (j = 0; j <
Level->Rooms[i].NumStaticMeshes; ++j) {
fprintf(fp, " StaticMesh %2d: pos (%6d, %6d,
%6d) Rotation 0x%04x Unknown2 %d Unknown3 %d ObjectID
%d\n", j,
Level->Rooms[i].StaticMeshes[j].x, Level->Rooms[i].StaticMeshes[j].y, Level->Rooms[i].StaticMeshes[j].z,
Level->Rooms[i].StaticMeshes[j].Rotation, Level->Rooms[i].StaticMeshes[j].Intensity1,
Level->Rooms[i].StaticMeshes[j].Intensity2,
Level->Rooms[i].StaticMeshes[j].ObjectID);
}
// HexDump(Level->Rooms[i].LightData,
Level->Rooms[i].NumLightData * 20, 0, " ",
fp);
fprintf(fp, "
AlternateRoom: %d Flags: %d\n", Level->Rooms[i].AlternateRoom, Level->Rooms[i].Flags);
if (Level->EngineVersion
>= TombRaider_3) {
fprintf(fp, " RoomLightColour: R %d G %d B %d\n", Level->Rooms[i].RoomLightColour.Red,
Level->Rooms[i].RoomLightColour.Green, Level->Rooms[i].RoomLightColour.Blue);
}
}
#ifdef NEVER_COMPILE_THIS
fprintf(fp, "NumFloorData: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumFloorData);
for (i = 0; i < (int)Level->NumFloorData;
++i) {
fprintf(fp, "
FloorData[%4d] = 0x%04x\n", i, Level->FloorData[i]);
}
#endif
// FloorData parser
fprintf(fp, "Parsed FloorData: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
fprintf(fp, " FloorData[
0] = 0x%04x (ignored)\n", Level->FloorData[0]);
for (i = 1; i < (int)Level->NumFloorData;
) {
bitu8 Function, SubFunction,
More;
Function = Level->FloorData[i]
& 0xff;
SubFunction = (Level->FloorData[i]
& 0x7fff) >> 8;
More = ((Level->FloorData[i]
& 0x8000) == 0);
fprintf(fp, "
FloorData[%4d]: 0x%04x ", i, Level->FloorData[i]);
++i;
switch (Function)
{
case 0x01 : // door
fprintf(fp, "0x%04x Door to room %d (SubFunction 0x%02x)\n", Level->FloorData[i],
Level->FloorData[i], SubFunction);
++i; // skip room number
break;
case 0x02 : // floor slant
fprintf(fp, "0x%04x Floor Slant UD %4d LR %4d (SubFunction
0x%02x)\n", Level->FloorData[i], (bit8)((Level->FloorData[i] >> 8) &
0xff), (bit8)(Level->FloorData[i] & 0xff), SubFunction);
++i; // skip slant value
break;
case 0x03 : // ceiling slant
fprintf(fp, "0x%04x Ceiling Slant UD %4d LR %4d (SubFunction
0x%02x)\n", Level->FloorData[i], (bit8)((Level->FloorData[i] >> 8) &
0xff), (bit8)(Level->FloorData[i] & 0xff), SubFunction);
++i; // skip slant value
break;
case 0x04 : // fdlist action
fprintf(fp, "0x%04x ", Level->FloorData[i]);
switch (SubFunction) {
case 0x00 :
fprintf(fp, "Run FDlist (ACTIVATE)");
break;
case 0x01 :
fprintf(fp, "If Lara on Ground, Run FDlist (ACTIVATE)");
break;
case 0x02 :
fprintf(fp, "Activate/Deactivate FDlist+1 if item %d active/inactive",
Level->FloorData[i + 1] & 0x03ff);
break;
case 0x03 :
fprintf(fp, "Activate FDlist+1 if item %d active", Level->FloorData[i +
1] & 0x03ff);
break;
case 0x04 :
fprintf(fp, "Activate FDlist+1 if item %d is picked up", Level->FloorData[i
+ 1] & 0x03ff);
break;
case 0x06 :
fprintf(fp, "If Lara on Ground, Run FDlist (DEactivate)");
break;
case 0x09 :
fprintf(fp, "Run FDlist (DEactivate)");
break;
default :
fprintf(fp, "[unknown FDlist SubFunction 0x%02x]", SubFunction);
break;
}
fprintf(fp, " (unknown1 = %d)\n", Level->FloorData[i]);
++i; // skip unknown1
j = 0;
do {
bitu8 command;
fprintf(fp, " FDlist[%3d]: 0x%04x ",
j++, Level->FloorData[i]);
command = (Level->FloorData[i] >> 10) & 0x1f;
switch (command) {
case 0x00 :
fprintf(fp, "Activate/Deactivate item %d\n", Level->FloorData[i] &
0x03ff);
break;
case 0x01 :
fprintf(fp, "Switch to camera [%d]\n", Level->FloorData[i] & 0x03ff);
break;
case 0x02 :
k = Level->FloorData[i] & 0x03ff;
fprintf(fp, "Camera Delay %d sec, %s, unk bit %d\n", k & 0xff, (k &
0x100) ? "ONCE" : "Every Time", (k & 0x200) != 0);
break;
case 0x06 :
fprintf(fp, "Look at item %d\n", Level->FloorData[i] & 0x03ff);
break;
case 0x07 :
fprintf(fp, "End Level\n");
break;
default :
fprintf(fp, "Unknown command 0x%02x, data 0x%04x\n", command, Level->FloorData[i]
& 0x03ff);
break;
}
} while ((Level->FloorData[i++] & 0x8000) == 0);
break;
case 0x05 : // kills Lara
fprintf(fp, " Kills Lara - SubFunction
0x%02x\n", SubFunction);
break;
case 0x06 : // used for walls that are climbable
fprintf(fp, " Climbable Wall%s:",
(SubFunction == 1 || SubFunction == 2 || SubFunction == 4 || SubFunction
== 8) ? "" : "s");
if (SubFunction & 0x02)
fprintf(fp, " +X");
if (SubFunction & 0x01)
fprintf(fp, " +Z");
if (SubFunction & 0x08)
fprintf(fp, " -X");
if (SubFunction & 0x04)
fprintf(fp, " -Z");
fprintf(fp, "\n");
break;
}
if (!More)
fprintf(fp, " --- end ---\n");
}
fprintf(fp, "NumMeshes: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumMeshes);
for (i = 0; i < Level->NumMeshes;
++i) {
fprintf(fp, "Meshes[%3d]:
Centre (%5d, %5d, %5d)\n", i, Level->Meshes[i].Centre.x, Level->Meshes[i].Centre.y,
Level->Meshes[i].Centre.z);
fprintf(fp, "
CollisionSize: 0x08lx\n", Level->Meshes[i].CollisionSize);
fprintf(fp, "
NumVertices: %d\n", Level->Meshes[i].NumVertices);
for (j = 0; j <
Level->Meshes[i].NumVertices; ++j) {
fprintf(fp, " Vertex[%3d]:
(%5d, %5d, %5d)\n", j,
Level->Meshes[i].Vertices[j].x,
Level->Meshes[i].Vertices[j].y,
Level->Meshes[i].Vertices[j].z);
}
fprintf(fp, "
NumNormals: %d\n", Level->Meshes[i].NumNormals * ((Level->Meshes[i].MeshLights
!= NULL) ? -1 : 1));
for (j = 0; j <
Level->Meshes[i].NumNormals; ++j) {
if (Level->Meshes[i].MeshLights != NULL) {
fprintf(fp, " MeshLights[%3d]:
%5d\n", j, Level->Meshes[i].MeshLights[j]);
}
else {
fprintf(fp, " Normal[%3d]:
(%5d, %5d, %5d)\n", j,
Level->Meshes[i].Normals[j].x,
Level->Meshes[i].Normals[j].y,
Level->Meshes[i].Normals[j].z);
}
}
fprintf(fp, "
NumTexturedRectangles: %d\n", Level->Meshes[i].NumTexturedRectangles);
for (j = 0; j <
Level->Meshes[i].NumTexturedRectangles; ++j) {
fprintf(fp, " TexturedRectangles[%3d]:
Vertices (%3d %3d %3d %3d) Texture %d\n", j,
Level->Meshes[i].TexturedRectangles[j].Vertices[0],
Level->Meshes[i].TexturedRectangles[j].Vertices[1],
Level->Meshes[i].TexturedRectangles[j].Vertices[2],
Level->Meshes[i].TexturedRectangles[j].Vertices[3],
Level->Meshes[i].TexturedRectangles[j].Texture);
}
fprintf(fp, "
NumTexturedTriangles: %d\n", Level->Meshes[i].NumTexturedTriangles);
for (j = 0; j <
Level->Meshes[i].NumTexturedTriangles; ++j) {
fprintf(fp, " TexturedTriangles[%3d]:
Vertices (%3d %3d %3d) Texture %d\n", j,
Level->Meshes[i].TexturedTriangles[j].Vertices[0],
Level->Meshes[i].TexturedTriangles[j].Vertices[1],
Level->Meshes[i].TexturedTriangles[j].Vertices[2],
Level->Meshes[i].TexturedTriangles[j].Texture);
}
fprintf(fp, "
NumColouredRectangles: %d\n", Level->Meshes[i].NumColouredRectangles);
for (j = 0; j <
Level->Meshes[i].NumColouredRectangles; ++j) {
fprintf(fp, " ColouredRectangles[%3d]:
Vertices (%3d %3d %3d %3d) Colour 0x%02x (=%d?)\n", j,
Level->Meshes[i].ColouredRectangles[j].Vertices[0],
Level->Meshes[i].ColouredRectangles[j].Vertices[1],
Level->Meshes[i].ColouredRectangles[j].Vertices[2],
Level->Meshes[i].ColouredRectangles[j].Vertices[3],
Level->Meshes[i].ColouredRectangles[j].Texture,
Level->Meshes[i].ColouredRectangles[j].Texture & 0xff);
}
fprintf(fp, "
NumColouredTriangles: %d\n", Level->Meshes[i].NumColouredTriangles);
for (j = 0; j <
Level->Meshes[i].NumColouredTriangles; ++j) {
fprintf(fp, " ColouredTriangles[%3d]:
Vertices (%3d %3d %3d) Colour 0x%02x (=%d?)\n", j,
Level->Meshes[i].ColouredTriangles[j].Vertices[0],
Level->Meshes[i].ColouredTriangles[j].Vertices[1],
Level->Meshes[i].ColouredTriangles[j].Vertices[2],
Level->Meshes[i].ColouredTriangles[j].Texture,
Level->Meshes[i].ColouredTriangles[j].Texture & 0xff);
}
}
fprintf(fp, "NumAnimations: %d\n", Level->NumAnimations);
for (i = 0; i < (int)Level->NumAnimations;
++i) {
fprintf(fp, "Animations[%3d]:
FrameOffset %d (FrameIndex %d) Unknown1 %d FrameSize
%d\n", i,
Level->Animations[i].FrameOffset, Level->Animations[i].FrameOffset / 2,
Level->Animations[i].Unknown1, Level->Animations[i].FrameSize);
fprintf(fp, "
Unknown2 (10):\n");
HexDump((bitu8 *)&Level->Animations[i].Unknown1,
8, 0, " ", fp);
fprintf(fp, "
FrameStart %d FrameEnd %d\n", Level->Animations[i].FrameStart, Level->Animations[i].FrameEnd);
fprintf(fp, "
AnimationChain Anim:Frame %d:%d\n",
Level->Animations[i].NextAnimation,
Level->Animations[i].NextFrame);
fprintf(fp, "
StartingStateChange %d NumStateChanges %d\n",
Level->Animations[i].StateChangeOffset,
Level->Animations[i].NumStateChanges);
fprintf(fp, "
NumAnimCommands %d AnimCommands %d\n",
Level->Animations[i].NumAnimCommands,
Level->Animations[i].AnimCommand);
}
fprintf(fp, "NumStateChanges: %d
(", Level->NumStateChanges);
if (Level->NumStateChanges > 0) {
for (i = 0, j = 0, k = 0x7fffffff; i < (int)Level->NumStateChanges;
++i) {
if (Level->StateChanges[i].StateID > j)
j = Level->StateChanges[i].StateID;
if (Level->StateChanges[i].StateID < k)
k = Level->StateChanges[i].StateID;
}
fprintf(fp, "MinMax %d..%d", k, j);
}
fprintf(fp, ") -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
for (i = 0; i < (int)Level->NumStateChanges;
++i) {
fprintf(fp, "
StateChanges[%3d]: StateID %3d NumAnimDispatches %2d AnimDispatch %d\n",
i,
Level->StateChanges[i].StateID,
Level->StateChanges[i].NumAnimDispatches,
Level->StateChanges[i].AnimDispatch);
}
fprintf(fp, "NumAnimDispatches: %d (",
Level->NumAnimDispatches);
if (Level->NumAnimDispatches > 0) {
for (i = 0, j = 0,
k = 0x7fffffff; i < (int)Level->NumAnimDispatches; ++i) {
if (Level->AnimDispatches[i].Low > j)
j = Level->AnimDispatches[i].Low;
if (Level->AnimDispatches[i].Low < k)
k = Level->AnimDispatches[i].Low;
}
fprintf(fp, "MinMax
%d..%d", k, j);
}
fprintf(fp, ") -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
for (i = 0; i < (int)Level->NumAnimDispatches;
++i) {
fprintf(fp, "
AnimDispatches[%3d]: Low %5d High %5d NextAnimation %5d
NextFrame %5d\n", i,
Level->AnimDispatches[i].Low,
Level->AnimDispatches[i].High,
Level->AnimDispatches[i].NextAnimation,
Level->AnimDispatches[i].NextFrame);
}
for (i = 0, j = 0, k = 0x7fffffff; i
< (int)Level->NumAnimCommands; ++i) {
if (Level->AnimCommands[i].Value
> j)
j = Level->AnimCommands[i].Value;
if (Level->AnimCommands[i].Value
< k)
k = Level->AnimCommands[i].Value;
}
fprintf(fp, "NumAnimCommands: %d (min
%d max %d) -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", Level->NumAnimCommands,
k, j);
for (i = 0; i < (int)Level->NumAnimCommands;
++i) {
fprintf(fp, "
AnimCommands[%4d] Value %6d (0x%04x)\n", i, Level->AnimCommands[i].Value,
(bitu16)Level->AnimCommands[i].Value);
}
fprintf(fp, "NumMeshTrees: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumMeshTrees);
for (i = 0; i < (int)(Level->NumMeshTrees
/ sizeof(tr2_meshtree)); ++i) {
fprintf(fp, "
MeshTrees[%4d] Flags 0x%04x x %d y %d z %d\n", i,
Level->MeshTrees[i].Flags,
Level->MeshTrees[i].x, Level->MeshTrees[i].y, Level->MeshTrees[i].z);
}
for (i = 0, j = 0, k = 0x7fffffff; i
< (int)Level->NumFrames; ++i) {
if (Level->Frames[i]
> j)
j = Level->Frames[i];
if (Level->Frames[i]
< k)
k = Level->Frames[i];
}
fprintf(fp, "NumFrames: %d (min %d
max %d) -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", Level->NumFrames, k, j);
for (i = 0; i < (int)Level->NumFrames;
++i) {
fprintf(fp, "
Frames[%6d]: %5d (0x%04x) (%6d)\n", i, Level->Frames[i], Level->Frames[i],
(bit16)Level->Frames[i]);
}
fprintf(fp, "NumMoveables: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumMoveables);
for (i = 0; i < (int)Level->NumMoveables;
++i) {
fprintf(fp, "
Moveables[%3d]: ObjectID %d (0x%08x)\n", i,
Level->Moveables[i].ObjectID,
Level->Moveables[i].ObjectID);
fprintf(fp, "
NumMeshes %d StartingMesh %d MeshTree %d\n",
Level->Moveables[i].NumMeshes,
Level->Moveables[i].StartingMesh,
Level->Moveables[i].MeshTree);
fprintf(fp, "
FrameOffset %d Animation %d\n",
Level->Moveables[i].FrameOffset,
Level->Moveables[i].Animation);
}
fprintf(fp, "NumStaticMeshes: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumStaticMeshes);
#ifdef NEVER_COMPILE_THIS
for (i = 0; i < (int)Level->NumStaticMeshes;
++i) {
fprintf(fp, "
StaticMeshes[%3d]: ObjectID %d Unknown1 %d StartingMesh %d\n",
i,
Level->StaticMeshes[i].ObjectID, Level->StaticMeshes[i].Unknown1, Level->StaticMeshes[i].StartingMesh);
for (j = 0; j <
13; ++j) {
fprintf(fp, " Unknown2[%2d]: %5d (0x%04x)
(%6d)\n", j, Level->StaticMeshes[i].Unknown2[j], Level->StaticMeshes[i].Unknown2[j],
(bit16)Level->StaticMeshes[i].Unknown2[j]);
}
}
#endif
fprintf(fp, "NumObjectTextures: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumObjectTextures);
for (i = 0; i < (int)Level->NumObjectTextures;
++i) {
fprintf(fp, "
ObjectTextures[%4d]: TransparencyFlags %d Tile %d\n", i, Level->ObjectTextures[i].TransparencyFlags,
Level->ObjectTextures[i].Tile);
for (j = 0; j <
4; ++j) {
fprintf(fp, "
Vertex %2d: XCoord %3d XPixel %3d Ycoord %3d Ypixel %3d\n",
j,
Level->ObjectTextures[i].Vertices[j].Xcoordinate,
Level->ObjectTextures[i].Vertices[j].Xpixel,
Level->ObjectTextures[i].Vertices[j].Ycoordinate,
Level->ObjectTextures[i].Vertices[j].Ypixel);
}
}
fprintf(fp, "NumSpriteTextures: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumSpriteTextures);
for (i = 0; i < (int)Level->NumSpriteTextures;
++i) {
fprintf(fp, "
SpriteTextures[%3d]: Tile %d X %d Y %d\n", i,
Level->SpriteTextures[i].Tile,
Level->SpriteTextures[i].x,
Level->SpriteTextures[i].y);
fprintf(fp, "
Width %3d Height %3d\n",
Level->SpriteTextures[i].Width,
Level->SpriteTextures[i].Height);
fprintf(fp, "
LeftSide %3d TopSide %3d RightSide %3d BottomSide %3d\n",
Level->SpriteTextures[i].LeftSide,
Level->SpriteTextures[i].TopSide,
Level->SpriteTextures[i].RightSide,
Level->SpriteTextures[i].BottomSide);
}
fprintf(fp, "NumSpriteSequences: %d
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", Level->NumSpriteSequences);
for (i = 0; i < (int)Level->NumSpriteSequences;
++i) {
fprintf(fp, "
SpriteSequences[%2d]: ObjectID %d (0x%08x) NegativeLength %4d
Offset %3d (0x%04x)\n",
i, Level->SpriteSequences[i].ObjectID, Level->SpriteSequences[i].ObjectID,
Level->SpriteSequences[i].NegativeLength, Level->SpriteSequences[i].Offset,
(bitu16)Level->SpriteSequences[i].Offset);
}
fprintf(fp, "NumCameras: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumCameras);
for (i = 0; i < Level->NumCameras;
++i) {
fprintf(fp, "
Cameras[%2d]: ", i);
fprintf(fp, "X %d
Y %d Z %d Room %d Index2 %d\n",
Level->Cameras[i].x, Level->Cameras[i].y, Level->Cameras[i].z, Level->Cameras[i].Room,
Level->Cameras[i].Unknown1);
}
fprintf(fp, "NumSoundSources: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumSoundSources);
for (i = 0; i < Level->NumSoundSources;
++i) {
fprintf(fp, "
SoundSources[%d]: x %ld y %ld z %ld SoundMapIndex %d
Flags 0x%04x\n", i,
Level->SoundSources[i].x, Level->SoundSources[i].y, Level->SoundSources[i].z,
Level->SoundSources[i].SoundID, Level->SoundSources[i].Flags);
}
fprintf(fp, "NumBoxes: %d ", Level->NumBoxes);
{
int MinZ0 = 999,
MinZ1 = 999,
MaxZ0 = -999,
MaxZ1 = -999,
MinX0 = 999,
MinX1 = 999,
MaxX0 = -999,
MaxX1 = -999;
for (i = 0; i <
Level->NumBoxes; ++i) {
if (Level->Boxes[i].Zmin < MinZ0)
MinZ0 = Level->Boxes[i].Zmin;
if (Level->Boxes[i].Zmin > MaxZ0)
MaxZ0 = Level->Boxes[i].Zmin;
if (Level->Boxes[i].Zmax < MinZ1)
MinZ1 = Level->Boxes[i].Zmax;
if (Level->Boxes[i].Zmax > MaxZ1)
MaxZ1 = Level->Boxes[i].Zmax;
if (Level->Boxes[i].Xmin < MinX0)
MinX0 = Level->Boxes[i].Xmin;
if (Level->Boxes[i].Xmin > MaxX0)
MaxX0 = Level->Boxes[i].Xmin;
if (Level->Boxes[i].Xmax < MinX1)
MinX1 = Level->Boxes[i].Xmax;
if (Level->Boxes[i].Xmax > MaxX1)
MaxX1 = Level->Boxes[i].Xmax;
}
fprintf(fp, "Zmin
%d/%d Zmax %d/%d Xmin %d/%d Xmax %d/%d ", MinZ0, MaxZ0,
MinZ1, MaxZ1, MinX0, MaxX0, MinX1, MaxX1);
}
for (i = 0, j = 0, k = 0x7fffffff; i
< Level->NumBoxes; ++i) {
if (Level->Boxes[i].TrueFloor
> j)
j = Level->Boxes[i].TrueFloor;
if (Level->Boxes[i].TrueFloor
< k)
k = Level->Boxes[i].TrueFloor;
}
fprintf(fp, "TrueFloor %d/%d ", k, j);
{
int BoxFloors[2048], BoxIx = 0, jjj;
for (i = 0, l = 0; i < Level->NumBoxes;
++i) {
int found = FALSE;
for (jjj = 0; jjj
< BoxIx && !found; ++jjj) {
if (Level->Boxes[i].TrueFloor == BoxFloors[jjj])
found = TRUE;
}
if (!found)
BoxFloors[BoxIx++] = Level->Boxes[i].TrueFloor;
}
fprintf(fp, "(%d unique TrueFloors)
", BoxIx);
}
for (i = l, j = 0, k = 0x7fffffff; i
< Level->NumBoxes; ++i) {
if (Level->Boxes[i].OverlapIndex
> j)
j = Level->Boxes[i].OverlapIndex;
if (Level->Boxes[i].OverlapIndex
< k)
k = Level->Boxes[i].OverlapIndex;
}
fprintf(fp, "OverlapIndex %d/%d ", k,
j);
fprintf(fp, "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
for (i = 0; i < Level->NumBoxes;
++i) {
fprintf(fp, "
Boxes[%4d]: (Z %3d %3d, X %3d %3d) TrueFloor %6d OverlapIndex
%5d", i,
Level->Boxes[i].Zmin, Level->Boxes[i].Zmax, Level->Boxes[i].Xmin, Level->Boxes[i].Xmax,
Level->Boxes[i].TrueFloor, Level->Boxes[i].OverlapIndex & 0x7fff);
if (Level->Boxes[i].OverlapIndex
& 0x8000)
fprintf(fp, " | 0x8000");
fprintf(fp, "\n");
}
for (i = 0, j = 0, k = 0x7fffffff; i
< Level->NumOverlaps; ++i) {
if ((Level->Overlaps[i]
& 0x7fff) > j)
j = Level->Overlaps[i] & 0x7fff;
if ((Level->Overlaps[i]
& 0x7fff) < k)
k = Level->Overlaps[i] & 0x7fff;
}
if (Level->NumOverlaps == 0) {
j = 0;
k = 0;
}
fprintf(fp, "NumOverlaps: %d (min %d
max %d)", Level->NumOverlaps, k, j);
j = 99999;
k = 0;
for (i = 0; i < Level->NumOverlaps;
) {
l = 1;
while (!(Level->Overlaps[i++]
& 0x8000))
++l;
if (l < j)
j = l;
if (l > k)
k = l;
}
fprintf(fp, " (runs: shortest %d
longest %d) -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", j, k);
for (i = 0; i < Level->NumOverlaps;
++i) {
fprintf(fp, "
Overlaps[%4d]: %6d (0x%04x) (%5d)\n", i, Level->Overlaps[i], (bitu16)Level->Overlaps[i],
Level->Overlaps[i] & 0x7fff);
}
fprintf(fp, "Zones: MinMax ");
for (l = 0; l < 10; ++l) {
for (i = 0, j = 0,
k = 0x7fffffff; i < Level->NumBoxes; ++i) {
if (Level->Zones[(l * Level->NumBoxes) + i] > j)
j = Level->Zones[(l * Level->NumBoxes) + i];
if (Level->Zones[(l * Level->NumBoxes) + i] < k)
k = Level->Zones[(l * Level->NumBoxes) + i];
}
fprintf(fp, "%3d/%3d
", k, j);
}
fprintf(fp, " Num %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumBoxes);
for (i = 0; i < Level->NumBoxes; ++i)
{
fprintf(fp, "
Zones[%4d]: ", i);
for (j = 0; j < 10; ++j)
{
fprintf(fp,
"%6d ", Level->Zones[(j * Level->NumBoxes) + i]);
}
fprintf(fp, "\n");
}
fprintf(fp, "NumAnimatedTextures: %d
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", Level->NumAnimatedTextures);
for (i = 0; i < Level->NumAnimatedTextures;
++i) {
fprintf(fp, "
AnimatedTextures[%2d]: %6d (0x%04x)\n", i, Level->AnimatedTextures[i],
(bitu16)Level->AnimatedTextures[i]);
}
fprintf(fp, "NumItems: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumItems);
for (i = 0; i < Level->NumItems;
++i) {
fprintf(fp, "
Items[%3d]: ObjectID %d (0x%04x) Room %d\n", i, Level->Items[i].ObjectID,
Level->Items[i].ObjectID, Level->Items[i].Room);
fprintf(fp, "
position (%5d, %5d, %5d)\n", Level->Items[i].x, Level->Items[i].y, Level->Items[i].z);
fprintf(fp, "
Angle %d Intensity1 %d Intensity2 %d Flags %d\n",
Level->Items[i].Angle,
Level->Items[i].Intensity1,
Level->Items[i].Intensity2,
Level->Items[i].Flags);
}
fprintf(fp, "LightMap (32 * 256) -=-=-=-=-=-=-=-=-=-=-=-\n");
for (i = 0; i < 32; ++i) {
fprintf(fp, "LightMap[%d]:\n",
i);
HexDump(&Level->LightMap[i
* 256], 256, 0, " ", fp);
}
fprintf(fp, "NumCinematicFrames: %d
(%d bytes) -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", Level->NumCinematicFrames,
Level->NumCinematicFrames * 16);
for (i = 0; i < Level->NumCinematicFrames;
++i) {
fprintf(fp, "
CinematicFrames[%3d]: ", i);
HexDump((bitu8 *)&Level->CinematicFrames[i
* 8], 16, i * 16, "", fp);
fprintf(fp, "
[%3d]: %6d %6d %6d %6d %6d %6d %6d %6d\n", i,
Level->CinematicFrames[(i * 8) + 0],
Level->CinematicFrames[(i * 8) + 1],
Level->CinematicFrames[(i * 8) + 2],
Level->CinematicFrames[(i * 8) + 3],
Level->CinematicFrames[(i * 8) + 4],
Level->CinematicFrames[(i * 8) + 5],
Level->CinematicFrames[(i * 8) + 6],
Level->CinematicFrames[(i * 8) + 7]);
}
fprintf(fp, "NumDemoData: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumDemoData);
HexDump(Level->DemoData, Level->NumDemoData,
0, " ", fp);
fprintf(fp, "SoundMap -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
for (i = 0; i < 370; ++i)
fprintf(fp, "
SoundMap[%3d] = %d\n", i, Level->SoundMap[i]);
fprintf(fp, "NumSoundDetails: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumSoundDetails);
for (i = 0; i < Level->NumSoundDetails;
++i) {
fprintf(fp, "
SoundDetails[%3d]: Sample %3d Volume %d Unknown1 %6d
Unknown2 0x%04x (flags1 %d / numsamp %d / flags2 %d)\n", i,
Level->SoundDetails[i].Sample, Level->SoundDetails[i].Volume, Level->SoundDetails[i].SoundRange,
Level->SoundDetails[i].Flags, (Level->SoundDetails[i].Flags & 0xf000)
>> 24,
(Level->SoundDetails[i].Flags & 0x0ffc) >> 2, Level->SoundDetails[i].Flags
& 0x0003);
}
fprintf(fp, "NumSampleIndices: %d -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n",
Level->NumSampleIndices);
for (i = 0; i < Level->NumSampleIndices;
++i) {
fprintf(fp, "
SampleIndices[%3d]: %6d (0x%08x)\n", i, Level->SampleIndices[i], Level->SampleIndices[i]);
}
fclose(fp);
ReturnValue = 0;
}
else {
perror(FileName);
ReturnValue = -1;
}
return(ReturnValue);
}