Пример #1
0
        public long WriteTr3(BinaryWriterEx writer)
        {
            var meshOffset1 = writer.BaseStream.Position;

            writer.WriteBlock(Center);
            writer.Write(Radius);

            writer.Write(NumVertices);
            writer.WriteBlockArray(Vertices);

            writer.Write(NumNormals);
            if (NumNormals > 0)
            {
                writer.WriteBlockArray(Normals);
            }
            else if (NumNormals < 0)
            {
                writer.WriteBlockArray(Lights);
            }

            writer.Write(NumTexturedQuads);
            for (var k = 0; k < NumTexturedQuads; k++)
            {
                TexturedQuads[k].Write(writer);
            }
            // writer.WriteBlockArray(Meshes[i].TexturedRectangles);

            writer.Write(NumTexturedTriangles);
            for (var k = 0; k < NumTexturedTriangles; k++)
            {
                TexturedTriangles[k].Write(writer);
            }

            //  writer.WriteBlockArray(Meshes[i].TexturedTriangles);

            writer.Write(NumColoredRectangles);
            //writer.WriteBlockArray(Meshes[i].ColoredRectangles);

            writer.Write(NumColoredTriangles);
            //writer.WriteBlockArray(Meshes[i].ColoredTriangles);

            var meshOffset2 = writer.BaseStream.Position;
            var meshSize    = meshOffset2 - meshOffset1;

            if (meshSize % 4 != 0)
            {
                const ushort tempFiller = 0;
                writer.Write(tempFiller);
                meshSize += 2;
            }

            return(meshSize);
        }
Пример #2
0
        public long WriteTr4AndTr5(BinaryWriterEx writer)
        {
            long meshOffset1 = writer.BaseStream.Position;

            writer.WriteBlock(Center);
            writer.Write(Radius);

            writer.Write(NumVertices);
            writer.WriteBlockArray(Vertices);

            writer.Write(NumNormals);
            if (NumNormals > 0)
            {
                writer.WriteBlockArray(Normals);
            }
            else if (NumNormals < 0)
            {
                writer.WriteBlockArray(Lights);
            }

            writer.Write(NumTexturedQuads);
            if (NumTexturedQuads != 0)
            {
                writer.WriteBlockArray(TexturedQuads);
            }

            writer.Write(NumTexturedTriangles);
            if (NumTexturedTriangles != 0)
            {
                writer.WriteBlockArray(TexturedTriangles);
            }

            var meshOffset2 = writer.BaseStream.Position;
            var meshSize    = meshOffset2 - meshOffset1;

            if (meshSize % 4 != 0)
            {
                const ushort tempFiller = 0;
                writer.Write(tempFiller);
                meshSize += 2;
            }

            return(meshSize);
        }
Пример #3
0
        public static bool DecryptLevel(string source, string target)
        {
            if (!File.Exists(source))
            {
                return(false);
            }
            if (File.Exists(target))
            {
                File.Delete(target);
            }
            File.Copy(source, target);

            bool result = false;

            var iStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
            var oStream = new FileStream(target, FileMode.Open, FileAccess.Write, FileShare.Read);
            var sr      = new BinaryReaderEx(iStream);
            var sw      = new BinaryWriterEx(oStream);

            try
            {
                sw.Seek(3, SeekOrigin.Begin);
                sw.Write((byte)0);

                sr.BaseStream.Seek(14, SeekOrigin.Begin);
                sw.BaseStream.Seek(14, SeekOrigin.Begin);

                uint   size;
                byte[] header = new byte[_keyLength];

                int chunk;
                for (chunk = 0; chunk < 4; ++chunk)
                {
                    size = sr.ReadUInt32();
                    sr.ReadBlockArray(out header, _keyLength);
                    sw.Seek(4, SeekOrigin.Current);
                    sw.WriteBlockArray(Decrypt(header, (int)size >> (chunk + 5) & 0x0F, (int)size >> (chunk + 1) & 0x0F));
                    sw.BaseStream.Seek(size - _keyLength + sizeof(uint), SeekOrigin.Current);
                    sr.BaseStream.Seek(size - _keyLength + sizeof(uint), SeekOrigin.Current);
                }

                result = true;
            }
            finally
            {
                sw.Close();
                sr.Close();
                oStream.Close();
                iStream.Close();
            }

            return(result);
        }
