public void LoadFromStream(Stream stream) { Contract.Requires <ArgumentNullException>(stream != null); Contract.Requires(stream.CanRead); Contract.Requires(stream.CanSeek); Contract.Requires(stream.Length >= 0x80); // Header Size, Minimum Required var origin = stream.Position; if (stream.ReadByte() != 0x54 || stream.ReadByte() != 0x49 || stream.ReadByte() != 0x44) { throw new InvalidDataException("This isn't a TID file."); } var version = (byte)stream.ReadByte(); if (!Versions.ContainsKey(version)) { throw new InvalidVersionException($"{version} isn't a valid version that can be read."); } using (var reader = new EndianBinaryReader(stream, Encoding.UTF8, true, (version & 0x01) == 0)) { stream.Seek(0x20 + origin, SeekOrigin.Begin); var filename = reader.ReadBytes(32); stream.Seek(0x44 + origin, SeekOrigin.Begin); var width = reader.ReadInt32(); var height = reader.ReadInt32(); stream.Seek(0x58 + origin, SeekOrigin.Begin); var dataLength = reader.ReadInt32(); stream.Seek(0x64 + origin, SeekOrigin.Begin); int compressionValue = reader.ReadInt32(); if (!Enum.IsDefined(typeof(CompressionAlgorithm), compressionValue)) { throw new InvalidCompressionMethodException($"{compressionValue} represents an incorrect or unknown compression algorithm."); } CompressionAlgorithm compression = (CompressionAlgorithm)compressionValue; if (stream.Length < 0x80 + dataLength) { throw new EndOfStreamException(); } stream.Seek(0x80 + origin, SeekOrigin.Begin); byte[] data; if (version == 0x9A) { data = reader.ReadBytes(width * height * 4); } else { data = reader.ReadBytes(dataLength); } if (compression == CompressionAlgorithm.None) { if (version == 0x9A) { } else if ((version & 0x02) == 0x02) { data = BitmapArrayTools.Swap32BppColorChannels(data, 3, 2, 1, 0); } else { data = BitmapArrayTools.Swap32BppColorChannels(data, 2, 1, 0, 3); } } else { data = Squish.DecompressImage(data, width, height, compression.ToSquishFlags()); data = BitmapArrayTools.Swap32BppColorChannels(data, 2, 1, 0, 3); } if (compression == CompressionAlgorithm.Dxt1) { data = BitmapArrayTools.Fill32BppAlpha(data, 0, 0xFF); } var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); var bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); Marshal.Copy(data, 0, bitmapData.Scan0, data.Length); bitmap.UnlockBits(bitmapData); Bitmap = bitmap; Filename = filename; Compression = compression; Version = version; Loaded = true; } }
public void WriteToStream(Stream stream) { Contract.Requires <FileNotLoadedException>(Loaded); Contract.Requires <ArgumentNullException>(stream != null); Contract.Requires <ArgumentException>(stream.CanWrite); Contract.Requires <ArgumentException>(stream.CanSeek); Contract.Requires <InvalidCompressionMethodException>(Compression == CompressionAlgorithm.None, "Compressing is not yet supported !"); Contract.Requires <InvalidCompressionMethodException>(Compression == CompressionAlgorithm.None ? Versions[Version] == CompressionState.Both || Versions[Version] == CompressionState.UncompressedOnly : Versions[Version] == CompressionState.Both || Versions[Version] == CompressionState.CompressedOnly); var origin = stream.Position; using (var writer = new EndianBinaryWriter(stream, new UTF8Encoding(false, true), false, IsLittleEndian)) { writer.Write('T', 'I', 'D'); writer.Write(Version); stream.Seek(0x04, SeekOrigin.Current); writer.Write(0x80); writer.Write(0x01); writer.Write(0x01); writer.Write(0x20); stream.Seek(0x20 + origin, SeekOrigin.Begin); writer.Write(Filename.GetCustomLength(0x20)); writer.Write(0x60); writer.Write(Width); writer.Write(Height); writer.Write(Compression == CompressionAlgorithm.None ? 0x20 : 0x00); // TODO : Understand and write correct data for offset 0x50 writer.Write(0x010001); stream.Seek(0x04, SeekOrigin.Current); var bitmapData = Bitmap.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var data = new byte[Width * Height * 4]; Marshal.Copy(bitmapData.Scan0, data, 0, bitmapData.Width * bitmapData.Height * 4); Bitmap.UnlockBits(bitmapData); if (Compression == CompressionAlgorithm.None) { if (Version == 0x9A) { } else if ((Version & 0x02) == 0x02) { data = BitmapArrayTools.Swap32BppColorChannels(data, 3, 2, 1, 0); } else { data = BitmapArrayTools.Swap32BppColorChannels(data, 2, 1, 0, 3); } } else { data = BitmapArrayTools.Swap32BppColorChannels(data, 2, 1, 0, 3); data = Squish.CompressImage(data, Width, Height, Compression.ToSquishFlags()); // TODO : This line throws a FatalExecutionEngineError for an unknown reason } writer.Write(data.Length); writer.Write(0x80); writer.Write(Compression == CompressionAlgorithm.None ? 0x00 : 0x04); if (Compression == CompressionAlgorithm.Dxt1) { writer.Write(827611204); } else if (Compression == CompressionAlgorithm.Dxt5) { writer.Write(894720068); } else { writer.Write(0x00); } // TODO : Understand and write correct data for offsets 0x68 and 0x78 stream.Seek(0x78 + origin, SeekOrigin.Begin); writer.Write(0x101); stream.Seek(0x80 + origin, SeekOrigin.Begin); writer.Write(data); int fileSize = (int)stream.Position; stream.Seek(0x04 + origin, SeekOrigin.Begin); writer.Write(Compression == CompressionAlgorithm.None ? fileSize : 0x80); } }