5
0
mirror of synced 2024-11-30 17:34:30 +01:00

Add support for compressed data

This commit is contained in:
windyfairy 2019-03-09 23:39:59 +09:00
parent 19ed8568a8
commit f8fdce7df9

View File

@ -55,6 +55,13 @@ namespace gitadora_texbintool
return BitConverter.ToInt32(data, 0); 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) static byte[] Decompress(BinaryReader reader)
{ {
var decompSize = ReadInt32(reader); var decompSize = ReadInt32(reader);
@ -148,6 +155,134 @@ namespace gitadora_texbintool
return outputData; 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) static int CalculateHash(string input)
{ {
int hash = 0; int hash = 0;
@ -519,7 +654,7 @@ namespace gitadora_texbintool
return nameSection; 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 = 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(); var filelist_unique = filelist.Select(Path.GetFileNameWithoutExtension).Distinct().Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToList();
@ -552,9 +687,16 @@ namespace gitadora_texbintool
fileinfoSection.AddRange(BitConverter.GetBytes(data.Length + 0x08)); fileinfoSection.AddRange(BitConverter.GetBytes(data.Length + 0x08));
fileinfoSection.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + (filelist_unique.Count * 0x0c) + dataSection.Count)); fileinfoSection.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + (filelist_unique.Count * 0x0c) + dataSection.Count));
if (compressedData)
{
dataSection.AddRange(Compress(data));
}
else
{
dataSection.AddRange(BitConverter.GetBytes(data.Length).Reverse()); dataSection.AddRange(BitConverter.GetBytes(data.Length).Reverse());
dataSection.AddRange(BitConverter.GetBytes(0)); dataSection.AddRange(BitConverter.GetBytes(0));
dataSection.AddRange(data); dataSection.AddRange(data);
}
imageRectInfo[filelist_unique[i]] = new Tuple<ushort, ushort>((ushort)((data[0x11] << 8) | data[0x10]), (ushort)((data[0x13] << 8) | data[0x12])); imageRectInfo[filelist_unique[i]] = new Tuple<ushort, ushort>((ushort)((data[0x11] << 8) | data[0x10]), (ushort)((data[0x13] << 8) | data[0x12]));
} }
@ -649,28 +791,38 @@ namespace gitadora_texbintool
{ {
if (args.Length <= 0) if (args.Length <= 0)
{ {
Console.WriteLine("usage: {0} [--no-rect/--nr] [--no-split/--ns] input_filename", AppDomain.CurrentDomain.FriendlyName); 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-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("--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); Environment.Exit(1);
} }
var splitImage = true; var splitImage = true;
var generateRectSection = true; var generateRectSection = true;
var compressedData = true;
var filenames = new List<string>(); var filenames = new List<string>();
for (var index = 0; index < args.Length; index++) for (var index = 0; index < args.Length; index++)
{ {
if (String.CompareOrdinal(args[index].ToLower(), "--no-rect") == 0 if (String.CompareOrdinal(args[index].ToLower(), "--no-rect") == 0
|| String.CompareOrdinal(args[index].ToLower(), "--nr") == 0) || String.CompareOrdinal(args[index].ToLower(), "--nr") == 0
|| String.CompareOrdinal(args[index].ToLower(), "-nr") == 0)
{ {
generateRectSection = false; generateRectSection = false;
} }
else if (String.CompareOrdinal(args[index].ToLower(), "--no-split") == 0 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; 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 else
{ {
filenames.Add(args[index]); filenames.Add(args[index]);
@ -681,7 +833,7 @@ namespace gitadora_texbintool
{ {
if (Directory.Exists(filename)) if (Directory.Exists(filename))
{ {
CreateTexbinFile(filename, generateRectSection); CreateTexbinFile(filename, generateRectSection, compressedData);
} }
else if (File.Exists(filename)) else if (File.Exists(filename))
{ {