From a4036e89d680b34b3c4261c13fbed751c391e406 Mon Sep 17 00:00:00 2001 From: Chenx221 Date: Sat, 19 Oct 2024 13:50:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TmrHiroRepack.sln | 25 ++++ TmrHiroRepack/Program.cs | 148 +++++++++++++++++++ TmrHiroRepack/Properties/launchSettings.json | 8 + TmrHiroRepack/TmrHiroRepack.csproj | 10 ++ TmrHiroRepack/Utils.cs | 126 ++++++++++++++++ 5 files changed, 317 insertions(+) create mode 100644 TmrHiroRepack.sln create mode 100644 TmrHiroRepack/Program.cs create mode 100644 TmrHiroRepack/Properties/launchSettings.json create mode 100644 TmrHiroRepack/TmrHiroRepack.csproj create mode 100644 TmrHiroRepack/Utils.cs diff --git a/TmrHiroRepack.sln b/TmrHiroRepack.sln new file mode 100644 index 0000000..c432a96 --- /dev/null +++ b/TmrHiroRepack.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TmrHiroRepack", "TmrHiroRepack\TmrHiroRepack.csproj", "{F4FBB98A-9511-4CFA-B6C1-D1505E52ACB9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F4FBB98A-9511-4CFA-B6C1-D1505E52ACB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4FBB98A-9511-4CFA-B6C1-D1505E52ACB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4FBB98A-9511-4CFA-B6C1-D1505E52ACB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4FBB98A-9511-4CFA-B6C1-D1505E52ACB9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {21903E7F-A268-4FD1-880E-7DD9BDC671B8} + EndGlobalSection +EndGlobal diff --git a/TmrHiroRepack/Program.cs b/TmrHiroRepack/Program.cs new file mode 100644 index 0000000..d984386 --- /dev/null +++ b/TmrHiroRepack/Program.cs @@ -0,0 +1,148 @@ + +using System.IO; +using System.Text; + +namespace TmrHiroRepack +{ + public class Entry + { + public string Name { get; set; } + public long Offset { get; set; } + public uint Size { get; set; } + } + public class Index + { + public string name; + public EntryInfoV1 entryInfoV1; + public EntryInfoV2 entryInfoV2; + } + public class EntryInfoV1 + { + public uint entryOffset; + public uint entrySize; + } + public class EntryInfoV2 + { + public long entryOffset; + public uint entrySize; + } + + internal class Program + { + static void Main(string[] args) + { + if (args.Length < 2) + { + Console.WriteLine("Usage: TmrHiroRepack "); + return; + } + + string folderPath = args[0]; + string versionArg = args[1]; + + if (!Directory.Exists(folderPath)) + { + Console.WriteLine("Invalid FolderPath"); + return; + } + + if (!int.TryParse(versionArg, out int version) || (version != 1 && version != 2)) + { + Console.WriteLine("Invalid Version"); + return; + } + + Console.WriteLine($"Repacking {Path.GetFileName(folderPath)}"); + bool status = Repack(folderPath, version); + Console.WriteLine(status ? "Repack Successful" : "Repack Failed"); + } + + private static bool Repack(string folderPath, int version) + { + EncodingProvider provider = CodePagesEncodingProvider.Instance; + Encoding? shiftJis = provider.GetEncoding("shift-jis"); + string[] files = Directory.GetFiles(folderPath); + short count = (short)files.Length; //文件数量 + byte name_length = 0x16; //文件名长度(貌似固定16?) + uint data_offset; + if (version == 1) + data_offset = 7 + ((uint)name_length + 8) * (uint)count; //Data区偏移 + else if (version == 2) + data_offset = 7 + ((uint)name_length + 12) * (uint)count; //Data区偏移 + else + throw new Exception("Invalid Version"); + long offset = 0; //Index区偏移 + List indexs = new(); + string[] extensions = { ".ogg", ".grd", ".srp" }; //需要移除的文件后缀,因为这是Garbro添加的 + foreach (string file in files) + { + Index i = new(); + string fileName = Path.GetFileName(file); + FileInfo fileInfo = new(file); + //remove .ogg/.grd/.srp + foreach (var extension in extensions) + { + if (fileName.EndsWith(extension, StringComparison.OrdinalIgnoreCase)) + { + fileName = fileName[..^extension.Length]; + if (shiftJis.GetBytes(fileName).Length > name_length) + throw new Exception("Something Wrong, File name too long"); + } + } + i.name = fileName; + if (version == 1) + { + i.entryInfoV1 = new() + { + entryOffset = (uint)offset, + entrySize = (uint)fileInfo.Length + }; + } + + else if (version == 2) + { + i.entryInfoV2 = new() + { + entryOffset = offset, + entrySize = (uint)fileInfo.Length + }; + } + offset += fileInfo.Length; + indexs.Add(i); + } + //准备开写 + string outputPath = Path.Combine(Path.GetDirectoryName(folderPath), Path.GetFileName(folderPath) + ".pac"); + using (FileStream fs = new(outputPath, FileMode.Create)) + using (BinaryWriter bw = new(fs)) + { + bw.Write(count);//文件数量 + bw.Write(name_length);//文件名长度 + bw.Write(data_offset);//Data区偏移 + foreach (Index i in indexs) + { + byte[] name = new byte[name_length]; + Array.Copy(shiftJis.GetBytes(i.name), name, shiftJis.GetBytes(i.name).Length); + bw.Write(name);//文件名 + if (version == 1) + { + bw.Write(i.entryInfoV1.entryOffset);//文件偏移 + bw.Write(i.entryInfoV1.entrySize);//文件大小 + } + else if (version == 2) + { + bw.Write(i.entryInfoV2.entryOffset);//文件偏移 + bw.Write(i.entryInfoV2.entrySize);//文件大小 + } + } + foreach (string file in files) + { + using (FileStream fs2 = new(file, FileMode.Open)) + { + fs2.CopyTo(fs); + } + } + } + return true; + } + } +} diff --git a/TmrHiroRepack/Properties/launchSettings.json b/TmrHiroRepack/Properties/launchSettings.json new file mode 100644 index 0000000..c57714b --- /dev/null +++ b/TmrHiroRepack/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "TmrHiroRepack": { + "commandName": "Project", + "commandLineArgs": "G:\\x221.local\\test\\Agrd\r\n2" + } + } +} \ No newline at end of file diff --git a/TmrHiroRepack/TmrHiroRepack.csproj b/TmrHiroRepack/TmrHiroRepack.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/TmrHiroRepack/TmrHiroRepack.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/TmrHiroRepack/Utils.cs b/TmrHiroRepack/Utils.cs new file mode 100644 index 0000000..f15d205 --- /dev/null +++ b/TmrHiroRepack/Utils.cs @@ -0,0 +1,126 @@ +using System.Reflection; +using System.Text; + +namespace TmrHiroRepack +{ + public class Utils + { + public static string ReadStringFromTextData(byte[] sheet_text, int offset) + { + return ReadStringFromTextData(sheet_text, offset, -1); + } + + public static string ReadStringFromTextData(byte[] sheet_text, int offset, int length_limit) + { + EncodingProvider provider = CodePagesEncodingProvider.Instance; + Encoding? shiftJis = provider.GetEncoding("shift-jis") ?? throw new InvalidOperationException("Shift-JIS encoding not supported."); + return ReadStringFromTextData(sheet_text, offset, length_limit, shiftJis); + } + + public static string ReadStringFromTextData(byte[] sheet_text, int offset, int length_limit, Encoding enc) + { + List stringBytes = []; + int end = length_limit != -1 ? Math.Min(offset + length_limit, sheet_text.Length) : sheet_text.Length; + for (int i = offset; i < end && sheet_text[i] != 0x00; i++) + { + stringBytes.Add(sheet_text[i]); + } + return enc.GetString(stringBytes.ToArray()); + } + + public static byte[] ReadBytes(BinaryReader reader, ulong length) + { + const int bufferSize = 8192; + byte[] data = new byte[length]; + ulong bytesRead = 0; + while (bytesRead < length) + { + int toRead = (int)Math.Min(bufferSize, length - bytesRead); + int read = reader.Read(data, (int)bytesRead, toRead); + if (read == 0) + break; + bytesRead += (ulong)read; + } + return data; + } + + public static uint ToUInt32(TArray value, int index) where TArray : IList + { + return (uint)(value[index] | value[index + 1] << 8 | value[index + 2] << 16 | value[index + 3] << 24); + } + + public static uint ReadUInt32(BinaryReader reader) + { + byte[] bytes = reader.ReadBytes(4); + if (bytes.Length < 4) + throw new EndOfStreamException("Unexpected end of stream while reading UInt32."); + return BitConverter.ToUInt32(bytes, 0); + } + + public static void ExtractEmbeddedDatabase(string outputPath) + { + if (File.Exists(outputPath)) + { + Console.WriteLine($"File {outputPath} already exists. Do you want to overwrite it? (y/n)"); + string? input = Console.ReadLine(); + if (input?.ToLower() != "y") + { + Console.WriteLine("Task cancelled, Exporting database aborted."); + return; + } + } + var assembly = Assembly.GetExecutingAssembly(); + string resourceName = "EscudeTools.empty.db"; + using Stream stream = assembly.GetManifestResourceStream(resourceName) ?? throw new Exception($"Error, No resource with name {resourceName} found."); + using FileStream fileStream = new(outputPath, FileMode.Create, FileAccess.Write); + stream.CopyTo(fileStream); + } + + public static string GetSQLiteColumnType(ushort type) + { + return type switch + { + // int + 0x1 => "INTEGER", + // float + 0x2 => "REAL", + // string + 0x3 => "TEXT", + // bool + 0x4 => "INTEGER", + _ => throw new NotSupportedException($"Unsupported column type: {type}"), + }; + throw new NotImplementedException(); + } + + public static bool ISKANJI(byte x) + { + return (x ^ 0x20) - 0xa1 <= 0x3b; + } + + public static ushort GetColumnTypeFromSQLite(string v) + { + return v[^2] switch + { + '1' => 0x1, + '2' => 0x2, + '3' => 0x3, + '4' => 0x4, + _ => throw new NotSupportedException($"Unsupported column type: {v}"), + }; + + } + + public static ushort GetColumnSize(string v) + { + return v[^1] switch + { + '1' => 0x1, + '2' => 0x2, + '3' => 0x3, + '4' => 0x4, + _ => throw new NotSupportedException($"Unsupported column Size: {v}"), + }; + } + } +}