/// <summary> /// Parses compressed texture and returns it on the output. /// </summary> /// <param name="byteptr_t">Pointer to the tpk block array.</param> /// <param name="offslot">Offslot of the texture to be parsed</param> /// <returns>Decompressed texture valid to the current support.</returns> protected override unsafe void ParseCompTexture(byte *byteptr_t, OffSlot offslot) { byteptr_t += offslot.AbsoluteOffset; if (*(uint *)byteptr_t != TPK.COMPRESSED_TEXTURE) { return; // if not a compressed texture } // Decompress all data excluding 0x18 byte header var data = new byte[offslot.CompressedSize - 0x18]; for (int a1 = 0; a1 < data.Length; ++a1) { data[a1] = *(byteptr_t + 0x18 + a1); } data = JDLZ.Decompress(data); // In compressed textures, their header lies right in the end (0x7C + 0x18 bytes) fixed(byte *dataptr_t = &data[0]) { int offset = data.Length - 0x7C - 0x18; var Read = new Texture(dataptr_t, offset, 0x7C, this._collection_name, this.Database); Read.ReadData(dataptr_t, 0, true); uint compression = *(uint *)(dataptr_t + offset + 0x7C + 0xC); this.compressions.Add(compression); this.Textures.Add(Read); } }
public override TexturePack Get() { if (ContainerSize == 0) { throw new Exception("containerSize is not set!"); } var data = new byte[ContainerSize]; BinaryReader.BaseStream.Read(data, 0, data.Length); var decompressed = JDLZ.Decompress(data); const string newName = "_tmpCompressedTpk.dejdlz"; using (var stream = new FileStream(newName, FileMode.Create)) { stream.Write(decompressed, 0, decompressed.Length); } var readStream = new FileStream(newName, FileMode.Open); readStream.Seek(8, SeekOrigin.Current); var tpkContainer = new TPKReadContainer(new BinaryReader(readStream), FileName, decompressed.Length, true); var result = tpkContainer.Get(); readStream.Close(); File.Delete(newName); return(result); }
/// <summary> /// Check to see if the file needs to be decompressed. /// If so, decompress it. /// </summary> private void CheckCompression() { var flag = Reader.ReadChars(4); Reader.BaseStream.Position -= 4; if (flag[0] == 'J' && flag[1] == 'D' && flag[2] == 'L' && flag[3] == 'Z') { var allData = Reader.ReadBytes((int)Reader.BaseStream.Length); var decompressed = JDLZ.Decompress(allData); Reader = new BinaryReader(new MemoryStream(decompressed)); } }
public override FNGFile Get() { if (ContainerSize == 0) { throw new Exception("containerSize is not set!"); } BinaryReader.BaseStream.Seek(4, SeekOrigin.Current); var test = BinaryReader.ReadChars(4); BinaryReader.BaseStream.Seek(-4, SeekOrigin.Current); if (test[0] != 'J' || test[1] != 'D' || test[2] != 'L' || test[3] != 'Z') { return(new FNGFile(ChunkID.BCHUNK_FENG_PACKAGE, ContainerSize, BinaryReader.BaseStream.Position) { Name = "__UNSUPPORTED__", HasData = false }); } var data = new byte[ContainerSize - 4]; BinaryReader.BaseStream.Read(data, 0, data.Length); var decompressed = JDLZ.Decompress(data); const string newName = "_tmpCompressedFENG.dejdlz"; using (var stream = new FileStream(newName, FileMode.Create)) { stream.Write(decompressed, 0, decompressed.Length); } var readStream = new FileStream(newName, FileMode.Open); var fngContainer = new FNGReadContainer(new BinaryReader(readStream), FileName, decompressed.Length); var result = fngContainer.Get(); readStream.Close(); File.Delete(newName); return(result); }
public override void Run(ReaderContext context) { var fileName = _fileName = $"decompress_{_random.Next()}.bin"; Console.WriteLine($"decompressing to {_fileName}"); context.Reader.BaseStream.Position = 0; var fileData = new byte[context.Reader.BaseStream.Length]; context.Reader.Read(fileData, 0, fileData.Length); byte[] decompressed; switch (_type) { case CompressionType.JDLZ: decompressed = JDLZ.Decompress(fileData); break; case CompressionType.WorldBin: decompressed = fileData; for (var i = decompressed.Length - 1; i >= 1; --i) { decompressed[i] ^= decompressed[i - 1]; } break; default: decompressed = new byte[0]; break; } using (var writer = new BinaryWriter(new FileStream(fileName, FileMode.Create))) { writer.Write(decompressed, 0, decompressed.Length); } context.Reader = new BinaryReader(File.OpenRead(fileName)); }
/// <summary> /// Loads English file and disassembles its blocks /// </summary> /// <param name="Language_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadLanguage(string Language_dir, Database.Underground2 db) { Language_dir += @"\LANGUAGES\"; // Get everything from language files try { db._LngGlobal = File.ReadAllBytes(Language_dir + "English.bin"); db._LngLabels = File.ReadAllBytes(Language_dir + "Labels.bin"); Log.Write("Reading data from English.bin..."); Log.Write("Reading data from Labels.bin..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._LngGlobal = JDLZ.Decompress(db._LngGlobal); db._LngLabels = JDLZ.Decompress(db._LngLabels); // Use pointers to speed up process fixed(byte *strptr = &db._LngGlobal[0], labptr = &db._LngLabels[0]) { db.STRBlocks.Collections.Add(new STRBlock(strptr, labptr, db._LngGlobal.Length, db._LngLabels.Length, db)); } return(true); }
/// <summary> /// Decompresses .fng JDLZ-compressed file. /// </summary> /// <param name="fng">.fng file as a byte array.</param> /// <returns>Decompressed FEng file as a byte array.</returns> public static unsafe byte[] Decompress(byte[] fng) { if (fng[0] == 3) // return if already decompressed { return(fng); } byte[] InterData = new byte[fng.Length - 12]; Buffer.BlockCopy(fng, 12, InterData, 0, fng.Length - 12); var NewData = JDLZ.Decompress(InterData); byte[] result = new byte[8 + NewData.Length]; fixed(byte *byteptr_t = &result[0]) { *(uint *)(byteptr_t + 0) = Global.FEngFiles; *(int *)(byteptr_t + 4) = NewData.Length; } Buffer.BlockCopy(NewData, 0, result, 8, NewData.Length); return(result); }
/// <summary> /// Loads GlobalA file and disassembles its blocks /// </summary> /// <param name="GlobalA_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadGlobalA(string GlobalA_dir, Database.Underground2 db) { GlobalA_dir += @"\GLOBAL\GlobalA.bun"; // Get everything from GlobalA.bun try { db._GlobalABUN = File.ReadAllBytes(GlobalA_dir); Log.Write("Reading data from GlobalA.bun..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._GlobalABUN = JDLZ.Decompress(db._GlobalABUN); // Use pointers to speed up process fixed(byte *byteptr_t = &db._GlobalABUN[0]) { uint offset = 0; // to calculate current offset uint ID = 0; // to get the ID of the block being read uint size = 0; // to get the size of the block being read while (offset < db._GlobalABUN.Length) { ID = *(uint *)(byteptr_t + offset); // read ID size = *(uint *)(byteptr_t + offset + 4); // read size if (offset + size > db._GlobalABUN.Length) { if (Process.MessageShow) { MessageBox.Show("GlobalA: unable to read beyond the stream.", "Failure"); } else { Console.WriteLine("GlobalA: unable to read beyond the stream."); } return(false); } switch (ID) { case Global.TPKBlocks: int count = db.TPKBlocks.Length; db.TPKBlocks.Collections.Add(new TPKBlock(byteptr_t + offset, count, db)); db.TPKBlocks[count].InGlobalA = true; break; case Global.FEngFiles: case Global.FNGCompress: E_FNGroup(byteptr_t + offset, size + 8, db); break; default: break; } offset += 8 + size; // advance in offset } } return(true); }
protected override void ReadChunks(long totalSize) { var runTo = (_compressed ? BinaryReader.BaseStream.Position - 8 : BinaryReader.BaseStream.Position) + totalSize; for (var i = 0; i < 0xFFFF && BinaryReader.BaseStream.Position < runTo; i++) { var chunkId = BinaryReader.ReadUInt32(); var chunkSize = BinaryReader.ReadUInt32(); var chunkRunTo = BinaryReader.BaseStream.Position + chunkSize; var normalizedId = (long)(chunkId & 0xffffffff); BinaryUtil.ReadPadding(BinaryReader, ref chunkSize); BinaryUtil.PrintID(BinaryReader, chunkId, normalizedId, chunkSize, GetType(), _logLevel, typeof(TPKChunks)); switch (normalizedId) { case (long)TPKChunks.TPKRoot: // TPK root case (long)TPKChunks.TPKDataRoot: // TPK data root _logLevel = 2; ReadChunks(chunkSize); _logLevel = 1; break; case (long)TPKChunks.TPKInfo: // TPK info { var header = BinaryUtil.ReadStruct <TpkInfoHeader>(BinaryReader); _texturePack.Name = header.Name; _texturePack.Path = header.Path; _texturePack.Hash = header.Hash; break; } case (long)TPKChunks.TPKTextureHashes: // Texture hashes { // Every entry is 8 bytes; a 4-byte hash and 4 bytes of 0x00. var numTextures = chunkSize / 8; for (var j = 0; j < numTextures; j++) { var hash = BinaryReader.ReadUInt32(); BinaryReader.BaseStream.Seek(4, SeekOrigin.Current); _texturePack.Hashes.Add(hash); } break; } case (long)TPKChunks.TPKTextureHeaders: // Texture headers { for (var j = 0; j < _texturePack.Hashes.Count; j++) { var textureHeader = BinaryUtil.ReadStruct <TpkTextureHeader>(BinaryReader); var texture = new Texture { TextureHash = textureHeader.TextureHash, TypeHash = textureHeader.TypeHash, Name = textureHeader.Name, Width = textureHeader.Width, Height = textureHeader.Height, MipMap = textureHeader.MipMap, DataOffset = textureHeader.DataOffset, DataSize = textureHeader.DataSize }; _texturePack.Textures.Add(texture); } break; } case (long)TPKChunks.TPKDXTHeaders: // DXT headers { foreach (var texture in _texturePack.Textures) { BinaryReader.BaseStream.Seek(20, SeekOrigin.Current); texture.CompressionType = BinaryReader.ReadInt32(); BinaryReader.BaseStream.Seek(0x08, SeekOrigin.Current); } break; } case (long)TPKChunks.TPKData: { if (_texturePack.Hashes.Any() && !_texturePack.Textures.Any()) { // Probably compressed? #if DEBUG Console.WriteLine("Seems to be a compressed TPK"); #endif var jdlzPositions = new List <long>(); while (BinaryReader.BaseStream.Position < chunkRunTo) { var tmpCompressionFlag = BinaryReader.ReadBytes(4); if (tmpCompressionFlag[0] == 'J' && tmpCompressionFlag[1] == 'D' && tmpCompressionFlag[2] == 'L' && tmpCompressionFlag[3] == 'Z') { #if DEBUG Console.WriteLine("JDLZ!"); #endif var headerPos = BinaryReader.BaseStream.Position - 4; jdlzPositions.Add(headerPos); } } foreach (var jdlzPos in jdlzPositions) { BinaryReader.BaseStream.Seek(jdlzPos, SeekOrigin.Begin); var header = BinaryUtil.ReadStruct <CommonStructs.JDLZHeader>(BinaryReader); #if DEBUG Console.WriteLine( $"JDLZ: {header.CompressedLength} bytes compressed, {header.UncompressedLength} uncompressed | header pos 0x{jdlzPos:X8}"); #endif var compressedData = new byte[header.CompressedLength]; BinaryReader.BaseStream.Position = jdlzPos; BinaryReader.Read(compressedData, 0, compressedData.Length); var uncompressedData = JDLZ.Decompress(compressedData); #if DEBUG Console.WriteLine(BinaryUtil.HexDump(uncompressedData)); #endif } } else if (_texturePack.Hashes.Count != _texturePack.Textures.Count) { // What? throw new NFSException( $"Expected {_texturePack.Hashes.Count} textures, only got {_texturePack.Textures.Count}"); } else { } break; } } BinaryUtil.ValidatePosition(BinaryReader, chunkRunTo, GetType()); BinaryReader.BaseStream.Seek(chunkRunTo - BinaryReader.BaseStream.Position, SeekOrigin.Current); } }
protected override void ReadChunks(long totalSize) { if (BinaryReader.BaseStream.Length == 0) { return; } var curPos = BinaryReader.BaseStream.Position; if (BinaryReader.ReadChar() == 'J' && BinaryReader.ReadChar() == 'D' && BinaryReader.ReadChar() == 'L' && BinaryReader.ReadChar() == 'Z') { #if DEBUG Console.WriteLine("JDLZ compressed!"); #endif BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); var data = new byte[BinaryReader.BaseStream.Length]; BinaryReader.BaseStream.Read(data, 0, data.Length); var decompressed = JDLZ.Decompress(data); var newName = _fileName + ".dejdlz"; var stream = new FileStream(newName, FileMode.CreateNew); stream.Write(decompressed, 0, decompressed.Length); stream.Close(); BinaryReader = new BinaryReader(new FileStream(newName, FileMode.Open)); File.Delete(newName); } else { BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); } var runTo = BinaryReader.BaseStream.Position + totalSize; for (var i = 0; i < 0xFFFF && BinaryReader.BaseStream.Position < runTo; i++ ) { var chunkId = BinaryReader.ReadUInt32(); var chunkSize = BinaryReader.ReadUInt32(); var chunkRunTo = BinaryReader.BaseStream.Position + chunkSize; var normalizedId = (int)chunkId & 0xffffffff; var endChecks = true; BinaryUtil.PrintID(BinaryReader, chunkId, normalizedId, chunkSize, GetType()); switch (normalizedId) { case (long)ChunkID.BCHUNK_TRACKSTREAMER_SECTIONS: _dataModels.Add(new SectionListReadContainer(BinaryReader, FileName, chunkSize).Get()); break; case (long)ChunkID.BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS: _dataModels.Add(new TpkReadContainer(BinaryReader, FileName, chunkSize).Get()); break; case (long)ChunkID.BCHUNK_SPEED_ESOLID_LIST_CHUNKS: var solidList = new SolidListReadContainer(BinaryReader, FileName, chunkSize).Get(); _dataModels.Add(solidList); if (solidList.Compressed) { endChecks = false; } break; default: _dataModels.Add(new NullModel(normalizedId, chunkSize, BinaryReader.BaseStream.Position)); break; } if (endChecks) { BinaryUtil.ValidatePosition(BinaryReader, chunkRunTo, GetType()); BinaryReader.BaseStream.Seek(chunkRunTo - BinaryReader.BaseStream.Position, SeekOrigin.Current); } else { BinaryReader.BaseStream.Seek(chunkRunTo, SeekOrigin.Begin); } } }
/// <summary> /// Loads GlobalB file and disassembles its blocks /// </summary> /// <param name="GlobalB_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadGlobalB(string GlobalB_dir, Database.MostWanted db) { LibColBlockExists = false; GlobalB_dir += @"\GLOBAL\GlobalB.lzc"; // Get everything from GlobalB.lzc try { db._GlobalBLZC = File.ReadAllBytes(GlobalB_dir); Log.Write("Reading data from GlobalB.lzc..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._GlobalBLZC = JDLZ.Decompress(db._GlobalBLZC); // Use pointers to speed up process fixed(byte *byteptr_t = &db._GlobalBLZC[0]) { uint offset = 0; // to calculate current offset uint ID = 0; // to get the ID of the block being read uint size = 0; // to get the size of the block being read uint proff = 0; // offset of the preset rides block uint prsize = 0; // size of the preset rides block uint cpoff = 0; // offset of the carparts block uint cpsize = 0; // size of the carparts block uint cooff = 0; // offset of the collision block uint cosize = 0; // size of the collision block while (offset < db._GlobalBLZC.Length) { ID = *(uint *)(byteptr_t + offset); // read ID size = *(uint *)(byteptr_t + offset + 4); // read size if (offset + size > db._GlobalBLZC.Length) { if (Process.MessageShow) { MessageBox.Show("GlobalB: unable to read beyond the stream.", "Failure"); } else { Console.WriteLine("GlobalB: unable to read beyond the stream."); } return(false); } switch (ID) { case 0: if (*(uint *)(byteptr_t + offset + 8) == Global.GlobalLib) { E_GlobalLibBlock(byteptr_t + offset, size + 8); } break; case Global.Materials: E_Material(byteptr_t + offset, db); break; case Global.TPKBlocks: int count = db.TPKBlocks.Length; db.TPKBlocks.Collections.Add(new TPKBlock(byteptr_t + offset, count, db)); break; case Global.CarTypeInfo: E_CarTypeInfo(byteptr_t + offset + 8, size, db); break; case Global.PresetRides: proff = offset + 8; prsize = size; break; case Global.CarParts: cpoff = offset + 8; cpsize = size; break; case Global.SlotTypes: E_SlotType(byteptr_t + offset, size + 8, db); break; case Global.Collisions: cooff = offset + 8; cosize = size; break; case Global.FEngFiles: case Global.FNGCompress: E_FNGroup(byteptr_t + offset, size + 8, db); break; default: break; } offset += 8 + size; // advance in offset } // CarParts and Collisions blocks are the last ones to disassemble E_CarParts(byteptr_t + cpoff, cpsize, db); E_Collisions(byteptr_t + cooff, cosize, db); E_PresetRides(byteptr_t + proff, prsize, db); } // Disperse spoilers across cartypeinfo E_Spoilers(db); return(true); }
private void ReadCompressed() { foreach (var compHeader in _compressionHeaders) { #if DEBUG Console.WriteLine($"0x{compHeader.TextureHash:X8} @ 0x{compHeader.AbsoluteOffset:X8}"); #endif var skip = false; if (compHeader.AbsoluteOffset != 0) { BinaryReader.BaseStream.Position = compHeader.AbsoluteOffset; // relative to the beginning of the chunk or file var readBytes = 0L; var blocks = new List <byte[]>(); var partSizes = new uint[0xFFFF]; while (readBytes < compHeader.Size) { if (BinaryReader.ReadInt32() == 0x55441122) { BinaryReader.BaseStream.Position -= 4; var cbh = BinaryUtil.ReadStruct <CommonStructs.CompressBlockHead>(BinaryReader); var flag = BinaryReader.ReadChars(4); BinaryReader.BaseStream.Position -= 4; var data = BinaryReader.ReadBytes((int)(cbh.TotalBlockSize - 24)); if (flag[0] == 'J' && flag[1] == 'D' && flag[2] == 'L' && flag[3] == 'Z') { blocks.Add(JDLZ.Decompress(data)); } else if (_compLibEnabled) { var decompressed = new byte[cbh.OutSize]; Compression.Decompress(data, decompressed); blocks.Add(decompressed); } else { #if DEBUG Console.WriteLine("CompLib disabled, can't decompress"); #endif skip = true; break; } data = null; readBytes += cbh.TotalBlockSize; partSizes[blocks.Count - 1] = cbh.OutSize; } } if (skip) { continue; } if (blocks.Count == 1) { var infoBlock = blocks[0]; var blockReader = new BinaryReader(new MemoryStream(infoBlock)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 212; // 12 0x00 at the beginning of each header blockReader.BaseStream.Seek(12, SeekOrigin.Current); var textureHash = blockReader.ReadUInt32(); var typeHash = blockReader.ReadUInt32(); blockReader.ReadInt32(); var dataSize = blockReader.ReadUInt32(); blockReader.ReadInt32(); var width = blockReader.ReadInt32(); var height = blockReader.ReadInt32(); var mipMap = blockReader.ReadInt32(); blockReader.ReadUInt32(); blockReader.ReadUInt32(); blockReader.BaseStream.Seek(24, SeekOrigin.Current); blockReader.ReadUInt32(); var dataOffset = blockReader.ReadUInt32(); blockReader.BaseStream.Seek(60, SeekOrigin.Current); var nameLength = (int)blockReader.ReadByte(); var name = new string(blockReader.ReadChars(nameLength).Where(c => c != '\0').ToArray()); #if DEBUG Console.WriteLine( $"{name} (0x{textureHash:X8}/0x{typeHash:X8}) - {width}x{height}, {dataSize} bytes, @ {dataOffset}"); #endif blockReader.BaseStream.Position = 0; var data = blockReader.ReadBytes((int)(blockReader.BaseStream.Length - 212)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 20; var texture = new Texture { TextureHash = textureHash, TypeHash = typeHash, Name = name, Width = width, Height = height, MipMap = mipMap, DataOffset = dataOffset, DataSize = (uint)(infoBlock.Length - 212), CompressionType = blockReader.ReadInt32(), Data = data }; _texturePack.Textures.Add(texture); infoBlock = null; data = null; blockReader.Dispose(); blocks.Clear(); } else { var infoBlock = blocks[blocks.Count - 2]; var blockReader = new BinaryReader(new MemoryStream(infoBlock)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 212; // 12 0x00 at the beginning of each header blockReader.BaseStream.Seek(12, SeekOrigin.Current); var textureHash = blockReader.ReadUInt32(); var typeHash = blockReader.ReadUInt32(); blockReader.ReadInt32(); var dataSize = blockReader.ReadUInt32(); blockReader.ReadInt32(); var width = blockReader.ReadInt32(); var height = blockReader.ReadInt32(); var mipMap = blockReader.ReadInt32(); blockReader.ReadUInt32(); blockReader.ReadUInt32(); blockReader.BaseStream.Seek(24, SeekOrigin.Current); blockReader.ReadUInt32(); var dataOffset = blockReader.ReadUInt32(); blockReader.BaseStream.Seek(60, SeekOrigin.Current); var nameLength = (int)blockReader.ReadByte(); var name = new string(blockReader.ReadChars(nameLength).Where(c => c != '\0').ToArray()); #if DEBUG Console.WriteLine( $"{name} (0x{textureHash:X8}/0x{typeHash:X8}) - {width}x{height}, {dataSize} bytes, @ {dataOffset}"); #endif blockReader.BaseStream.Position = blockReader.BaseStream.Length - 20; var texture = new Texture { TextureHash = textureHash, TypeHash = typeHash, Name = name, Width = width, Height = height, MipMap = mipMap, DataOffset = dataOffset, DataSize = 0, CompressionType = blockReader.ReadInt32() }; #if DEBUG Console.WriteLine($"0x{texture.CompressionType:X8}"); #endif var data = new List <byte>(); foreach (var block in blocks) { if (blocks.IndexOf(block) == blocks.Count - 2) { continue; } data.AddRange(block); texture.DataSize = (uint)data.Count; } texture.Data = data.ToArray(); _texturePack.Textures.Add(texture); infoBlock = null; blockReader.Dispose(); blocks.Clear(); data.Clear(); } partSizes = null; } else { // Files are located in a folder with the same name as the texture pack. var baseDir = Path.Combine(Path.GetDirectoryName(FileName) ?? throw new InvalidOperationException(), _texturePack.Name); using (var textureReader = new BinaryReader(File.OpenRead(Path.Combine(baseDir, $"{compHeader.TextureHash:X8}.stp")))) { var readBytes = 0L; var blocks = new List <byte[]>(); var partSizes = new uint[0xFFFF]; while (readBytes < compHeader.Size) { if (textureReader.ReadInt32() == 0x55441122) { textureReader.BaseStream.Position -= 4; var cbh = BinaryUtil.ReadStruct <CommonStructs.CompressBlockHead>(textureReader); var flag = textureReader.ReadChars(4); textureReader.BaseStream.Position -= 4; var data = textureReader.ReadBytes((int)(cbh.TotalBlockSize - 24)); if (flag[0] == 'J' && flag[1] == 'D' && flag[2] == 'L' && flag[3] == 'Z') { blocks.Add(JDLZ.Decompress(data)); } else if (_compLibEnabled) { var decompressed = new byte[cbh.OutSize]; Compression.Decompress(data, decompressed); blocks.Add(decompressed); } else { #if DEBUG Console.WriteLine("CompLib disabled, can't decompress"); #endif skip = true; break; } readBytes += cbh.TotalBlockSize; partSizes[blocks.Count - 1] = cbh.OutSize; data = null; } } if (skip) { continue; } if (blocks.Count == 1) { var infoBlock = blocks[0]; var blockReader = new BinaryReader(new MemoryStream(infoBlock)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 212; // 12 0x00 at the beginning of each header blockReader.BaseStream.Seek(12, SeekOrigin.Current); var textureHash = blockReader.ReadUInt32(); var typeHash = blockReader.ReadUInt32(); blockReader.ReadInt32(); var dataSize = blockReader.ReadUInt32(); blockReader.ReadInt32(); var width = blockReader.ReadInt32(); var height = blockReader.ReadInt32(); var mipMap = blockReader.ReadInt32(); blockReader.ReadUInt32(); blockReader.ReadUInt32(); blockReader.BaseStream.Seek(24, SeekOrigin.Current); blockReader.ReadUInt32(); var dataOffset = blockReader.ReadUInt32(); blockReader.BaseStream.Seek(60, SeekOrigin.Current); var nameLength = (int)blockReader.ReadByte(); var name = new string(blockReader.ReadChars(nameLength).Where(c => c != '\0').ToArray()); #if DEBUG Console.WriteLine( $"{name} (0x{textureHash:X8}/0x{typeHash:X8}) - {width}x{height}, {dataSize} bytes, @ {dataOffset}"); #endif blockReader.BaseStream.Position = 0; var data = blockReader.ReadBytes((int)(blockReader.BaseStream.Length - 212)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 20; var texture = new Texture { TextureHash = textureHash, TypeHash = typeHash, Name = name, Width = width, Height = height, MipMap = mipMap, DataOffset = dataOffset, DataSize = (uint)(infoBlock.Length - 212), CompressionType = blockReader.ReadInt32(), Data = data }; _texturePack.Textures.Add(texture); infoBlock = null; blockReader.Dispose(); blocks.Clear(); data = null; } else { var infoBlock = blocks[blocks.Count - 2]; var blockReader = new BinaryReader(new MemoryStream(infoBlock)); blockReader.BaseStream.Position = blockReader.BaseStream.Length - 212; // 12 0x00 at the beginning of each header blockReader.BaseStream.Seek(12, SeekOrigin.Current); var textureHash = blockReader.ReadUInt32(); var typeHash = blockReader.ReadUInt32(); blockReader.ReadInt32(); var dataSize = blockReader.ReadUInt32(); blockReader.ReadInt32(); var width = blockReader.ReadInt32(); var height = blockReader.ReadInt32(); var mipMap = blockReader.ReadInt32(); blockReader.ReadUInt32(); blockReader.ReadUInt32(); blockReader.BaseStream.Seek(24, SeekOrigin.Current); blockReader.ReadUInt32(); var dataOffset = blockReader.ReadUInt32(); blockReader.BaseStream.Seek(60, SeekOrigin.Current); var nameLength = (int)blockReader.ReadByte(); var name = new string(blockReader.ReadChars(nameLength).Where(c => c != '\0').ToArray()); #if DEBUG Console.WriteLine( $"{name} (0x{textureHash:X8}/0x{typeHash:X8}) - {width}x{height}, {dataSize} bytes, @ {dataOffset}"); #endif blockReader.BaseStream.Position = blockReader.BaseStream.Length - 20; var texture = new Texture { TextureHash = textureHash, TypeHash = typeHash, Name = name, Width = width, Height = height, MipMap = mipMap, DataOffset = dataOffset, DataSize = 0, CompressionType = blockReader.ReadInt32() }; #if DEBUG Console.WriteLine($"0x{texture.CompressionType:X8}"); #endif var data = new List <byte>(); foreach (var block in blocks) { if (blocks.IndexOf(block) == blocks.Count - 2) { continue; } data.AddRange(block); texture.DataSize = (uint)data.Count; } texture.Data = data.ToArray(); _texturePack.Textures.Add(texture); data.Clear(); blocks.Clear(); infoBlock = null; blockReader.Dispose(); } partSizes = null; } } } GC.Collect(); }
/// <summary> /// Loads GlobalB file and disassembles its blocks /// </summary> /// <param name="GlobalB_dir">Directory of the game.</param> /// <param name="db">Database of classes.</param> /// <returns>True if success.</returns> public static unsafe bool LoadGlobalB(string GlobalB_dir, Database.Underground2 db) { GlobalB_dir += @"\GLOBAL\GlobalB.lzc"; // Get everything from GlobalB.lzc try { db._GlobalBLZC = File.ReadAllBytes(GlobalB_dir); Log.Write("Reading data from GlobalB.lzc..."); } catch (Exception e) { while (e.InnerException != null) { e = e.InnerException; } if (Process.MessageShow) { MessageBox.Show($"Error occured: {e.Message}", "Failure", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { Console.WriteLine(e.Message); } return(false); } // Decompress if compressed db._GlobalBLZC = JDLZ.Decompress(db._GlobalBLZC); // Use pointers to speed up process fixed(byte *byteptr_t = &db._GlobalBLZC[0]) { uint offset = 0; // to calculate current offset uint ID = 0; // to get the ID of the block being read uint size = 0; // to get the size of the block being read uint proff = 0; // offset of the preset rides block uint prsize = 0; // size of the preset rides block uint troff = 0; // offset of the tracks block uint trsize = 0; // size of the tracks block uint csoff = 0; // offset of the carskins block uint cssize = 0; // size of the carskins block uint gcoff = 0xFFFFFFFF; // offset of the gcareerinfo block while (offset < db._GlobalBLZC.Length) { ID = *(uint *)(byteptr_t + offset); // read ID size = *(uint *)(byteptr_t + offset + 4); // read size if (offset + size > db._GlobalBLZC.Length) { if (Process.MessageShow) { MessageBox.Show("GlobalB: unable to read beyond the stream.", "Failure"); } else { Console.WriteLine("GlobalB: unable to read beyond the stream."); } return(false); } switch (ID) { case Global.Materials: E_Material(byteptr_t + offset, db); break; case Global.TPKBlocks: int count = db.TPKBlocks.Length; db.TPKBlocks.Collections.Add(new TPKBlock(byteptr_t + offset, count, db)); break; case Global.CarTypeInfo: E_CarTypeInfo(byteptr_t + offset + 8, size, db); break; case Global.PresetRides: proff = offset + 8; prsize = size; break; case Global.CarParts: E_CarParts(byteptr_t + offset + 8, size, db); break; case Global.SunInfos: E_SunInfo(byteptr_t + offset + 8, size, db); break; case Global.Tracks: troff = offset + 8; trsize = size; break; case Global.CarSkins: csoff = offset + 8; cssize = size; break; case Global.SlotTypes: E_SlotType(byteptr_t + offset, size + 8, db); break; case Global.CareerInfo: if (gcoff == 0xFFFFFFFF) { gcoff = offset; } break; case Global.AcidEffects: E_AcidEffects(byteptr_t + offset + 8, size, db); break; case Global.FEngFiles: case Global.FNGCompress: E_FNGroup(byteptr_t + offset, size + 8, db); break; default: break; } offset += 8 + size; // advance in offset } // Track, Presets and CarSkins are last one to disperse E_Tracks(byteptr_t + troff, trsize, db); E_PresetRides(byteptr_t + proff, prsize, db); E_CarSkins(byteptr_t + csoff, cssize, db); CareerManager.Disassemble(byteptr_t + gcoff, db); } // Disperse spoilers across cartypeinfo E_SpoilMirrs(db); return(true); }
protected override void ReadChunks(long totalSize) { if (BinaryReader.BaseStream.Length == 0) { return; } var curPos = BinaryReader.BaseStream.Position; if (BinaryReader.ReadChar() == 'J' && BinaryReader.ReadChar() == 'D' && BinaryReader.ReadChar() == 'L' && BinaryReader.ReadChar() == 'Z') { #if DEBUG Console.WriteLine("JDLZ compressed!"); #endif BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); var data = new byte[BinaryReader.BaseStream.Length]; BinaryReader.BaseStream.Read(data, 0, data.Length); var decompressed = JDLZ.Decompress(data); var newName = _fileName + ".dejdlz"; var stream = new FileStream(newName, FileMode.CreateNew); stream.Write(decompressed, 0, decompressed.Length); stream.Close(); BinaryReader = new BinaryReader(new FileStream(newName, FileMode.Open)); File.Delete(newName); } else { BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); } var runTo = BinaryReader.BaseStream.Position + totalSize; for (var i = 0; i < 0xFFFF && BinaryReader.BaseStream.Position < runTo; i++ ) { var chunkId = BinaryReader.ReadUInt32(); var chunkSize = BinaryReader.ReadUInt32(); var chunkRunTo = BinaryReader.BaseStream.Position + chunkSize; var normalizedId = (int)chunkId & 0xffffffff; BinaryUtil.PrintID(BinaryReader, chunkId, normalizedId, chunkSize, GetType()); switch (normalizedId) { default: _dataModels.Add(new NullModel(normalizedId, chunkSize, BinaryReader.BaseStream.Position)); if (DebugUtil.IsContainerChunk(chunkId)) { ReadChunks(chunkSize); } break; } BinaryUtil.ValidatePosition(BinaryReader, chunkRunTo, GetType()); BinaryReader.BaseStream.Seek(chunkRunTo - BinaryReader.BaseStream.Position, SeekOrigin.Current); } }
protected override void ReadChunks(long totalSize) { if (BinaryReader.BaseStream.Length == 0) { return; } var curPos = BinaryReader.BaseStream.Position; if (BinaryReader.ReadChar() == 'J' && BinaryReader.ReadChar() == 'D' && BinaryReader.ReadChar() == 'L' && BinaryReader.ReadChar() == 'Z') { #if DEBUG Console.WriteLine("JDLZ compressed!"); #endif BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); var data = new byte[BinaryReader.BaseStream.Length]; BinaryReader.BaseStream.Read(data, 0, data.Length); var decompressed = JDLZ.Decompress(data); var newName = _fileName + ".dejdlz"; var stream = new FileStream(newName, FileMode.CreateNew); stream.Write(decompressed, 0, decompressed.Length); stream.Close(); BinaryReader = new BinaryReader(new FileStream(newName, FileMode.Open)); File.Delete(newName); } else { BinaryReader.BaseStream.Seek(curPos, SeekOrigin.Begin); } var runTo = BinaryReader.BaseStream.Position + totalSize; for (var i = 0; i < 0xFFFF && BinaryReader.BaseStream.Position < runTo; i++ ) { var chunkId = BinaryReader.ReadUInt32(); var chunkSize = BinaryReader.ReadUInt32(); var chunkRunTo = BinaryReader.BaseStream.Position + chunkSize; var normalizedId = (int)chunkId & 0xffffffff; #if DEBUG BinaryUtil.PrintID(BinaryReader, chunkId, normalizedId, chunkSize, GetType()); #endif switch (normalizedId) { case (long)ChunkID.BCHUNK_CARINFO_ARRAY: var carListContainer = new MWCarListReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(carListContainer.Get()); break; case (long)ChunkID.BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS: { var tpkContainer = new TPKReadContainer(BinaryReader, _fileName, chunkSize, false); _dataModels.Add(tpkContainer.Get()); break; } case (long)ChunkID.BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS_ANIM: { var tpkContainer = new AnimatedTPKReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(tpkContainer.Get()); break; } case (long)ChunkID.BCHUNK_SPEED_TEXTURE_PACK_LIST_CHUNKS_COMPRESSED: { var tpkContainer = new CompressedTPKReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(tpkContainer.Get()); break; } case (long)ChunkID.BCHUNK_LANGUAGE: var languageContainer = new LanguageReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(languageContainer.Get()); break; case (long)ChunkID.BCHUNK_TRACKINFO: var trackListContainer = new TrackListReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(trackListContainer.Get()); break; case (long)ChunkID.BCHUNK_TRACKSTREAMER_SECTIONS: var sectionsContainer = new SectionListReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(sectionsContainer.Get()); break; case (long)ChunkID.BCHUNK_SPEED_ESOLID_LIST_CHUNKS: var solidListContainer = new SolidListReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(solidListContainer.Get()); break; case (long)ChunkID.BCHUNK_FENG_PACKAGE: { var fngContainer = new FNGReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(fngContainer.Get()); break; } case (long)ChunkID.BCHUNK_FENG_PACKAGE_COMPRESSED: { var fngContainer = new CompressedFNGReadContainer(BinaryReader, _fileName, chunkSize); _dataModels.Add(fngContainer.Get()); break; } default: _dataModels.Add(new NullModel(normalizedId, chunkSize, BinaryReader.BaseStream.Position)); break; } BinaryUtil.ValidatePosition(BinaryReader, chunkRunTo, GetType()); BinaryReader.BaseStream.Seek(chunkRunTo - BinaryReader.BaseStream.Position, SeekOrigin.Current); } }