From fc588e00db0086abd812572c165ee933fbada9f2 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 31 Dec 2017 00:14:35 -0800 Subject: [PATCH] continued improvements can now convert some bflims to png, some are still tile scrambled (too fried atm to finish) --- pk3DS.Core/CTR/BFLIM.cs | 216 ++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 95 deletions(-) diff --git a/pk3DS.Core/CTR/BFLIM.cs b/pk3DS.Core/CTR/BFLIM.cs index 6b2b016..d66fc19 100644 --- a/pk3DS.Core/CTR/BFLIM.cs +++ b/pk3DS.Core/CTR/BFLIM.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; namespace pk3DS.Core.CTR { @@ -37,18 +39,20 @@ private void ReadBFLIM(Stream ms) /// public byte[] GetImageData() { + var orienter = new BFLIMOrienter(Footer.Width, Footer.Height, Footer.Orientation); uint[] pixels = GetPixels(); - int p = gcm(Footer.Width, 8) / 8; - if (p == 0) p = 1; // uint[] -> byte[] byte[] array = new byte[Footer.Width * Footer.Height * 4]; - for (int i = 0; i < array.Length; i += 4) + for (uint i = 0; i < pixels.Length; i++) { - int o = GetOffset((uint)i, Footer.Width, p); - if (o + 3 >= array.Length) + var coord = orienter.Get(i); + if (coord.X >= Footer.Width || coord.Y >= Footer.Height) continue; - var val = pixels[i / 4]; + + var val = pixels[i]; + Debug.WriteLine($"Writing {val:X8} for coord: X:{coord.X} | Y{coord.Y}"); + uint o = 4 * (coord.X + coord.Y * Footer.Width); array[o + 0] = (byte)(val & 0xFF); array[o + 1] = (byte)(val >> 8 & 0xFF); array[o + 2] = (byte)(val >> 16 & 0xFF); @@ -58,66 +62,19 @@ public byte[] GetImageData() } public uint[] GetPixels() { - return PixelConverter.GetPixels(PixelData, (BFLIMEncoding)Footer.Format).ToArray(); - } - - internal static int gcm(int n, int m) - { - return (n + m - 1) / m * m; - } - private int GetOffset(uint i, ushort footerWidth, int p) - { - BCLIM.d2xy(i % 64, out uint x, out uint y); - var tile = i / 64; - - // Shift Tile Coordinate into Tilemap - x += (uint)(tile % p) * 8; - y += (uint)(tile / p) * 8; - if (x > footerWidth) - return int.MaxValue; - - return 4 * (int)(x + y * footerWidth); - } - - /// - /// Decimal Ordinate In to X / Y Coordinate Out - /// - /// Loop integer which will be decoded to X/Y - /// Output X coordinate - /// Output Y coordinate - internal static void d2xy(uint d, out uint x, out uint y) - { - x = d; - y = x >> 1; - x &= 0x55555555; - y &= 0x55555555; - x |= x >> 1; - y |= y >> 1; - x &= 0x33333333; - y &= 0x33333333; - x |= x >> 2; - y |= y >> 2; - x &= 0x0f0f0f0f; - y &= 0x0f0f0f0f; - x |= x >> 4; - y |= y >> 4; - x &= 0x00ff00ff; - y &= 0x00ff00ff; - x |= x >> 8; - y |= y >> 8; - x &= 0x0000ffff; - y &= 0x0000ffff; + return PixelConverter.GetPixels(PixelData, Footer.Format).ToArray(); } } - + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FLIM { public const int SIZE = 40; + public const string Identifier = "FLIM"; public bool Valid => LittleEndian && Magic == 0x4D_49_4C_46; // FLIM public bool LittleEndian => BOM == 0xFEFF; public bool BigEndian => BOM == 0xFFFE; - public uint Magic; // FLIM + public uint Magic; // FLIM public ushort BOM; // 0xFFFE public ushort HeaderLength; // always 0x14 public int Version; @@ -129,27 +86,27 @@ public struct FLIM public ushort Width; public ushort Height; public short Alignment; - public byte Format; - public byte Orientation; + public BFLIMEncoding Format; + public BFLIMOrientation Orientation; public uint DataSize; } - public enum BFLIMEncoding + public enum BFLIMEncoding : byte { - L8_UNORM = 0x00, // 8 Luminance - A8_UNORM = 0x01, // 8 Alpha - LA4_UNORM = 0x02, // 8 Luminance + Alpha - LA8_UNORM = 0x03, // 16 Luminance + Alpha - HILO8 = 0x04, // 16 ? - RGB565_UNORM = 0x05, // 16 Color - RGBX8_UNORM = 0x06, // 24 Color - RGB5A1_UNORM = 0x07, // 16 Color + Alpha - RGBA4_UNORM = 0x08, // 16 Color + Alpha - RGBA8_UNORM = 0x09, // 32 Color + Alpha - ETC1_UNORM = 0x0A, // 4 Color - ETC1A4_UNORM = 0x0B, // 8 Color + Alpha - L4_UNORM = 0x0C, // 4 Luminance - A4_UNORM = 0x0D, // 4 Alpha + L8 = 0x00, // 8 Luminance + A8 = 0x01, // 8 Alpha + LA4 = 0x02, // 8 Luminance + Alpha + LA8 = 0x03, // 16 Luminance + Alpha + HILO8 = 0x04, // 16 ? + RGB565 = 0x05, // 16 Color + RGBX8 = 0x06, // 24 Color + RGB5A1 = 0x07, // 16 Color + Alpha + RGBA4 = 0x08, // 16 Color + Alpha + RGBA8 = 0x09, // 32 Color + Alpha + ETC1 = 0x0A, // 4 Color + ETC1A4 = 0x0B, // 8 Color + Alpha + L4 = 0x0C, // 4 Luminance + A4 = 0x0D, // 4 Alpha } public static class PixelConverter @@ -188,14 +145,14 @@ private static uint GetDecodedPixelValue(uint val, BFLIMEncoding e) byte a = byte.MaxValue, r = 0, g = 0, b = 0; switch (e) { - case BFLIMEncoding.L4_UNORM: - case BFLIMEncoding.L8_UNORM: + case BFLIMEncoding.L4: + case BFLIMEncoding.L8: { r = g = b = (byte)val; break; } - case BFLIMEncoding.A4_UNORM: - case BFLIMEncoding.A8_UNORM: + case BFLIMEncoding.A4: + case BFLIMEncoding.A8: { r = g = b = 0xFF; a = (byte)val; @@ -208,27 +165,27 @@ private static uint GetDecodedPixelValue(uint val, BFLIMEncoding e) b = byte.MaxValue; break; } - case BFLIMEncoding.LA4_UNORM: + case BFLIMEncoding.LA4: { r = g = b = (byte)(val >> 4); a = (byte)(val & 0x0F); break; } - case BFLIMEncoding.LA8_UNORM: + case BFLIMEncoding.LA8: { r = g = b = (byte)(val >> 8); a = (byte)val; break; } - case BFLIMEncoding.RGBX8_UNORM: + case BFLIMEncoding.RGBX8: { return val | 0xFF000000; } - case BFLIMEncoding.RGBA8_UNORM: + case BFLIMEncoding.RGBA8: { return val; } - case BFLIMEncoding.RGBA4_UNORM: + case BFLIMEncoding.RGBA4: { a = (byte)(0x11 * (val & 0xf)); r = (byte)(0x11 * ((val >> 12) & 0xf)); @@ -236,14 +193,14 @@ private static uint GetDecodedPixelValue(uint val, BFLIMEncoding e) b = (byte)(0x11 * ((val >> 4) & 0xf)); break; } - case BFLIMEncoding.RGB565_UNORM: + case BFLIMEncoding.RGB565: { r = Convert5To8[(val >> 11) & 0x1F]; g = (byte)(((val >> 5) & 0x3F) * 4); b = Convert5To8[val & 0x1F]; break; } - case BFLIMEncoding.RGB5A1_UNORM: + case BFLIMEncoding.RGB5A1: { r = Convert5To8[(val >> 11) & 0x1F]; g = Convert5To8[(val >> 6) & 0x1F]; @@ -290,26 +247,95 @@ public static int GetBitsPerPixel(this BFLIMEncoding e) private const int BPP_4 = 4; private static readonly HashSet _32 = new HashSet { - BFLIMEncoding.RGBA8_UNORM, + BFLIMEncoding.RGBA8, }; private static readonly HashSet _24 = new HashSet { - BFLIMEncoding.RGBX8_UNORM, + BFLIMEncoding.RGBX8, }; private static readonly HashSet _16 = new HashSet { - BFLIMEncoding.LA8_UNORM, + BFLIMEncoding.LA8, BFLIMEncoding.HILO8, - BFLIMEncoding.RGB565_UNORM, - BFLIMEncoding.RGB5A1_UNORM, - BFLIMEncoding.RGBA4_UNORM, + BFLIMEncoding.RGB565, + BFLIMEncoding.RGB5A1, + BFLIMEncoding.RGBA4, }; private static readonly HashSet _8 = new HashSet { - BFLIMEncoding.L8_UNORM, - BFLIMEncoding.A8_UNORM, - BFLIMEncoding.LA4_UNORM, - BFLIMEncoding.ETC1A4_UNORM, + BFLIMEncoding.L8, + BFLIMEncoding.A8, + BFLIMEncoding.LA4, + BFLIMEncoding.ETC1A4, }; } + + [Flags] + public enum BFLIMOrientation : byte + { + None = 0, + Rotate90 = 4, + Transpose = 8, + } + + public class BFLIMOrienter + { + readonly BFLIMOrientation _orientation; + + public uint Width { get; } + public uint Height { get; } + public uint PanelsPerWidth { get; } + + public BFLIMOrienter(int width, int height, BFLIMOrientation orientation) + { + Width = (uint)BCLIM.nlpo2(BCLIM.gcm(width, 8)); + Height = (uint)BCLIM.nlpo2(BCLIM.gcm(height, 8)); + Debug.WriteLine($"Base Size = Width: {Width}, Height = {Height}"); + + PanelsPerWidth = (uint)BCLIM.gcm(orientation == BFLIMOrientation.None ? width : height, 8) / 8; + Debug.WriteLine($"PPW: {PanelsPerWidth}"); + + _orientation = orientation; + } + + public Coordinate Get(uint i) + { + BCLIM.d2xy(i & 0x3F, out uint x, out uint y); + + // Shift Tile Coordinate into Tilemap + var tile = i >> 6; + x |= (tile % PanelsPerWidth) << 3; + y |= (tile / PanelsPerWidth) << 3; + + var coord = new Coordinate(x, y); + if (_orientation.HasFlag(BFLIMOrientation.Rotate90)) + coord.Rotate90(Height); + if (_orientation.HasFlag(BFLIMOrientation.Transpose)) + coord.Transpose(); + return coord; + } + } + + public class Coordinate + { + public uint X { get; private set; } + public uint Y { get; private set; } + + public Coordinate(uint x, uint y) + { + X = x; Y = y; + } + public void Transpose() + { + var tmp = X; + X = Y; + Y = tmp; + } + public void Rotate90(uint height) + { + var tmp = X; + X = Y; + Y = height - 1 - tmp; + } + } }