skidded ASTC Texture support + fixed crash if no ufont

This commit is contained in:
iAmAsval 2020-08-18 20:58:08 +02:00
parent 358b03de75
commit f19033f04c
12 changed files with 2479 additions and 441 deletions

View File

@ -63,7 +63,7 @@ namespace FModel.Creator.Texts
if (Globals.Game.ActualGame == EGame.Fortnite)
{
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
else BundleDefaultTypeface = DefaultTypeface;
@ -78,7 +78,7 @@ namespace FModel.Creator.Texts
if (!namePath.Equals(_FORTNITE_BASE_PATH))
{
t = Utils.GetPropertyArraySegmentByte(namePath);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
}
else DisplayNameTypeface = DefaultTypeface;
@ -91,7 +91,7 @@ namespace FModel.Creator.Texts
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_REGULAR :
_NOTO_SANS_REGULAR);
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
else DescriptionTypeface = DefaultTypeface;
@ -106,7 +106,7 @@ namespace FModel.Creator.Texts
if (!bundleNamePath.Equals(_FORTNITE_BASE_PATH))
{
t = Utils.GetPropertyArraySegmentByte(bundleNamePath);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
BundleDisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
}
else BundleDisplayNameTypeface = BundleDefaultTypeface;
@ -122,7 +122,7 @@ namespace FModel.Creator.Texts
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _TUNGSTEN_SIMPLIFIED_CHINESE :
_TUNGSTEN_BOLD);
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(namePath);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
else DisplayNameTypeface = DefaultTypeface;
@ -134,12 +134,12 @@ namespace FModel.Creator.Texts
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTOSANS_CJK_LIGHT :
_DINNEXT_W1G_LIGHT);
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
else DescriptionTypeface = DefaultTypeface;
t = Utils.GetPropertyArraySegmentByte(_VALORANT_BASE_PATH + _DINNEXT_W1G_BOLD);
if (t != null && t.Length == 3)
if (t != null && t.Length == 3 && t[2].Array != null)
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
else BundleDefaultTypeface = DefaultTypeface;
}

View File

@ -20,8 +20,11 @@ namespace PakReader.Parsers.Class
string FontFilename = Path.GetFileName(str.Value);
string folder = FModel.Properties.Settings.Default.OutputPath + "\\Fonts\\";
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
ufont.CopyTo(fileStream);
if (ufont != null)
{
using var fileStream = new FileStream(folder + FontFilename, FileMode.Create, FileAccess.Write);
ufont.CopyTo(fileStream);
}
}
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using PakReader.Parsers.Objects;
using PakReader.Textures;
using SkiaSharp;
namespace PakReader.Parsers.Class

View File

