mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-09-12 03:55:49 -05:00
continued improvements
can now convert some bflims to png, some are still tile scrambled (too fried atm to finish)
This commit is contained in:
@@ -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)
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decimal Ordinate In to X / Y Coordinate Out
|
||||
/// </summary>
|
||||
/// <param name="d">Loop integer which will be decoded to X/Y</param>
|
||||
/// <param name="x">Output X coordinate</param>
|
||||
/// <param name="y">Output Y coordinate</param>
|
||||
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<BFLIMEncoding> _32 = new HashSet<BFLIMEncoding>
|
||||
{
|
||||
BFLIMEncoding.RGBA8_UNORM,
|
||||
BFLIMEncoding.RGBA8,
|
||||
};
|
||||
private static readonly HashSet<BFLIMEncoding> _24 = new HashSet<BFLIMEncoding>
|
||||
{
|
||||
BFLIMEncoding.RGBX8_UNORM,
|
||||
BFLIMEncoding.RGBX8,
|
||||
};
|
||||
private static readonly HashSet<BFLIMEncoding> _16 = new HashSet<BFLIMEncoding>
|
||||
{
|
||||
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<BFLIMEncoding> _8 = new HashSet<BFLIMEncoding>
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user