From 9112755b2cdb831c85e0c8dc5dce8e6b9628c004 Mon Sep 17 00:00:00 2001 From: InvoxiPlayGames Date: Sun, 12 May 2024 06:19:33 +0100 Subject: [PATCH 1/2] partial broken png_xb1/bmp_xb1 support --- LibForge/LibForge/Texture/Texture.cs | 2 ++ LibForge/LibForge/Texture/TextureConverter.cs | 18 +++++++++++++---- LibForge/LibForge/Texture/TextureReader.cs | 20 ++++++++++++++++++- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/LibForge/LibForge/Texture/Texture.cs b/LibForge/LibForge/Texture/Texture.cs index 3569346..0620296 100644 --- a/LibForge/LibForge/Texture/Texture.cs +++ b/LibForge/LibForge/Texture/Texture.cs @@ -12,6 +12,8 @@ public class Mipmap public int Width; public int Height; public int Flags; + public int DecompressedSize; + public int CompressionFlags; public byte[] Data; } public int Version; diff --git a/LibForge/LibForge/Texture/TextureConverter.cs b/LibForge/LibForge/Texture/TextureConverter.cs index 85a4cd3..c0ba440 100644 --- a/LibForge/LibForge/Texture/TextureConverter.cs +++ b/LibForge/LibForge/Texture/TextureConverter.cs @@ -43,9 +43,14 @@ public static Bitmap ToBitmap(Texture t, int mipmap) { DecodeDXT(m, imageData, false); } + else if (m.Data.Length >= imageData.Length) + { + // xb1 texture with mipmaps embedded + DecodeDXT(m, imageData, true, true); + } else { - throw new Exception($"Don't know what to do with this texture (version={t.Version})"); + throw new Exception($"Don't know what to do with this texture (version={t.Version}, length={m.Data.Length}, imageLength={imageData.Length})"); } // Copy data to bitmap { @@ -56,7 +61,7 @@ public static Bitmap ToBitmap(Texture t, int mipmap) return output; } - private static void DecodeDXT(Texture.Mipmap m, int[] imageData, bool DXT5) + private static void DecodeDXT(Texture.Mipmap m, int[] imageData, bool DXT5, bool xboxswizzle = false) { int[] colors = new int[4]; using (var s = new MemoryStream(m.Data)) @@ -66,7 +71,7 @@ private static void DecodeDXT(Texture.Mipmap m, int[] imageData, bool DXT5) for (var y = 0; y < m.Height; y += 4) for (var x = 0; x < m.Width; x += 4) { - if (DXT5) s.Seek(8, SeekOrigin.Current); + if (DXT5) s.Seek(0x8, SeekOrigin.Current); ushort c0 = s.ReadUInt16LE(); ushort c1 = s.ReadUInt16LE(); colors[0] = RGB565ToARGB(c0); @@ -94,12 +99,17 @@ private static void DecodeDXT(Texture.Mipmap m, int[] imageData, bool DXT5) colors[3] = Color.Black.ToArgb(); } var offset = y * m.Width + x; + if (xboxswizzle) + { + // Xbox texture swizzling offset: https://github.com/Free60Project/libxenon/blob/master/libxenon/drivers/console/console.c#L60 + offset = (((y >> 5) * 32 * m.Width + ((x >> 5) << 10) +(x & 3) + ((y & 1) << 2) + (((x & 31) >> 2) << 3) + (((y & 31) >> 1) << 6)) ^ ((y & 8) << 2)) / 4; + } for (var i = 0; i < 4; i++) { for (var j = 0; j < 4; j++) { var idx = (iData[i] >> (2 * j)) & 0x3; - imageData[offset + i * m.Width + j] = colors[idx]; + imageData[offset + (i * m.Width) + j] = colors[idx]; } } } diff --git a/LibForge/LibForge/Texture/TextureReader.cs b/LibForge/LibForge/Texture/TextureReader.cs index a15d4ec..4588cfc 100644 --- a/LibForge/LibForge/Texture/TextureReader.cs +++ b/LibForge/LibForge/Texture/TextureReader.cs @@ -4,6 +4,7 @@ using System.Text; using System.IO; using LibForge.Util; +using System.IO.Compression; namespace LibForge.Texture { @@ -29,12 +30,29 @@ public override Texture Read() { Width = Int(), Height = Int(), - Flags = version == 0xC ? Int().Then(Skip(8)) : Int() + Flags = Int(), + DecompressedSize = version == 0xC ? Int() : 0, + CompressionFlags = version == 0xC ? Int() : 0, }, MipmapLevels); UInt(); for(var i = 0; i < Mipmaps.Length; i++) { Mipmaps[i].Data = Arr(Byte); + // If there's a decompressed size we should zlib decompress the data + if (Mipmaps[i].DecompressedSize > 0) + { + byte[] decompressed = new byte[Mipmaps[i].DecompressedSize]; + using (MemoryStream ms = new MemoryStream(Mipmaps[i].Data)) + using (DeflateStream deflate = new DeflateStream(ms, CompressionMode.Decompress)) + { + ms.Read(decompressed, 0, 2); // skip over zlib header + deflate.Read(decompressed, 0, Mipmaps[i].DecompressedSize); + } + Mipmaps[i].Data = decompressed; + } + // DDS textures (png_xb1/bmp_xb1) only have 1 blob of data for mipmaps + if (Mipmaps[i].CompressionFlags != 0) + break; } var footerData = FixedArr(Byte, 0x1C); return new Texture From 977bb2c366b6c2a154d7e60abadb665b9e1d9d4c Mon Sep 17 00:00:00 2001 From: InvoxiPlayGames Date: Sun, 12 May 2024 06:27:10 +0100 Subject: [PATCH 2/2] xb1 dxt1 --- LibForge/LibForge/Texture/TextureConverter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/LibForge/LibForge/Texture/TextureConverter.cs b/LibForge/LibForge/Texture/TextureConverter.cs index c0ba440..4e9f193 100644 --- a/LibForge/LibForge/Texture/TextureConverter.cs +++ b/LibForge/LibForge/Texture/TextureConverter.cs @@ -48,6 +48,11 @@ public static Bitmap ToBitmap(Texture t, int mipmap) // xb1 texture with mipmaps embedded DecodeDXT(m, imageData, true, true); } + else if (m.Data.Length >= (imageData.Length / 2)) + { + // xb1 texture with mipmaps embedded + DecodeDXT(m, imageData, false, true); + } else { throw new Exception($"Don't know what to do with this texture (version={t.Version}, length={m.Data.Length}, imageLength={imageData.Length})");