Пример #4
0
        private void WriteLevelTr5Main()
        {
            // Now begin to compile the geometry block in a MemoryStream
            byte[] geometryDataBuffer;
            using (var geometryDataStream = new MemoryStream())
            {
                var writer = new BinaryWriterEx(geometryDataStream); // Don't dispose
                ReportProgress(80, "Writing geometry data to memory buffer");

                const int filler = 0;
                writer.Write(filler);

                var numRooms = (uint)_level.Rooms.Count(r => r != null);
                writer.Write(numRooms);

                foreach (var r in _level.Rooms.Where(r => r != null))
                {
                    _tempRooms[r].WriteTr5(writer);
                }

                // Write floordata
                var numFloorData = (uint)_floorData.Count;
                writer.Write(numFloorData);
                writer.WriteBlockArray(_floorData);

                // Write meshes
                var offset = writer.BaseStream.Position;

                const int numMeshData = 0;
                writer.Write(numMeshData);
                var totalMeshSize = 0;

                for (var i = 0; i < _meshes.Count; i++)
                {
                    var meshSize = _meshes[i].WriteTr4AndTr5(writer);
                    totalMeshSize += (int)meshSize;
                }

                var offset2 = writer.BaseStream.Position;
                // ReSharper disable once SuggestVarOrType_BuiltInTypes
                uint meshDataSize = (uint)((offset2 - offset - 4) / 2);

                // Save the size of the meshes
                writer.BaseStream.Seek(offset, SeekOrigin.Begin);
                writer.Write(meshDataSize);
                writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

                // Write mesh pointers
                writer.Write((uint)_meshPointers.Count);
                writer.WriteBlockArray(_meshPointers);

                // Write animations' data
                writer.Write((uint)_animations.Count);
                foreach (var anim in _animations)
                {
                    anim.Write(writer, _level);
                }

                writer.Write((uint)_stateChanges.Count);
                writer.WriteBlockArray(_stateChanges);

                writer.Write((uint)_animDispatches.Count);
                writer.WriteBlockArray(_animDispatches);

                writer.Write((uint)_animCommands.Count);
                writer.WriteBlockArray(_animCommands);

                writer.Write((uint)_meshTrees.Count);
                writer.WriteBlockArray(_meshTrees);

                writer.Write((uint)_frames.Count);
                writer.WriteBlockArray(_frames);

                writer.Write((uint)_moveables.Count);
                for (var k = 0; k < _moveables.Count; k++)
                {
                    writer.WriteBlock(_moveables[k]);
                    writer.Write((ushort)0xfeff);
                }

                writer.Write((uint)_staticMeshes.Count);
                writer.WriteBlockArray(_staticMeshes);

                // SPR block
                writer.WriteBlockArray(new byte[] { 0x53, 0x50, 0x52, 0x00 });

                writer.Write((uint)_spriteTextures.Count);
                writer.WriteBlockArray(_spriteTextures);

                writer.Write((uint)_spriteSequences.Count);
                writer.WriteBlockArray(_spriteSequences);

                // Write camera, flyby and sound sources
                writer.Write((uint)_cameras.Count);
                writer.WriteBlockArray(_cameras);

                writer.Write((uint)_flyByCameras.Count);
                writer.WriteBlockArray(_flyByCameras);

                writer.Write((uint)_soundSources.Count);
                writer.WriteBlockArray(_soundSources);

                // Write pathfinding data
                writer.Write((uint)_boxes.Length);
                writer.WriteBlockArray(_boxes);

                writer.Write((uint)_overlaps.Length);
                writer.WriteBlockArray(_overlaps);

                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Alternate);
                }

                // Write animated textures
                _textureInfoManager.WriteAnimatedTextures(writer);

                // Write object textures
                writer.Write(checked ((byte)_textureInfoManager.UvRotateCount));
                writer.Write(new byte[] { 0x54, 0x45, 0x58, 0x00 });

                _textureInfoManager.WriteTextureInfos(writer, _level);

                // Write items and AI objects
                writer.Write((uint)_items.Count);
                writer.WriteBlockArray(_items);

                writer.Write((uint)_aiItems.Count);
                writer.WriteBlockArray(_aiItems);

                // Write sound meta data
                PrepareSoundsData();
                WriteSoundMetadata(writer);

                // Finish it
                writer.Write((ushort)0xcdcd);
                writer.Write((ushort)0xcdcd);
                writer.Write((ushort)0xcdcd);

                geometryDataBuffer = geometryDataStream.ToArray();
            }

            using (var fs = new FileStream(_dest, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                using (var writer = new BinaryWriterEx(fs))
                {
                    ReportProgress(90, "Writing final level");
                    writer.WriteBlockArray(new byte[] { 0x54, 0x52, 0x34, 0x00 });

                    ReportProgress(91, "Writing textures");

                    // The room texture tile count currently also currently contains the wad textures
                    // But lets not bother with those fields too much since they only matter when bump maps are used and we don't use them.
                    writer.Write((ushort)_textureInfoManager.NumRoomPages);
                    writer.Write((ushort)_textureInfoManager.NumObjectsPages);
                    // Bump map pages must be multiplied by 2 or tile index will be wrong
                    writer.Write((ushort)(_textureInfoManager.NumBumpPages * 2));

                    // Compress data
                    ReportProgress(95, "Compressing data");

                    byte[] texture32 = null;
                    int    texture32UncompressedSize = -1;
                    byte[] texture16 = null;
                    int    texture16UncompressedSize = -1;
                    byte[] textureMisc = null;
                    int    textureMiscUncompressedSize = -1;
                    byte[] geometryData = geometryDataBuffer;
                    int    geometryDataUncompressedSize = geometryData.Length;

                    using (Task Texture32task = Task.Factory.StartNew(() =>
                    {
                        texture32 = ZLib.CompressData(_texture32Data);
                        texture32UncompressedSize = _texture32Data.Length;
                    }))
                        using (Task Texture16task = Task.Factory.StartNew(() =>
                        {
                            byte[] texture16Data = PackTextureMap32To16Bit(_texture32Data, _level.Settings);
                            texture16 = ZLib.CompressData(texture16Data);
                            texture16UncompressedSize = texture16Data.Length;
                        }))
                            using (Task textureMiscTask = Task.Factory.StartNew(() =>
                            {
                                Stream textureMiscData = PrepareFontAndSkyTexture();
                                textureMisc = ZLib.CompressData(textureMiscData);
                                textureMiscUncompressedSize = (int)textureMiscData.Length;
                            }))

                                Task.WaitAll(Texture32task, Texture16task, textureMiscTask);

                    // Write data
                    ReportProgress(97, "Writing compressed data to file.");

                    writer.Write(texture32UncompressedSize);
                    writer.Write(texture32.Length);
                    writer.Write(texture32);

                    writer.Write(texture16UncompressedSize);
                    writer.Write(texture16.Length);
                    writer.Write(texture16);

                    writer.Write(textureMiscUncompressedSize);
                    writer.Write(textureMisc.Length);
                    writer.Write(textureMisc);

                    writer.Write((ushort)_level.Settings.Tr5LaraType);
                    writer.Write((ushort)_level.Settings.Tr5WeatherType);

                    for (var i = 0; i < 28; i++)
                    {
                        writer.Write((byte)0);
                    }

                    // In TR5 geometry data is not compressed
                    writer.Write(geometryDataUncompressedSize);
                    writer.Write(geometryDataUncompressedSize);
                    writer.Write(geometryData);

                    ReportProgress(98, "Writing WAVE sounds");
                    WriteSoundData(writer);

                    // Write extra data
                    _volumeScripts = new List <VolumeScriptInstance>();

                    using (var ms = new MemoryStream())
                    {
                        var chunkIO = new ChunkWriter(new byte[] { 0x54, 0x52, 0x35, 0x4D }, new BinaryWriterFast(ms));

                        int currRoom = 0;
                        foreach (var r in _level.Rooms.Where(r => r != null))
                        {
                            // Add further extra data conditions here, otherwise compiler will skip this room altogether
                            if (r.Volumes.Count() > 0)
                            {
                                currRoom++;
                            }
                            else
                            {
                                currRoom++;
                                continue;
                            }

                            using (var extraRoomDataChunk = chunkIO.WriteChunk(Tr5MainExtraRoomData))
                            {
                                // First and only param after signature is internal room number
                                chunkIO.Raw.Write(currRoom);

                                using (var volumeListChunk = chunkIO.WriteChunk(Tr5MainChunkVolumeList))
                                {
                                    var trRoom = _tempRooms[r];

                                    foreach (var vol in r.Volumes)
                                    {
                                        using (var volumeChunk = chunkIO.WriteChunk(Tr5MainChunkVolume))
                                        {
                                            int scriptIndex = 0;
                                            if (_volumeScripts.Contains(vol.Scripts))
                                            {
                                                scriptIndex = _volumeScripts.IndexOf(vol.Scripts);
                                            }
                                            else
                                            {
                                                _volumeScripts.Add(vol.Scripts);
                                                scriptIndex = _volumeScripts.Count - 1;
                                            }

                                            // FIXME is it needed?
                                            int add = 0;
                                            if (vol is BoxVolumeInstance)
                                            {
                                                add = (int)((vol as BoxVolumeInstance).Size.Y / 2.0f);
                                            }

                                            var X = (int)Math.Round(trRoom.Info.X + vol.Position.X);
                                            var Y = (int)-Math.Round(r.WorldPos.Y + vol.Position.Y + add);
                                            var Z = (int)Math.Round(trRoom.Info.Z + vol.Position.Z);

                                            if (vol is BoxVolumeInstance)
                                            {
                                                chunkIO.Raw.Write(0);
                                            }
                                            else if (vol is SphereVolumeInstance)
                                            {
                                                chunkIO.Raw.Write(1);
                                            }
                                            else if (vol is PrismVolumeInstance)
                                            {
                                                chunkIO.Raw.Write(2);
                                            }

                                            chunkIO.Raw.Write(X);
                                            chunkIO.Raw.Write(Y);
                                            chunkIO.Raw.Write(Z);
                                            chunkIO.Raw.Write((short)vol.Activators);
                                            chunkIO.Raw.Write(scriptIndex);

                                            if (vol is BoxVolumeInstance)
                                            {
                                                var bv   = (BoxVolumeInstance)vol;
                                                var min  = vol.Position - (bv.Size / 2.0f);
                                                var max  = vol.Position + (bv.Size / 2.0f);
                                                var rotY = (ushort)Math.Max(0, Math.Min(ushort.MaxValue,
                                                                                        Math.Round(bv.RotationY * (65536.0 / 360.0))));
                                                var rotX = (ushort)Math.Max(0, Math.Min(ushort.MaxValue,
                                                                                        Math.Round(bv.RotationX * (65536.0 / 360.0))));

                                                chunkIO.Raw.Write(rotY);
                                                chunkIO.Raw.Write(rotX);
                                                chunkIO.Raw.Write((short)min.X);
                                                chunkIO.Raw.Write((short)min.Y);
                                                chunkIO.Raw.Write((short)min.Z);
                                                chunkIO.Raw.Write((short)max.X);
                                                chunkIO.Raw.Write((short)max.Y);
                                                chunkIO.Raw.Write((short)max.Z);
                                            }
                                            else if (vol is SphereVolumeInstance)
                                            {
                                                chunkIO.Raw.Write((vol as SphereVolumeInstance).Size);
                                            }
                                            else if (vol is PrismVolumeInstance)
                                            {
                                                var pv = (PrismVolumeInstance)vol;
                                                chunkIO.Raw.Write(pv.RotationY);
                                                chunkIO.Raw.Write(pv.Size);
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        /*
                         * using (var extraDataChunk = chunkIO.WriteChunk(Tr5MainExtraData))
                         * {
                         *  using (var volScriptListChunk = chunkIO.WriteChunk(Tr5MainChunkVolumeScriptList))
                         *  {
                         *      for (int i = 0; i < _volumeScripts.Count; i++)
                         *      {
                         *          var script = _volumeScripts[i];
                         *          using (var volScriptChunk = chunkIO.WriteChunk(Tr5MainChunkVolumeScript))
                         *          {
                         *              chunkIO.Raw.WriteStringUTF8(script.Name);
                         *
                         *              string onEnter = string.Empty;
                         *              string onInside = string.Empty;
                         *              string onLeave = string.Empty;
                         *
                         *              if (script.OnEnter.Trim().Length > 0)
                         *                  onEnter = "volscripts[" + i + "].OnEnter  = function(activator) \n" +
                         *                              _indent + script.OnEnter.Replace("\n", "\n" + _indent) + "\n" + "end;";
                         *
                         *              if (script.OnInside.Trim().Length > 0)
                         *                  onInside = "volscripts[" + i + "].OnInside = function(activator) \n" +
                         *                              _indent + script.OnInside.Replace("\n", "\n" + _indent) + "\n" + "end;";
                         *
                         *              if (script.OnLeave.Trim().Length > 0)
                         *                  onLeave  = "volscripts[" + i + "].OnLeave = function(activator) \n" +
                         *                              _indent + script.OnLeave.Replace("\n", "\n" + _indent) + "\n" + "end;";
                         *
                         *              string functionCode =
                         *                  onEnter  + (string.IsNullOrEmpty(onEnter)  ? string.Empty : "\n\n") +
                         *                  onInside + (string.IsNullOrEmpty(onInside) ? string.Empty : "\n\n") +
                         *                  onLeave  + (string.IsNullOrEmpty(onLeave)  ? string.Empty : "\n\n") ;
                         *
                         *              chunkIO.Raw.WriteStringUTF8(functionCode);
                         *          }
                         *      }
                         *  }
                         *
                         *  using (var chunkLuaIds = chunkIO.WriteChunk(Tr5MainChunkLuaIds))
                         *  {
                         *      for (int i = 0; i < _luaIdToItems.Count; i++)
                         *      {
                         *          chunkIO.WriteChunk(Tr5MainChunkLuaId, () =>
                         *          {
                         *              chunkIO.Raw.Write(_luaIdToItems.ElementAt(i).Key);
                         *              chunkIO.Raw.Write(_luaIdToItems.ElementAt(i).Value);
                         *          });
                         *      }
                         *  }
                         * }
                         */

                        chunkIO.Raw.Flush();

                        writer.Write((int)(ms.Length + 4));
                        writer.Write((int)(ms.Length + 4));
                        writer.Write(ms.ToArray(), 0, (int)ms.Length);
                        writer.Write((int)0);
                    }

                    ReportProgress(99, "Done");
                }
            }
        }
Пример #5
0
        private void WriteLevelTr3()
        {
            // Now begin to compile the geometry block in a MemoryStream
            using (var writer = new BinaryWriterEx(new FileStream(_dest, FileMode.Create, FileAccess.Write, FileShare.None)))
            {
                ReportProgress(80, "Writing geometry data to memory buffer");

                // Write version
                writer.WriteBlockArray(new byte[] { 0x38, 0x00, 0x18, 0xFF });

                /*using (var readerPalette = new BinaryReader(new FileStream("Editor\\Misc\\Palette.Tr3.bin", FileMode.Open, FileAccess.Write, FileShare.None)))
                 * {
                 *  var palette = readerPalette.ReadBytes(1792);
                 *  // Write palette
                 *  writer.Write(palette);
                 * }*/

                // TODO: for now I write fake palette, they should be needed only for 8 bit textures
                for (var i = 0; i < 768; i++)
                {
                    writer.Write((byte)0x00);
                }
                for (var i = 0; i < 1024; i++)
                {
                    writer.Write((byte)0x00);
                }

                // Write textures
                int numTextureTiles = _texture32Data.GetLength(0) / (256 * 256 * 4);
                writer.Write(numTextureTiles);

                // TODO 8 bit textures (altough who uses 8 bit textures in 2018?)
                var fakeTextures = new byte[256 * 256 * numTextureTiles];
                writer.Write(fakeTextures);

                // 16 bit textures
                byte[] texture16Data = PackTextureMap32To16Bit(_texture32Data, _level.Settings);
                writer.Write(texture16Data);

                const int filler = 0;
                writer.Write(filler);

                var numRooms = (ushort)_level.Rooms.Count(r => r != null);
                writer.Write(numRooms);

                long offset;
                long offset2;
                foreach (var r in _level.Rooms.Where(r => r != null))
                {
                    _tempRooms[r].WriteTr3(writer);
                }

                // Write floordata
                var numFloorData = (uint)_floorData.Count;
                writer.Write(numFloorData);
                writer.WriteBlockArray(_floorData);

                // Write meshes
                offset = writer.BaseStream.Position;

                const int numMeshData = 0;
                writer.Write(numMeshData);
                var totalMeshSize = 0;

                for (var i = 0; i < _meshes.Count; i++)
                {
                    var meshSize = _meshes[i].WriteTr3(writer);
                    totalMeshSize += (int)meshSize;
                }

                offset2 = writer.BaseStream.Position;
                uint meshDataSize = (uint)((offset2 - offset - 4) / 2);

                // Save the size of the meshes
                writer.BaseStream.Seek(offset, SeekOrigin.Begin);
                writer.Write(meshDataSize);
                writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

                // Write mesh pointers
                writer.Write((uint)_meshPointers.Count);
                writer.WriteBlockArray(_meshPointers);

                // Write animations' data
                writer.Write((uint)_animations.Count);
                foreach (var anim in _animations)
                {
                    anim.Write(writer, _level);
                }

                writer.Write((uint)_stateChanges.Count);
                writer.WriteBlockArray(_stateChanges);

                writer.Write((uint)_animDispatches.Count);
                writer.WriteBlockArray(_animDispatches);

                writer.Write((uint)_animCommands.Count);
                writer.WriteBlockArray(_animCommands);

                writer.Write((uint)_meshTrees.Count);
                writer.WriteBlockArray(_meshTrees);

                writer.Write((uint)_frames.Count);
                writer.WriteBlockArray(_frames);

                writer.Write((uint)_moveables.Count);
                writer.WriteBlockArray(_moveables);

                writer.Write((uint)_staticMeshes.Count);
                writer.WriteBlockArray(_staticMeshes);

                // Sprites
                writer.Write((uint)_spriteTextures.Count);
                writer.WriteBlockArray(_spriteTextures);

                writer.Write((uint)_spriteSequences.Count);
                writer.WriteBlockArray(_spriteSequences);

                // Write camera, sound sources
                writer.Write((uint)_cameras.Count);
                writer.WriteBlockArray(_cameras);

                writer.Write((uint)_soundSources.Count);
                writer.WriteBlockArray(_soundSources);

                // Write pathfinding data
                writer.Write((uint)_boxes.Length);
                writer.WriteBlockArray(_boxes);

                writer.Write((uint)_overlaps.Length);
                writer.WriteBlockArray(_overlaps);

                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Alternate);
                }

                // Write animated textures
                _textureInfoManager.WriteAnimatedTextures(writer);

                // Write object textures
                _textureInfoManager.WriteTextureInfos(writer, _level);

                // Write items and AI objects
                writer.Write((uint)_items.Count);
                writer.WriteBlockArray(_items);

                // TODO Figure out light map
                var lightmap = new byte[8192];
                writer.Write(lightmap);

                const ushort numDemo            = 0;
                const ushort numCinematicFrames = 0;
                writer.Write(numDemo);
                writer.Write(numCinematicFrames);

                // Write sound meta data
                PrepareSoundsData();
                WriteSoundMetadata(writer);

                writer.Flush();
            }
        }
Пример #6
0
        public TestLevel(string fileName, string outFileName = "")
        {
            this.fileName = fileName;

            FileStream     fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReaderEx reader     = new BinaryReaderEx(fileStream);

            byte[] buffer;

            reader.ReadBlock(out Version);
            reader.ReadBlock(out NumRoomTextureTiles);
            reader.ReadBlock(out NumObjectTextureTiles);
            reader.ReadBlock(out NumBumpTextureTiles);

            reader.ReadBlock(out Texture32UncompressedSize);
            reader.ReadBlock(out Texture32CompressedSize);

            Texture32 = new byte[Texture32CompressedSize];
            reader.ReadBlockArray(out Texture32, Texture32CompressedSize);
            Texture32 = ZLib.DecompressData(Texture32);

            ImageC img = ImageC.FromByteArray(Texture32, 256, (int)Texture32UncompressedSize / 262144 * 256);
            //img.Save("H:\\karnak.png");

            BinaryWriterEx wrttext = new BinaryWriterEx(new FileStream("textures.raw", FileMode.Create, FileAccess.Write, FileShare.None));

            wrttext.WriteBlockArray(Texture32);
            wrttext.Flush();
            wrttext.Close();

            reader.ReadBlock(out Texture16UncompressedSize);
            reader.ReadBlock(out Texture16CompressedSize);
            Texture16 = new byte[Texture16CompressedSize];
            reader.ReadBlockArray(out Texture16, Texture16CompressedSize);
            Texture16 = ZLib.DecompressData(Texture16);

            reader.ReadBlock(out MiscTextureUncompressedSize);
            reader.ReadBlock(out MiscTextureCompressedSize);
            MiscTexture = new byte[MiscTextureCompressedSize];
            reader.ReadBlockArray(out MiscTexture, MiscTextureCompressedSize);
            MiscTexture = ZLib.DecompressData(MiscTexture);

            reader.ReadBlock(out LevelUncompressedSize);
            reader.ReadBlock(out LevelCompressedSize);
            buffer = new byte[LevelCompressedSize];
            reader.ReadBlockArray(out buffer, LevelCompressedSize);
            buffer = ZLib.DecompressData(buffer);

            var stream = new MemoryStream();

            stream.Write(buffer, 0, (int)LevelUncompressedSize);
            stream.Seek(0, SeekOrigin.Begin);

            BinaryWriterEx wrt = new BinaryWriterEx(new FileStream("coastal.bin", FileMode.Create, FileAccess.Write, FileShare.None));

            wrt.Write(buffer, 0, (int)LevelUncompressedSize);
            wrt.Flush();
            wrt.Close();

            BinaryWriterEx wrs = new BinaryWriterEx(new FileStream("samples." + outFileName + ".bin", FileMode.Create, FileAccess.Write, FileShare.None));

            byte[] samples = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
            wrs.Write(samples);
            wrs.Flush();
            wrs.Close();

            reader.Close();

            reader = new BinaryReaderEx(stream);
            reader.ReadBlock(out Unused);
            reader.ReadBlock(out NumRooms);

            int max = 0;

            StreamWriter wp = new StreamWriter(new FileStream("portals" + outFileName + ".txt", FileMode.Create, FileAccess.Write, FileShare.None));

            Rooms = new tr_room[NumRooms];
            for (int i = 0; i < NumRooms; i++)
            {
                wp.WriteLine("=====================================================================");
                wp.WriteLine("ROOM #" + i);
                wp.WriteLine("=====================================================================");

                reader.ReadBlock(out Rooms[i].Info);
                reader.ReadBlock(out Rooms[i].NumDataWords);
                reader.ReadBlock(out Rooms[i].NumVertices);
                //  Rooms[i].Vertices = new tr_room_vertex[Rooms[i].NumVertices];
                reader.ReadBlockArray(out Rooms[i].Vertices, Rooms[i].NumVertices);
                if (Rooms[i].NumVertices > max)
                {
                    max = Rooms[i].NumVertices;
                }
                reader.ReadBlock(out Rooms[i].NumRectangles);
                Rooms[i].Rectangles = new tr_face4[Rooms[i].NumRectangles];
                for (int j = 0; j < Rooms[i].NumRectangles; j++)
                {
                    // Rooms[i].Rectangles[j].Vertices = new ushort[4];
                    reader.ReadBlockArray(out Rooms[i].Rectangles[j].Vertices, 4);
                    reader.ReadBlock(out Rooms[i].Rectangles[j].Texture);
                }

                reader.ReadBlock(out Rooms[i].NumTriangles);
                Rooms[i].Triangles = new tr_face3[Rooms[i].NumTriangles];
                for (int j = 0; j < Rooms[i].NumTriangles; j++)
                {
                    // Rooms[i].Triangles[j].Vertices = new ushort[3];
                    reader.ReadBlockArray(out Rooms[i].Triangles[j].Vertices, 3);
                    reader.ReadBlock(out Rooms[i].Triangles[j].Texture);
                }

                reader.ReadBlock(out Rooms[i].NumSprites);

                reader.ReadBlock(out Rooms[i].NumPortals);
                //Rooms[i].Portals = new tr_room_portal[Rooms[i].NumPortals];
                reader.ReadBlockArray(out Rooms[i].Portals, Rooms[i].NumPortals);

                for (int nn = 0; nn < Rooms[i].Portals.Length; nn++)
                {
                    tr_room_portal pt = Rooms[i].Portals[nn];
                    wp.WriteLine(nn + ": ");
                    wp.WriteLine("Room: " + pt.AdjoiningRoom);
                    for (int vv = 0; vv < 4; vv++)
                    {
                        wp.Write("V" + vv + " = " + pt.Vertices[vv].X + ", " + pt.Vertices[vv].Y + ", " + pt.Vertices[vv].Z);
                        wp.WriteLine("");
                    }
                    wp.WriteLine("");
                }

                reader.ReadBlock(out Rooms[i].NumZSectors);
                reader.ReadBlock(out Rooms[i].NumXSectors);
                //Rooms[i].Sectors = new tr_room_sector[Rooms[i].NumZSectors * Rooms[i].NumXSectors];
                reader.ReadBlockArray(out Rooms[i].Sectors, (uint)Rooms[i].NumZSectors * Rooms[i].NumXSectors);

                reader.ReadBlock(out Rooms[i].AmbientIntensity1);
                reader.ReadBlock(out Rooms[i].AmbientIntensity2);

                reader.ReadBlock(out Rooms[i].NumLights);
                reader.ReadBlockArray(out Rooms[i].Lights, Rooms[i].NumLights);

                reader.ReadBlock(out Rooms[i].NumStaticMeshes);
                reader.ReadBlockArray(out Rooms[i].StaticMeshes, Rooms[i].NumStaticMeshes);

                reader.ReadBlock(out Rooms[i].AlternateRoom);
                reader.ReadBlock(out Rooms[i].Flags);
                reader.ReadBlock(out Rooms[i].Param1);
                reader.ReadBlock(out Rooms[i].Unknown1);
                reader.ReadBlock(out Rooms[i].Unknown2);
            }

            wp.Flush();
            wp.Close();
            //return;

            reader.ReadBlock(out NumFloorData);
            reader.ReadBlockArray(out FloorData, NumFloorData);

            reader.ReadBlock(out NumMeshData);

            /*for (int i = 0; i < NumFloorData; i++)
             *  Console.WriteLine(FloorData[i].ToString("X2"));*/

            int   numBytes   = 0;
            int   totalBytes = 0;
            int   l          = 0;
            short temp       = 0;

            Meshes = new tr_mesh[2048];
            while (totalBytes < NumMeshData * 2)
            {
                long offset1 = reader.BaseStream.Position;

                reader.ReadBlock(out Meshes[l].Center);
                reader.ReadBlock(out Meshes[l].Radius);
                numBytes += 10;

                reader.ReadBlock(out Meshes[l].NumVertices);
                reader.ReadBlockArray(out Meshes[l].Vertices, Meshes[l].NumVertices);
                numBytes += 2 + 6 * Meshes[l].NumVertices;

                reader.ReadBlock(out Meshes[l].NumNormals);
                if (Meshes[l].NumNormals > 0)
                {
                    reader.ReadBlockArray(out Meshes[l].Normals, Meshes[l].NumNormals);
                    numBytes += 2 + 6 * Meshes[l].NumNormals;
                }
                else
                {
                    reader.ReadBlockArray(out Meshes[l].Lights, -Meshes[l].NumNormals);
                    numBytes += 2 - 2 * Meshes[l].NumNormals;
                }

                reader.ReadBlock(out Meshes[l].NumTexturedRectangles);
                reader.ReadBlockArray(out Meshes[l].TexturedRectangles, Meshes[l].NumTexturedRectangles);
                numBytes += 2 + 12 * Meshes[l].NumTexturedRectangles;

                reader.ReadBlock(out Meshes[l].NumTexturedTriangles);
                reader.ReadBlockArray(out Meshes[l].TexturedTriangles, Meshes[l].NumTexturedTriangles);
                numBytes += 2 + 10 * Meshes[l].NumTexturedTriangles;

                long offset2 = reader.BaseStream.Position;
                int  diff    = (int)(offset2 - offset1);
                if (diff % 4 != 0)
                {
                    reader.ReadBlock(out temp); diff += 2;
                }
                Meshes[l].MeshSize    = numBytes;
                Meshes[l].MeshPointer = totalBytes;

                if (l == 209)
                {
                    BinaryWriterEx tmpwriter = new BinaryWriterEx(new FileStream("cleopal.msh", FileMode.Create, FileAccess.Write, FileShare.None));
                    tmpwriter.WriteBlock(Meshes[l].Center);
                    tmpwriter.WriteBlock(Meshes[l].Radius);
                    tmpwriter.WriteBlock(Meshes[l].NumVertices);
                    tmpwriter.WriteBlockArray(Meshes[l].Vertices);
                    tmpwriter.WriteBlock(Meshes[l].NumNormals);
                    if (Meshes[l].NumNormals > 0)
                    {
                        tmpwriter.WriteBlockArray(Meshes[l].Normals);
                    }
                    else
                    {
                        tmpwriter.WriteBlockArray(Meshes[l].Lights);
                    }
                    tmpwriter.WriteBlock(Meshes[l].NumTexturedRectangles);
                    tmpwriter.WriteBlockArray(Meshes[l].TexturedRectangles);
                    tmpwriter.WriteBlock(Meshes[l].NumTexturedTriangles);
                    tmpwriter.WriteBlockArray(Meshes[l].TexturedTriangles);

                    tmpwriter.Flush();
                    tmpwriter.Close();
                }

                totalBytes += diff;// numBytes;
                numBytes    = 0;
                l++;
            }

            Array.Resize(ref Meshes, l);

            NumMeshes = (uint)Meshes.Length;

            reader.ReadBlock(out NumMeshPointers);
            reader.ReadBlockArray(out MeshPointers, NumMeshPointers);

            reader.ReadBlock(out NumAnimations);
            reader.ReadBlockArray(out Animations, NumAnimations);

            reader.ReadBlock(out NumStateChanges);
            reader.ReadBlockArray(out StateChanges, NumStateChanges);

            reader.ReadBlock(out NumAnimDispatches);
            reader.ReadBlockArray(out AnimDispatches, NumAnimDispatches);

            reader.ReadBlock(out NumAnimCommands);
            reader.ReadBlockArray(out AnimCommands, NumAnimCommands);

            reader.ReadBlock(out NumMeshTrees);
            reader.ReadBlockArray(out MeshTrees, NumMeshTrees);

            logger.Debug(reader.BaseStream.Position.ToString());
            reader.ReadBlock(out NumFrames);
            reader.ReadBlockArray(out Frames, NumFrames);

            reader.ReadBlock(out NumMoveables);
            reader.ReadBlockArray(out Moveables, NumMoveables);

            reader.ReadBlock(out NumStaticMeshes);
            reader.ReadBlockArray(out StaticMeshes, NumStaticMeshes);

            reader.ReadBlockArray(out SPR, 3);

            reader.ReadBlock(out NumSpriteTextures);
            reader.ReadBlockArray(out SpriteTextures, NumSpriteTextures);

            reader.ReadBlock(out NumSpriteSequences);
            reader.ReadBlockArray(out SpriteSequences, NumSpriteSequences);

            reader.ReadBlock(out NumCameras);
            reader.ReadBlockArray(out Cameras, NumCameras);

            reader.ReadBlock(out NumFlyByCameras);
            reader.ReadBlockArray(out FlyByCameras, NumFlyByCameras * 40);

            reader.ReadBlock(out NumSoundSources);
            reader.ReadBlockArray(out SoundSources, NumSoundSources);

            reader.ReadBlock(out NumBoxes);
            reader.ReadBlockArray(out Boxes, NumBoxes);

            reader.ReadBlock(out NumOverlaps);
            reader.ReadBlockArray(out Overlaps, NumOverlaps);

            // reader.ReadBlockArray(out Zones, NumBoxes * 10);
            Zones = new short[NumBoxes * 10];
            for (int n = 0; n < NumBoxes * 10; n++)
            {
                Zones[n] = reader.ReadInt16();
            }

            reader.ReadBlock(out NumAnimatedTextures);
            short[] animTextures;
            reader.ReadBlockArray(out animTextures, NumAnimatedTextures);

            string fn = Path.GetFileNameWithoutExtension(fileName);

            if (File.Exists("pathfinding." + fn + "." + outFileName + ".txt"))
            {
                File.Delete("pathfinding." + fn + "." + outFileName + ".txt");
            }
            StreamWriter writer = new StreamWriter(new FileStream("pathfinding." + fn + "." + outFileName + ".txt", FileMode.Create, FileAccess.Write, FileShare.None));

            writer.WriteLine("BOXES");

            for (int n = 0; n < Boxes.Length; n++)
            {
                writer.WriteLine("[" + n + "] " + "Xmin: " + Boxes[n].Xmin + ", " + "Xmax: " + Boxes[n].Xmax + ", " +
                                 "Zmin: " + Boxes[n].Zmin + ", " + "Zmax: " + Boxes[n].Zmax + ", " +
                                 "Floor: " + Boxes[n].TrueFloor + ", Overlap Index: " + Boxes[n].OverlapIndex);
            }

            writer.WriteLine(" ");
            writer.WriteLine("OVERLAPS");

            for (int n = 0; n < Overlaps.Length; n++)
            {
                writer.WriteLine("[" + n + "] " + (Overlaps[n] & 0x7fff));
                if ((Overlaps[n] & 0x8000) != 0)
                {
                    writer.WriteLine("--- END OF LIST ---");
                }
            }

            writer.WriteLine(" ");
            writer.WriteLine("ZONES");

            for (int n = 0; n < Boxes.Length; n++)
            {
                /*writer.WriteLine("[" + n + "] " + "Ground1: " + Zones[n * 10 + 0] + ", " + "Ground2: " + Zones[n * 10 + 1] + ", " +
                 *               "Ground3: " + Zones[n * 10 + 2] + ", " + "Ground4: " + Zones[n * 10 + 3] + ", " +
                 *               "Fly: " + Zones[n * 10 + 4] + ", A_Ground1: " + Zones[n * 10 + 5] + ", " + "A_Ground2: " + Zones[n * 10 + 6] + ", " +
                 *               "A_Ground3: " + Zones[n * 10 + 7] + ", " + "A_Ground4: " + Zones[n * 10 + 8] + ", " +
                 *               "A_Fly: " + Zones[n * 10 + 9]);*/
                writer.WriteLine("[" + n + "] " + "Ground1: " + Zones[n] + ", " + "Ground2: " + Zones[1 * NumBoxes + n] + ", " +
                                 "Ground3: " + Zones[2 * NumBoxes + n] + ", " + "Ground4: " + Zones[3 * NumBoxes + n] + ", " +
                                 "Fly: " + Zones[4 * NumBoxes + n] + ", A_Ground1: " + Zones[5 * NumBoxes + n] + ", " + "A_Ground2: " + Zones[6 * NumBoxes + n] + ", " +
                                 "A_Ground3: " + Zones[7 * NumBoxes + n] + ", " + "A_Ground4: " + Zones[8 * NumBoxes + n] + ", " +
                                 "A_Fly: " + Zones[9 * NumBoxes + n]);
            }

            writer.Flush();
            writer.Close();

            reader.ReadBlockArray(out TEX, 4);

            reader.ReadBlock(out NumObjectTextures);
            reader.ReadBlockArray(out ObjectTextures, NumObjectTextures);

            if (File.Exists("textures." + fn + "." + outFileName + ".txt"))
            {
                File.Delete("textures." + fn + "." + outFileName + ".txt");
            }
            writer = new StreamWriter(new FileStream("textures." + fn + "." + outFileName + ".txt", FileMode.Create, FileAccess.Write, FileShare.None));

            for (int ii = 0; ii < NumObjectTextures; ii++)
            {
                writer.WriteLine("TEXTURE #" + ii);
                writer.WriteLine("    TileAndFlags: " + (ObjectTextures[ii].Tile).ToString());
                writer.WriteLine("    NewFlags: " + (ObjectTextures[ii].Flags).ToString());
                writer.WriteLine("    Tile: " + (ObjectTextures[ii].Tile & 0xFF).ToString());
                for (int jj = 0; jj < 4; jj++)
                {
                    writer.WriteLine("    " + jj + " X: " +
                                     ((ushort)(ObjectTextures[ii].Vertices[jj].Xpixel << 8 + ObjectTextures[ii].Vertices[jj].Xcoordinate)).ToString() +
                                     " Y: " +
                                     ((ushort)(ObjectTextures[ii].Vertices[jj].Ypixel << 8 + ObjectTextures[ii].Vertices[jj].Ycoordinate)).ToString() +
                                     " (" + ObjectTextures[ii].Vertices[jj].Xpixel + ", " + ObjectTextures[ii].Vertices[jj].Ypixel + ")");
                }

                /* BinaryWriterEx tmpwriter2 = new BinaryWriterEx(new FileStream("test\\cleopal_" + ii + ".text", FileMode.Create, FileAccess.Write, FileShare.None));
                 * tmpwriter2.WriteBlock(ObjectTextures[ii]);
                 * tmpwriter2.Flush();
                 * tmpwriter2.Close();*/
            }

            writer.Flush();
            writer.Close();

            reader.ReadBlock(out NumItems);
            reader.ReadBlockArray(out Items, NumItems);

            reader.ReadBlock(out NumAiItems);
            reader.ReadBlockArray(out AiItems, NumAiItems);

            StreamWriter aiw = new StreamWriter(new FileStream("AI" + outFileName + ".txt", FileMode.Create, FileAccess.Write, FileShare.None));

            for (int n = 0; n < NumAiItems; n++)
            {
                aiw.WriteLine("[" + n + "]");
                aiw.WriteLine("    ObjectID: " + AiItems[n].ObjectID);
                aiw.WriteLine("    X: " + AiItems[n].X);
                aiw.WriteLine("    Y: " + AiItems[n].Y);
                aiw.WriteLine("    Z: " + AiItems[n].Z);
                aiw.WriteLine("    Room: " + AiItems[n].Room);
                aiw.WriteLine("    Angle: " + AiItems[n].Angle);
            }

            aiw.Flush();
            aiw.Close();

            BinaryWriterEx bwex = new BinaryWriterEx(new FileStream("sounds" + outFileName + ".sfx", FileMode.Create, FileAccess.Write, FileShare.None));

            var numDemo = reader.ReadInt16();

            byte[] soundmap        = reader.ReadBytes((numDemo != 0 ? numDemo * 2 : 740));
            int    numSoundDetails = reader.ReadInt32();

            byte[] details    = reader.ReadBytes(8 * numSoundDetails);
            int    numIndices = reader.ReadInt32();

            byte[] indices = reader.ReadBytes(4 * numIndices);

            bwex.Write(soundmap);
            bwex.Write(numSoundDetails);
            bwex.Write(details);
            bwex.Write(numIndices);
            bwex.Write(indices);

            bwex.Flush();
            bwex.Close();

            List <byte> bytes = new List <byte>();

            while (reader.BaseStream.Position < reader.BaseStream.Length)
            {
                bytes.Add(reader.ReadByte());
            }
        }
Пример #7
0
        private void WriteLevelTr2()
        {
            // Now begin to compile the geometry block in a MemoryStream
            using (var writer = new BinaryWriterEx(new FileStream(_dest, FileMode.Create, FileAccess.Write, FileShare.None)))
            {
                ReportProgress(80, "Writing geometry data to memory buffer");

                // Write version
                writer.WriteBlockArray(new byte[] { 0x2D, 0x00, 0x00, 0x00 });

                // TODO: for now I write fake palette, they should be needed only for 8 bit textures
                tr_color[] palette8 = new tr_color[256];
                //Following colors have hardcoded meaning in TR2
                // https://github.com/Arsunt/TR2Main/blob/0586ba8965fc3c260080d9e6ea05f3e17033ba4b/global/types.h#L931
                palette8[1] = new tr_color()
                {
                    Red = 128, Green = 128, Blue = 128
                };
                palette8[2] = new tr_color()
                {
                    Red = 255, Green = 255, Blue = 255
                };
                palette8[3] = new tr_color()
                {
                    Red = 255, Green = 0, Blue = 0
                };
                palette8[4] = new tr_color()
                {
                    Red = 255, Green = 165, Blue = 0
                };
                palette8[5] = new tr_color()
                {
                    Red = 255, Green = 255, Blue = 0
                };
                palette8[12] = new tr_color()
                {
                    Red = 0, Green = 128, Blue = 0
                };
                palette8[13] = new tr_color()
                {
                    Red = 0, Green = 255, Blue = 0
                };
                palette8[14] = new tr_color()
                {
                    Red = 0, Green = 255, Blue = 255
                };
                palette8[14] = new tr_color()
                {
                    Red = 0, Green = 0, Blue = 255
                };
                palette8[15] = new tr_color()
                {
                    Red = 255, Green = 0, Blue = 255
                };
                foreach (tr_color c in palette8)
                {
                    c.write(writer);
                }
                for (var i = 0; i < 1024; i++)
                {
                    writer.Write((byte)0x00);
                }

                // Write textures
                int numTextureTiles = _texture32Data.GetLength(0) / (256 * 256 * 4);
                writer.Write(numTextureTiles);

                // TODO 8 bit textures (altough who uses 8 bit textures in 2018?)
                var fakeTextures = new byte[256 * 256 * numTextureTiles];
                writer.Write(fakeTextures);

                // 16 bit textures
                byte[] texture16Data = PackTextureMap32To16Bit(_texture32Data, _level.Settings);
                writer.Write(texture16Data);

                const int filler = 0;
                writer.Write(filler);

                var numRooms = (ushort)_level.Rooms.Count(r => r != null);
                writer.Write(numRooms);

                long offset;
                long offset2;
                foreach (var r in _level.Rooms.Where(r => r != null))
                {
                    _tempRooms[r].WriteTr2(writer);
                }

                // Write floordata
                var numFloorData = (uint)_floorData.Count;
                writer.Write(numFloorData);
                writer.WriteBlockArray(_floorData);

                // Write meshes
                offset = writer.BaseStream.Position;

                const int numMeshData = 0;
                writer.Write(numMeshData);
                var totalMeshSize = 0;

                for (var i = 0; i < _meshes.Count; i++)
                {
                    var meshSize = _meshes[i].WriteTr3(writer);
                    totalMeshSize += (int)meshSize;
                }

                offset2 = writer.BaseStream.Position;
                uint meshDataSize = (uint)((offset2 - offset - 4) / 2);

                // Save the size of the meshes
                writer.BaseStream.Seek(offset, SeekOrigin.Begin);
                writer.Write(meshDataSize);
                writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

                // Write mesh pointers
                writer.Write((uint)_meshPointers.Count);
                writer.WriteBlockArray(_meshPointers);

                // Write animations' data
                writer.Write((uint)_animations.Count);
                foreach (var anim in _animations)
                {
                    anim.Write(writer, _level);
                }

                writer.Write((uint)_stateChanges.Count);
                writer.WriteBlockArray(_stateChanges);

                writer.Write((uint)_animDispatches.Count);
                writer.WriteBlockArray(_animDispatches);

                writer.Write((uint)_animCommands.Count);
                writer.WriteBlockArray(_animCommands);

                writer.Write((uint)_meshTrees.Count);
                writer.WriteBlockArray(_meshTrees);

                writer.Write((uint)_frames.Count);
                writer.WriteBlockArray(_frames);

                writer.Write((uint)_moveables.Count);
                writer.WriteBlockArray(_moveables);

                writer.Write((uint)_staticMeshes.Count);
                writer.WriteBlockArray(_staticMeshes);

                // Write object textures
                _textureInfoManager.WriteTextureInfos(writer, _level);

                // Sprites
                writer.Write((uint)_spriteTextures.Count);
                writer.WriteBlockArray(_spriteTextures);

                writer.Write((uint)_spriteSequences.Count);
                writer.WriteBlockArray(_spriteSequences);

                // Write camera, sound sources
                writer.Write((uint)_cameras.Count);
                writer.WriteBlockArray(_cameras);

                writer.Write((uint)_soundSources.Count);
                writer.WriteBlockArray(_soundSources);

                // Write pathfinding data
                writer.Write((uint)_boxes.Length);
                writer.WriteBlockArray(_boxes);

                writer.Write((uint)_overlaps.Length);
                writer.WriteBlockArray(_overlaps);

                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Alternate);
                }

                // Write animated textures
                _textureInfoManager.WriteAnimatedTextures(writer);

                // Write items and AI objects
                writer.Write((uint)_items.Count);
                writer.WriteBlockArray(_items);

                // TODO Figure out light map
                var lightmap = new byte[8192];
                writer.Write(lightmap);

                const ushort numDemo            = 0;
                const ushort numCinematicFrames = 0;
                writer.Write(numDemo);
                writer.Write(numCinematicFrames);

                // Write sound meta data
                PrepareSoundsData();
                WriteSoundMetadata(writer);
            }
        }
Пример #8
0
        private void WriteLevelTr4(string ngVersion = null)
        {
            // Now begin to compile the geometry block in a MemoryStream
            byte[] geometryDataBuffer;
            using (var geometryDataStream = new MemoryStream())
            {
                var writer = new BinaryWriterEx(geometryDataStream); // Don't dispose
                ReportProgress(80, "Writing geometry data to memory buffer");

                const int filler = 0;
                writer.Write(filler);

                var numRooms = (ushort)_level.Rooms.Count(r => r != null);
                writer.Write(numRooms);

                foreach (var r in _level.Rooms.Where(r => r != null))
                {
                    _tempRooms[r].WriteTr4(writer);
                }

                // Write floordata
                var numFloorData = (uint)_floorData.Count;
                writer.Write(numFloorData);
                writer.WriteBlockArray(_floorData);

                // Write meshes
                var offset = writer.BaseStream.Position;

                const int numMeshData = 0;
                writer.Write(numMeshData);
                var totalMeshSize = 0;

                for (var i = 0; i < _meshes.Count; i++)
                {
                    var meshSize = _meshes[i].WriteTr4AndTr5(writer);
                    totalMeshSize += (int)meshSize;
                }

                var offset2 = writer.BaseStream.Position;
                // ReSharper disable once SuggestVarOrType_BuiltInTypes
                uint meshDataSize = (uint)((offset2 - offset - 4) / 2);

                // Save the size of the meshes
                writer.BaseStream.Seek(offset, SeekOrigin.Begin);
                writer.Write(meshDataSize);
                writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

                // Write mesh pointers
                writer.Write((uint)_meshPointers.Count);
                writer.WriteBlockArray(_meshPointers);

                // Write animations' data
                writer.Write((uint)_animations.Count);
                foreach (var anim in _animations)
                {
                    anim.Write(writer, _level);
                }

                writer.Write((uint)_stateChanges.Count);
                writer.WriteBlockArray(_stateChanges);

                writer.Write((uint)_animDispatches.Count);
                writer.WriteBlockArray(_animDispatches);

                writer.Write((uint)_animCommands.Count);
                writer.WriteBlockArray(_animCommands);

                writer.Write((uint)_meshTrees.Count);
                writer.WriteBlockArray(_meshTrees);

                writer.Write((uint)_frames.Count);
                writer.WriteBlockArray(_frames);

                writer.Write((uint)_moveables.Count);
                writer.WriteBlockArray(_moveables);

                writer.Write((uint)_staticMeshes.Count);
                writer.WriteBlockArray(_staticMeshes);

                // SPR block
                writer.WriteBlockArray(new byte[] { 0x53, 0x50, 0x52 });

                writer.Write((uint)_spriteTextures.Count);
                writer.WriteBlockArray(_spriteTextures);

                writer.Write((uint)_spriteSequences.Count);
                writer.WriteBlockArray(_spriteSequences);

                // Write camera, flyby and sound sources
                writer.Write((uint)_cameras.Count);
                writer.WriteBlockArray(_cameras);

                writer.Write((uint)_flyByCameras.Count);
                writer.WriteBlockArray(_flyByCameras);

                writer.Write((uint)_soundSources.Count);
                writer.WriteBlockArray(_soundSources);

                // Write pathfinding data
                writer.Write((uint)_boxes.Length);
                writer.WriteBlockArray(_boxes);

                writer.Write((uint)_overlaps.Length);
                writer.WriteBlockArray(_overlaps);

                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Normal);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone1_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone2_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone3_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].GroundZone4_Alternate);
                }
                for (var i = 0; i < _boxes.Length; i++)
                {
                    writer.Write(_zones[i].FlyZone_Alternate);
                }

                // Write animated textures
                _textureInfoManager.WriteAnimatedTextures(writer);

                // Write object textures
                writer.Write(checked ((byte)_textureInfoManager.UvRotateCount));
                writer.Write(new byte[] { 0x54, 0x45, 0x58 });

                _textureInfoManager.WriteTextureInfos(writer, _level);

                // Write items and AI objects
                writer.Write((uint)_items.Count);
                writer.WriteBlockArray(_items);

                writer.Write((uint)_aiItems.Count);
                writer.WriteBlockArray(_aiItems);

                // Write sound meta data
                PrepareSoundsData();
                WriteSoundMetadata(writer);

                // Finish it
                writer.Write((short)0);
                writer.Write((short)0);
                writer.Write((short)0);

                geometryDataBuffer = geometryDataStream.ToArray();
            }

            using (var writer = new BinaryWriterEx(new FileStream(_dest, FileMode.Create, FileAccess.Write, FileShare.None)))
            {
                ReportProgress(90, "Writing final level");
                writer.WriteBlockArray(new byte[] { 0x54, 0x52, 0x34, 0x00 });

                ReportProgress(91, "Writing textures");

                writer.Write((ushort)_textureInfoManager.NumRoomPages);
                writer.Write((ushort)_textureInfoManager.NumObjectsPages);
                // Bump map pages must be multiplied by 2 or tile index will be wrong
                writer.Write((ushort)(_textureInfoManager.NumBumpPages * 2));

                // Compress data
                ReportProgress(95, "Compressing data");

                byte[] texture32 = null;
                int    texture32UncompressedSize = -1;
                byte[] texture16 = null;
                int    texture16UncompressedSize = -1;
                byte[] textureMisc = null;
                int    textureMiscUncompressedSize = -1;
                byte[] geometryData = null;
                int    geometryDataUncompressedSize = -1;

                using (Task Texture32task = Task.Factory.StartNew(() =>
                {
                    texture32 = ZLib.CompressData(_texture32Data);
                    texture32UncompressedSize = _texture32Data.Length;
                }))
                    using (Task Texture16task = Task.Factory.StartNew(() =>
                    {
                        byte[] texture16Data = PackTextureMap32To16Bit(_texture32Data, _level.Settings);
                        texture16 = ZLib.CompressData(texture16Data);
                        texture16UncompressedSize = texture16Data.Length;
                    }))
                        using (Task textureMiscTask = Task.Factory.StartNew(() =>
                        {
                            Stream textureMiscData = PrepareFontAndSkyTexture();
                            textureMisc = ZLib.CompressData(textureMiscData);
                            textureMiscUncompressedSize = (int)textureMiscData.Length;
                        }))
                            using (Task GeometryDataTask = Task.Factory.StartNew(() =>
                            {
                                geometryData = ZLib.CompressData(geometryDataBuffer);
                                geometryDataUncompressedSize = geometryDataBuffer.Length;
                            }))
                                Task.WaitAll(Texture32task, Texture16task, textureMiscTask, GeometryDataTask);

                // Write data
                ReportProgress(96, "Writing compressed data to file.");

                writer.Write(texture32UncompressedSize);
                writer.Write(texture32.Length);
                writer.Write(texture32);

                writer.Write(texture16UncompressedSize);
                writer.Write(texture16.Length);
                writer.Write(texture16);

                writer.Write(textureMiscUncompressedSize);
                writer.Write(textureMisc.Length);
                writer.Write(textureMisc);

                writer.Write(geometryDataUncompressedSize);
                writer.Write(geometryData.Length);
                writer.Write(geometryData);

                ReportProgress(97, "Writing WAVE sounds");
                WriteSoundData(writer);

                // Write NG header
                if (!string.IsNullOrEmpty(ngVersion))
                {
                    ReportProgress(98, "Writing NG header");
                    WriteNgHeader(writer, ngVersion);
                }

                ReportProgress(99, "Done");
            }
        }
