1
0
mirror of synced 2024-09-24 11:38:22 +02:00

Start to support 3DS ptcl

This commit is contained in:
KillzXGaming 2019-05-03 20:53:48 -04:00
parent 8269de8624
commit ed33ef65f9
16 changed files with 851 additions and 13 deletions

Binary file not shown.

View File

@ -48,9 +48,12 @@ namespace FirstPlugin
public Header header;
public PTCL_WiiU.Header headerU;
public PTCL_3DS.Header header3DS;
public byte[] data;
bool IsWiiU = false;
bool Is3DS = false;
public void Load(Stream stream)
{
@ -64,8 +67,19 @@ namespace FirstPlugin
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
string Signature = reader.ReadString(4, Encoding.ASCII);
uint Version = reader.ReadUInt32();
Console.WriteLine(Version.ToString("x"));
if (Version == 0x33000000)
Is3DS = true;
reader.Position = 0;
if (Signature == "EFTF" || Signature == "SPBD")
if (Is3DS)
{
reader.ByteOrder = ByteOrder.LittleEndian;
header3DS = new PTCL_3DS.Header();
header3DS.Read(reader, this);
}
else if (Signature == "EFTF" || Signature == "SPBD")
{
IsWiiU = true;
headerU = new PTCL_WiiU.Header();
@ -73,7 +87,6 @@ namespace FirstPlugin
}
else
{
CanSave = true;
header = new Header();
header.Read(reader, this);
}
@ -92,8 +105,9 @@ namespace FirstPlugin
public byte[] Save()
{
MemoryStream mem = new MemoryStream();
if (IsWiiU)
if (Is3DS)
header3DS.Write(new FileWriter(mem), this);
else if (IsWiiU)
headerU.Write(new FileWriter(mem), this);
else
header.Write(new FileWriter(mem));

View File

@ -0,0 +1,602 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Text;
using Switch_Toolbox.Library.IO;
using Switch_Toolbox.Library;
using System.IO;
using Syroot.BinaryData;
using System.Windows.Forms;
using Switch_Toolbox.Library.Forms;
using Bfres.Structs;
namespace FirstPlugin
{
public class PTCL_3DS
{
public class Header
{
public List<EmitterSet> emitterSets = new List<EmitterSet>();
public List<TextureInfo> Textures = new List<TextureInfo>();
public bool IsSPBD = false;
public uint EffectNameTableOffset;
public uint TextureBlockTableOffset;
public uint TextureBlockTableSize;
public uint ShaderGtxTableOffset;
public uint ShaderGtxTableSize;
public void Read(FileReader reader, PTCL pctl)
{
uint Position = (uint)reader.Position; //Offsets are relative to this
reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian;
string Signature = reader.ReadString(4, Encoding.ASCII);
if (Signature == "SPBD")
IsSPBD = true;
uint Version = reader.ReadUInt32();
uint EmitterCount = reader.ReadUInt32();
uint Padding = reader.ReadUInt32();
EffectNameTableOffset = reader.ReadUInt32();
TextureBlockTableOffset = reader.ReadUInt32();
TextureBlockTableSize = reader.ReadUInt32();
ShaderGtxTableOffset = reader.ReadUInt32();
ShaderGtxTableSize = reader.ReadUInt32();
uint KeyAnimationTableOffset = reader.ReadUInt32();
uint KeyAnimationTableSize = reader.ReadUInt32();
uint PrimativeTableOffset = reader.ReadUInt32();
uint PrimativeTableSize = reader.ReadUInt32();
uint ShaderParamTableOffset = reader.ReadUInt32();
uint ShaderParamTableSize = reader.ReadUInt32();
uint TotalTextureTableSize = reader.ReadUInt32();
uint Unknown1 = reader.ReadUInt32();
uint Unknown2 = reader.ReadUInt32();
var groupEmitterSets = new TreeNode("Emitter Sets");
var textureFolder = new TreeNode("Textures");
pctl.Nodes.Add(textureFolder);
pctl.Nodes.Add(groupEmitterSets);
reader.Seek(72, SeekOrigin.Begin);
for (int i = 0; i < EmitterCount; i++)
{
EmitterSet emitterSet = new EmitterSet();
emitterSet.Read(reader, this);
emitterSets.Add(emitterSet);
groupEmitterSets.Nodes.Add(emitterSet);
}
int index = 0;
foreach (var tex in Textures)
{
tex.Text = $"Texture{index++}";
textureFolder.Nodes.Add(tex);
}
reader.Dispose();
reader.Close();
}
public void Write(FileWriter writer, PTCL ptcl)
{
writer.ByteOrder = ByteOrder.BigEndian;
writer.Write(ptcl.data.ToArray());
foreach (TextureInfo tex in ptcl.header3DS.Textures)
{
//write texture blocks. Some slots are empty so check if it exists
tex.Write(writer, this);
}
foreach (EmitterSet emitterSets in emitterSets)
{
foreach (Emitter3DS emitter in emitterSets.Nodes)
{
writer.Seek(((Emitter3DS)emitter).ColorPosition, SeekOrigin.Begin);
foreach (var color0 in emitter.Color0Array)
{
writer.Write(color0.R);
writer.Write(color0.G);
writer.Write(color0.B);
writer.Write(color0.A);
}
foreach (var color1 in emitter.Color1Array)
{
writer.Write(color1.R);
writer.Write(color1.G);
writer.Write(color1.B);
writer.Write(color1.A);
}
}
}
writer.Flush();
writer.Close();
writer.Dispose();
}
}
public class EmitterSet : TreeNodeCustom
{
public void Read(FileReader reader, Header header)
{
uint NameOffset = 0;
uint EmitterCount = 0;
uint EmitterTableOffset = 0;
if (header.IsSPBD)
{
uint Description = reader.ReadUInt32();
uint Unknown = reader.ReadUInt32();
NameOffset = reader.ReadUInt32();
uint NamePointer = reader.ReadUInt32();
EmitterCount = reader.ReadUInt32();
EmitterTableOffset = reader.ReadUInt32();
uint Unknown2 = reader.ReadUInt32();
}
else
{
uint padding = reader.ReadUInt32();
uint padding2 = reader.ReadUInt32();
NameOffset = reader.ReadUInt32();
uint padding3 = reader.ReadUInt32();
EmitterCount = reader.ReadUInt32();
EmitterTableOffset = reader.ReadUInt32();
uint padding4 = reader.ReadUInt32();
}
using (reader.TemporarySeek(header.EffectNameTableOffset + NameOffset, SeekOrigin.Begin))
{
Text = reader.ReadString(BinaryStringFormat.ZeroTerminated);
}
long pos = reader.Position;
reader.Seek(EmitterTableOffset, SeekOrigin.Begin);
for (int i = 0; i < EmitterCount; i++)
{
EmitterTable table = new EmitterTable();
table.Read(reader, header);
Nodes.Add(table.emitter);
}
reader.Seek(pos, SeekOrigin.Begin);
}
public void Write(FileWriter writer)
{
}
}
public class EmitterTable
{
public Emitter3DS emitter;
public uint EmitterOffset;
public void Read(FileReader reader, Header header)
{
if (header.IsSPBD)
{
EmitterOffset = reader.ReadUInt32();
uint padding = reader.ReadUInt32();
}
else
{
EmitterOffset = reader.ReadUInt32();
uint padding = reader.ReadUInt32();
uint padding2 = reader.ReadUInt32();
uint padding3 = reader.ReadUInt32();
}
long pos = reader.Position;
reader.Seek(EmitterOffset, SeekOrigin.Begin);
emitter = new Emitter3DS();
emitter.Read(reader, header);
reader.Seek(pos, SeekOrigin.Begin);
}
public void Write(FileWriter writer)
{
}
}
public class Emitter3DS : PTCL.Emitter
{
public long ColorPosition;
public override void OnClick(TreeView treeview)
{
EmitterEditor editor = (EmitterEditor)LibraryGUI.Instance.GetActiveContent(typeof(EmitterEditor));
if (editor == null)
{
editor = new EmitterEditor();
LibraryGUI.Instance.LoadEditor(editor);
}
editor.Text = Text;
editor.Dock = DockStyle.Fill;
editor.LoadEmitter(this);
}
public void Read(FileReader reader, Header header)
{
long pos = reader.Position;
reader.Seek(56);
uint NameOffset = reader.ReadUInt32();
uint padding = reader.ReadUInt32();
if (NameOffset != PTCL.NullOffset)
{
using (reader.TemporarySeek(header.EffectNameTableOffset + NameOffset, SeekOrigin.Begin))
{
Text = reader.ReadString(BinaryStringFormat.ZeroTerminated);
}
}
for (int i = 0; i < 2; i++) //Max of 2 textures. Any more and it'll overlap some data
{
TextureInfo textureInfo = new TextureInfo();
textureInfo.Read(reader, header, Text);
if (!textureInfo.IsEmpty())
{
DrawableTex.Add(textureInfo);
bool HasImage = header.Textures.Any(item => item.DataPos == textureInfo.DataPos);
if (!HasImage)
{
header.Textures.Add(textureInfo);
}
}
}
reader.Seek(pos + 1616, SeekOrigin.Begin);
ColorPosition = reader.Position;
for (int i = 0; i < 8; i++)
{
ColorData clr = new ColorData();
clr.R = reader.ReadSingle();
clr.G = reader.ReadSingle();
clr.B = reader.ReadSingle();
clr.A = reader.ReadSingle();
Color0Array[i] = clr;
int red = Utils.FloatToIntClamp(clr.R);
int green = Utils.FloatToIntClamp(clr.G);
int blue = Utils.FloatToIntClamp(clr.B);
int alpha = Utils.FloatToIntClamp(clr.A);
// Console.WriteLine($"Color0 {i} R {R} G {G} B {B} A {A}");
// Console.WriteLine($"Color0 {i} R {red} G {green} B {blue} A {alpha}");
Color0s[i] = Color.FromArgb(alpha, red, green, blue);
}
for (int i = 0; i < 8; i++)
{
ColorData clr = new ColorData();
clr.R = reader.ReadSingle();
clr.G = reader.ReadSingle();
clr.B = reader.ReadSingle();
clr.A = reader.ReadSingle();
Color1Array[i] = clr;
int red = Utils.FloatToIntClamp(clr.R);
int green = Utils.FloatToIntClamp(clr.G);
int blue = Utils.FloatToIntClamp(clr.B);
int alpha = Utils.FloatToIntClamp(clr.A);
// Console.WriteLine($"Color1 {i} R {R} G {G} B {B} A {A}");
// Console.WriteLine($"Color1 {i} R {red} G {green} B {blue} A {alpha}");
Color1s[i] = Color.FromArgb(alpha, red, green, blue);
}
}
public void Write(FileWriter writer)
{
}
}
public class TextureInfo : STGenericTexture
{
public const uint Alignment = 8192;
public TextureInfo()
{
ImageKey = "Texture";
SelectedImageKey = "Texture";
}
public override void OnClick(TreeView treeView)
{
UpdateEditor();
}
public override string ExportFilter => FileFilters.GTX;
public override string ReplaceFilter => FileFilters.GTX;
public override void Export(string FileName)
{
Export(FileName, false, false, 0, 0);
}
public override void Replace(string FileName)
{
int size = data.Length;
FTEX ftex = new FTEX();
ftex.ReplaceTexture(FileName, MipCount, SupportedFormats, true, true, true, Format);
if (ftex.texture != null)
{
byte[] ImageData = ftex.texture.Data;
if (ftex.texture.MipData != null)
ImageData = Utils.CombineByteArray(ftex.texture.Data, ftex.texture.MipData);
// if (ImageData.Length != size)
// MessageBox.Show($"Image size does not match! Make sure mip map count, format, height and width are all the same! Original Size {size} Import {ImageData.Length}", );
Swizzle = (byte)ftex.texture.Swizzle;
byte[] NewData = new byte[size];
Array.Copy(ImageData, 0, NewData, 0, size);
data = NewData;
UpdateEditor();
}
}
public byte[] AlignData(byte[] data)
{
using (var mem = new MemoryStream(data))
{
using (var writer = new FileWriter(mem))
{
writer.Write(data);
writer.Align((int)Alignment);
return mem.ToArray();
}
}
}
public void UpdateEditor()
{
ImageEditorBase editor = (ImageEditorBase)LibraryGUI.Instance.GetActiveContent(typeof(ImageEditorBase));
if (editor == null)
{
editor = new ImageEditorBase();
editor.Dock = DockStyle.Fill;
LibraryGUI.Instance.LoadEditor(editor);
}
editor.Text = Text;
editor.LoadProperties(GenericProperties);
editor.LoadImage(this);
}
public override TEX_FORMAT[] SupportedFormats
{
get
{
return new TEX_FORMAT[]
{
TEX_FORMAT.BC1_UNORM,
TEX_FORMAT.BC1_UNORM_SRGB,
TEX_FORMAT.BC2_UNORM,
TEX_FORMAT.BC2_UNORM_SRGB,
TEX_FORMAT.BC3_UNORM,
TEX_FORMAT.BC3_UNORM_SRGB,
TEX_FORMAT.BC4_UNORM,
TEX_FORMAT.BC4_SNORM,
TEX_FORMAT.BC5_UNORM,
TEX_FORMAT.BC5_SNORM,
TEX_FORMAT.B5G5R5A1_UNORM,
TEX_FORMAT.B5G6R5_UNORM,
TEX_FORMAT.B8G8R8A8_UNORM_SRGB,
TEX_FORMAT.B8G8R8A8_UNORM,
TEX_FORMAT.R10G10B10A2_UNORM,
TEX_FORMAT.R16_UNORM,
TEX_FORMAT.B4G4R4A4_UNORM,
TEX_FORMAT.B5_G5_R5_A1_UNORM,
TEX_FORMAT.R8G8B8A8_UNORM_SRGB,
TEX_FORMAT.R8G8B8A8_UNORM,
TEX_FORMAT.R8_UNORM,
TEX_FORMAT.R8G8_UNORM,
TEX_FORMAT.R32G8X24_FLOAT,
};
}
}
public override bool CanEdit { get; set; } = false;
public enum SurfaceFormat
{
INVALID = 0x0,
TCS_R8_G8_B8_A8 = 2,
T_BC1_UNORM = 3,
T_BC1_SRGB = 4,
T_BC2_UNORM = 5,
T_BC2_SRGB = 6,
T_BC3_UNORM = 7,
T_BC3_SRGB = 8,
T_BC4_UNORM = 9,
T_BC4_SNORM = 10,
T_BC5_UNORM = 11,
T_BC5_SNORM = 12,
TC_R8_UNORM = 13,
TC_R8_G8_UNORM = 14,
TCS_R8_G8_B8_A8_UNORM = 15,
TC_R8_SNORM = 16,
TC_R4_R4_SNORM = 17,
ETC1_A4 = 18,
ETC1 = 19,
HIL08 = 20,
L4 = 21,
A4 = 22,
L8 = 23,
A8 = 24,
TCS_R5_G6_B5_UNORM = 25,
};
public uint TileMode;
public uint Swizzle;
public byte WrapMode;
public uint CompSel;
public uint ImageSize;
public uint ImageOffset;
public SurfaceFormat SurfFormat;
public byte[] data;
public uint DataPos;
public bool IsEmpty()
{
if (Width == 0 || Height == 0 || SurfFormat == 0)
return true;
else
return false;
}
public void Read(FileReader reader, Header header, string EmitterName)
{
CanReplace = true;
CanRename = false;
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
Swizzle = reader.ReadUInt32();
uint Alignment = reader.ReadUInt32();
uint Pitch = reader.ReadUInt32();
WrapMode = reader.ReadByte(); //11 = repeat, 22 = mirror
byte unk = reader.ReadByte();
byte unk2 = reader.ReadByte();
byte unk3 = reader.ReadByte();
uint mipCount = reader.ReadUInt32();
CompSel = reader.ReadUInt32();
uint[] MipOffsets = reader.ReadUInt32s(13);
uint[] unk4 = reader.ReadUInt32s(4);
uint originalDataFormat = reader.ReadUInt32();
uint originalDataPos = reader.ReadUInt32();
uint originalDataSize = reader.ReadUInt32();
SurfFormat = reader.ReadEnum<SurfaceFormat>(false);
ImageSize = reader.ReadUInt32();
DataPos = reader.ReadUInt32();
uint handle = reader.ReadUInt32();
ArrayCount = 1;
if (Width != 0 && Height != 0 && SurfFormat != 0)
{
using (reader.TemporarySeek(header.TextureBlockTableOffset + DataPos, SeekOrigin.Begin))
{
data = reader.ReadBytes((int)ImageSize);
}
}
if (data != null && data.Length > 0)
{
ConvertFormat();
}
}
public void Write(FileWriter writer, Header header)
{
if (data != null && data.Length > 0)
{
using (writer.TemporarySeek(header.TextureBlockTableOffset + DataPos, SeekOrigin.Begin))
{
writer.Write(data);
}
}
}
public override void SetImageData(Bitmap bitmap, int ArrayLevel)
{
throw new NotImplementedException("Cannot set image data! Operation not implemented!");
}
uint GX2Format = 0;
private void ConvertFormat()
{
switch (SurfFormat)
{
case SurfaceFormat.T_BC1_UNORM:
Format = TEX_FORMAT.BC1_UNORM;
break;
case SurfaceFormat.T_BC1_SRGB:
Format = TEX_FORMAT.BC1_UNORM_SRGB;
break;
case SurfaceFormat.T_BC2_UNORM:
Format = TEX_FORMAT.BC2_UNORM;
break;
case SurfaceFormat.T_BC2_SRGB:
Format = TEX_FORMAT.BC2_UNORM_SRGB;
break;
case SurfaceFormat.T_BC3_UNORM:
Format = TEX_FORMAT.BC3_UNORM;
break;
case SurfaceFormat.T_BC3_SRGB:
Format = TEX_FORMAT.BC3_UNORM_SRGB;
break;
case SurfaceFormat.T_BC4_UNORM:
Format = TEX_FORMAT.BC4_UNORM;
break;
case SurfaceFormat.T_BC4_SNORM:
Format = TEX_FORMAT.BC4_SNORM;
break;
case SurfaceFormat.T_BC5_UNORM:
Format = TEX_FORMAT.BC5_UNORM;
break;
case SurfaceFormat.T_BC5_SNORM:
Format = TEX_FORMAT.BC5_SNORM;
break;
case SurfaceFormat.TC_R8_G8_UNORM:
Format = TEX_FORMAT.R8G8_UNORM;
break;
case SurfaceFormat.TCS_R8_G8_B8_A8_UNORM:
Format = TEX_FORMAT.R8G8B8A8_UNORM;
break;
case SurfaceFormat.TCS_R8_G8_B8_A8:
Format = TEX_FORMAT.R8G8B8A8_UNORM;
break;
case SurfaceFormat.TC_R8_UNORM:
GX2Format = (uint)GX2.GX2SurfaceFormat.TC_R8_UNORM;
Format = TEX_FORMAT.R8_UNORM;
break;
case SurfaceFormat.TCS_R5_G6_B5_UNORM:
Format = TEX_FORMAT.B5G6R5_UNORM;
break;
case SurfaceFormat.ETC1:
Format = TEX_FORMAT.ETC1;
break;
case SurfaceFormat.ETC1_A4:
Format = TEX_FORMAT.ETC1_A4;
break;
case SurfaceFormat.L4:
// Format = TEX_FORMAT.A8_UNORM;
break;
case SurfaceFormat.L8:
// Format = TEX_FORMAT.A8_UNORM;
break;
default:
throw new Exception("Format unsupported! " + SurfFormat);
}
}
public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0)
{
return data;
}
public void Write(FileWriter writer)
{
}
}
}
}

View File

@ -208,6 +208,7 @@
<Compile Include="FileFormats\Bin\KartParts.cs" />
<Compile Include="FileFormats\Collision\KclFile.cs" />
<Compile Include="FileFormats\Effects\EFCF.cs" />
<Compile Include="FileFormats\Effects\PTCL_3DS.cs" />
<Compile Include="FileFormats\Effects\PTCL_WiiU.cs" />
<Compile Include="FileFormats\Font\BFFNT.cs" />
<Compile Include="FileFormats\Audio\Archives\BFGRP.cs" />

View File

@ -1 +1 @@
d219efb401d2997f5445fb7466548421188b3edf
fa60e761d9f1c6e0b70e45d0b2afad8de06fe3e0

View File

@ -0,0 +1,210 @@
using System;
using System.Drawing;
using System.IO;
namespace Switch_Toolbox.Library
{
//From https://github.com/gdkchan/SPICA/blob/42c4181e198b0fd34f0a567345ee7e75b54cb58b/SPICA/PICA/Converters/TextureCompression.cs
public class ETC1
{
private static byte[] XT = { 0, 4, 0, 4 };
private static byte[] YT = { 0, 0, 4, 4 };
private static ulong Swap64(ulong Value)
{
Value = ((Value & 0xffffffff00000000ul) >> 32) | ((Value & 0x00000000fffffffful) << 32);
Value = ((Value & 0xffff0000ffff0000ul) >> 16) | ((Value & 0x0000ffff0000fffful) << 16);
Value = ((Value & 0xff00ff00ff00ff00ul) >> 8) | ((Value & 0x00ff00ff00ff00fful) << 8);
return Value;
}
public static byte[] ETC1Decompress(byte[] Input, int Width, int Height, bool Alpha)
{
byte[] Output = new byte[Width * Height * 4];
using (MemoryStream MS = new MemoryStream(Input))
{
BinaryReader Reader = new BinaryReader(MS);
for (int TY = 0; TY < Height; TY += 8)
{
for (int TX = 0; TX < Width; TX += 8)
{
for (int T = 0; T < 4; T++)
{
ulong AlphaBlock = 0xfffffffffffffffful;
if (Alpha) AlphaBlock = Reader.ReadUInt64();
ulong ColorBlock = Swap64(Reader.ReadUInt64());
byte[] Tile = ETC1Tile(ColorBlock);
int TileOffset = 0;
for (int PY = YT[T]; PY < 4 + YT[T]; PY++)
{
for (int PX = XT[T]; PX < 4 + XT[T]; PX++)
{
int OOffs = ((Height - 1 - (TY + PY)) * Width + TX + PX) * 4;
Buffer.BlockCopy(Tile, TileOffset, Output, OOffs, 3);
int AlphaShift = ((PX & 3) * 4 + (PY & 3)) << 2;
byte A = (byte)((AlphaBlock >> AlphaShift) & 0xf);
Output[OOffs + 3] = (byte)((A << 4) | A);
TileOffset += 4;
}
}
}
}
}
return Output;
}
}
private static byte[] ETC1Tile(ulong Block)
{
uint BlockLow = (uint)(Block >> 32);
uint BlockHigh = (uint)(Block >> 0);
bool Flip = (BlockHigh & 0x1000000) != 0;
bool Diff = (BlockHigh & 0x2000000) != 0;
uint R1, G1, B1;
uint R2, G2, B2;
if (Diff)
{
B1 = (BlockHigh & 0x0000f8) >> 0;
G1 = (BlockHigh & 0x00f800) >> 8;
R1 = (BlockHigh & 0xf80000) >> 16;
B2 = (uint)((sbyte)(B1 >> 3) + ((sbyte)((BlockHigh & 0x000007) << 5) >> 5));
G2 = (uint)((sbyte)(G1 >> 3) + ((sbyte)((BlockHigh & 0x000700) >> 3) >> 5));
R2 = (uint)((sbyte)(R1 >> 3) + ((sbyte)((BlockHigh & 0x070000) >> 11) >> 5));
B1 |= B1 >> 5;
G1 |= G1 >> 5;
R1 |= R1 >> 5;
B2 = (B2 << 3) | (B2 >> 2);
G2 = (G2 << 3) | (G2 >> 2);
R2 = (R2 << 3) | (R2 >> 2);
}
else
{
B1 = (BlockHigh & 0x0000f0) >> 0;
G1 = (BlockHigh & 0x00f000) >> 8;
R1 = (BlockHigh & 0xf00000) >> 16;
B2 = (BlockHigh & 0x00000f) << 4;
G2 = (BlockHigh & 0x000f00) >> 4;
R2 = (BlockHigh & 0x0f0000) >> 12;
B1 |= B1 >> 4;
G1 |= G1 >> 4;
R1 |= R1 >> 4;
B2 |= B2 >> 4;
G2 |= G2 >> 4;
R2 |= R2 >> 4;
}
uint Table1 = (BlockHigh >> 29) & 7;
uint Table2 = (BlockHigh >> 26) & 7;
byte[] Output = new byte[4 * 4 * 4];
if (!Flip)
{
for (int Y = 0; Y < 4; Y++)
{
for (int X = 0; X < 2; X++)
{
Color Color1 = ETC1Pixel(R1, G1, B1, X + 0, Y, BlockLow, Table1);
Color Color2 = ETC1Pixel(R2, G2, B2, X + 2, Y, BlockLow, Table2);
int Offset1 = (Y * 4 + X) * 4;
Output[Offset1 + 0] = Color1.B;
Output[Offset1 + 1] = Color1.G;
Output[Offset1 + 2] = Color1.R;
int Offset2 = (Y * 4 + X + 2) * 4;
Output[Offset2 + 0] = Color2.B;
Output[Offset2 + 1] = Color2.G;
Output[Offset2 + 2] = Color2.R;
}
}
}
else
{
for (int Y = 0; Y < 2; Y++)
{
for (int X = 0; X < 4; X++)
{
Color Color1 = ETC1Pixel(R1, G1, B1, X, Y + 0, BlockLow, Table1);
Color Color2 = ETC1Pixel(R2, G2, B2, X, Y + 2, BlockLow, Table2);
int Offset1 = (Y * 4 + X) * 4;
Output[Offset1 + 0] = Color1.B;
Output[Offset1 + 1] = Color1.G;
Output[Offset1 + 2] = Color1.R;
int Offset2 = ((Y + 2) * 4 + X) * 4;
Output[Offset2 + 0] = Color2.B;
Output[Offset2 + 1] = Color2.G;
Output[Offset2 + 2] = Color2.R;
}
}
}
return Output;
}
private static int[,] ETC1LUT =
{
{ 2, 8, -2, -8 },
{ 5, 17, -5, -17 },
{ 9, 29, -9, -29 },
{ 13, 42, -13, -42 },
{ 18, 60, -18, -60 },
{ 24, 80, -24, -80 },
{ 33, 106, -33, -106 },
{ 47, 183, -47, -183 }
};
private static Color ETC1Pixel(uint R, uint G, uint B, int X, int Y, uint Block, uint Table)
{
int Index = X * 4 + Y;
uint MSB = Block << 1;
int Pixel = Index < 8
? ETC1LUT[Table, ((Block >> (Index + 24)) & 1) + ((MSB >> (Index + 8)) & 2)]
: ETC1LUT[Table, ((Block >> (Index + 8)) & 1) + ((MSB >> (Index - 8)) & 2)];
R = Saturate((int)(R + Pixel));
G = Saturate((int)(G + Pixel));
B = Saturate((int)(B + Pixel));
return Color.FromArgb((int)R, (int)G, (int)B);
}
private static byte Saturate(int Value)
{
if (Value > byte.MaxValue) return byte.MaxValue;
if (Value < byte.MinValue) return byte.MinValue;
return (byte)Value;
}
}
}

View File

@ -314,13 +314,20 @@ namespace Switch_Toolbox.Library
if (data == null)
throw new Exception("Data is null!");
if (Format == TEX_FORMAT.BC5_SNORM)
return DDSCompressor.DecompressBC5(data, (int)width, (int)height, true);
Bitmap bitmap = BitmapExtension.GetBitmap(DecodeBlock(data, width, height, Format),
(int)width, (int)height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
return bitmap;
switch (Format)
{
case TEX_FORMAT.BC5_SNORM:
return DDSCompressor.DecompressBC5(data, (int)width, (int)height, true);
case TEX_FORMAT.ETC1:
return BitmapExtension.GetBitmap(ETC1.ETC1Decompress(data, (int)width, (int)height, false),
(int)width, (int)height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
case TEX_FORMAT.ETC1_A4:
return BitmapExtension.GetBitmap(ETC1.ETC1Decompress(data, (int)width, (int)height, true),
(int)width, (int)height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
default:
return BitmapExtension.GetBitmap(DecodeBlock(data, width, height, Format),
(int)width, (int)height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}
}
catch (Exception ex)
{

View File

@ -159,5 +159,8 @@ namespace Switch_Toolbox.Library
ASTC_12x12_UNORM = 186,
ASTC_12x12_SRGB = 187,
B5_G5_R5_A1_UNORM = 229,
ETC1 = 230,
ETC1_A4 = 231,
}
}

View File

@ -208,6 +208,7 @@
<Compile Include="FileFormats\DAE\ColladaHelper.cs" />
<Compile Include="FileFormats\DAE\collada_schema_1_4.cs" />
<Compile Include="FileFormats\DAE\DAE.cs" />
<Compile Include="FileFormats\ETC1.cs" />
<Compile Include="FileFormats\OBJ.cs" />
<Compile Include="Forms\BatchFormatExport.cs">
<SubType>Form</SubType>

View File

@ -646,7 +646,7 @@ namespace Switch_Toolbox.Library
public static List<List<byte[]>> Decode(GX2Surface tex, string DebugTextureName = "")
{
if (tex.data == null || tex.data.Length <= 0)
throw new Exception("Invalid GX2 surface data. Make sure to not open Tex2 files if this is one. Those will load automatically next to Tex1!");
throw new Exception("Invalid GX2 surface data. Make sure to not open Tex2 files if this is one. Those will load automatically next to Tex1!");
Console.WriteLine("DECODING TEX " + DebugTextureName);