@ -1,432 +0,0 @@
using System;
using System.IO;
using PakReader.Parsers.Objects;
using SkiaSharp;
namespace PakReader
{
static class TextureDecoder
{
public static SKImage DecodeImage(byte[] sequence, int width, int height, int depth, EPixelFormat format)
{
byte[] data;
SKColorType colorType;
switch (format)
{
case EPixelFormat.PF_DXT5:
data = DXTDecoder.DecodeDXT5(sequence, width, height, depth);
colorType = SKColorType.Rgba8888;
break;
case EPixelFormat.PF_DXT1:
data = DXTDecoder.DecodeDXT1(sequence, width, height, depth);
colorType = SKColorType.Rgba8888;
break;
case EPixelFormat.PF_B8G8R8A8:
data = sequence;
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_BC5:
data = BCDecoder.DecodeBC5(sequence, width, height);
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_BC4:
data = BCDecoder.DecodeBC4(sequence, width, height);
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_G8:
data = sequence;
colorType = SKColorType.Gray8;
break;
case EPixelFormat.PF_FloatRGBA:
data = sequence;
colorType = SKColorType.RgbaF16;
break;
default:
throw new NotImplementedException($"Cannot decode {format} format");
}
using var bitmap = new SKBitmap(new SKImageInfo(width, height, colorType, SKAlphaType.Unpremul));
unsafe
{
fixed (byte* p = data)
{
bitmap.SetPixels(new IntPtr(p));
}
}
return SKImage.FromBitmap(bitmap);
}
static class BCDecoder
{
public static byte[] DecodeBC4(byte[] inp, int width, int height)
{
byte[] ret = new byte[width * height * 4];
using var reader = new BinaryReader(new MemoryStream(inp));
for (int y_block = 0; y_block < height / 4; y_block++)
{
for (int x_block = 0; x_block < width / 4; x_block++)
{
var r_bytes = DecodeBC3Block(reader);
for (int i = 0; i < 16; i++)
{
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
}
}
}
return ret;
}
public static byte[] DecodeBC5(byte[] inp, int width, int height)
{
byte[] ret = new byte[width * height * 4];
using var reader = new BinaryReader(new MemoryStream(inp));
for (int y_block = 0; y_block < height / 4; y_block++)
{
for (int x_block = 0; x_block < width / 4; x_block++)
{
var r_bytes = DecodeBC3Block(reader);
var g_bytes = DecodeBC3Block(reader);
for (int i = 0; i < 16; i++)
{
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 1)] = g_bytes[i];
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 2)] = GetZNormal(r_bytes[i], g_bytes[i]);
}
}
}
return ret;
}
static int GetPixelLoc(int width, int x, int y, int bpp, int off) => (y * width + x) * bpp + off;
static byte GetZNormal(byte x, byte y)
{
var xf = (x / 127.5f) - 1;
var yf = (y / 127.5f) - 1;
var zval = 1 - xf * xf - yf * yf;
var zval_ = (float)Math.Sqrt(zval > 0 ? zval : 0);
zval = zval_ < 1 ? zval_ : 1;
return (byte)((zval * 127) + 128);
}
static byte[] DecodeBC3Block(BinaryReader reader)
{
float ref0 = reader.ReadByte();
float ref1 = reader.ReadByte();
float[] ref_sl = new float[8];
ref_sl[0] = ref0;
ref_sl[1] = ref1;
if (ref0 > ref1)
{
ref_sl[2] = (6 * ref0 + 1 * ref1) / 7;
ref_sl[3] = (5 * ref0 + 2 * ref1) / 7;
ref_sl[4] = (4 * ref0 + 3 * ref1) / 7;
ref_sl[5] = (3 * ref0 + 4 * ref1) / 7;
ref_sl[6] = (2 * ref0 + 5 * ref1) / 7;
ref_sl[7] = (1 * ref0 + 6 * ref1) / 7;
}
else
{
ref_sl[2] = (4 * ref0 + 1 * ref1) / 5;
ref_sl[3] = (3 * ref0 + 2 * ref1) / 5;
ref_sl[4] = (2 * ref0 + 3 * ref1) / 5;
ref_sl[5] = (1 * ref0 + 4 * ref1) / 5;
ref_sl[6] = 0;
ref_sl[7] = 255;
}
byte[] index_block1 = GetBC3Indices(reader.ReadBytes(3));
byte[] index_block2 = GetBC3Indices(reader.ReadBytes(3));
byte[] bytes = new byte[16];
for (int i = 0; i < 8; i++)
{
bytes[7 - i] = (byte)ref_sl[index_block1[i]];
}
for (int i = 0; i < 8; i++)
{
bytes[15 - i] = (byte)ref_sl[index_block2[i]];
}
return bytes;
}
static byte[] GetBC3Indices(byte[] buf_block) =>
new byte[] {
(byte)((buf_block[2] & 0b1110_0000) >> 5),
(byte)((buf_block[2] & 0b0001_1100) >> 2),
(byte)(((buf_block[2] & 0b0000_0011) << 1) | ((buf_block[1] & 0b1 << 7) >> 7)),
(byte)((buf_block[1] & 0b0111_0000) >> 4),
(byte)((buf_block[1] & 0b0000_1110) >> 1),
(byte)(((buf_block[1] & 0b0000_0001) << 2) | ((buf_block[0] & 0b11 << 6) >> 6)),
(byte)((buf_block[0] & 0b0011_1000) >> 3),
(byte)(buf_block[0] & 0b0000_0111)
};
}
static class DXTDecoder
{
struct Colour8888
{
public byte red;
public byte green;
public byte blue;
public byte alpha;
}
public static byte[] DecodeDXT1(byte[] inp, int width, int height, int depth)
{
var bpp = 4;
var bps = width * bpp * 1;
var sizeofplane = bps * height;
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
var colours = new Colour8888[4];
colours[0].alpha = 0xFF;
colours[1].alpha = 0xFF;
colours[2].alpha = 0xFF;
unsafe
{
fixed (byte* bytePtr = inp)
{
byte* temp = bytePtr;
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
ushort colour0 = *((ushort*)temp);
ushort colour1 = *((ushort*)(temp + 2));
DxtcReadColor(colour0, ref colours[0]);
DxtcReadColor(colour1, ref colours[1]);
uint bitmask = ((uint*)temp)[1];
temp += 8;
if (colour0 > colour1)
{
// Four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
colours[3].alpha = 0xFF;
}
else
{
// Three-color block: derive the other color.
// 00 = color_0, 01 = color_1, 10 = color_2,
// 11 = transparent.
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2);
colours[2].green = (byte)((colours[0].green + colours[1].green) / 2);
colours[2].red = (byte)((colours[0].red + colours[1].red) / 2);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
colours[3].alpha = 0x00;
}
for (int j = 0, k = 0; j < 4; j++)
{
for (int i = 0; i < 4; i++, k++)
{
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
Colour8888 col = colours[select];
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
rawData[offset + 0] = col.red;
rawData[offset + 1] = col.green;
rawData[offset + 2] = col.blue;
rawData[offset + 3] = col.alpha;
}
}
}
}
}
}
}
}
return rawData;
}
public static byte[] DecodeDXT5(byte[] inp, int width, int height, int depth)
{
var bpp = 4;
var bps = width * bpp * 1;
var sizeofplane = bps * height;
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
var colours = new Colour8888[4];
ushort[] alphas = new ushort[8];
unsafe
{
fixed (byte* bytePtr = inp)
{
byte* temp = bytePtr;
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
if (y >= height || x >= width)
break;
alphas[0] = temp[0];
alphas[1] = temp[1];
byte* alphamask = (temp + 2);
temp += 8;
DxtcReadColors(temp, colours);
uint bitmask = ((uint*)temp)[1];
temp += 8;
// Four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
//colours[3].alpha = 0xFF;
int k = 0;
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 4; k++, i++)
{
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
Colour8888 col = colours[select];
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
rawData[offset] = col.red;
rawData[offset + 1] = col.green;
rawData[offset + 2] = col.blue;
}
}
}
// 8-alpha or 6-alpha block?
if (alphas[0] > alphas[1])
{
// 8-alpha block: derive the other six alphas.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010
alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011
alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100
alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101
alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110
alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111
}
else
{
// 6-alpha block.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010
alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011
alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100
alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101
alphas[6] = 0x00; // Bit code 110
alphas[7] = 0xFF; // Bit code 111
}
// Note: Have to separate the next two loops,
// it operates on a 6-byte system.
// First three bytes
//uint bits = (uint)(alphamask[0]);
uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16));
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 4; i++)
{
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
rawData[offset] = (byte)alphas[bits & 0x07];
}
bits >>= 3;
}
}
// Last three bytes
//bits = (uint)(alphamask[3]);
bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16));
for (int j = 2; j < 4; j++)
{
for (int i = 0; i < 4; i++)
{
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
rawData[offset] = (byte)alphas[bits & 0x07];
}
bits >>= 3;
}
}
}
}
}
}
return rawData;
}
}
static unsafe void DxtcReadColors(byte* data, Colour8888[] op)
{
byte buf = (byte)((data[1] & 0xF8) >> 3);
op[0].red = (byte)(buf << 3 | buf >> 2);
buf = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3));
op[0].green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data[0] & 0x1F);
op[0].blue = (byte)(buf << 3 | buf >> 2);
buf = (byte)((data[3] & 0xF8) >> 3);
op[1].red = (byte)(buf << 3 | buf >> 2);
buf = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3));
op[1].green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data[2] & 0x1F);
op[1].blue = (byte)(buf << 3 | buf >> 2);
}
static void DxtcReadColor(ushort data, ref Colour8888 op)
{
byte buf = (byte)((data & 0xF800) >> 11);
op.red = (byte)(buf << 3 | buf >> 2);
buf = (byte)((data & 0x7E0) >> 5);
op.green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data & 0x1f);
op.blue = (byte)(buf << 3 | buf >> 2);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,133 @@
using System;
namespace PakReader.Textures.ASTC
{
class ASTCPixel
{
public short R { get; set; }
public short G { get; set; }
public short B { get; set; }
public short A { get; set; }
readonly byte[] BitDepth = new byte[4];
public ASTCPixel(short _A, short _R, short _G, short _B)
{
A = _A;
R = _R;
G = _G;
B = _B;
for (int i = 0; i < 4; i++)
BitDepth[i] = 8;
}
public void ClampByte()
{
R = Math.Min(Math.Max(R, (short)0), (short)255);
G = Math.Min(Math.Max(G, (short)0), (short)255);
B = Math.Min(Math.Max(B, (short)0), (short)255);
A = Math.Min(Math.Max(A, (short)0), (short)255);
}
public short GetComponent(int Index)
{
return Index switch
{
0 => A,
1 => R,
2 => G,
3 => B,
_ => 0,
};
}
public void SetComponent(int Index, int Value)
{
switch (Index)
{
case 0:
A = (short)Value;
break;
case 1:
R = (short)Value;
break;
case 2:
G = (short)Value;
break;
case 3:
B = (short)Value;
break;
}
}
public void ChangeBitDepth(byte[] Depth)
{
for (int i = 0; i < 4; i++)
{
int Value = ChangeBitDepth(GetComponent(i), BitDepth[i], Depth[i]);
SetComponent(i, Value);
BitDepth[i] = Depth[i];
}
}
short ChangeBitDepth(short Value, byte OldDepth, byte NewDepth)
{
if (OldDepth == NewDepth)
{
// Do nothing
return Value;
}
else if (OldDepth == 0 && NewDepth != 0)
{
return (short)((1 << NewDepth) - 1);
}
else if (NewDepth > OldDepth)
{
return (short)BitArrayStream.Replicate(Value, OldDepth, NewDepth);
}
else
{
// oldDepth > newDepth
if (NewDepth == 0)
{
return 0xFF;
}
else
{
byte BitsWasted = (byte)(OldDepth - NewDepth);
short TempValue = Value;
TempValue = (short)((TempValue + (1 << (BitsWasted - 1))) >> BitsWasted);
TempValue = Math.Min(Math.Max((short)0, TempValue), (short)((1 << NewDepth) - 1));
return (byte)(TempValue);
}
}
}
public int Pack()
{
ASTCPixel NewPixel = new ASTCPixel(A, R, G, B);
byte[] eightBitDepth = { 8, 8, 8, 8 };
NewPixel.ChangeBitDepth(eightBitDepth);
return (byte)NewPixel.A << 24 |
(byte)NewPixel.B << 16 |
(byte)NewPixel.G << 8 |
(byte)NewPixel.R << 0;
}
// Adds more precision to the blue channel as described
// in C.2.14
public static ASTCPixel BlueContract(int a, int r, int g, int b)
{
return new ASTCPixel((short)(a),
(short)((r + b) >> 1),
(short)((g + b) >> 1),
(short)(b));
}
}
}

