添加项目文件。
This commit is contained in:
parent
b0956c8536
commit
a4036e89d6
25
TmrHiroRepack.sln
Normal file
25
TmrHiroRepack.sln
Normal file
@ -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
|
148
TmrHiroRepack/Program.cs
Normal file
148
TmrHiroRepack/Program.cs
Normal file
@ -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 <FolderPath> <Version>");
|
||||||
|
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<Index> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
TmrHiroRepack/Properties/launchSettings.json
Normal file
8
TmrHiroRepack/Properties/launchSettings.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"TmrHiroRepack": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "G:\\x221.local\\test\\Agrd\r\n2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
TmrHiroRepack/TmrHiroRepack.csproj
Normal file
10
TmrHiroRepack/TmrHiroRepack.csproj
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
126
TmrHiroRepack/Utils.cs
Normal file
126
TmrHiroRepack/Utils.cs
Normal file
@ -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<byte> 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>(TArray value, int index) where TArray : IList<byte>
|
||||||
|
{
|
||||||
|
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}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user