From f8fdce7df967833316b5c809d4385cd20ac37e7f Mon Sep 17 00:00:00 2001 From: windyfairy Date: Sat, 9 Mar 2019 23:39:59 +0900 Subject: [PATCH] Add support for compressed data --- gitadora-texbintool/Program.cs | 178 ++++++++++++++++++++++++++++++--- 1 file changed, 165 insertions(+), 13 deletions(-) diff --git a/gitadora-texbintool/Program.cs b/gitadora-texbintool/Program.cs index 5e58777..200b7c2 100644 --- a/gitadora-texbintool/Program.cs +++ b/gitadora-texbintool/Program.cs @@ -53,6 +53,13 @@ namespace gitadora_texbintool var data = reader.ReadBytes(4); Array.Reverse(data); return BitConverter.ToInt32(data, 0); + } + + static void WriteInt32(MemoryStream writer, int value) + { + var data = BitConverter.GetBytes(value); + Array.Reverse(data); + writer.Write(data, 0, 4); } static byte[] Decompress(BinaryReader reader) @@ -146,6 +153,134 @@ namespace gitadora_texbintool } return outputData; + } + + public static byte[] Compress(byte[] Data) + { + // Based on: https://github.com/gdkchan/LegaiaText/blob/bbec0465428a9ff1858e4177588599629ca43302/LegaiaText/Legaia/Compression/LZSS.cs + using (MemoryStream Output = new MemoryStream()) + { + ulong[] LookUp = new ulong[0x10000]; + + byte[] Dict = new byte[0x1000]; + + int DictAddr = 4078; + int SrcAddr = 0; + int BitsAddr = 0; + + ushort Mask = 0x80; + + byte Header = 0; + + Output.Write(BitConverter.GetBytes(0), 0, 4); + Output.Write(BitConverter.GetBytes(0), 0, 4); + + while (SrcAddr < Data.Length) + { + if ((Mask <<= 1) == 0x100) + { + int OldAddr = BitsAddr; + + BitsAddr = (int)Output.Position; + + Output.Seek(OldAddr, SeekOrigin.Begin); + Output.WriteByte(Header); + + Output.Seek(BitsAddr, SeekOrigin.Begin); + Output.WriteByte(0); + + Header = 0; + Mask = 1; + } + + int Length = 2; + int DictPos = 0; + + if (SrcAddr + 2 < Data.Length) + { + int Value; + + Value = Data[SrcAddr + 0] << 8; + Value |= Data[SrcAddr + 1] << 0; + + for (int i = 0; i < 5; i++) + { + int Index = (int)((LookUp[Value] >> (i * 12)) & 0xfff); + + //First byte doesn't match, so the others won't match too + if (Data[SrcAddr] != Dict[Index]) break; + + //Temporary dictionary used on comparisons + byte[] CmpDict = new byte[0x1000]; + Array.Copy(Dict, CmpDict, Dict.Length); + int CmpAddr = DictAddr; + + int MatchLen = 0; + + for (int j = 0; j < 18 && SrcAddr + j < Data.Length; j++) + { + if (CmpDict[(Index + j) & 0xfff] == Data[SrcAddr + j]) + MatchLen++; + else + break; + + CmpDict[CmpAddr] = Data[SrcAddr + j]; + CmpAddr = (CmpAddr + 1) & 0xfff; + } + + if (MatchLen > Length && MatchLen < Output.Length) + { + Length = MatchLen; + DictPos = Index; + } + } + } + + if (Length > 2) + { + Output.WriteByte((byte)DictPos); + + int NibLo = (Length - 3) & 0xf; + int NibHi = (DictPos >> 4) & 0xf0; + + Output.WriteByte((byte)(NibLo | NibHi)); + } + else + { + Header |= (byte)Mask; + + Output.WriteByte(Data[SrcAddr]); + + Length = 1; + } + + for (int i = 0; i < Length; i++) + { + if (SrcAddr + 1 < Data.Length) + { + int Value; + + Value = Data[SrcAddr + 0] << 8; + Value |= Data[SrcAddr + 1] << 0; + + LookUp[Value] <<= 12; + LookUp[Value] |= (uint)DictAddr; + } + + Dict[DictAddr] = Data[SrcAddr++]; + DictAddr = (DictAddr + 1) & 0xfff; + } + } + + Output.Seek(BitsAddr, SeekOrigin.Begin); + Output.WriteByte(Header); + + Output.Seek(0, SeekOrigin.Begin); + WriteInt32(Output, Data.Length); + WriteInt32(Output, (int)Output.Length - 8); + + return Output.ToArray(); + } } static int CalculateHash(string input) @@ -519,7 +654,7 @@ namespace gitadora_texbintool return nameSection; } - static void CreateTexbinFile(string pathname, bool generateRectSection = true) + static void CreateTexbinFile(string pathname, bool generateRectSection = true, bool compressedData = true) { var filelist = Directory.GetFiles(pathname).Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToArray(); var filelist_unique = filelist.Select(Path.GetFileNameWithoutExtension).Distinct().Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToList(); @@ -551,11 +686,18 @@ namespace gitadora_texbintool fileinfoSection.AddRange(BitConverter.GetBytes(0)); fileinfoSection.AddRange(BitConverter.GetBytes(data.Length + 0x08)); fileinfoSection.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + (filelist_unique.Count * 0x0c) + dataSection.Count)); - - dataSection.AddRange(BitConverter.GetBytes(data.Length).Reverse()); - dataSection.AddRange(BitConverter.GetBytes(0)); - dataSection.AddRange(data); + if (compressedData) + { + dataSection.AddRange(Compress(data)); + } + else + { + dataSection.AddRange(BitConverter.GetBytes(data.Length).Reverse()); + dataSection.AddRange(BitConverter.GetBytes(0)); + dataSection.AddRange(data); + } + imageRectInfo[filelist_unique[i]] = new Tuple((ushort)((data[0x11] << 8) | data[0x10]), (ushort)((data[0x13] << 8) | data[0x12])); } @@ -649,28 +791,38 @@ namespace gitadora_texbintool { if (args.Length <= 0) { - Console.WriteLine("usage: {0} [--no-rect/--nr] [--no-split/--ns] input_filename", AppDomain.CurrentDomain.FriendlyName); - Console.WriteLine("--no-rect/--nr: Don't create a rect table (Some games like Jubeat don't use the rect table)"); - Console.WriteLine("--no-split/--ns: Don't split images into separate images if they use the rect table"); + Console.WriteLine("usage: {0} [--no-rect/-nr] [--no-split/-ns] [--uncompressed/-u] input_filename", AppDomain.CurrentDomain.FriendlyName); + Console.WriteLine("--no-rect/-nr: Don't create a rect table (Some games like Jubeat don't use the rect table)"); + Console.WriteLine("--no-split/-ns: Don't split images into separate images if they use the rect table"); + Console.WriteLine("--uncompressed/-u: Don't compress data when creating archive"); Environment.Exit(1); } var splitImage = true; var generateRectSection = true; - + var compressedData = true; + var filenames = new List(); for (var index = 0; index < args.Length; index++) { - if (String.CompareOrdinal(args[index].ToLower(), "--no-rect") == 0 - || String.CompareOrdinal(args[index].ToLower(), "--nr") == 0) + if (String.CompareOrdinal(args[index].ToLower(), "--no-rect") == 0 + || String.CompareOrdinal(args[index].ToLower(), "--nr") == 0 + || String.CompareOrdinal(args[index].ToLower(), "-nr") == 0) { generateRectSection = false; } else if (String.CompareOrdinal(args[index].ToLower(), "--no-split") == 0 - || String.CompareOrdinal(args[index].ToLower(), "--ns") == 0) + || String.CompareOrdinal(args[index].ToLower(), "--ns") == 0 + || String.CompareOrdinal(args[index].ToLower(), "-ns") == 0) { splitImage = false; } + else if (String.CompareOrdinal(args[index].ToLower(), "--uncompressed") == 0 + || String.CompareOrdinal(args[index].ToLower(), "--u") == 0 + || String.CompareOrdinal(args[index].ToLower(), "-u") == 0) + { + compressedData = false; + } else { filenames.Add(args[index]); @@ -681,7 +833,7 @@ namespace gitadora_texbintool { if (Directory.Exists(filename)) { - CreateTexbinFile(filename, generateRectSection); + CreateTexbinFile(filename, generateRectSection, compressedData); } else if (File.Exists(filename)) {