View File

@ -0,0 +1,120 @@
using System;
using System.Collections;
namespace PakReader.Textures.ASTC
{
public class BitArrayStream
{
public BitArray BitsArray;
public int Position { get; private set; }
public BitArrayStream(BitArray BitArray)
{
BitsArray = BitArray;
Position = 0;
}
public short ReadBits(int Length)
{
int RetValue = 0;
for (int i = Position; i < Position + Length; i++)
{
if (BitsArray[i])
{
RetValue |= 1 << (i - Position);
}
}
Position += Length;
return (short)RetValue;
}
public int ReadBits(int Start, int End)
{
int RetValue = 0;
for (int i = Start; i <= End; i++)
{
if (BitsArray[i])
{
RetValue |= 1 << (i - Start);
}
}
return RetValue;
}
public int ReadBit(int Index)
{
return Convert.ToInt32(BitsArray[Index]);
}
public void WriteBits(int Value, int Length)
{
for (int i = Position; i < Position + Length; i++)
{
BitsArray[i] = ((Value >> (i - Position)) & 1) != 0;
}
Position += Length;
}
public byte[] ToByteArray()
{
byte[] RetArray = new byte[(BitsArray.Length + 7) / 8];
BitsArray.CopyTo(RetArray, 0);
return RetArray;
}
public static int Replicate(int Value, int NumberBits, int ToBit)
{
if (NumberBits == 0) return 0;
if (ToBit == 0) return 0;
int TempValue = Value & ((1 << NumberBits) - 1);
int RetValue = TempValue;
int ResLength = NumberBits;
while (ResLength < ToBit)
{
int Comp = 0;
if (NumberBits > ToBit - ResLength)
{
int NewShift = ToBit - ResLength;
Comp = NumberBits - NewShift;
NumberBits = NewShift;
}
RetValue <<= NumberBits;
RetValue |= TempValue >> Comp;
ResLength += NumberBits;
}
return RetValue;
}
public static int PopCnt(int Number)
{
int Counter;
for (Counter = 0; Number != 0; Counter++)
{
Number &= Number - 1;
}
return Counter;
}
public static void Swap<T>(ref T lhs, ref T rhs)
{
T Temp = lhs;
lhs = rhs;
rhs = Temp;
}
// Transfers a bit as described in C.2.14
public static void BitTransferSigned(ref int a, ref int b)
{
b >>= 1;
b |= a & 0x80;
a >>= 1;
a &= 0x3F;
if ((a & 0x20) != 0) a -= 0x40;
}
}
}