Пример #9
0
        public void WriteTr5(BinaryWriterEx writer)
        {
            var roomStartOffset = writer.BaseStream.Position;

            var xela = System.Text.Encoding.ASCII.GetBytes("XELA");

            writer.Write(xela);

            var startOfRoomPosition = writer.BaseStream.Position;
            var roomDataSize        = 0;

            writer.Write((uint)roomDataSize);

            var separator = 0xcdcdcdcd;

            writer.Write(separator);

            var EndSDOffsetPosition = writer.BaseStream.Position;
            var EndSDOffset         = (uint)0;

            writer.Write(EndSDOffset);

            var StartOfSDOffsetPosition = writer.BaseStream.Position;
            var StartOfSDOffset         = (uint)0;

            writer.Write(StartOfSDOffset);

            writer.Write(separator);

            var EndPortalOffsetPosition = writer.BaseStream.Position;
            var EndPortalOffset         = (uint)0;

            writer.Write(EndPortalOffset);

            // tr5_room_info
            writer.Write(Info.X);
            writer.Write(0);
            writer.Write(Info.Z);
            writer.Write(Info.YBottom);
            writer.Write(Info.YTop);

            writer.Write(NumZSectors);
            writer.Write(NumXSectors);

            writer.Write(AmbientIntensity);

            writer.Write((ushort)Lights.Count);
            writer.Write((ushort)StaticMeshes.Count);

            writer.Write(ReverbInfo);
            writer.Write(AlternateGroup);
            writer.Write(WaterScheme);
            writer.Write((byte)0);

            writer.Write((uint)0x00007fff);
            writer.Write((uint)0x00007fff);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xffffffff);

            writer.Write(AlternateRoom);
            writer.Write(Flags);
            writer.Write((ushort)0xffff);

            writer.Write((ushort)0);
            writer.Write((uint)0);
            writer.Write((uint)0);

            writer.Write(0xcdcdcdcd);
            writer.Write((uint)0);

            writer.Write((float)Info.X);
            writer.Write((float)Info.YBottom);
            writer.Write((float)Info.Z);

            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write((uint)0);
            writer.Write(0xcdcdcdcd);

            writer.Write((uint)Triangles.Count);
            writer.Write((uint)Quads.Count);

            writer.Write((uint)0);

            writer.Write((uint)(Lights.Count * 88));
            writer.Write((uint)Lights.Count);

            writer.Write((uint)0);

            writer.Write(Info.YTop);
            writer.Write(Info.YBottom);

            writer.Write((uint)1);

            var LayerOffsetPosition = writer.BaseStream.Position;
            var LayerOffset         = 0;

            writer.Write(LayerOffset);

            var VerticesOffsetPosition = writer.BaseStream.Position;
            var VerticesOffset         = 0;

            writer.Write(VerticesOffset);

            var PolyOffsetPosition = writer.BaseStream.Position;
            var PolyOffset         = 0;

            writer.Write(PolyOffset);
            writer.Write(PolyOffset);

            writer.Write((uint)(Vertices.Count * 28));

            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);
            writer.Write(0xcdcdcdcd);

            // Start of room data (after 216 bytes from XELA)
            foreach (var light in Lights)
            {
                writer.Write((float)light.X);
                writer.Write((float)light.Y);
                writer.Write((float)light.Z);
                writer.Write(light.Color.Red / 255.0f);
                writer.Write(light.Color.Green / 255.0f);
                writer.Write(light.Color.Blue / 255.0f);

                writer.Write(0xcdcdcdcd);

                writer.Write(light.In);
                writer.Write(light.Out);

                writer.Write((float)(light.LightType == 2 ? Math.Acos(light.In) * 2.0f : 0));
                writer.Write((float)(light.LightType == 2 ? Math.Acos(light.Out) * 2.0f : 0));
                writer.Write(light.CutOff);

                writer.Write(light.DirectionX);
                writer.Write(light.DirectionY);
                writer.Write(light.DirectionZ);

                writer.Write(light.X);
                writer.Write(light.Y);
                writer.Write(light.Z);

                writer.Write((int)light.DirectionX);
                writer.Write((int)light.DirectionY);
                writer.Write((int)light.DirectionZ);

                writer.Write(light.LightType);

                writer.Write((byte)0xcd);
                writer.Write((byte)0xcd);
                writer.Write((byte)0xcd);
            }

            StartOfSDOffset = (uint)(writer.BaseStream.Position - roomStartOffset - 216);
            writer.WriteBlockArray(Sectors);
            EndSDOffset = (uint)(StartOfSDOffset + NumXSectors * NumZSectors * 8);

            writer.Write((ushort)Portals.Count);
            if (Portals.Count != 0)
            {
                writer.WriteBlockArray(Portals);
            }

            writer.Write((ushort)0xcdcd);
            EndPortalOffset = (uint)(writer.BaseStream.Position - roomStartOffset - 216);

            if (StaticMeshes.Count != 0)
            {
                writer.WriteBlockArray(StaticMeshes);
            }

            LayerOffset = (int)(writer.BaseStream.Position - roomStartOffset - 216);

            // Write just one layer
            writer.Write((uint)Vertices.Count);
            writer.Write((ushort)0);
            writer.Write((ushort)Quads.Count);
            writer.Write((ushort)Triangles.Count);
            writer.Write((ushort)0);

            writer.Write((ushort)0);
            writer.Write((ushort)0);

            writer.Write(1024.0f);
            writer.Write((float)Info.YBottom);
            writer.Write(1024.0f);
            writer.Write((NumXSectors - 1) * 1024.0f);
            writer.Write((float)Info.YTop);
            writer.Write((NumZSectors - 1) * 1024.0f);

            writer.Write((uint)0);
            var LayerVerticesOffset = writer.BaseStream.Position;

            writer.Write((uint)0);
            var LayerPolygonsOffset = writer.BaseStream.Position;

            writer.Write((uint)0);
            writer.Write((uint)0);

            PolyOffset = LayerOffset + 56;

            for (var k = 0; k < Quads.Count; k++)
            {
                Quads[k].Write(writer);
                writer.Write((ushort)0);
            }
            for (var k = 0; k < Triangles.Count; k++)
            {
                Triangles[k].Write(writer);
                writer.Write((ushort)0);
            }

            if (Triangles.Count % 2 != 0)
            {
                writer.Write((ushort)0xcdcd);
            }

            VerticesOffset = (int)(writer.BaseStream.Position - roomStartOffset - 216);

            foreach (var vertex in Vertices)
            {
                writer.Write((float)vertex.Position.X);
                writer.Write((float)vertex.Position.Y);
                writer.Write((float)vertex.Position.Z);
                writer.Write((float)vertex.Normal.X);
                writer.Write((float)vertex.Normal.Y);
                writer.Write((float)vertex.Normal.Z);
                writer.Write(vertex.Color);
            }

            var endOfRoomOffset = writer.BaseStream.Position;

            roomDataSize = (int)(endOfRoomOffset - roomStartOffset - 8);

            writer.Seek((int)startOfRoomPosition, SeekOrigin.Begin);
            writer.Write(roomDataSize);

            writer.Seek((int)EndSDOffsetPosition, SeekOrigin.Begin);
            writer.Write((int)EndSDOffset);
            writer.Write((int)StartOfSDOffset);

            writer.Seek((int)EndPortalOffsetPosition, SeekOrigin.Begin);
            writer.Write((int)EndPortalOffset);

            writer.Seek((int)LayerOffsetPosition, SeekOrigin.Begin);
            writer.Write(LayerOffset);
            writer.Write(VerticesOffset);
            writer.Write(PolyOffset);
            writer.Write(PolyOffset);

            writer.Seek((int)LayerVerticesOffset, SeekOrigin.Begin);
            writer.Write(VerticesOffset);
            writer.Write(PolyOffset);
            writer.Write(PolyOffset);

            writer.Seek((int)endOfRoomOffset, SeekOrigin.Begin);
        }
