private static byte[] CompressData(byte[] data) { byte[] prefix = { 0x00, 0x00, 0x06, 0x00 }; // These bytes are important, otherwise the game won't launch byte[] compressedData = ZLib.CompressData(data); return(prefix.Concat(compressedData).ToArray()); }
/// <summary> /// Builds a compressed payload from the current content's data. /// </summary> /// <param name="opcode">The packet's opcode.</param> /// <returns>A new compressed payload.</returns> public byte[] GetCompressedPayload(short opcode) { byte[] compressedData = ZLib.CompressData(_contentData); byte[] packetId = BigEndian.GetBytes(opcode); byte[] size = BigEndian.GetBytes(compressedData.Length + sizeof(int)); byte[] compressionFlag = { 1 }; // true byte[] decompressedSize = LittleEndian.GetBytes(_contentData.Length); byte[] padding = { 0, 0, 0, 0 }; return(Sequence.Concat(packetId, size, compressionFlag, decompressedSize, compressedData, padding)); }
/// <summary> /// Builds a compressed payload from the current content's data. /// </summary> /// <param name="id">The packet's id.</param> /// <returns>A new compressed payload.</returns> public byte[] GetCompressedPayload(CenterOpcodes oId) { short id = (short)oId; byte[] compressedData = ZLib.CompressData(Data); byte[] packetId = BigEndian.GetBytes(id); byte[] size = BigEndian.GetBytes(compressedData.Length + 4); byte[] compressionFlag = { 1 }; // true byte[] decompressedSize = LittleEndian.GetBytes(Data.Length); byte[] padding = { 0, 0, 0 }; return(Sequence.Concat(packetId, size, compressionFlag, decompressedSize, compressedData, padding)); }
public void Dispose() { if (_baseStream == null) { return; } try { switch (_compression) { case Compression.Zlib: byte[] compressedData = ZLib.CompressData(_writer.BaseStream, _compressionLevel); new BinaryWriter(_baseStream).Write(compressedData.Length); _baseStream.Write(compressedData, 0, compressedData.Length); break; } } finally { _writer.Dispose(); } }
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"); } } }
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"); } }