diff --git a/.vs/Switch_Toolbox/v15/.suo b/.vs/Switch_Toolbox/v15/.suo index 1c2a89ae..638fb3ed 100644 Binary files a/.vs/Switch_Toolbox/v15/.suo and b/.vs/Switch_Toolbox/v15/.suo differ diff --git a/.vs/Switch_Toolbox/v15/Browse.VC.db b/.vs/Switch_Toolbox/v15/Browse.VC.db index a03afe28..ebbc659c 100644 Binary files a/.vs/Switch_Toolbox/v15/Browse.VC.db and b/.vs/Switch_Toolbox/v15/Browse.VC.db differ diff --git a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide index 0c838a08..93c804e4 100644 Binary files a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide and b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide differ diff --git a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-shm b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-shm index 79412c5c..780cc49c 100644 Binary files a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-shm and b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-shm differ diff --git a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-wal b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-wal index cc6a0503..a7e41c31 100644 Binary files a/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-wal and b/.vs/Switch_Toolbox/v15/Server/sqlite3/storage.ide-wal differ diff --git a/.vs/Switch_Toolbox/v15/ipch/AutoPCH/81b12396c2c26f85/DECOMPRESS2.ipch b/.vs/Switch_Toolbox/v15/ipch/AutoPCH/81b12396c2c26f85/DECOMPRESS2.ipch new file mode 100644 index 00000000..203aac1e Binary files /dev/null and b/.vs/Switch_Toolbox/v15/ipch/AutoPCH/81b12396c2c26f85/DECOMPRESS2.ipch differ diff --git a/Switch_FileFormatsMain/FileFormats/Archives/GFA.cs b/Switch_FileFormatsMain/FileFormats/Archives/GFA.cs new file mode 100644 index 00000000..6f63ab91 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/Archives/GFA.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Switch_Toolbox; +using System.Windows.Forms; +using Switch_Toolbox.Library; +using Switch_Toolbox.Library.IO; + +namespace FirstPlugin +{ + public class GFA : IArchiveFile, IFileFormat + { + public FileType FileType { get; set; } = FileType.Archive; + + public bool CanSave { get; set; } + public string[] Description { get; set; } = new string[] { "Good Feel Archive" }; + public string[] Extension { get; set; } = new string[] { "*.gfa" }; + public string FileName { get; set; } + public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + + public bool CanAddFiles { get; set; } + public bool CanRenameFiles { get; set; } + public bool CanReplaceFiles { get; set; } = true; + public bool CanDeleteFiles { get; set; } + + public bool Identify(System.IO.Stream stream) + { + using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true)) + { + return reader.CheckSignature(4, "GFAC"); + } + } + + public Type[] Types + { + get + { + List types = new List(); + return types.ToArray(); + } + } + + public List files = new List(); + + public IEnumerable Files => files; + + private uint Unknown1; + private uint Version; + public void Load(System.IO.Stream stream) + { + using (var reader = new FileReader(stream)) + { + reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; + reader.ReadSignature(4, "GFAC"); + Unknown1 = reader.ReadUInt32(); + Version = reader.ReadUInt32(); + uint FileInfoOffset = reader.ReadUInt32(); + uint FileInfoSize = reader.ReadUInt32(); + uint DataOffset = reader.ReadUInt32(); + uint DataSize = reader.ReadUInt32(); + byte[] Padding = reader.ReadBytes(0x10); //Not sure + + reader.SeekBegin(FileInfoOffset); + uint FileCount = reader.ReadUInt32(); + for (int i = 0; i < FileCount; i++) + { + var file = new FileEntry(); + file.Read(reader); + files.Add(file); + } + + reader.SeekBegin(DataOffset); + reader.ReadSignature(4, "GFCP"); + uint VersionGFCP = reader.ReadUInt32(); + uint CompressionType = reader.ReadUInt32(); + uint DecompressedSize = reader.ReadUInt32(); + uint CompressedSize = reader.ReadUInt32(); + } + } + + public void Unload() + { + + } + + public byte[] Save() + { + var mem = new System.IO.MemoryStream(); + using (var writer = new FileWriter(mem)) + { + writer.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; + + writer.WriteSignature("GFAC"); + writer.Write(Unknown1); + writer.Write(Version); + writer.Write(uint.MaxValue); //Info offset for later + writer.Write(uint.MaxValue); //Info size for later + writer.Write(uint.MaxValue); //Data offset for later + writer.Write(uint.MaxValue); //Data size for later + writer.Write(new byte[0x10]); //Padding + + writer.WriteUint32Offset(12); //Save info offset + writer.Write(files.Count); + + /* //Save info + for (int i = 0; i < files.Count; i++) + files[i].Write(writer); + + //Save strings and offsets + for (int i = 0; i < files.Count; i++) + files[i].Write(writer); + + writer.Write(new uint[files.Count]); //Save space for offsets + for (int i = 0; i < files.Count; i++) + writer.Write(files[i].FileData.Length); + */ + } + + return mem.ToArray(); + } + + private void Align(FileWriter writer, int alignment) + { + var startPos = writer.Position; + long position = writer.Seek((-writer.Position % alignment + alignment) % alignment, System.IO.SeekOrigin.Current); + + writer.Seek(startPos, System.IO.SeekOrigin.Begin); + while (writer.Position != position) + { + writer.Write((byte)0x30); + } + } + + public bool AddFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + + public bool DeleteFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + + public class FileEntry : ArchiveFileInfo + { + public uint Hash { get; set; } + + public void Read(FileReader reader) + { + Hash = reader.ReadUInt32(); + FileName = GetName(reader); + uint Size = reader.ReadUInt32(); + uint Offset = reader.ReadUInt32(); + } + } + + private static string GetName(FileReader reader) + { + uint Offset = reader.ReadUInt32(); + using (reader.TemporarySeek(Offset & 0x00ffffff, System.IO.SeekOrigin.Begin)) + { + return reader.ReadZeroTerminatedString(); + } + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/Archives/LM2/LM2_DICT.cs b/Switch_FileFormatsMain/FileFormats/Archives/LM2/LM2_DICT.cs new file mode 100644 index 00000000..819930c0 --- /dev/null +++ b/Switch_FileFormatsMain/FileFormats/Archives/LM2/LM2_DICT.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Switch_Toolbox; +using System.Windows.Forms; +using Switch_Toolbox.Library; +using Switch_Toolbox.Library.IO; +using Switch_Toolbox.Library.Forms; +using System.Drawing; + +namespace FirstPlugin +{ + //Parse info based on https://github.com/TheFearsomeDzeraora/LM2L + public class LM2_DICT : TreeNodeFile, IFileFormat + { + public FileType FileType { get; set; } = FileType.Archive; + + public bool CanSave { get; set; } + public string[] Description { get; set; } = new string[] { "Luigi's Mansion 2 Dark Moon Archive Dictionary" }; + public string[] Extension { get; set; } = new string[] { "*.dict" }; + public string FileName { get; set; } + public string FilePath { get; set; } + public IFileInfo IFileInfo { get; set; } + + public bool CanAddFiles { get; set; } + public bool CanRenameFiles { get; set; } + public bool CanReplaceFiles { get; set; } + public bool CanDeleteFiles { get; set; } + + public bool Identify(System.IO.Stream stream) + { + using (var reader = new Switch_Toolbox.Library.IO.FileReader(stream, true)) + { + reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; + return reader.ReadUInt32() == 0x5824F3A9; + } + } + + public Type[] Types + { + get + { + List types = new List(); + return types.ToArray(); + } + } + + public List files = new List(); + + public bool IsCompressed = false; + + public void Load(System.IO.Stream stream) + { + using (var reader = new FileReader(stream)) + { + reader.ByteOrder = Syroot.BinaryData.ByteOrder.LittleEndian; + uint Identifier = reader.ReadUInt32(); + ushort Unknown = reader.ReadUInt16(); //Could also be 2 bytes, not sure. Always 0x0401 + IsCompressed = reader.ReadByte() == 1; + reader.ReadByte(); //Padding + uint FileCount = reader.ReadUInt32(); + uint LargestCompressedFile = reader.ReadUInt32(); + + reader.SeekBegin(0x2C); + byte[] Unknowns = reader.ReadBytes((int)FileCount); + for (int i = 0; i < FileCount; i++) + { + var file = new FileEntry(this); + file.Read(reader); + if (file.FileType != 0) + { + file.Text = $"File {i} (Unknowns {file.FileType} {file.Unknown2} {file.Unknown3})"; + files.Add(file); + } + } + + //Now go through each file and format and connect the headers and blocks + uint ImageIndex = 0; + + List Textures = new List(); + + TreeNode textureFolder = new TreeNode("Textures"); + Nodes.Add(textureFolder); + + for (int i = 0; i < files.Count; i++) + { + if (files[i].FileType == FileEntry.FileDataType.Texture) + { + if (files[i].Unknown3 == 1) //Info + { + //Read the info + using (var textureReader = new FileReader(files[i].FileData)) + { + while (textureReader.ReadUInt32() == TexturePOWE.Identifier) + { + var texture = new TexturePOWE(); + texture.Index = ImageIndex; + texture.Read(textureReader); + texture.Text = $"Texture {ImageIndex}"; + textureFolder.Nodes.Add(texture); + Textures.Add(texture); + } + } + } + else //Block + { + foreach (var tex in Textures) + { + if (tex.Index == ImageIndex) + tex.ImageData = files[i].FileData; + } + ImageIndex++; + } + } + else + Nodes.Add(files[i]); + } + } + } + + public void Unload() + { + + } + + public byte[] Save() + { + return null; + } + + public bool AddFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + + public bool DeleteFile(ArchiveFileInfo archiveFileInfo) + { + return false; + } + + public class TexturePOWE : STGenericTexture + { + public static readonly uint Identifier = 0xE977D350; + + public uint Index { get; set; } + + public uint ID { get; set; } + public uint ImageSize { get; set; } + public uint ID2 { get; set; } + + public byte[] ImageData { get; set; } + + public void Read(FileReader reader) + { + PlatformSwizzle = PlatformSwizzle.Platform_3DS; + + ID = reader.ReadUInt32(); + ImageSize = reader.ReadUInt32(); + ID2 = reader.ReadUInt32(); + reader.Seek(0x8); + Width = reader.ReadUInt16(); + Height = reader.ReadUInt16(); + reader.Seek(3); + var numMips = reader.ReadByte(); + reader.Seek(0x14); + byte FormatCtr = reader.ReadByte(); + reader.Seek(3); + + MipCount = 1; + Format = CTR_3DS.ConvertPICAToGenericFormat((CTR_3DS.PICASurfaceFormat)FormatCtr); + } + + public override void OnClick(TreeView treeview) + { + 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(this.GenericProperties); + editor.LoadImage(this); + } + + public override bool CanEdit { get; set; } = false; + + public override void SetImageData(Bitmap bitmap, int ArrayLevel) + { + throw new NotImplementedException(); + } + + public override byte[] GetImageData(int ArrayLevel = 0, int MipLevel = 0) + { + return ImageData; + } + + public override TEX_FORMAT[] SupportedFormats + { + get + { + return new TEX_FORMAT[] + { + TEX_FORMAT.B5G6R5_UNORM, + TEX_FORMAT.R8G8_UNORM, + TEX_FORMAT.B5G5R5A1_UNORM, + TEX_FORMAT.B4G4R4A4_UNORM, + TEX_FORMAT.LA8, + TEX_FORMAT.HIL08, + TEX_FORMAT.L8, + TEX_FORMAT.A8_UNORM, + TEX_FORMAT.LA4, + TEX_FORMAT.A4, + TEX_FORMAT.ETC1_UNORM, + TEX_FORMAT.ETC1_A4, + }; + } + } + } + + public class FileEntry : TreeNodeCustom + { + public LM2_DICT ParentDictionary { get; set; } + + public uint Offset; + public uint DecompressedSize; + public uint CompressedSize; + public FileDataType FileType; + public byte Unknown2; + public byte Unknown3; //Possibly the effect? 0 for image block, 1 for info + + public enum FileDataType : ushort + { + Texture = 0x80, + } + + public byte[] FileData + { + get { return GetData(); } + set + { + + } + } + + public override void OnClick(TreeView treeview) + { + HexEditor editor = (HexEditor)LibraryGUI.Instance.GetActiveContent(typeof(HexEditor)); + if (editor == null) + { + editor = new HexEditor(); + LibraryGUI.Instance.LoadEditor(editor); + } + editor.Dock = DockStyle.Fill; + editor.LoadData(FileData); + } + + public FileEntry(LM2_DICT dict) + { + ParentDictionary = dict; + } + + public void Read(FileReader reader) + { + Offset = reader.ReadUInt32(); + DecompressedSize = reader.ReadUInt32(); + CompressedSize = reader.ReadUInt32(); + FileType = reader.ReadEnum(false); + Unknown2 = reader.ReadByte(); + Unknown3 = reader.ReadByte(); + } + + private bool IsTextureBinary() + { + byte[] Data = GetData(); + + if (Data.Length < 4) + return false; + + using (var reader = new FileReader(Data)) + { + return reader.ReadUInt32() == 0xE977D350; + } + } + + private byte[] GetData() + { + byte[] Data = new byte[DecompressedSize]; + + string FolderPath = System.IO.Path.GetDirectoryName(ParentDictionary.FilePath); + string DataFile = System.IO.Path.Combine(FolderPath, $"{ParentDictionary.FileName.Replace(".dict", ".data")}"); + + if (System.IO.File.Exists(DataFile)) + { + using (var reader = new FileReader(DataFile)) + { + reader.SeekBegin(Offset); + if (ParentDictionary.IsCompressed) + { + ushort Magic = reader.ReadUInt16(); + reader.SeekBegin(Offset); + + Data = reader.ReadBytes((int)CompressedSize); + if (Magic == 0x9C78 || Magic == 0xDA78) + return STLibraryCompression.ZLIB.Decompress(Data); + else //Unknown compression + return Data; + } + else + { + return reader.ReadBytes((int)DecompressedSize); + } + } + } + + return Data; + } + } + } +} diff --git a/Switch_FileFormatsMain/FileFormats/Archives/LZARC.cs b/Switch_FileFormatsMain/FileFormats/Archives/LZARC.cs index 425b98c4..6e7baa50 100644 --- a/Switch_FileFormatsMain/FileFormats/Archives/LZARC.cs +++ b/Switch_FileFormatsMain/FileFormats/Archives/LZARC.cs @@ -94,8 +94,10 @@ namespace FirstPlugin public void Read(FileReader reader) { - char[] name = reader.ReadChars(128); - FileName = new string(name); + long pos = reader.Position; + FileName = reader.ReadZeroTerminatedString(); + reader.SeekBegin(pos + 128); + uint Offset = reader.ReadUInt32(); CompressedSize = reader.ReadUInt32(); uint Unknown = reader.ReadUInt32(); @@ -104,8 +106,9 @@ namespace FirstPlugin using (reader.TemporarySeek((int)Offset, System.IO.SeekOrigin.Begin)) { - FileData = reader.ReadBytes((int)CompressedSize); - // STLibraryCompression.LZSS.Decompress(FileData, DecompressedSize); + reader.ReadBytes(8); + FileData = reader.ReadBytes((int)CompressedSize - 8); + FileData = STLibraryCompression.LZSS.Decompress(FileData, DecompressedSize); } } } diff --git a/Switch_FileFormatsMain/FileFormats/Archives/NARC.cs b/Switch_FileFormatsMain/FileFormats/Archives/NARC.cs index ab4e90a8..aadecde4 100644 --- a/Switch_FileFormatsMain/FileFormats/Archives/NARC.cs +++ b/Switch_FileFormatsMain/FileFormats/Archives/NARC.cs @@ -109,6 +109,7 @@ namespace FirstPlugin public byte[] DecompressBlock() { byte[] data = GetBlock(); + Console.WriteLine("DATA " + data.Length); var reader = new FileReader(data); reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian; @@ -121,7 +122,8 @@ namespace FirstPlugin uint decompSize = reader.ReadUInt32(); uint compSize = (uint)reader.BaseStream.Length - 8; - return STLibraryCompression.MTA_CUSTOM.Decompress(data, decompSize); + var comp = new STLibraryCompression.MTA_CUSTOM(); + return comp.Decompress(data, decompSize); } else if (compType == 0x30) { diff --git a/Switch_FileFormatsMain/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs b/Switch_FileFormatsMain/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs index 7bce968f..0766ddec 100644 --- a/Switch_FileFormatsMain/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs +++ b/Switch_FileFormatsMain/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs @@ -1328,6 +1328,12 @@ namespace Bfres.Structs output.Z = input.X * matrix.M13 + input.Y * matrix.M23 + input.Z * matrix.M33 + matrix.M43; return output; } + + public override void SaveVertexBuffer() + { + SaveVertexBuffer(GetResFileU() != null); + } + public void SaveVertexBuffer(bool IsWiiU) { if (IsWiiU) diff --git a/Switch_FileFormatsMain/Main.cs b/Switch_FileFormatsMain/Main.cs index 73b3c628..d6858aa8 100644 --- a/Switch_FileFormatsMain/Main.cs +++ b/Switch_FileFormatsMain/Main.cs @@ -335,8 +335,8 @@ namespace FirstPlugin Formats.Add(typeof(NCA)); Formats.Add(typeof(RARC)); Formats.Add(typeof(ME01)); - - + // Formats.Add(typeof(LM2_DICT)); + //Formats.Add(typeof(GFA)); //Unfinished wip formats not ready for use if (Runtime.DEVELOPER_DEBUG_MODE) diff --git a/Switch_FileFormatsMain/Switch_FileFormatsMain.csproj b/Switch_FileFormatsMain/Switch_FileFormatsMain.csproj index 0aabdbf4..16d6572a 100644 --- a/Switch_FileFormatsMain/Switch_FileFormatsMain.csproj +++ b/Switch_FileFormatsMain/Switch_FileFormatsMain.csproj @@ -198,7 +198,9 @@ + + @@ -280,7 +282,7 @@ - Form + UserControl AampEditor.cs @@ -469,7 +471,7 @@ - Form + UserControl EffectTableEditor.cs @@ -481,7 +483,7 @@ CollisionMaterialEditor.cs - Form + UserControl MSBTEditor.cs @@ -498,10 +500,10 @@ - Form + UserControl - Form + UserControl Form @@ -776,7 +778,7 @@ AttributeEditor.cs - Form + UserControl MK8MapCameraEditor.cs @@ -857,7 +859,7 @@ Form - Form + UserControl ByamlEditor.cs diff --git a/Switch_FileFormatsMain/obj/Release/DesignTimeResolveAssemblyReferences.cache b/Switch_FileFormatsMain/obj/Release/DesignTimeResolveAssemblyReferences.cache index 34a0ac15..13da6c1b 100644 Binary files a/Switch_FileFormatsMain/obj/Release/DesignTimeResolveAssemblyReferences.cache and b/Switch_FileFormatsMain/obj/Release/DesignTimeResolveAssemblyReferences.cache differ diff --git a/Switch_FileFormatsMain/obj/Release/Switch_FileFormatsMain.csprojAssemblyReference.cache b/Switch_FileFormatsMain/obj/Release/Switch_FileFormatsMain.csprojAssemblyReference.cache index 34b1f811..2244b89a 100644 Binary files a/Switch_FileFormatsMain/obj/Release/Switch_FileFormatsMain.csprojAssemblyReference.cache and b/Switch_FileFormatsMain/obj/Release/Switch_FileFormatsMain.csprojAssemblyReference.cache differ diff --git a/Switch_Toolbox_Library/Compression/LZ77_WII.cs b/Switch_Toolbox_Library/Compression/LZ77_WII.cs new file mode 100644 index 00000000..84e37f62 --- /dev/null +++ b/Switch_Toolbox_Library/Compression/LZ77_WII.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Runtime.InteropServices; +using Switch_Toolbox.Library.IO; + +namespace Switch_Toolbox.Library +{ + public class LZ77_WII + { + public static byte[] Decompress(byte[] input) + { + if (input == null) throw new ArgumentNullException(nameof(input)); + return input; + } + } +} diff --git a/Switch_Toolbox_Library/Compression/STLibraryCompression.cs b/Switch_Toolbox_Library/Compression/STLibraryCompression.cs index cb3b04bb..1518809c 100644 --- a/Switch_Toolbox_Library/Compression/STLibraryCompression.cs +++ b/Switch_Toolbox_Library/Compression/STLibraryCompression.cs @@ -65,7 +65,7 @@ namespace Switch_Toolbox.Library.IO { if (br.ReadString(4) == "ZCMP") { - return DecompressZCMP(b); + return DecompressZCMP(b); } else { @@ -119,16 +119,33 @@ namespace Switch_Toolbox.Library.IO } } + public class BPE + { + public static unsafe byte[] Decompress(byte[] input, uint decompressedLength) + { + fixed (byte* outputPtr = new byte[decompressedLength]) + { + fixed (byte* inputPtr = input) + { + Decompress(outputPtr, inputPtr, decompressedLength); + } + + byte[] decomp = new byte[decompressedLength]; + Marshal.Copy((IntPtr)outputPtr, decomp, 0, decomp.Length); + return decomp; + } + } + + public static unsafe void Decompress(byte* output, byte* input, uint decompressedLength) + { + + } + } + //Mario Tennis Aces Custom compression public class MTA_CUSTOM { - private static uint Swap(uint X) - { - return ((X >> 24) & 0xff | (X >> 8) & 0xff00 | - (X << 8) & 0xff0000 | (X << 24) & 0xff000000); - } - - public static unsafe byte[] Decompress(byte[] input, uint decompressedLength) + public unsafe byte[] Decompress(byte[] input, uint decompressedLength) { fixed (byte* outputPtr = new byte[decompressedLength]) { @@ -145,7 +162,7 @@ namespace Switch_Toolbox.Library.IO //Thanks Simon. Code ported from //https://github.com/simontime/MarioTennisAces0x50Decompressor/blob/master/decompress.c - public static unsafe void Decompress(byte* output, byte* input, uint decompressedLength) + public unsafe void Decompress(byte* output, byte* input, uint decompressedLength) { uint pos = 8; byte* end = input + decompressedLength; @@ -158,8 +175,6 @@ namespace Switch_Toolbox.Library.IO { uint flag; - Console.WriteLine($"Pass 1 "); - while (true) { flag = 0xFF000000 * data[0]; @@ -171,28 +186,36 @@ namespace Switch_Toolbox.Library.IO for (int i = 0; i < 8; i++) *output++ = *data++; - CheckFinished(data, end); + var checkFinished = CheckFinished(data, end); + data = checkFinished.data; + end = checkFinished.end; } - Console.WriteLine($"Pass 2 "); - flag |= 0x800000; data++; + Console.WriteLine($"flag " + flag); + //IterateFlag while ((flag & 0x80000000) == 0) { - IterateFlag(flag, data, output); + flag <<= 1; + *output++ = *data++; } + Console.WriteLine($"Pass 3 "); while (true) { flag <<= 1; if (flag == 0) - CheckFinished(data, end); + { + var checkFinished2 = CheckFinished(data, end); + data = checkFinished2.data; + end = checkFinished2.end; + } int op_ofs = (data[0] >> 4) | (data[1] << 4); int op_len = data[0] & 0xF; @@ -200,7 +223,7 @@ namespace Switch_Toolbox.Library.IO if (op_ofs == 0) return; - byte* chunk = output -op_ofs; + byte* chunk = output - op_ofs; if (op_len > 1) data += 2; else @@ -215,7 +238,14 @@ namespace Switch_Toolbox.Library.IO op_len = op_len_ext + add_len; if (op_ofs >= 2) - Loop1(flag, op_len, chunk, data, output); + { + var loop1Data = Loop1(flag, op_len, chunk, data, output); + flag = loop1Data.flag; + op_len = loop1Data.op_len; + chunk = loop1Data.chunk; + data = loop1Data.data; + output = loop1Data.output; + } } else { @@ -223,165 +253,232 @@ namespace Switch_Toolbox.Library.IO op_len = op_len_ext; if (op_ofs >= 2) { - Loop1(flag, op_len, chunk, data, output); - - Loop2(flag, op_len, data, output, chunk); + var loop1Data = Loop1(flag, op_len, chunk, data, output); + flag = loop1Data.flag; + op_len = loop1Data.op_len; + chunk = loop1Data.chunk; + data = loop1Data.data; + output = loop1Data.output; } } } + + var loop2Data2 = Loop2(flag, op_len, data, output, chunk); + flag = loop2Data2.flag; + op_len = loop2Data2.op_len; + chunk = loop2Data2.chunk; + data = loop2Data2.data; + output = loop2Data2.output; } } - EndOperation(data, end); + var endOp = EndOperation(data, end); + data = endOp.data; + end = endOp.end; } - } - private static unsafe void Loop1(uint flag, int op_len, byte* chunk, byte* data, byte* output) - { - if (((*chunk ^ *output) & 1) == 0) - { - if ((*chunk & 1) != 0) + private unsafe class Data + { + public uint flag; + public int op_len; + public byte* chunk; + public byte* data; + public byte* output; + public byte* end; + } + + unsafe Data Loop1(uint flag, int op_len, byte* chunk, byte* data, byte* output) + { + if ((((byte)*chunk ^ (byte)*output) & 1) == 0) { - *output++ = *chunk++; - op_len--; - } - - int op_len_sub = op_len - 2; - - if (op_len >= 2) - { - int masked_len = ((op_len_sub >> 1) + 1) & 7; - - byte* out_ptr = output; - byte* chunk_ptr = chunk; - - if (masked_len != 0) + if (((byte)*chunk & 1) != 0) { - while (masked_len-- != 0) - { - *out_ptr++ = *chunk_ptr++; - *out_ptr++ = *chunk_ptr++; - op_len -= 2; - } + *output++ = *chunk++; + op_len--; } - uint masked_ext_len = (uint)op_len_sub & 0xFFFFFFFE; + uint op_len_sub = (uint)op_len - 2; - if (op_len_sub >= 0xE) + if (op_len >= 2) { - do + int masked_len = (((int)op_len_sub >> 1) + 1) & 7; + + byte* out_ptr = output; + byte* chunk_ptr = chunk; + + if (masked_len != 0) { - for (int i = 0; i < 0x10; i++) + while (masked_len-- != 0) + { *out_ptr++ = *chunk_ptr++; - - op_len -= 0x10; + *out_ptr++ = *chunk_ptr++; + op_len -= 2; + } } - while (op_len > 1); + + uint masked_ext_len = op_len_sub & 0xFFFFFFFE; + + if (op_len_sub >= 0xE) + { + do + { + for (int i = 0; i < 0x10; i++) + *out_ptr++ = *chunk_ptr++; + + op_len -= 0x10; + } + while (op_len > 1); + } + + output += masked_ext_len + 2; + op_len = (int)op_len_sub - (int)masked_ext_len; + chunk += masked_ext_len + 2; } - output += masked_ext_len + 2; - op_len = op_len_sub - (int)masked_ext_len; - chunk += masked_ext_len + 2; + if (op_len == 0) + { + if ((flag & 0x80000000) == 0) + { + flag <<= 1; + *output++ = *data++; + } + } } - if (op_len == 0) - CheckFlag(flag, data, output); + return Loop2(flag, op_len, data, output, chunk); } - Loop2(flag, op_len, data, output, chunk); - } - - private static unsafe void Loop2(uint flag, int op_len, byte* data, byte* output, byte* chunk) - { - int masked_len = op_len & 7; - byte* out_ptr = output; - byte* chunk_ptr = chunk; - - if (masked_len != 0) + unsafe Data Loop2(uint flag, int op_len, byte* data, byte* output, byte* chunk) { - while (masked_len-- != 0) - *out_ptr++ = *chunk_ptr++; - } + int masked_len = op_len & 7; + byte* out_ptr = output; + byte* chunk_ptr = chunk; - if (op_len - 1 >= 7) - { - do + if (masked_len != 0) { - for (int i = 0; i < 8; i++) + while (masked_len-- != 0) *out_ptr++ = *chunk_ptr++; } - while (chunk_ptr != chunk + op_len); + + if (op_len - 1 >= 7) + { + do + { + for (int i = 0; i < 8; i++) + *out_ptr++ = *chunk_ptr++; + } + while (chunk_ptr != chunk + op_len); + } + + output += op_len; + + if ((flag & 0x80000000) == 0) + { + flag <<= 1; + *output++ = *data++; + } + + return new Data() + { + flag = flag, + op_len = op_len, + data = data, + output = output, + chunk = chunk, + }; } - output += op_len; + unsafe Data CheckFinished(byte* data, byte* end) + { + if (data >= end) + return EndOperation(data, end); - CheckFlag(flag, data, output); - } + return new Data() + { + data = data, + end = end, + }; + } - private static unsafe void CheckFlag(uint flag, byte* data, byte* output) - { - if ((flag & 0x80000000) == 0) - IterateFlag(flag, data, output); - } + unsafe Data EndOperation(byte* data, byte* end) + { + byte* ext = end + 0x20; + if (data < ext) + do + *end-- = *--ext; + while (data < ext); - private static unsafe void IterateFlag(uint flag, byte* data, byte* output) - { - flag <<= 1; - *output++ = *data++; - } - - private static unsafe void CheckFinished(byte* data, byte* end) - { - if (data >= end) - EndOperation(data, end); - } - - private static unsafe void EndOperation(byte* data, byte* end) - { - byte* ext = end + 0x20; - if (data < ext) - do - *end-- = *--ext; - while (data < ext); + return new Data() + { + data = data, + end = end, + }; + } } public class LZSS { - //From https://github.com/IcySon55/Kuriimu/blob/f670c2719affc1eaef8b4c40e40985881247acc7/src/Kontract/Compression/LZSS.cs - //Todo does not work with Paper Mario Color Splash - public static byte[] Decompress(byte[] data, uint decompressedLength) + static class LzssParameters { - using (FileReader reader = new FileReader(new MemoryStream(data), true)) + /// Size of the ring buffer. + public const int N = 4096; + /// Maximum match length for position coding. (0x0F + THRESHOLD). + public const int F = 18; + /// Minimum match length for position coding. + public const int THRESHOLD = 3; + /// Index for root of binary search trees. + public const int NIL = N; + /// Character used to fill the ring buffer initially. + //private const ubyte BUFF_INIT = ' '; + public const byte BUFF_INIT = 0; // Changed for F-Zero GX + } + + public static byte[] Decompress(byte[] input, uint decompressedLength) + { + List output = new List(); + byte[] ringBuf = new byte[LzssParameters.N]; + int inputPos = 0, ringBufPos = LzssParameters.N - LzssParameters.F; + + ushort flags = 0; + + // Clear ringBuf with a character that will appear often + for (int i = 0; i < LzssParameters.N - LzssParameters.F; i++) + ringBuf[i] = LzssParameters.BUFF_INIT; + + while (inputPos < input.Length) { - List result = new List(); + // Use 16 bits cleverly to count to 8. + // (After 8 shifts, the high bits will be cleared). + if ((flags & 0xFF00) == 0) + flags = (ushort)(input[inputPos++] | 0x8000); - for (int i = 0, flags = 0; ; i++) + if ((flags & 1) == 1) { - if (i % 8 == 0) flags = reader.ReadByte(); - if ((flags & 0x80) == 0) result.Add(reader.ReadByte()); - else - { - int lengthDist = BitConverter.ToUInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); - int offs = lengthDist % 4096 + 1; - int length = lengthDist / 4096 + 3; - while (length > 0) - { - result.Add(result[result.Count - offs]); - length--; - } - } - - if (result.Count == decompressedLength) - { - return result.ToArray(); - } - else if (result.Count > decompressedLength) - { - throw new InvalidDataException("Went past the end of the stream"); - } - flags <<= 1; + // Copy data literally from input + byte c = input[inputPos++]; + output.Add(c); + ringBuf[ringBufPos++ % LzssParameters.N] = c; } + else + { + // Copy data from the ring buffer (previous data). + int index = ((input[inputPos + 1] & 0xF0) << 4) | input[inputPos]; + int count = (input[inputPos + 1] & 0x0F) + LzssParameters.THRESHOLD; + inputPos += 2; + + for (int i = 0; i < count; i++) + { + byte c = ringBuf[(index + i) % LzssParameters.N]; + output.Add(c); + ringBuf[ringBufPos++ % LzssParameters.N] = c; + } + } + + // Advance flags & count bits + flags >>= 1; } + + return output.ToArray(); } } diff --git a/Switch_Toolbox_Library/Forms/Editors/UV/UVEditor.cs b/Switch_Toolbox_Library/Forms/Editors/UV/UVEditor.cs index 5bcc5617..da7e9517 100644 --- a/Switch_Toolbox_Library/Forms/Editors/UV/UVEditor.cs +++ b/Switch_Toolbox_Library/Forms/Editors/UV/UVEditor.cs @@ -437,6 +437,7 @@ namespace Switch_Toolbox.Library.Forms shape.TransformUVs(Translate, Scale, UvChannelIndex); shape.UpdateVertexData(); + shape.SaveVertexBuffer(); } scaleXUD.Value = 1; diff --git a/Switch_Toolbox_Library/Generics/GenericObject.cs b/Switch_Toolbox_Library/Generics/GenericObject.cs index babe9634..1b441366 100644 --- a/Switch_Toolbox_Library/Generics/GenericObject.cs +++ b/Switch_Toolbox_Library/Generics/GenericObject.cs @@ -24,6 +24,11 @@ namespace Switch_Toolbox.Library } + public virtual void SaveVertexBuffer() + { + + } + public List PolygonGroups = new List(); public bool HasPos; diff --git a/Switch_Toolbox_Library/Generics/GenericTexture.cs b/Switch_Toolbox_Library/Generics/GenericTexture.cs index 019dd7ab..44a936de 100644 --- a/Switch_Toolbox_Library/Generics/GenericTexture.cs +++ b/Switch_Toolbox_Library/Generics/GenericTexture.cs @@ -144,7 +144,7 @@ namespace Switch_Toolbox.Library set { if (value == 0) mipCount = 1; - else if (value > 14) + else if (value > 17) throw new Exception($"Invalid mip map count! Texture: {Text} Value: {value}"); else mipCount = value; diff --git a/Switch_Toolbox_Library/IO/STFileLoader.cs b/Switch_Toolbox_Library/IO/STFileLoader.cs index 4c6d6833..a2f43087 100644 --- a/Switch_Toolbox_Library/IO/STFileLoader.cs +++ b/Switch_Toolbox_Library/IO/STFileLoader.cs @@ -189,6 +189,16 @@ namespace Switch_Toolbox.Library.IO return OpenFileFormat(FileName, data, LeaveStreamOpen, InArchive, archiveNode, true, CompressionType.Yaz0, DecompressedFileSize, CompressedFileSize); } + if (Path.GetExtension(FileName) == ".lz") + { + if (data == null) + data = File.ReadAllBytes(FileName); + + data = LZ77_WII.Decompress(fileReader.getSection(16, data.Length - 16)); + + return OpenFileFormat(FileName, data, LeaveStreamOpen, InArchive, archiveNode, true, + CompressionType.Yaz0, DecompressedFileSize, CompressedFileSize); + } if (MagicHex == 0x28B52FFD || MagicHex == 0xFD2FB528) { if (data != null) diff --git a/Switch_Toolbox_Library/Switch_Toolbox_Library.csproj b/Switch_Toolbox_Library/Switch_Toolbox_Library.csproj index 6d230a9d..81328421 100644 --- a/Switch_Toolbox_Library/Switch_Toolbox_Library.csproj +++ b/Switch_Toolbox_Library/Switch_Toolbox_Library.csproj @@ -210,6 +210,7 @@ + diff --git a/Switch_Toolbox_Library/Swizzling/GX2.cs b/Switch_Toolbox_Library/Swizzling/GX2.cs index 84df14cc..41431e0a 100644 --- a/Switch_Toolbox_Library/Swizzling/GX2.cs +++ b/Switch_Toolbox_Library/Swizzling/GX2.cs @@ -657,6 +657,100 @@ namespace Switch_Toolbox.Library.Old return ((X - 1) | (Y - 1)) + 1; } + public static byte[] Decode(GX2Surface tex, int ArrayIndex = -1, int MipIndex = -1, string DebugTextureName = "") + { + uint blkWidth, blkHeight; + if (IsFormatBCN((GX2SurfaceFormat)tex.format)) + { + blkWidth = 4; + blkHeight = 4; + } + else + { + blkWidth = 1; + blkHeight = 1; + } + + var surfInfo = getSurfaceInfo((GX2SurfaceFormat)tex.format, tex.width, tex.height, tex.depth, (uint)tex.dim, (uint)tex.tileMode, (uint)tex.aa, 0); + uint bpp = DIV_ROUND_UP(surfInfo.bpp, 8); + + if (tex.numArray == 0) + tex.numArray = 1; + + byte[] data = new byte[tex.data.Length]; + byte[] mipdata = new byte[0]; + + if (tex.mipData != null) + mipdata = new byte[tex.mipData.Length]; + + uint mipCount = tex.numMips; + if (tex.mipData == null || tex.mipData.Length <= 0) + mipCount = 1; + + int dataOffset = 0; + int mipDataOffset = 0; + int mipSpliceSize = 0; + + for (int arrayLevel = 0; arrayLevel < tex.depth; arrayLevel++) + { + for (int mipLevel = 0; mipLevel < mipCount; mipLevel++) + { + bool GetLevel = (arrayLevel == ArrayIndex && mipLevel == MipIndex); + + uint swizzle = tex.swizzle; + + uint width_ = (uint)Math.Max(1, tex.width >> mipLevel); + uint height_ = (uint)Math.Max(1, tex.height >> mipLevel); + + uint size = DIV_ROUND_UP(width_, blkWidth) * DIV_ROUND_UP(height_, blkHeight) * bpp; + + uint mipOffset; + if (mipLevel != 0) + { + if (tex.mip_swizzle != 0) + swizzle = tex.mip_swizzle; + + mipOffset = (tex.mipOffset[mipLevel - 1]); + if (mipLevel == 1) + { + mipOffset -= (uint)surfInfo.surfSize; + mipOffset += (uint)mipDataOffset; + mipSpliceSize += (int)mipOffset; + } + + Console.WriteLine("mipOffset " + mipOffset); + if (GetLevel) + { + surfInfo = getSurfaceInfo((GX2SurfaceFormat)tex.format, tex.width, tex.height, tex.depth, (uint)tex.dim, (uint)tex.tileMode, (uint)tex.aa, mipLevel); + + Array.Copy(tex.mipData, 0, mipdata, 0, tex.mipData.Length); + Array.Copy(tex.mipData, (int)mipOffset, mipdata, 0, (int)surfInfo.sliceSize); + data = mipdata; + } + } + else if (GetLevel) + { + Array.Copy(tex.data, 0, data, 0, tex.data.Length); + Array.Copy(tex.data, (uint)dataOffset, data, 0, size); + } + + if (GetLevel) + { + byte[] deswizzled = deswizzle(width_, height_, surfInfo.depth, surfInfo.height, (uint)tex.format, + surfInfo.tileMode, (uint)swizzle, surfInfo.pitch, surfInfo.bpp, data, arrayLevel); + //Create a copy and use that to remove uneeded data + byte[] result_ = new byte[size]; + Array.Copy(deswizzled, 0, result_, 0, size); + return result_; + } + } + + dataOffset += (int)surfInfo.sliceSize; + mipDataOffset += mipSpliceSize; + } + return null; + } + public static List> Decode(GX2Surface tex, string DebugTextureName = "") { if (tex.data == null || tex.data.Length <= 0)