View File

@ -0,0 +1,268 @@
using System.Collections;
using System.Collections.Generic;
namespace PakReader.Textures.ASTC
{
public struct IntegerEncoded
{
public enum EIntegerEncoding
{
JustBits,
Quint,
Trit
}
readonly EIntegerEncoding Encoding;
public int NumberBits { get; private set; }
public int BitValue { get; private set; }
public int TritValue { get; private set; }
public int QuintValue { get; private set; }
public IntegerEncoded(EIntegerEncoding _Encoding, int NumBits)
{
Encoding = _Encoding;
NumberBits = NumBits;
BitValue = 0;
TritValue = 0;
QuintValue = 0;
}
public bool MatchesEncoding(IntegerEncoded Other)
{
return Encoding == Other.Encoding && NumberBits == Other.NumberBits;
}
public EIntegerEncoding GetEncoding()
{
return Encoding;
}
public int GetBitLength(int NumberVals)
{
int TotalBits = NumberBits * NumberVals;
if (Encoding == EIntegerEncoding.Trit)
{
TotalBits += (NumberVals * 8 + 4) / 5;
}
else if (Encoding == EIntegerEncoding.Quint)
{
TotalBits += (NumberVals * 7 + 2) / 3;
}
return TotalBits;
}
public static IntegerEncoded CreateEncoding(int MaxVal)
{
while (MaxVal > 0)
{
int Check = MaxVal + 1;
// Is maxVal a power of two?
if ((Check & (Check - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.JustBits, BitArrayStream.PopCnt(MaxVal));
}
// Is maxVal of the type 3*2^n - 1?
if ((Check % 3 == 0) && ((Check / 3) & ((Check / 3) - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.Trit, BitArrayStream.PopCnt(Check / 3 - 1));
}
// Is maxVal of the type 5*2^n - 1?
if ((Check % 5 == 0) && ((Check / 5) & ((Check / 5) - 1)) == 0)
{
return new IntegerEncoded(EIntegerEncoding.Quint, BitArrayStream.PopCnt(Check / 5 - 1));
}
// Apparently it can't be represented with a bounded integer sequence...
// just iterate.
MaxVal--;
}
return new IntegerEncoded(EIntegerEncoding.JustBits, 0);
}
public static void DecodeTritBlock(
BitArrayStream BitStream,
List<IntegerEncoded> ListIntegerEncoded,
int NumberBitsPerValue)
{
// Implement the algorithm in section C.2.12
int[] m = new int[5];
int[] t = new int[5];
int T;
// Read the trit encoded block according to
// table C.2.14
m[0] = BitStream.ReadBits(NumberBitsPerValue);
T = BitStream.ReadBits(2);
m[1] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(2) << 2;
m[2] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(1) << 4;
m[3] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(2) << 5;
m[4] = BitStream.ReadBits(NumberBitsPerValue);
T |= BitStream.ReadBits(1) << 7;
int C;
BitArrayStream Tb = new BitArrayStream(new BitArray(new int[] { T }));
if (Tb.ReadBits(2, 4) == 7)
{
C = (Tb.ReadBits(5, 7) << 2) | Tb.ReadBits(0, 1);
t[4] = t[3] = 2;
}
else
{
C = Tb.ReadBits(0, 4);
if (Tb.ReadBits(5, 6) == 3)
{
t[4] = 2;
t[3] = Tb.ReadBit(7);
}
else
{
t[4] = Tb.ReadBit(7);
t[3] = Tb.ReadBits(5, 6);
}
}
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
if (Cb.ReadBits(0, 1) == 3)
{
t[2] = 2;
t[1] = Cb.ReadBit(4);
t[0] = (Cb.ReadBit(3) << 1) | (Cb.ReadBit(2) & ~Cb.ReadBit(3));
}
else if (Cb.ReadBits(2, 3) == 3)
{
t[2] = 2;
t[1] = 2;
t[0] = Cb.ReadBits(0, 1);
}
else
{
t[2] = Cb.ReadBit(4);
t[1] = Cb.ReadBits(2, 3);
t[0] = (Cb.ReadBit(1) << 1) | (Cb.ReadBit(0) & ~Cb.ReadBit(1));
}
for (int i = 0; i < 5; i++)
{
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Trit, NumberBitsPerValue)
{
BitValue = m[i],
TritValue = t[i]
};
ListIntegerEncoded.Add(IntEncoded);
}
}
public static void DecodeQuintBlock(
BitArrayStream BitStream,
List<IntegerEncoded> ListIntegerEncoded,
int NumberBitsPerValue)
{
// Implement the algorithm in section C.2.12
int[] m = new int[3];
int[] q = new int[3];
int Q;
// Read the trit encoded block according to
// table C.2.15
m[0] = BitStream.ReadBits(NumberBitsPerValue);
Q = BitStream.ReadBits(3);
m[1] = BitStream.ReadBits(NumberBitsPerValue);
Q |= BitStream.ReadBits(2) << 3;
m[2] = BitStream.ReadBits(NumberBitsPerValue);
Q |= BitStream.ReadBits(2) << 5;
BitArrayStream Qb = new BitArrayStream(new BitArray(new int[] { Q }));
if (Qb.ReadBits(1, 2) == 3 && Qb.ReadBits(5, 6) == 0)
{
q[0] = q[1] = 4;
q[2] = (Qb.ReadBit(0) << 2) | ((Qb.ReadBit(4) & ~Qb.ReadBit(0)) << 1) | (Qb.ReadBit(3) & ~Qb.ReadBit(0));
}
else
{
int C;
if (Qb.ReadBits(1, 2) == 3)
{
q[2] = 4;
C = (Qb.ReadBits(3, 4) << 3) | ((~Qb.ReadBits(5, 6) & 3) << 1) | Qb.ReadBit(0);
}
else
{
q[2] = Qb.ReadBits(5, 6);
C = Qb.ReadBits(0, 4);
}
BitArrayStream Cb = new BitArrayStream(new BitArray(new int[] { C }));
if (Cb.ReadBits(0, 2) == 5)
{
q[1] = 4;
q[0] = Cb.ReadBits(3, 4);
}
else
{
q[1] = Cb.ReadBits(3, 4);
q[0] = Cb.ReadBits(0, 2);
}
}
for (int i = 0; i < 3; i++)
{
IntegerEncoded IntEncoded = new IntegerEncoded(EIntegerEncoding.Quint, NumberBitsPerValue)
{
BitValue = m[i],
QuintValue = q[i]
};
ListIntegerEncoded.Add(IntEncoded);
}
}
public static void DecodeIntegerSequence(
List<IntegerEncoded> DecodeIntegerSequence,
BitArrayStream BitStream,
int MaxRange,
int NumberValues)
{
// Determine encoding parameters
IntegerEncoded IntEncoded = CreateEncoding(MaxRange);
// Start decoding
int NumberValuesDecoded = 0;
while (NumberValuesDecoded < NumberValues)
{
switch (IntEncoded.GetEncoding())
{
case EIntegerEncoding.Quint:
{
DecodeQuintBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
NumberValuesDecoded += 3;
break;
}
case EIntegerEncoding.Trit:
{
DecodeTritBlock(BitStream, DecodeIntegerSequence, IntEncoded.NumberBits);
NumberValuesDecoded += 5;
break;
}
case EIntegerEncoding.JustBits:
{
IntEncoded.BitValue = BitStream.ReadBits(IntEncoded.NumberBits);
DecodeIntegerSequence.Add(IntEncoded);
NumberValuesDecoded++;
break;
}
}
}
}
}
}

View File

@ -0,0 +1,117 @@
using System;
using System.IO;
namespace PakReader.Textures.BC
{
public static class BCDecoder
{
public static byte[] DecodeBC4(byte[] inp, int width, int height)
{
byte[] ret = new byte[width * height * 4];
using var reader = new BinaryReader(new MemoryStream(inp));
for (int y_block = 0; y_block < height / 4; y_block++)
{
for (int x_block = 0; x_block < width / 4; x_block++)
{
var r_bytes = DecodeBC3Block(reader);
for (int i = 0; i < 16; i++)
{
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
}
}
}
return ret;
}
public static byte[] DecodeBC5(byte[] inp, int width, int height)
{
byte[] ret = new byte[width * height * 4];
using var reader = new BinaryReader(new MemoryStream(inp));
for (int y_block = 0; y_block < height / 4; y_block++)
{
for (int x_block = 0; x_block < width / 4; x_block++)
{
var r_bytes = DecodeBC3Block(reader);
var g_bytes = DecodeBC3Block(reader);
for (int i = 0; i < 16; i++)
{
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 1)] = g_bytes[i];
ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 2)] = GetZNormal(r_bytes[i], g_bytes[i]);
}
}
}
return ret;
}
static int GetPixelLoc(int width, int x, int y, int bpp, int off) => (y * width + x) * bpp + off;
static byte GetZNormal(byte x, byte y)
{
var xf = (x / 127.5f) - 1;
var yf = (y / 127.5f) - 1;
var zval = 1 - xf * xf - yf * yf;
var zval_ = (float)Math.Sqrt(zval > 0 ? zval : 0);
zval = zval_ < 1 ? zval_ : 1;
return (byte)((zval * 127) + 128);
}
static byte[] DecodeBC3Block(BinaryReader reader)
{
float ref0 = reader.ReadByte();
float ref1 = reader.ReadByte();
float[] ref_sl = new float[8];
ref_sl[0] = ref0;
ref_sl[1] = ref1;
if (ref0 > ref1)
{
ref_sl[2] = (6 * ref0 + 1 * ref1) / 7;
ref_sl[3] = (5 * ref0 + 2 * ref1) / 7;
ref_sl[4] = (4 * ref0 + 3 * ref1) / 7;
ref_sl[5] = (3 * ref0 + 4 * ref1) / 7;
ref_sl[6] = (2 * ref0 + 5 * ref1) / 7;
ref_sl[7] = (1 * ref0 + 6 * ref1) / 7;
}
else
{
ref_sl[2] = (4 * ref0 + 1 * ref1) / 5;
ref_sl[3] = (3 * ref0 + 2 * ref1) / 5;
ref_sl[4] = (2 * ref0 + 3 * ref1) / 5;
ref_sl[5] = (1 * ref0 + 4 * ref1) / 5;
ref_sl[6] = 0;
ref_sl[7] = 255;
}
byte[] index_block1 = GetBC3Indices(reader.ReadBytes(3));
byte[] index_block2 = GetBC3Indices(reader.ReadBytes(3));
byte[] bytes = new byte[16];
for (int i = 0; i < 8; i++)
{
bytes[7 - i] = (byte)ref_sl[index_block1[i]];
}
for (int i = 0; i < 8; i++)
{
bytes[15 - i] = (byte)ref_sl[index_block2[i]];
}
return bytes;
}
static byte[] GetBC3Indices(byte[] buf_block) =>
new byte[] {
(byte)((buf_block[2] & 0b1110_0000) >> 5),
(byte)((buf_block[2] & 0b0001_1100) >> 2),
(byte)(((buf_block[2] & 0b0000_0011) << 1) | ((buf_block[1] & 0b1 << 7) >> 7)),
(byte)((buf_block[1] & 0b0111_0000) >> 4),
(byte)((buf_block[1] & 0b0000_1110) >> 1),
(byte)(((buf_block[1] & 0b0000_0001) << 2) | ((buf_block[0] & 0b11 << 6) >> 6)),
(byte)((buf_block[0] & 0b0011_1000) >> 3),
(byte)(buf_block[0] & 0b0000_0111)
};
}
}

