mirror of
https://github.com/kwsch/pk3DS.git
synced 2026-08-28 20:56:47 -05:00
Migrate out etc handling
currently only works for bclim, dunno if I want to try and fix the swizzling / standardize the reading basically just outputs scrambled ETC1 images rather than throwing an exception
This commit is contained in:
@@ -2,8 +2,6 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using pk3DS.Core.Properties; // For other projects, replace this line with the namespace that contains your ETC1Lib.dll resource.
|
||||
|
||||
namespace pk3DS.Core.CTR
|
||||
{
|
||||
@@ -103,7 +101,7 @@ public static Image makeBMP(string path, bool autosave = false, bool crop = true
|
||||
// PKM XY Format 7 (Color Palette)
|
||||
img = getIMG_XY7(bclim);
|
||||
else if (f == 10 || f == 11)
|
||||
img = getIMG_ETC(bclim);
|
||||
img = ImageUtil.GetBitmapETC(bclim);
|
||||
else
|
||||
img = getIMG(bclim);
|
||||
|
||||
@@ -190,7 +188,7 @@ public static Bitmap getIMG(CLIM bclim)
|
||||
if (bclim.FileFormat == 7 && BitConverter.ToUInt16(bclim.Data, 0) == 2) // XY7
|
||||
return getIMG_XY7(bclim);
|
||||
if (bclim.FileFormat == 10 || bclim.FileFormat == 11) // Use ETC1 to get image instead.
|
||||
return getIMG_ETC(bclim);
|
||||
return ImageUtil.GetBitmapETC(bclim);
|
||||
// New Image
|
||||
int w = nlpo2(gcm(bclim.Width, 8));
|
||||
int h = nlpo2(gcm(bclim.Height, 8));
|
||||
@@ -251,102 +249,6 @@ public static Bitmap getIMG_XY7(CLIM bclim)
|
||||
}
|
||||
return img;
|
||||
}
|
||||
public static Bitmap getIMG_ETC(CLIM bclim)
|
||||
{
|
||||
Bitmap img = new Bitmap(Math.Max(nlpo2(bclim.Width), 16), Math.Max(nlpo2(bclim.Height), 16));
|
||||
string dllpath = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + "\\ETC1Lib.dll";
|
||||
if (!File.Exists(dllpath)) File.WriteAllBytes(dllpath, Resources.ETC1Lib);
|
||||
|
||||
try
|
||||
{
|
||||
/* http://jul.rustedlogic.net/thread.php?id=17312
|
||||
* Much of this code is taken/modified from Tharsis. Thank you to Tharsis's creator, xdaniel.
|
||||
* https://github.com/xdanieldzd/Tharsis
|
||||
*/
|
||||
|
||||
/* Get compressed data & handle to it */
|
||||
byte[] textureData = bclim.Data;
|
||||
//textureData = switchEndianness(textureData, 0x10);
|
||||
ushort[] input = new ushort[textureData.Length/sizeof (ushort)];
|
||||
Buffer.BlockCopy(textureData, 0, input, 0, textureData.Length);
|
||||
GCHandle pInput = GCHandle.Alloc(input, GCHandleType.Pinned);
|
||||
|
||||
/* Marshal data around, invoke ETC1.dll for conversion, etc */
|
||||
uint size1 = 0;
|
||||
UInt16 w = (ushort)img.Width, h = (ushort)img.Height;
|
||||
|
||||
ETC1.ConvertETC1(IntPtr.Zero, ref size1, IntPtr.Zero, w, h, bclim.FileFormat == 0xB); // true = etc1a4, false = etc1
|
||||
// System.Diagnostics.Debug.WriteLine(size1);
|
||||
uint[] output = new uint[size1];
|
||||
GCHandle pOutput = GCHandle.Alloc(output, GCHandleType.Pinned);
|
||||
ETC1.ConvertETC1(pOutput.AddrOfPinnedObject(), ref size1, pInput.AddrOfPinnedObject(), w, h, bclim.FileFormat == 0xB);
|
||||
pOutput.Free();
|
||||
pInput.Free();
|
||||
|
||||
|
||||
/* Unscramble if needed // could probably be done in ETC1Lib.dll, it's probably pretty ugly, but whatever... */
|
||||
/* Non-square code blocks could need some cleanup, verification, etc. as well... */
|
||||
uint[] finalized = new uint[output.Length];
|
||||
|
||||
// Act if it's square because BCLIM swizzling is stupid
|
||||
Buffer.BlockCopy(output, 0, finalized, 0, finalized.Length);
|
||||
|
||||
byte[] tmp = new byte[finalized.Length];
|
||||
Buffer.BlockCopy(finalized, 0, tmp, 0, tmp.Length);
|
||||
byte[] imgData = tmp;
|
||||
|
||||
for (int i = 0; i < w; i++)
|
||||
{
|
||||
for (int j = 0; j < h; j++)
|
||||
{
|
||||
int k = (j + i*img.Height)*4;
|
||||
img.SetPixel(i, j, Color.FromArgb(imgData[k + 3], imgData[k], imgData[k + 1], imgData[k + 2]));
|
||||
}
|
||||
}
|
||||
// Image is 13 instead of 12
|
||||
// 24 34
|
||||
img.RotateFlip(RotateFlipType.Rotate90FlipX);
|
||||
if (w > h)
|
||||
{
|
||||
// Image is now in appropriate order, but the shifting is messed up. Let's fix that.
|
||||
Bitmap img2 = new Bitmap(Math.Max(nlpo2(bclim.Width), 16), Math.Max(nlpo2(bclim.Height), 16));
|
||||
for (int y = 0; y < Math.Max(nlpo2(bclim.Width), 16); y += 8)
|
||||
{
|
||||
for (int x = 0; x < Math.Max(nlpo2(bclim.Height), 16); x++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
// Treat every 8 vertical pixels as 1 pixel for purposes of calculation, add to offset later.
|
||||
{
|
||||
int x1 = (x + y/8*h)%img2.Width; // Reshift x
|
||||
int y1 = (x + y/8*h)/img2.Width*8; // Reshift y
|
||||
img2.SetPixel(x1, y1 + j, img.GetPixel(x, y + j)); // Reswizzle
|
||||
}
|
||||
}
|
||||
}
|
||||
img = img2;
|
||||
}
|
||||
else if (h > w)
|
||||
{
|
||||
Bitmap img2 = new Bitmap(Math.Max(nlpo2(bclim.Width), 16), Math.Max(nlpo2(bclim.Height), 16));
|
||||
for (int y = 0; y < Math.Max(nlpo2(bclim.Width), 16); y += 8)
|
||||
{
|
||||
for (int x = 0; x < Math.Max(nlpo2(bclim.Height), 16); x++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
// Treat every 8 vertical pixels as 1 pixel for purposes of calculation, add to offset later.
|
||||
{
|
||||
int x1 = x%img2.Width; // Reshift x
|
||||
int y1 = (x + y/8*h)/img2.Width*8; // Reshift y
|
||||
img2.SetPixel(x1, y1 + j, img.GetPixel(x, y + j)); // Reswizzle
|
||||
}
|
||||
}
|
||||
}
|
||||
img = img2;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return img;
|
||||
}
|
||||
|
||||
// BCLIM Data Writing
|
||||
public static int write16BitColorPalette(Bitmap img, ref MemoryStream ms)
|
||||
@@ -865,10 +767,10 @@ public static CLIM analyze(string path)
|
||||
return bclim;
|
||||
}
|
||||
}
|
||||
public struct CLIM
|
||||
public struct CLIM : IXLIM
|
||||
{
|
||||
public uint Magic; // CLIM = 0x4D494C43
|
||||
public UInt16 BOM; // 0xFFFE
|
||||
public uint Magic { get; set; }// CLIM = 0x4D494C43
|
||||
public ushort BOM; // 0xFFFE
|
||||
public uint CLIMLength; // HeaderLength - 14
|
||||
public int TileWidth; // 1<<[[n]]
|
||||
public int TileHeight; // 1<<[[n]]
|
||||
@@ -877,12 +779,12 @@ public struct CLIM
|
||||
|
||||
public char[] imag; // imag = 0x67616D69
|
||||
public uint imagLength; // HeaderLength - 10
|
||||
public UInt16 Width; // Final Dimensions
|
||||
public UInt16 Height; // Final Dimensions
|
||||
public ushort Width { get; set; } // Final Dimensions
|
||||
public ushort Height { get; set; } // Final Dimensions
|
||||
public int FileFormat; // ??
|
||||
public uint dataLength; // Pixel Data Region Length
|
||||
|
||||
public byte[] Data;
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public int BaseSize;
|
||||
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
|
||||
namespace pk3DS.Core.CTR
|
||||
{
|
||||
public class BFLIM
|
||||
public class BFLIM : IXLIM
|
||||
{
|
||||
public byte[] PixelData;
|
||||
public FLIM Footer;
|
||||
|
||||
public uint Magic { get => Footer.Magic; set => Footer.Magic = value; }
|
||||
public ushort Width { get => Footer.Width; set => Footer.Width = value; }
|
||||
public ushort Height { get => Footer.Height; set => Footer.Height = value; }
|
||||
public BFLIMEncoding Format { get => Footer.Format; set => Footer.Format = value; }
|
||||
|
||||
public BFLIM(Stream data) => ReadBFLIM(data);
|
||||
public BFLIM(byte[] data)
|
||||
{
|
||||
@@ -77,7 +82,7 @@ public byte[] GetPixelsRaw()
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public struct FLIM
|
||||
public struct FLIM : IXLIM
|
||||
{
|
||||
public const int SIZE = 40;
|
||||
public const string Identifier = "FLIM";
|
||||
@@ -85,7 +90,7 @@ public struct FLIM
|
||||
public bool LittleEndian => BOM == 0xFEFF;
|
||||
public bool BigEndian => BOM == 0xFFFE;
|
||||
|
||||
public uint Magic; // FLIM
|
||||
public uint Magic { get; set; } // FLIM
|
||||
public ushort BOM; // 0xFFFE
|
||||
public ushort HeaderLength; // always 0x14
|
||||
public int Version;
|
||||
@@ -94,8 +99,8 @@ public struct FLIM
|
||||
|
||||
public uint imag; // imag = 67616D69
|
||||
public uint imagLength; // always 0x10
|
||||
public ushort Width;
|
||||
public ushort Height;
|
||||
public ushort Width { get; set; }
|
||||
public ushort Height { get; set; }
|
||||
public short Alignment;
|
||||
public BFLIMEncoding Format;
|
||||
public BFLIMOrientation Orientation;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using pk3DS.Core.Properties;
|
||||
|
||||
namespace pk3DS.Core.CTR
|
||||
{
|
||||
@@ -7,5 +10,13 @@ internal static class ETC1
|
||||
{
|
||||
[DllImport("ETC1Lib.dll", EntryPoint = "ConvertETC1", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void ConvertETC1(IntPtr dataOut, ref uint dataOutSize, IntPtr dataIn, ushort width, ushort height, bool alpha);
|
||||
|
||||
public static void CheckETC1Lib()
|
||||
{
|
||||
var loc = Assembly.GetEntryAssembly()?.Location ?? typeof(ETC1).Assembly.Location;
|
||||
string dllpath = Path.GetDirectoryName(loc) + "\\ETC1Lib.dll";
|
||||
if (!File.Exists(dllpath))
|
||||
File.WriteAllBytes(dllpath, Resources.ETC1Lib);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
pk3DS.Core/CTR/IXLIM.cs
Normal file
12
pk3DS.Core/CTR/IXLIM.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace pk3DS.Core.CTR
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="BCLIM"/> and <see cref="BFLIM"/> header interface.
|
||||
/// </summary>
|
||||
public interface IXLIM
|
||||
{
|
||||
uint Magic { get; set; }
|
||||
ushort Width { get; set; }
|
||||
ushort Height { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Drawing;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using pk3DS.Core.CTR;
|
||||
|
||||
namespace pk3DS.Core
|
||||
@@ -17,6 +19,8 @@ public static class ImageUtil
|
||||
/// <returns>Human visible data</returns>
|
||||
public static Bitmap GetBitmap(this BFLIM bflim, bool crop = true)
|
||||
{
|
||||
if (bflim.Format == BFLIMEncoding.ETC1 || bflim.Format == BFLIMEncoding.ETC1A4)
|
||||
return GetBitmapETC(bflim);
|
||||
var data = bflim.GetImageData(crop);
|
||||
return GetBitmap(data, bflim.Footer.Width, bflim.Footer.Height);
|
||||
}
|
||||
@@ -27,10 +31,116 @@ public static Bitmap GetBitmap(byte[] data, int width, int height)
|
||||
var bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
|
||||
var ptr = bmpData.Scan0;
|
||||
System.Runtime.InteropServices.Marshal.Copy(data, 0, ptr, data.Length);
|
||||
Marshal.Copy(data, 0, ptr, data.Length);
|
||||
bmp.UnlockBits(bmpData);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static Bitmap GetBitmapETC(BCLIM.CLIM bclim)
|
||||
{
|
||||
ETC1.CheckETC1Lib();
|
||||
|
||||
Bitmap img = new Bitmap(Math.Max(BCLIM.nlpo2(bclim.Width), 16), Math.Max(BCLIM.nlpo2(bclim.Height), 16));
|
||||
try { return DecodeETC(bclim, img, bclim.Data, bclim.FileFormat == 0x0B); }
|
||||
catch { return img; }
|
||||
}
|
||||
public static Bitmap GetBitmapETC(BFLIM bflim)
|
||||
{
|
||||
ETC1.CheckETC1Lib();
|
||||
|
||||
Bitmap img = new Bitmap(Math.Max(BCLIM.nlpo2(bflim.Width), 16), Math.Max(BCLIM.nlpo2(bflim.Height), 16));
|
||||
try { return DecodeETC(bflim, img, bflim.PixelData, bflim.Footer.Format == BFLIMEncoding.ETC1A4); }
|
||||
catch { return img; }
|
||||
}
|
||||
|
||||
private static Bitmap DecodeETC(IXLIM bclim, Bitmap img, byte[] textureData, bool etc1A4)
|
||||
{
|
||||
/* http://jul.rustedlogic.net/thread.php?id=17312
|
||||
* Much of this code is taken/modified from Tharsis. Thank you to Tharsis's creator, xdaniel.
|
||||
* https://github.com/xdanieldzd/Tharsis
|
||||
*/
|
||||
|
||||
/* Get compressed data & handle to it */
|
||||
|
||||
//textureData = switchEndianness(textureData, 0x10);
|
||||
ushort[] input = new ushort[textureData.Length / sizeof(ushort)];
|
||||
Buffer.BlockCopy(textureData, 0, input, 0, textureData.Length);
|
||||
GCHandle pInput = GCHandle.Alloc(input, GCHandleType.Pinned);
|
||||
|
||||
/* Marshal data around, invoke ETC1.dll for conversion, etc */
|
||||
uint size1 = 0;
|
||||
var w = (ushort)img.Width;
|
||||
var h = (ushort)img.Height;
|
||||
|
||||
ETC1.ConvertETC1(IntPtr.Zero, ref size1, IntPtr.Zero, w, h, etc1A4); // true = etc1a4, false = etc1
|
||||
uint[] output = new uint[size1];
|
||||
GCHandle pOutput = GCHandle.Alloc(output, GCHandleType.Pinned);
|
||||
ETC1.ConvertETC1(pOutput.AddrOfPinnedObject(), ref size1, pInput.AddrOfPinnedObject(), w, h, etc1A4);
|
||||
pOutput.Free();
|
||||
pInput.Free();
|
||||
|
||||
/* Unscramble if needed // could probably be done in ETC1Lib.dll, it's probably pretty ugly, but whatever... */
|
||||
/* Non-square code blocks could need some cleanup, verification, etc. as well... */
|
||||
uint[] finalized = new uint[output.Length];
|
||||
|
||||
// Act if it's square because BCLIM swizzling is stupid
|
||||
Buffer.BlockCopy(output, 0, finalized, 0, finalized.Length);
|
||||
|
||||
byte[] tmp = new byte[finalized.Length];
|
||||
Buffer.BlockCopy(finalized, 0, tmp, 0, tmp.Length);
|
||||
byte[] imgData = tmp;
|
||||
|
||||
for (int i = 0; i < w; i++)
|
||||
{
|
||||
for (int j = 0; j < h; j++)
|
||||
{
|
||||
int k = (j + i * img.Height) * 4;
|
||||
img.SetPixel(i, j, Color.FromArgb(imgData[k + 3], imgData[k], imgData[k + 1], imgData[k + 2]));
|
||||
}
|
||||
}
|
||||
// Image is 13 instead of 12
|
||||
// 24 34
|
||||
img.RotateFlip(RotateFlipType.Rotate90FlipX);
|
||||
if (w > h)
|
||||
{
|
||||
// Image is now in appropriate order, but the shifting is messed up. Let's fix that.
|
||||
Bitmap img2 = new Bitmap(Math.Max(BCLIM.nlpo2(bclim.Width), 16), Math.Max(BCLIM.nlpo2(bclim.Height), 16));
|
||||
for (int y = 0; y < Math.Max(BCLIM.nlpo2(bclim.Width), 16); y += 8)
|
||||
{
|
||||
for (int x = 0; x < Math.Max(BCLIM.nlpo2(bclim.Height), 16); x++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
// Treat every 8 vertical pixels as 1 pixel for purposes of calculation, add to offset later.
|
||||
{
|
||||
int x1 = (x + y / 8 * h) % img2.Width; // Reshift x
|
||||
int y1 = (x + y / 8 * h) / img2.Width * 8; // Reshift y
|
||||
img2.SetPixel(x1, y1 + j, img.GetPixel(x, y + j)); // Reswizzle
|
||||
}
|
||||
}
|
||||
}
|
||||
img = img2;
|
||||
}
|
||||
else if (h > w)
|
||||
{
|
||||
Bitmap img2 = new Bitmap(Math.Max(BCLIM.nlpo2(bclim.Width), 16), Math.Max(BCLIM.nlpo2(bclim.Height), 16));
|
||||
for (int y = 0; y < Math.Max(BCLIM.nlpo2(bclim.Width), 16); y += 8)
|
||||
{
|
||||
for (int x = 0; x < Math.Max(BCLIM.nlpo2(bclim.Height), 16); x++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
// Treat every 8 vertical pixels as 1 pixel for purposes of calculation, add to offset later.
|
||||
{
|
||||
int x1 = x % img2.Width; // Reshift x
|
||||
int y1 = (x + y / 8 * h) / img2.Width * 8; // Reshift y
|
||||
img2.SetPixel(x1, y1 + j, img.GetPixel(x, y + j)); // Reswizzle
|
||||
}
|
||||
}
|
||||
}
|
||||
img = img2;
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user