Пример #10
0
        public void WriteTr4(BinaryWriterEx writer)
        {
            writer.WriteBlock(Info);

            var offset = writer.BaseStream.Position;

            writer.Write(0);

            writer.Write((ushort)Vertices.Count);
            for (var k = 0; k < Vertices.Count; k++)
            {
                writer.Write(Vertices[k].Position.X);
                writer.Write(Vertices[k].Position.Y);
                writer.Write(Vertices[k].Position.Z);
                writer.Write(Vertices[k].Lighting1);
                writer.Write(Vertices[k].Attributes);
                writer.Write(Vertices[k].Lighting2);
            }

            writer.Write((ushort)Quads.Count);
            for (var k = 0; k < Quads.Count; k++)
            {
                Quads[k].Write(writer);
            }

            writer.Write((ushort)Triangles.Count);
            for (var k = 0; k < Triangles.Count; k++)
            {
                Triangles[k].Write(writer);
            }

            // For sprites, not used
            writer.Write((ushort)0);

            // Now save current offset and calculate the size of the geometry
            var offset2 = writer.BaseStream.Position;
            // ReSharper disable once SuggestVarOrType_BuiltInTypes
            ushort roomGeometrySize = (ushort)((offset2 - offset - 4) / 2);

            // Save the size of the geometry
            writer.BaseStream.Seek(offset, SeekOrigin.Begin);
            writer.Write(roomGeometrySize);
            writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

            // Write portals
            writer.WriteBlock((ushort)Portals.Count);
            if (Portals.Count != 0)
            {
                writer.WriteBlockArray(Portals);
            }

            // Write sectors
            writer.Write(NumZSectors);
            writer.Write(NumXSectors);
            writer.WriteBlockArray(Sectors);

            // Write room color
            writer.Write(AmbientIntensity);

            // Write lights
            writer.WriteBlock((ushort)Lights.Count);
            if (Lights.Count != 0)
            {
                writer.WriteBlockArray(Lights);
            }

            // Write static meshes
            writer.WriteBlock((ushort)StaticMeshes.Count);
            if (StaticMeshes.Count != 0)
            {
                writer.WriteBlockArray(StaticMeshes);
            }

            // Write final data
            writer.Write(AlternateRoom);
            writer.Write(Flags);
            writer.Write(WaterScheme);
            writer.Write(ReverbInfo);
            writer.Write(AlternateGroup);
        }