View File

@ -0,0 +1,263 @@
namespace PakReader.Textures.DXT
{
public static class DXTDecoder
{
struct Colour8888
{
public byte red;
public byte green;
public byte blue;
public byte alpha;
}
public static byte[] DecodeDXT1(byte[] inp, int width, int height, int depth)
{
var bpp = 4;
var bps = width * bpp * 1;
var sizeofplane = bps * height;
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
var colours = new Colour8888[4];
colours[0].alpha = 0xFF;
colours[1].alpha = 0xFF;
colours[2].alpha = 0xFF;
unsafe
{
fixed (byte* bytePtr = inp)
{
byte* temp = bytePtr;
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
ushort colour0 = *((ushort*)temp);
ushort colour1 = *((ushort*)(temp + 2));
DxtcReadColor(colour0, ref colours[0]);
DxtcReadColor(colour1, ref colours[1]);
uint bitmask = ((uint*)temp)[1];
temp += 8;
if (colour0 > colour1)
{
// Four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
colours[3].alpha = 0xFF;
}
else
{
// Three-color block: derive the other color.
// 00 = color_0, 01 = color_1, 10 = color_2,
// 11 = transparent.
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2);
colours[2].green = (byte)((colours[0].green + colours[1].green) / 2);
colours[2].red = (byte)((colours[0].red + colours[1].red) / 2);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
colours[3].alpha = 0x00;
}
for (int j = 0, k = 0; j < 4; j++)
{
for (int i = 0; i < 4; i++, k++)
{
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
Colour8888 col = colours[select];
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
rawData[offset + 0] = col.red;
rawData[offset + 1] = col.green;
rawData[offset + 2] = col.blue;
rawData[offset + 3] = col.alpha;
}
}
}
}
}
}
}
}
return rawData;
}
public static byte[] DecodeDXT5(byte[] inp, int width, int height, int depth)
{
var bpp = 4;
var bps = width * bpp * 1;
var sizeofplane = bps * height;
byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
var colours = new Colour8888[4];
ushort[] alphas = new ushort[8];
unsafe
{
fixed (byte* bytePtr = inp)
{
byte* temp = bytePtr;
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y += 4)
{
for (int x = 0; x < width; x += 4)
{
if (y >= height || x >= width)
break;
alphas[0] = temp[0];
alphas[1] = temp[1];
byte* alphamask = (temp + 2);
temp += 8;
DxtcReadColors(temp, colours);
uint bitmask = ((uint*)temp)[1];
temp += 8;
// Four-color block: derive the other two colors.
// 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
// These 2-bit codes correspond to the 2-bit fields
// stored in the 64-bit block.
colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
//colours[2].alpha = 0xFF;
colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
//colours[3].alpha = 0xFF;
int k = 0;
for (int j = 0; j < 4; j++)
{
for (int i = 0; i < 4; k++, i++)
{
int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
Colour8888 col = colours[select];
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
rawData[offset] = col.red;
rawData[offset + 1] = col.green;
rawData[offset + 2] = col.blue;
}
}
}
// 8-alpha or 6-alpha block?
if (alphas[0] > alphas[1])
{
// 8-alpha block: derive the other six alphas.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010
alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011
alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100
alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101
alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110
alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111
}
else
{
// 6-alpha block.
// Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010
alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011
alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100
alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101
alphas[6] = 0x00; // Bit code 110
alphas[7] = 0xFF; // Bit code 111
}
// Note: Have to separate the next two loops,
// it operates on a 6-byte system.
// First three bytes
//uint bits = (uint)(alphamask[0]);
uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16));
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 4; i++)
{
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
rawData[offset] = (byte)alphas[bits & 0x07];
}
bits >>= 3;
}
}
// Last three bytes
//bits = (uint)(alphamask[3]);
bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16));
for (int j = 2; j < 4; j++)
{
for (int i = 0; i < 4; i++)
{
// only put pixels out < width or height
if (((x + i) < width) && ((y + j) < height))
{
uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
rawData[offset] = (byte)alphas[bits & 0x07];
}
bits >>= 3;
}
}
}
}
}
}
return rawData;
}
}
static unsafe void DxtcReadColors(byte* data, Colour8888[] op)
{
byte buf = (byte)((data[1] & 0xF8) >> 3);
op[0].red = (byte)(buf << 3 | buf >> 2);
buf = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3));
op[0].green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data[0] & 0x1F);
op[0].blue = (byte)(buf << 3 | buf >> 2);
buf = (byte)((data[3] & 0xF8) >> 3);
op[1].red = (byte)(buf << 3 | buf >> 2);
buf = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3));
op[1].green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data[2] & 0x1F);
op[1].blue = (byte)(buf << 3 | buf >> 2);
}
static void DxtcReadColor(ushort data, ref Colour8888 op)
{
byte buf = (byte)((data & 0xF800) >> 11);
op.red = (byte)(buf << 3 | buf >> 2);
buf = (byte)((data & 0x7E0) >> 5);
op.green = (byte)(buf << 2 | buf >> 3);
buf = (byte)(data & 0x1f);
op.blue = (byte)(buf << 3 | buf >> 2);
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using PakReader.Parsers.Objects;
using PakReader.Textures.ASTC;
using PakReader.Textures.BC;
using PakReader.Textures.DXT;
using SkiaSharp;
namespace PakReader.Textures
{
static class TextureDecoder
{
public static SKImage DecodeImage(byte[] sequence, int width, int height, int depth, EPixelFormat format)
{
byte[] data;
SKColorType colorType;
switch (format)
{
case EPixelFormat.PF_DXT5:
data = DXTDecoder.DecodeDXT5(sequence, width, height, depth);
colorType = SKColorType.Rgba8888;
break;
case EPixelFormat.PF_DXT1:
data = DXTDecoder.DecodeDXT1(sequence, width, height, depth);
colorType = SKColorType.Rgba8888;
break;
case EPixelFormat.PF_ASTC_8x8:
var x = (int)TextureFormatHelper.GetBlockWidth(format);
var y = (int)TextureFormatHelper.GetBlockHeight(format);
var z = (int)TextureFormatHelper.GetBlockDepth(format);
data = ASTCDecoder.DecodeToRGBA8888(sequence, x, y, z, width, height, 1);
colorType = SKColorType.Rgba8888;
break;
case EPixelFormat.PF_B8G8R8A8:
data = sequence;
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_BC5:
data = BCDecoder.DecodeBC5(sequence, width, height);
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_BC4:
data = BCDecoder.DecodeBC4(sequence, width, height);
colorType = SKColorType.Bgra8888;
break;
case EPixelFormat.PF_G8:
data = sequence;
colorType = SKColorType.Gray8;
break;
case EPixelFormat.PF_FloatRGBA:
data = sequence;
colorType = SKColorType.RgbaF16;
break;
default:
throw new NotImplementedException($"Cannot decode {format} format");
}
using var bitmap = new SKBitmap(new SKImageInfo(width, height, colorType, SKAlphaType.Unpremul));
unsafe
{
fixed (byte* p = data)
{
bitmap.SetPixels(new IntPtr(p));
}
}
return SKImage.FromBitmap(bitmap);
}
}
}

View File

@ -0,0 +1,159 @@
using PakReader.Parsers.Objects;
using System.Collections.Generic;
namespace PakReader.Textures
{
public class TextureFormatHelper
{
private enum TargetBuffer
{
Color = 1,
Depth = 2,
Stencil = 3,
DepthStencil = 4,
}
public static uint GetBlockWidth(EPixelFormat Format)
{
if (!HasFormatTableKey(Format)) return 8;
return FormatTable[GetBaseFormat(Format)].BlockWidth;
}
public static uint GetBlockHeight(EPixelFormat Format)
{
if (!HasFormatTableKey(Format)) return 8;
return FormatTable[GetBaseFormat(Format)].BlockHeight;
}
public static uint GetBytesPerPixel(EPixelFormat Format)
{
if (!HasFormatTableKey(Format)) return 8;
return FormatTable[GetBaseFormat(Format)].BytesPerPixel;
}
public static uint GetBlockDepth(EPixelFormat Format)
{
if (!HasFormatTableKey(Format)) return 8;
return FormatTable[GetBaseFormat(Format)].BlockDepth;
}
public static bool IsBCNCompressed(EPixelFormat Format)
{
return Format.ToString().StartsWith("BC");
}
public static bool HasFormatTableKey(EPixelFormat Format)
{
return FormatTable.ContainsKey(GetBaseFormat(Format));
}
private static string GetBaseFormat(EPixelFormat format)
{
string[] items = { "_UNORM", "_SRGB", "_SINT", "_SNORM", "_UINT", "_UFLOAT", "_SFLOAT", "_FLOAT", "H_SF16", "H_UF16" };
string output = format.ToString();
for (int i = 0; i < items.Length; i++)
output = output.Replace(items[i], string.Empty);
return output;
}
private static readonly Dictionary<string, FormatInfo> FormatTable =
new Dictionary<string, FormatInfo>()
{
{ "RGBA32", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
{ "RGBA16", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "RGBA8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "RGB8", new FormatInfo(3, 1, 1, 1, TargetBuffer.Color) },
{ "RGBA4", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "RGB32", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "RG32", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "RG16", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "RG8", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "RG4", new FormatInfo(1, 1, 1, 1, TargetBuffer.Color) },
{ "R32", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "R16", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "R8", new FormatInfo(1, 1, 1, 1, TargetBuffer.Color) },
{ "R32G8X24", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "RGBG8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "BGRX8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "BGR5A1", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "RGB5A1", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "BGRA8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "R5G5B5", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "RGBB10A2", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "RG11B10", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "BGRA4", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "B5G6R5", new FormatInfo(2, 1, 1, 1, TargetBuffer.Color) },
{ "BC1", new FormatInfo(8, 4, 4, 1, TargetBuffer.Color) },
{ "BC2", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "BC3", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "BC4", new FormatInfo(8, 4, 4, 1, TargetBuffer.Color) },
{ "BC5", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "BC6", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "BC7", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "ASTC_4x4", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "ASTC_5x4", new FormatInfo(16, 5, 4, 1, TargetBuffer.Color) },
{ "ASTC_5x5", new FormatInfo(16, 5, 5, 1, TargetBuffer.Color) },
{ "ASTC_6x5", new FormatInfo(16, 6, 5, 1, TargetBuffer.Color) },
{ "ASTC_6x6", new FormatInfo(16, 6, 6, 1, TargetBuffer.Color) },
{ "ASTC_8x5", new FormatInfo(16, 8, 5, 1, TargetBuffer.Color) },
{ "ASTC_8x6", new FormatInfo(16, 8, 6, 1, TargetBuffer.Color) },
{ "ASTC_8x8", new FormatInfo(16, 8, 8, 1, TargetBuffer.Color) },
{ "ASTC_10x5", new FormatInfo(16, 10, 5, 1, TargetBuffer.Color) },
{ "ASTC_10x6", new FormatInfo(16, 10, 6, 1, TargetBuffer.Color) },
{ "ASTC_10x8", new FormatInfo(16, 10, 8, 1, TargetBuffer.Color) },
{ "ASTC_10x10", new FormatInfo(16, 10, 10, 1, TargetBuffer.Color) },
{ "ASTC_12x10", new FormatInfo(16, 12, 10, 1, TargetBuffer.Color) },
{ "ASTC_12x12", new FormatInfo(16, 12, 12, 1, TargetBuffer.Color) },
{ "ETC1", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "ETC1_A4", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "HIL08", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
{ "L4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "LA4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "L8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "LA8", new FormatInfo(16, 1, 1, 1, TargetBuffer.Color) },
{ "A4", new FormatInfo(4, 1, 1, 1, TargetBuffer.Color) },
{ "A8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Color) },
{ "D16", new FormatInfo(2, 1, 1, 1, TargetBuffer.Depth) },
{ "D24S8", new FormatInfo(4, 1, 1, 1, TargetBuffer.Depth) },
{ "D32", new FormatInfo(4, 1, 1, 1, TargetBuffer.Depth) },
{ "D24_UNORM_S8", new FormatInfo(8, 1, 1, 1, TargetBuffer.Depth) },
{ "D32_FLOAT_S8X24", new FormatInfo(8, 1, 1, 1, TargetBuffer.Depth) },
{ "I4", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) },
{ "I8", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
{ "IA4", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
{ "IA8", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "RGB565", new FormatInfo(2, 4, 4, 1, TargetBuffer.Color) },
{ "RGB5A3", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "C4", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) },
{ "C8", new FormatInfo(8, 8, 4, 1, TargetBuffer.Color) },
{ "C14X2", new FormatInfo(16, 4, 4, 1, TargetBuffer.Color) },
{ "CMPR", new FormatInfo(4, 8, 8, 1, TargetBuffer.Color) }
};
class FormatInfo
{
public uint BytesPerPixel { get; private set; }
public uint BlockWidth { get; private set; }
public uint BlockHeight { get; private set; }
public uint BlockDepth { get; private set; }
public TargetBuffer TargetBuffer;
public FormatInfo(uint bytesPerPixel, uint blockWidth, uint blockHeight, uint blockDepth, TargetBuffer targetBuffer)
{
BytesPerPixel = bytesPerPixel;
BlockWidth = blockWidth;
BlockHeight = blockHeight;
BlockDepth = blockDepth;
TargetBuffer = targetBuffer;
}
}
}
}