public TR2Level ReadLevel(string Filename) { if (!Filename.ToUpper().Contains("TR2")) { throw new NotImplementedException("File reader only supports TR2 levels"); } TR2Level level = new TR2Level(); reader = new BinaryReader(File.Open(Filename, FileMode.Open)); //Version level.Version = reader.ReadUInt32(); if (level.Version != TR2VersionHeader) { throw new NotImplementedException("File reader only suppors TR2 levels"); } //Colour palettes and textures level.Palette = PopulateColourPalette(reader.ReadBytes((int)MAX_PALETTE_SIZE * 3)); level.Palette16 = PopulateColourPalette16(reader.ReadBytes((int)MAX_PALETTE_SIZE * 4)); level.NumImages = reader.ReadUInt32(); level.Images8 = new TRTexImage8[level.NumImages]; level.Images16 = new TRTexImage16[level.NumImages]; //Initialize the texture arrays for (int i = 0; i < level.NumImages; i++) { level.Images8[i] = new TRTexImage8(); level.Images16[i] = new TRTexImage16(); } //For each texture8 there are 256 * 256 bytes (65536) we can just do a straight byte read for (int i = 0; i < level.NumImages; i++) { level.Images8[i].Pixels = reader.ReadBytes(256 * 256); } //For each texture16 there are 256 * 256 * 2 bytes (131072) for (int i = 0; i < level.NumImages; i++) { level.Images16[i].Pixels = new ushort[256 * 256]; for (int j = 0; j < level.Images16[i].Pixels.Count(); j++) { level.Images16[i].Pixels[j] = reader.ReadUInt16(); } } //Rooms level.Unused = reader.ReadUInt32(); level.NumRooms = reader.ReadUInt16(); level.Rooms = new TR2Room[level.NumRooms]; for (int i = 0; i < level.NumRooms; i++) { TR2Room room = new TR2Room(); //Grab info room.Info = new TRRoomInfo { X = reader.ReadInt32(), Z = reader.ReadInt32(), YBottom = reader.ReadInt32(), YTop = reader.ReadInt32() }; //Grab data room.NumDataWords = reader.ReadUInt32(); room.Data = new ushort[room.NumDataWords]; for (int j = 0; j < room.NumDataWords; j++) { room.Data[j] = reader.ReadUInt16(); } //Store what we just read room.RoomData = ConvertToRoomData(room); //Portals room.NumPortals = reader.ReadUInt16(); room.Portals = new TRRoomPortal[room.NumPortals]; for (int j = 0; j < room.NumPortals; j++) { room.Portals[j] = TR2FileReadUtilities.ReadRoomPortal(reader); } //Sectors room.NumZSectors = reader.ReadUInt16(); room.NumXSectors = reader.ReadUInt16(); room.SectorList = new TRRoomSector[room.NumXSectors * room.NumZSectors]; for (int j = 0; j < (room.NumXSectors * room.NumZSectors); j++) { room.SectorList[j] = TR2FileReadUtilities.ReadRoomSector(reader); } //Lighting room.AmbientIntensity = reader.ReadInt16(); room.AmbientIntensity2 = reader.ReadInt16(); room.LightMode = reader.ReadInt16(); room.NumLights = reader.ReadUInt16(); room.Lights = new TR2RoomLight[room.NumLights]; for (int j = 0; j < room.NumLights; j++) { room.Lights[j] = TR2FileReadUtilities.ReadRoomLight(reader); } //Static meshes room.NumStaticMeshes = reader.ReadUInt16(); room.StaticMeshes = new TR2RoomStaticMesh[room.NumStaticMeshes]; for (int j = 0; j < room.NumStaticMeshes; j++) { room.StaticMeshes[j] = TR2FileReadUtilities.ReadRoomStaticMesh(reader); } room.AlternateRoom = reader.ReadInt16(); room.Flags = reader.ReadInt16(); level.Rooms[i] = room; } //Floordata level.NumFloorData = reader.ReadUInt32(); level.FloorData = new ushort[level.NumFloorData]; for (int i = 0; i < level.NumFloorData; i++) { level.FloorData[i] = reader.ReadUInt16(); } //Mesh Data //This tells us how much mesh data (# of words/uint16s) coming up //just like the rooms previously. level.NumMeshData = reader.ReadUInt32(); level.RawMeshData = new ushort[level.NumMeshData]; for (int i = 0; i < level.NumMeshData; i++) { level.RawMeshData[i] = reader.ReadUInt16(); } //Mesh Pointers level.NumMeshPointers = reader.ReadUInt32(); level.MeshPointers = new uint[level.NumMeshPointers]; for (int i = 0; i < level.NumMeshPointers; i++) { level.MeshPointers[i] = reader.ReadUInt32(); } //Mesh Construction //level.Meshes = ConstructMeshData(level.NumMeshData, level.NumMeshPointers, level.RawMeshData); //Animations level.NumAnimations = reader.ReadUInt32(); level.Animations = new TRAnimation[level.NumAnimations]; for (int i = 0; i < level.NumAnimations; i++) { level.Animations[i] = TR2FileReadUtilities.ReadAnimation(reader); } //State Changes level.NumStateChanges = reader.ReadUInt32(); level.StateChanges = new TRStateChange[level.NumStateChanges]; for (int i = 0; i < level.NumStateChanges; i++) { level.StateChanges[i] = TR2FileReadUtilities.ReadStateChange(reader); } //Animation Dispatches level.NumAnimDispatches = reader.ReadUInt32(); level.AnimDispatches = new TRAnimDispatch[level.NumAnimDispatches]; for (int i = 0; i < level.NumAnimDispatches; i++) { level.AnimDispatches[i] = TR2FileReadUtilities.ReadAnimDispatch(reader); } //Animation Commands level.NumAnimCommands = reader.ReadUInt32(); level.AnimCommands = new TRAnimCommand[level.NumAnimCommands]; for (int i = 0; i < level.NumAnimCommands; i++) { level.AnimCommands[i] = TR2FileReadUtilities.ReadAnimCommand(reader); } //Mesh Trees level.NumMeshTrees = reader.ReadUInt32(); level.NumMeshTrees /= 4; level.MeshTrees = new TRMeshTreeNode[level.NumMeshTrees]; for (int i = 0; i < level.NumMeshTrees; i++) { level.MeshTrees[i] = TR2FileReadUtilities.ReadMeshTreeNode(reader); } //Frames level.NumFrames = reader.ReadUInt32(); level.Frames = new ushort[level.NumFrames]; for (int i = 0; i < level.NumFrames; i++) { level.Frames[i] = reader.ReadUInt16(); } //Models level.NumModels = reader.ReadUInt32(); level.Models = new TRModel[level.NumModels]; for (int i = 0; i < level.NumModels; i++) { level.Models[i] = TR2FileReadUtilities.ReadModel(reader); } //Static Meshes level.NumStaticMeshes = reader.ReadUInt32(); level.StaticMeshes = new TRStaticMesh[level.NumStaticMeshes]; for (int i = 0; i < level.NumStaticMeshes; i++) { level.StaticMeshes[i] = TR2FileReadUtilities.ReadStaticMesh(reader); } //Object Textures level.NumObjectTextures = reader.ReadUInt32(); level.ObjectTextures = new TRObjectTexture[level.NumObjectTextures]; for (int i = 0; i < level.NumObjectTextures; i++) { level.ObjectTextures[i] = TR2FileReadUtilities.ReadObjectTexture(reader); } //Sprite Textures level.NumSpriteTextures = reader.ReadUInt32(); level.SpriteTextures = new TRSpriteTexture[level.NumSpriteTextures]; for (int i = 0; i < level.NumSpriteTextures; i++) { level.SpriteTextures[i] = TR2FileReadUtilities.ReadSpriteTexture(reader); } //Sprite Sequences level.NumSpriteSequences = reader.ReadUInt32(); level.SpriteSequences = new TRSpriteSequence[level.NumSpriteSequences]; for (int i = 0; i < level.NumSpriteSequences; i++) { level.SpriteSequences[i] = TR2FileReadUtilities.ReadSpriteSequence(reader); } //Cameras level.NumCameras = reader.ReadUInt32(); level.Cameras = new TRCamera[level.NumCameras]; for (int i = 0; i < level.NumCameras; i++) { level.Cameras[i] = TR2FileReadUtilities.ReadCamera(reader); } //Sound Sources level.NumSoundSources = reader.ReadUInt32(); level.SoundSources = new TRSoundSource[level.NumSoundSources]; for (int i = 0; i < level.NumSoundSources; i++) { level.SoundSources[i] = TR2FileReadUtilities.ReadSoundSource(reader); } //Boxes level.NumBoxes = reader.ReadUInt32(); level.Boxes = new TR2Box[level.NumBoxes]; for (int i = 0; i < level.NumBoxes; i++) { level.Boxes[i] = TR2FileReadUtilities.ReadBox(reader); } //Overlaps & Zones level.NumOverlaps = reader.ReadUInt32(); level.Overlaps = new ushort[level.NumOverlaps]; level.Zones = new short[10 * level.NumBoxes]; for (int i = 0; i < level.NumOverlaps; i++) { level.Overlaps[i] = reader.ReadUInt16(); } for (int i = 0; i < level.Zones.Count(); i++) { level.Zones[i] = reader.ReadInt16(); } //Animated Textures level.NumAnimatedTextures = reader.ReadUInt32(); level.AnimatedTextures = new ushort[level.NumAnimatedTextures]; for (int i = 0; i < level.NumAnimatedTextures; i++) { level.AnimatedTextures[i] = reader.ReadUInt16(); } //Entities level.NumEntities = reader.ReadUInt32(); level.Entities = new TR2Entity[level.NumEntities]; for (int i = 0; i < level.NumEntities; i++) { level.Entities[i] = TR2FileReadUtilities.ReadEntity(reader); } //Light Map - 32 * 256 = 8192 bytes level.LightMap = new byte[32 * 256]; for (int i = 0; i < level.LightMap.Count(); i++) { level.LightMap[i] = reader.ReadByte(); } //Cinematic Frames level.NumCinematicFrames = reader.ReadUInt16(); level.CinematicFrames = new TRCinematicFrame[level.NumCinematicFrames]; for (int i = 0; i < level.NumCinematicFrames; i++) { level.CinematicFrames[i] = TR2FileReadUtilities.ReadCinematicFrame(reader); } //Demo Data level.NumDemoData = reader.ReadUInt16(); level.DemoData = new byte[level.NumDemoData]; for (int i = 0; i < level.NumDemoData; i++) { level.DemoData[i] = reader.ReadByte(); } //Sound Map (370 shorts = 740 bytes) & Sound Details level.SoundMap = new short[370]; for (int i = 0; i < level.SoundMap.Count(); i++) { level.SoundMap[i] = reader.ReadInt16(); } level.NumSoundDetails = reader.ReadUInt32(); level.SoundDetails = new TRSoundDetails[level.NumSoundDetails]; for (int i = 0; i < level.NumSoundDetails; i++) { level.SoundDetails[i] = TR2FileReadUtilities.ReadSoundDetails(reader); } //Samples level.NumSampleIndices = reader.ReadUInt32(); level.SampleIndices = new uint[level.NumSampleIndices]; for (int i = 0; i < level.NumSampleIndices; i++) { level.SampleIndices[i] = reader.ReadUInt32(); } Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length); reader.Close(); return(level); }
private TRMesh[] ConstructMeshData(uint[] meshPointers, ushort[] rawMeshData) { byte[] target = new byte[rawMeshData.Length * 2]; Buffer.BlockCopy(rawMeshData, 0, target, 0, target.Length); // The mesh pointer list can contain duplicates so we must make // sure to iterate over distinct values only meshPointers = meshPointers.Distinct().ToArray(); List <TRMesh> meshes = new List <TRMesh>(); using (MemoryStream ms = new MemoryStream(target)) using (BinaryReader br = new BinaryReader(ms)) { for (int i = 0; i < meshPointers.Length; i++) { TRMesh mesh = new TRMesh(); meshes.Add(mesh); uint meshPointer = meshPointers[i]; br.BaseStream.Position = meshPointer; //Pointer mesh.Pointer = meshPointer; //Centre mesh.Centre = TR2FileReadUtilities.ReadVertex(br); //CollRadius mesh.CollRadius = br.ReadInt32(); //Vertices mesh.NumVertices = br.ReadInt16(); mesh.Vertices = new TRVertex[mesh.NumVertices]; for (int j = 0; j < mesh.NumVertices; j++) { mesh.Vertices[j] = TR2FileReadUtilities.ReadVertex(br); } //Lights or Normals mesh.NumNormals = br.ReadInt16(); if (mesh.NumNormals > 0) { mesh.Normals = new TRVertex[mesh.NumNormals]; for (int j = 0; j < mesh.NumNormals; j++) { mesh.Normals[j] = TR2FileReadUtilities.ReadVertex(br); } } else { mesh.Lights = new short[Math.Abs(mesh.NumNormals)]; for (int j = 0; j < mesh.Lights.Length; j++) { mesh.Lights[j] = br.ReadInt16(); } } //Textured Rectangles mesh.NumTexturedRectangles = br.ReadInt16(); mesh.TexturedRectangles = new TRFace4[mesh.NumTexturedRectangles]; for (int j = 0; j < mesh.NumTexturedRectangles; j++) { mesh.TexturedRectangles[j] = TR2FileReadUtilities.ReadTRFace4(br); } //Textured Triangles mesh.NumTexturedTriangles = br.ReadInt16(); mesh.TexturedTriangles = new TRFace3[mesh.NumTexturedTriangles]; for (int j = 0; j < mesh.NumTexturedTriangles; j++) { mesh.TexturedTriangles[j] = TR2FileReadUtilities.ReadTRFace3(br); } //Coloured Rectangles mesh.NumColouredRectangles = br.ReadInt16(); mesh.ColouredRectangles = new TRFace4[mesh.NumColouredRectangles]; for (int j = 0; j < mesh.NumColouredRectangles; j++) { mesh.ColouredRectangles[j] = TR2FileReadUtilities.ReadTRFace4(br); } //Coloured Triangles mesh.NumColouredTriangles = br.ReadInt16(); mesh.ColouredTriangles = new TRFace3[mesh.NumColouredTriangles]; for (int j = 0; j < mesh.NumColouredTriangles; j++) { mesh.ColouredTriangles[j] = TR2FileReadUtilities.ReadTRFace3(br); } // There may be alignment padding at the end of the mesh, but rather than // storing it, when the mesh is serialized the alignment should be considered. // It seems to be 4-byte alignment for mesh data. The basestream position is // moved to the next pointer in the next iteration, so we don't need to process // the additional data here. // See https://www.tombraiderforums.com/archive/index.php/t-215247.html } } return(meshes.ToArray()); }