Пример #11
0
        public void WriteTr3(BinaryWriterEx writer)
        {
            writer.WriteBlock(Info);

            var offset = writer.BaseStream.Position;

            writer.Write(0);

            writer.Write((ushort)Vertices.Count);
            for (var k = 0; k < Vertices.Count; k++)
            {
                writer.Write(Vertices[k].Position.X);
                writer.Write(Vertices[k].Position.Y);
                writer.Write(Vertices[k].Position.Z);
                writer.Write(Vertices[k].Lighting1);
                writer.Write(Vertices[k].Attributes);
                writer.Write(Vertices[k].Lighting2);
            }

            writer.Write((ushort)Quads.Count);
            for (var k = 0; k < Quads.Count; k++)
            {
                Quads[k].Write(writer);
            }

            writer.Write((ushort)Triangles.Count);
            for (var k = 0; k < Triangles.Count; k++)
            {
                Triangles[k].Write(writer);
            }

            // For sprites, not used
            writer.Write((ushort)0);

            // Now save current offset and calculate the size of the geometry
            var offset2 = writer.BaseStream.Position;
            // ReSharper disable once SuggestVarOrType_BuiltInTypes
            ushort roomGeometrySize = (ushort)((offset2 - offset - 4) / 2);

            // Save the size of the geometry
            writer.BaseStream.Seek(offset, SeekOrigin.Begin);
            writer.Write(roomGeometrySize);
            writer.BaseStream.Seek(offset2, SeekOrigin.Begin);

            // Write portals
            writer.WriteBlock((ushort)Portals.Count);
            if (Portals.Count != 0)
            {
                writer.WriteBlockArray(Portals);
            }

            // Write sectors
            writer.Write(NumZSectors);
            writer.Write(NumXSectors);
            writer.WriteBlockArray(Sectors);

            // Write room color
            writer.Write((ushort)(AmbientIntensity));

            // Light mode is broken in TR3
            writer.Write((ushort)0x00);

            // Write lights
            writer.WriteBlock((ushort)Lights.Count);
            if (Lights.Count != 0)
            {
                foreach (var light in Lights)
                {
                    writer.Write(light.X);
                    writer.Write(light.Y);
                    writer.Write(light.Z);
                    writer.Write(light.Color.Red);
                    writer.Write(light.Color.Green);
                    writer.Write(light.Color.Blue);
                    writer.Write(light.LightType);

                    if (light.LightType == 0) // FIXME: TR3 sun type - UNKNOWN NORMALS FORMAT!
                    {
                        writer.Write((ushort)(light.X + (light.DirectionX * 1024.0f)));
                        writer.Write((ushort)(light.Y + (light.DirectionY * 1024.0f)));
                        writer.Write((ushort)(light.Z + (light.DirectionZ * 1024.0f)));
                        writer.Write((ushort)0x0000); // Padding
                    }
                    else
                    {
                        writer.Write((uint)light.Intensity);
                        writer.Write((uint)light.Out);
                    }
                }
            }

            // Write static meshes
            writer.WriteBlock((ushort)StaticMeshes.Count);
            if (StaticMeshes.Count != 0)
            {
                writer.WriteBlockArray(StaticMeshes);
            }

            // Write final data
            writer.Write(AlternateRoom);
            writer.Write(Flags);
            writer.Write(WaterScheme);
            writer.Write(ReverbInfo);
            writer.Write((byte)0x00); // Alternate group was introduced in TR4
        }