Compare commits
3 Commits
f218a143da
...
608bbb36d6
Author | SHA1 | Date | |
---|---|---|---|
608bbb36d6 | |||
b0f19dbace | |||
bb66d3ff70 |
162
EscudeTools/Garbro/BitStream.cs
Normal file
162
EscudeTools/Garbro/BitStream.cs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
//! \file BitStream.cs
|
||||||
|
//! \date Sat Aug 22 21:33:39 2015
|
||||||
|
//! \brief Bit stream on top of the IO.Stream
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 by morkt
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to
|
||||||
|
// deal in the Software without restriction, including without limitation the
|
||||||
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
// sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
// IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace EscudeTools.Garbro
|
||||||
|
{
|
||||||
|
public class BitStream : IDisposable
|
||||||
|
{
|
||||||
|
protected Stream m_input;
|
||||||
|
private bool m_should_dispose;
|
||||||
|
|
||||||
|
protected int m_bits = 0;
|
||||||
|
protected int m_cached_bits = 0;
|
||||||
|
|
||||||
|
public Stream Input { get { return m_input; } }
|
||||||
|
public int CacheSize { get { return m_cached_bits; } }
|
||||||
|
|
||||||
|
protected BitStream(Stream file, bool leave_open)
|
||||||
|
{
|
||||||
|
m_input = file;
|
||||||
|
m_should_dispose = !leave_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
m_cached_bits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Members
|
||||||
|
bool m_disposed = false;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!m_disposed)
|
||||||
|
{
|
||||||
|
if (disposing && m_should_dispose && null != m_input)
|
||||||
|
m_input.Dispose();
|
||||||
|
m_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IBitStream
|
||||||
|
{
|
||||||
|
int GetBits(int count);
|
||||||
|
int GetNextBit();
|
||||||
|
void Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MsbBitStream : BitStream, IBitStream
|
||||||
|
{
|
||||||
|
public MsbBitStream(Stream file, bool leave_open = false)
|
||||||
|
: base(file, leave_open)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetBits(int count)
|
||||||
|
{
|
||||||
|
Debug.Assert(count <= 24, "MsbBitStream does not support sequences longer than 24 bits");
|
||||||
|
while (m_cached_bits < count)
|
||||||
|
{
|
||||||
|
int b = m_input.ReadByte();
|
||||||
|
if (-1 == b)
|
||||||
|
return -1;
|
||||||
|
m_bits = m_bits << 8 | b;
|
||||||
|
m_cached_bits += 8;
|
||||||
|
}
|
||||||
|
int mask = (1 << count) - 1;
|
||||||
|
m_cached_bits -= count;
|
||||||
|
return m_bits >> m_cached_bits & mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetNextBit()
|
||||||
|
{
|
||||||
|
return GetBits(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LsbBitStream : BitStream, IBitStream
|
||||||
|
{
|
||||||
|
public LsbBitStream(Stream file, bool leave_open = false)
|
||||||
|
: base(file, leave_open)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetBits(int count)
|
||||||
|
{
|
||||||
|
Debug.Assert(count <= 32, "LsbBitStream does not support sequences longer than 32 bits");
|
||||||
|
int value;
|
||||||
|
if (m_cached_bits >= count)
|
||||||
|
{
|
||||||
|
int mask = (1 << count) - 1;
|
||||||
|
value = m_bits & mask;
|
||||||
|
m_bits = (int)((uint)m_bits >> count);
|
||||||
|
m_cached_bits -= count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = m_bits & (1 << m_cached_bits) - 1;
|
||||||
|
count -= m_cached_bits;
|
||||||
|
int shift = m_cached_bits;
|
||||||
|
m_cached_bits = 0;
|
||||||
|
while (count >= 8)
|
||||||
|
{
|
||||||
|
int b = m_input.ReadByte();
|
||||||
|
if (-1 == b)
|
||||||
|
return -1;
|
||||||
|
value |= b << shift;
|
||||||
|
shift += 8;
|
||||||
|
count -= 8;
|
||||||
|
}
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
int b = m_input.ReadByte();
|
||||||
|
if (-1 == b)
|
||||||
|
return -1;
|
||||||
|
value |= (b & (1 << count) - 1) << shift;
|
||||||
|
m_bits = b >> count;
|
||||||
|
m_cached_bits = 8 - count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetNextBit()
|
||||||
|
{
|
||||||
|
return GetBits(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
//这里的提取代码参考(Ctrl+C, Ctrl+V)了Garbro中关于ESCUDE BIN封包的实现
|
//这里的提取代码参考(Ctrl+C, Ctrl+V)了Garbro中关于ESCUDE BIN封包的实现
|
||||||
|
using EscudeTools.Garbro;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace EscudeTools
|
namespace EscudeTools
|
||||||
{
|
{
|
||||||
@ -9,9 +11,15 @@ namespace EscudeTools
|
|||||||
public long Offset { get; set; }
|
public long Offset { get; set; }
|
||||||
public uint Size { get; set; }
|
public uint Size { get; set; }
|
||||||
}
|
}
|
||||||
|
public class LzwEntry
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int UnpackSize { get; set; }
|
||||||
|
}
|
||||||
public class PackManager
|
public class PackManager
|
||||||
{
|
{
|
||||||
static readonly byte[] fileSignature = [0x45, 0x53, 0x43, 0x2D, 0x41, 0x52, 0x43]; //"ESC-ARC"
|
static readonly byte[] fileSignature = [0x45, 0x53, 0x43, 0x2D, 0x41, 0x52, 0x43]; //"ESC-ARC"
|
||||||
|
static readonly byte[] LzwSignature = [0x61, 0x63, 0x70, 0x00]; //"acp\0"
|
||||||
static readonly byte[] supportPackVersion = [0x31, 0x32]; //1, 2
|
static readonly byte[] supportPackVersion = [0x31, 0x32]; //1, 2
|
||||||
private bool isLoaded = false;
|
private bool isLoaded = false;
|
||||||
private uint LoadedKey;
|
private uint LoadedKey;
|
||||||
@ -161,8 +169,10 @@ namespace EscudeTools
|
|||||||
string output = Path.Combine(Path.GetDirectoryName(pFile), "output", Path.GetFileNameWithoutExtension(pFile));
|
string output = Path.Combine(Path.GetDirectoryName(pFile), "output", Path.GetFileNameWithoutExtension(pFile));
|
||||||
if (!Directory.Exists(output))
|
if (!Directory.Exists(output))
|
||||||
Directory.CreateDirectory(output);
|
Directory.CreateDirectory(output);
|
||||||
|
var lzwManifest = new List<LzwEntry>();
|
||||||
using FileStream inputStream = new(pFile, FileMode.Open);
|
string jsonPath = Path.Combine(output, "lzwManifest.json");
|
||||||
|
using FileStream inputStream = new(pFile, FileMode.Open, FileAccess.Read);
|
||||||
|
using BinaryReader br = new(inputStream);
|
||||||
foreach (Entry entry in pItem)
|
foreach (Entry entry in pItem)
|
||||||
{
|
{
|
||||||
string entryPath = Path.Combine(output, entry.Name);
|
string entryPath = Path.Combine(output, entry.Name);
|
||||||
@ -170,22 +180,145 @@ namespace EscudeTools
|
|||||||
|
|
||||||
if (!Directory.Exists(entryDirectory))
|
if (!Directory.Exists(entryDirectory))
|
||||||
Directory.CreateDirectory(entryDirectory);
|
Directory.CreateDirectory(entryDirectory);
|
||||||
|
byte[] test;
|
||||||
|
br.BaseStream.Seek(entry.Offset, SeekOrigin.Begin);
|
||||||
|
test = br.ReadBytes(LzwSignature.Length);
|
||||||
|
|
||||||
using FileStream outputStream = new(entryPath, FileMode.Create);
|
if (entry.Size > 8 && test.SequenceEqual(LzwSignature))
|
||||||
inputStream.Seek(entry.Offset, SeekOrigin.Begin);
|
{
|
||||||
byte[] buffer = new byte[entry.Size];
|
entryPath += ".lzw";
|
||||||
inputStream.Read(buffer, 0, buffer.Length);
|
int upackSize = Utils.ToBigEndian(br.ReadInt32());
|
||||||
outputStream.Write(buffer, 0, buffer.Length);
|
lzwManifest.Add(new LzwEntry { Name = entry.Name, UnpackSize = upackSize });
|
||||||
|
using FileStream outputStream = new(entryPath, FileMode.Create);
|
||||||
|
inputStream.Seek(entry.Offset + 8, SeekOrigin.Begin);
|
||||||
|
byte[] buffer = new byte[entry.Size - 8];
|
||||||
|
inputStream.Read(buffer, 0, buffer.Length);
|
||||||
|
outputStream.Write(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using FileStream outputStream = new(entryPath, FileMode.Create);
|
||||||
|
inputStream.Seek(entry.Offset, SeekOrigin.Begin);
|
||||||
|
byte[] buffer = new byte[entry.Size];
|
||||||
|
inputStream.Read(buffer, 0, buffer.Length);
|
||||||
|
outputStream.Write(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lzwManifest.Count > 0)
|
||||||
|
{
|
||||||
|
using (FileStream fs = File.Create(jsonPath))
|
||||||
|
{
|
||||||
|
byte[] jsonBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(lzwManifest));
|
||||||
|
fs.Write(jsonBytes, 0, jsonBytes.Length);
|
||||||
|
}
|
||||||
|
LzwDecode(lzwManifest, output);
|
||||||
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LzwDecode(List<LzwEntry> lzwManifest, string output)
|
||||||
|
{
|
||||||
|
foreach (var i in lzwManifest)
|
||||||
|
{
|
||||||
|
string targetPath = Path.Combine(output, i.Name);
|
||||||
|
int unpacked_size = i.UnpackSize;
|
||||||
|
using FileStream inputStream = new(targetPath + ".lzw", FileMode.Open, FileAccess.Read);
|
||||||
|
using var decoder = new LzwDecoder(inputStream, unpacked_size);
|
||||||
|
decoder.Unpack();
|
||||||
|
using FileStream outputStream = new(targetPath, FileMode.Create);
|
||||||
|
outputStream.Write(decoder.Output, 0, decoder.Output.Length);
|
||||||
|
outputStream.Flush();
|
||||||
|
outputStream.Close();
|
||||||
|
inputStream.Close();
|
||||||
|
File.Delete(targetPath + ".lzw");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class LzwDecoder : IDisposable
|
||||||
|
{
|
||||||
|
private MsbBitStream m_input;
|
||||||
|
private byte[] m_output;
|
||||||
|
|
||||||
|
public byte[] Output { get { return m_output; } }
|
||||||
|
|
||||||
|
public LzwDecoder(Stream input, int unpacked_size)
|
||||||
|
{
|
||||||
|
m_input = new MsbBitStream(input, true);
|
||||||
|
m_output = new byte[unpacked_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unpack()
|
||||||
|
{
|
||||||
|
int dst = 0;
|
||||||
|
var lzw_dict = new int[0x8900];
|
||||||
|
int token_width = 9;
|
||||||
|
int dict_pos = 0;
|
||||||
|
while (dst < m_output.Length)
|
||||||
|
{
|
||||||
|
int token = m_input.GetBits(token_width);
|
||||||
|
if (-1 == token)
|
||||||
|
throw new EndOfStreamException("Invalid compressed stream");
|
||||||
|
else if (0x100 == token) // end of input
|
||||||
|
break;
|
||||||
|
else if (0x101 == token) // increase token width
|
||||||
|
{
|
||||||
|
++token_width;
|
||||||
|
if (token_width > 24)
|
||||||
|
throw new Exception("Invalid comressed stream");
|
||||||
|
}
|
||||||
|
else if (0x102 == token) // reset dictionary
|
||||||
|
{
|
||||||
|
token_width = 9;
|
||||||
|
dict_pos = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dict_pos >= lzw_dict.Length)
|
||||||
|
throw new Exception("Invalid comressed stream");
|
||||||
|
lzw_dict[dict_pos++] = dst;
|
||||||
|
if (token < 0x100)
|
||||||
|
{
|
||||||
|
m_output[dst++] = (byte)token;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token -= 0x103;
|
||||||
|
if (token >= dict_pos)
|
||||||
|
throw new Exception("Invalid comressed stream");
|
||||||
|
int src = lzw_dict[token];
|
||||||
|
int count = Math.Min(m_output.Length - dst, lzw_dict[token + 1] - src + 1);
|
||||||
|
if (count < 0)
|
||||||
|
throw new Exception("Invalid comressed stream");
|
||||||
|
Utils.CopyOverlapped(m_output, src, dst, count);
|
||||||
|
dst += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Members
|
||||||
|
bool _disposed = false;
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
m_input.Dispose();
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
public bool Repack(string path, int version, bool useCustomKey = false, string customKeyProviderPath = "") //目前支持v2v1
|
public bool Repack(string path, int version, bool useCustomKey = false, string customKeyProviderPath = "") //目前支持v2v1
|
||||||
{
|
{
|
||||||
if (useCustomKey)
|
if (useCustomKey)
|
||||||
LoadKey(customKeyProviderPath);
|
LoadKey(customKeyProviderPath);
|
||||||
GeneratePItem(path);
|
GeneratePItem(path);
|
||||||
|
if(File.Exists(Path.Combine(path, "lzwManifest.json")))
|
||||||
|
return false; //Q:为什么不支持 //A:因为我实在不想研究lzw算法,欢迎PR
|
||||||
m_seed = isLoaded ? LoadedKey : 2210579460;
|
m_seed = isLoaded ? LoadedKey : 2210579460;
|
||||||
string outputPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileName(path) + ".bin");
|
string outputPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileName(path) + ".bin");
|
||||||
using (FileStream fs = new(outputPath, FileMode.Create))
|
using (FileStream fs = new(outputPath, FileMode.Create))
|
||||||
@ -244,27 +377,29 @@ namespace EscudeTools
|
|||||||
}
|
}
|
||||||
foreach (Entry entry in pItem)
|
foreach (Entry entry in pItem)
|
||||||
{
|
{
|
||||||
byte[] data = File.ReadAllBytes(Path.Combine(path, entry.Name));
|
byte[] data = File.ReadAllBytes(Path.Combine(path, entry.Name));
|
||||||
bw.Write(data);
|
bw.Write(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GeneratePItem(string path)
|
private void GeneratePItem(string path)
|
||||||
{
|
{
|
||||||
pItem.Clear();
|
pItem.Clear();
|
||||||
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
|
var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories)
|
||||||
|
.Where(file => !file.EndsWith("lzwManifest.json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
.ToArray();
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
|
||||||
var relativePath = Path.GetRelativePath(path, file);
|
|
||||||
var fileInfo = new FileInfo(file);
|
|
||||||
pItem.Add(new Entry
|
|
||||||
{
|
{
|
||||||
Name = relativePath,
|
var relativePath = Path.GetRelativePath(path, file);
|
||||||
Size = (uint)fileInfo.Length
|
var fileInfo = new FileInfo(file);
|
||||||
});
|
pItem.Add(new Entry
|
||||||
}
|
{
|
||||||
|
Name = relativePath,
|
||||||
|
Size = (uint)fileInfo.Length
|
||||||
|
});
|
||||||
|
}
|
||||||
m_count = (uint)pItem.Count;
|
m_count = (uint)pItem.Count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,270 +6,270 @@ namespace EscudeTools
|
|||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
//批量处理EV/ST
|
////批量处理EV/ST
|
||||||
if (Directory.Exists(args[0]) && File.Exists(args[1]))
|
//if (Directory.Exists(args[0]) && File.Exists(args[1]))
|
||||||
//if (File.Exists(args[0]))
|
////if (File.Exists(args[0]))
|
||||||
{
|
//{
|
||||||
string graphicsDBPath = args[1];
|
// string graphicsDBPath = args[1];
|
||||||
using SqliteConnection connection = new($"Data Source={graphicsDBPath};");
|
// using SqliteConnection connection = new($"Data Source={graphicsDBPath};");
|
||||||
connection.Open();
|
// connection.Open();
|
||||||
List<string> tableNames = [];
|
// List<string> tableNames = [];
|
||||||
string[] foundTN = new string[3];
|
// string[] foundTN = new string[3];
|
||||||
List<int> tableIds = [];
|
// List<int> tableIds = [];
|
||||||
bool found1 = false, found2 = false, found3 = false;
|
// bool found1 = false, found2 = false, found3 = false;
|
||||||
using (var command = new SqliteCommand("SELECT name FROM sqlite_master WHERE type='table';", connection))
|
// using (var command = new SqliteCommand("SELECT name FROM sqlite_master WHERE type='table';", connection))
|
||||||
using (var reader = command.ExecuteReader())
|
// using (var reader = command.ExecuteReader())
|
||||||
{
|
// {
|
||||||
int id = 0;
|
// int id = 0;
|
||||||
while (reader.Read())
|
// while (reader.Read())
|
||||||
{
|
// {
|
||||||
string tableName = reader.GetString(0);
|
// string tableName = reader.GetString(0);
|
||||||
if (tableName.StartsWith("イベント"))
|
// if (tableName.StartsWith("イベント"))
|
||||||
{
|
// {
|
||||||
foundTN[0] = tableName;
|
// foundTN[0] = tableName;
|
||||||
found1 = true;
|
// found1 = true;
|
||||||
}
|
// }
|
||||||
else if (tableName.StartsWith("立ち"))
|
// else if (tableName.StartsWith("立ち"))
|
||||||
{
|
// {
|
||||||
foundTN[1] = tableName;
|
// foundTN[1] = tableName;
|
||||||
found2 = true;
|
// found2 = true;
|
||||||
}
|
// }
|
||||||
else if (tableName.StartsWith("表情"))
|
// else if (tableName.StartsWith("表情"))
|
||||||
{
|
// {
|
||||||
foundTN[2] = tableName;
|
// foundTN[2] = tableName;
|
||||||
found3 = true;
|
// found3 = true;
|
||||||
}
|
// }
|
||||||
tableNames.Add(tableName);
|
// tableNames.Add(tableName);
|
||||||
tableIds.Add(id++);
|
// tableIds.Add(id++);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (!(found1 && found2 && found3)) //这里的代码未经测试
|
// if (!(found1 && found2 && found3)) //这里的代码未经测试
|
||||||
{
|
// {
|
||||||
for (int i = 0; i < tableNames.Count; i++)
|
// for (int i = 0; i < tableNames.Count; i++)
|
||||||
Console.WriteLine($"{tableIds[i]}: {tableNames[i]}");
|
// Console.WriteLine($"{tableIds[i]}: {tableNames[i]}");
|
||||||
if (!found1)
|
// if (!found1)
|
||||||
{
|
// {
|
||||||
Console.WriteLine("自动识别失败,请选择存放CG信息的数据表ID: ");
|
// Console.WriteLine("自动识别失败,请选择存放CG信息的数据表ID: ");
|
||||||
string? input = Console.ReadLine();
|
// string? input = Console.ReadLine();
|
||||||
if (int.TryParse(input, out int userInputId))
|
// if (int.TryParse(input, out int userInputId))
|
||||||
{
|
// {
|
||||||
if (userInputId >= 0 && userInputId < tableIds.Count)
|
// if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
{
|
// {
|
||||||
foundTN[0] = tableNames[userInputId];
|
// foundTN[0] = tableNames[userInputId];
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid ID.");
|
// Console.WriteLine("Invalid ID.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid input. Please enter a valid number.");
|
// Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (!found2)
|
// if (!found2)
|
||||||
{
|
// {
|
||||||
Console.WriteLine("自动识别失败,请选择存放立绘信息的数据表ID: ");
|
// Console.WriteLine("自动识别失败,请选择存放立绘信息的数据表ID: ");
|
||||||
string? input = Console.ReadLine();
|
// string? input = Console.ReadLine();
|
||||||
if (int.TryParse(input, out int userInputId))
|
// if (int.TryParse(input, out int userInputId))
|
||||||
{
|
// {
|
||||||
if (userInputId >= 0 && userInputId < tableIds.Count)
|
// if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
{
|
// {
|
||||||
foundTN[1] = tableNames[userInputId];
|
// foundTN[1] = tableNames[userInputId];
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid ID.");
|
// Console.WriteLine("Invalid ID.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid input. Please enter a valid number.");
|
// Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (!found3)
|
// if (!found3)
|
||||||
{
|
// {
|
||||||
Console.WriteLine("自动识别失败,请选择存放表情信息的数据表ID: ");
|
// Console.WriteLine("自动识别失败,请选择存放表情信息的数据表ID: ");
|
||||||
string? input = Console.ReadLine();
|
// string? input = Console.ReadLine();
|
||||||
if (int.TryParse(input, out int userInputId))
|
// if (int.TryParse(input, out int userInputId))
|
||||||
{
|
// {
|
||||||
if (userInputId >= 0 && userInputId < tableIds.Count)
|
// if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
{
|
// {
|
||||||
foundTN[2] = tableNames[userInputId];
|
// foundTN[2] = tableNames[userInputId];
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid ID.");
|
// Console.WriteLine("Invalid ID.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine("Invalid input. Please enter a valid number.");
|
// Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
List<EvTable> evts = [];
|
// List<EvTable> evts = [];
|
||||||
List<StTable> stts = [];
|
// List<StTable> stts = [];
|
||||||
Face[] faces = new Face[32];
|
// Face[] faces = new Face[32];
|
||||||
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[0]};", connection))
|
// using (var command = new SqliteCommand($"SELECT * FROM {foundTN[0]};", connection))
|
||||||
{
|
// {
|
||||||
using var reader = command.ExecuteReader();
|
// using var reader = command.ExecuteReader();
|
||||||
while (reader.Read())
|
// while (reader.Read())
|
||||||
{
|
// {
|
||||||
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
// if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
continue;
|
// continue;
|
||||||
evts.Add(new EvTable
|
// evts.Add(new EvTable
|
||||||
{
|
// {
|
||||||
name = reader.GetString(0),
|
// name = reader.GetString(0),
|
||||||
file = reader.GetString(1),
|
// file = reader.GetString(1),
|
||||||
option = reader.GetString(2).Split(' '),
|
// option = reader.GetString(2).Split(' '),
|
||||||
coverd = (uint)reader.GetInt32(3),
|
// coverd = (uint)reader.GetInt32(3),
|
||||||
filter = (uint)reader.GetInt32(4),
|
// filter = (uint)reader.GetInt32(4),
|
||||||
color = (uint)reader.GetInt32(5),
|
// color = (uint)reader.GetInt32(5),
|
||||||
id = (uint)reader.GetInt32(6),
|
// id = (uint)reader.GetInt32(6),
|
||||||
loc = (uint)reader.GetInt32(7),
|
// loc = (uint)reader.GetInt32(7),
|
||||||
order = reader.GetInt32(8),
|
// order = reader.GetInt32(8),
|
||||||
link = (uint)reader.GetInt32(9)
|
// link = (uint)reader.GetInt32(9)
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[1]};", connection))
|
// using (var command = new SqliteCommand($"SELECT * FROM {foundTN[1]};", connection))
|
||||||
{
|
// {
|
||||||
using var reader = command.ExecuteReader();
|
// using var reader = command.ExecuteReader();
|
||||||
while (reader.Read())
|
// while (reader.Read())
|
||||||
{
|
// {
|
||||||
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
// if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
continue;
|
// continue;
|
||||||
stts.Add(new StTable
|
// stts.Add(new StTable
|
||||||
{
|
// {
|
||||||
name = reader.GetString(0),
|
// name = reader.GetString(0),
|
||||||
file = reader.GetString(1),
|
// file = reader.GetString(1),
|
||||||
option = reader.GetString(2).Split(' '),
|
// option = reader.GetString(2).Split(' '),
|
||||||
coverd = (uint)reader.GetInt32(3),
|
// coverd = (uint)reader.GetInt32(3),
|
||||||
filter = (uint)reader.GetInt32(4),
|
// filter = (uint)reader.GetInt32(4),
|
||||||
face = (uint)reader.GetInt32(5),
|
// face = (uint)reader.GetInt32(5),
|
||||||
id = (uint)reader.GetInt32(6),
|
// id = (uint)reader.GetInt32(6),
|
||||||
loc = (uint)reader.GetInt32(7),
|
// loc = (uint)reader.GetInt32(7),
|
||||||
order = reader.GetInt32(8),
|
// order = reader.GetInt32(8),
|
||||||
link = (uint)reader.GetInt32(9)
|
// link = (uint)reader.GetInt32(9)
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[2]};", connection))
|
// using (var command = new SqliteCommand($"SELECT * FROM {foundTN[2]};", connection))
|
||||||
{
|
// {
|
||||||
using var reader = command.ExecuteReader();
|
// using var reader = command.ExecuteReader();
|
||||||
while (reader.Read())
|
// while (reader.Read())
|
||||||
{
|
// {
|
||||||
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
// if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
continue;
|
// continue;
|
||||||
for (int i = 0; i < faces.Length; i++)
|
// for (int i = 0; i < faces.Length; i++)
|
||||||
{
|
// {
|
||||||
if (faces[i] == null)
|
// if (faces[i] == null)
|
||||||
faces[i] = new Face();
|
// faces[i] = new Face();
|
||||||
if (reader.GetInt32(2 + i) == 1)
|
// if (reader.GetInt32(2 + i) == 1)
|
||||||
faces[i].faceOptions.Add(reader.GetString(1));
|
// faces[i].faceOptions.Add(reader.GetString(1));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
string[] files = Directory.GetFiles(args[0], "*.lsf", SearchOption.AllDirectories);
|
// string[] files = Directory.GetFiles(args[0], "*.lsf", SearchOption.AllDirectories);
|
||||||
LsfManager lm = new();
|
// LsfManager lm = new();
|
||||||
foreach (string file in files)
|
// foreach (string file in files)
|
||||||
{
|
// {
|
||||||
if (lm.LoadLsf(file, true))
|
// if (lm.LoadLsf(file, true))
|
||||||
Console.WriteLine($"Load {file} Success");
|
// Console.WriteLine($"Load {file} Success");
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
Console.WriteLine($"Load {file} Failed");
|
// Console.WriteLine($"Load {file} Failed");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
connection.Close();
|
// connection.Close();
|
||||||
string outputDir = Path.Combine(Path.GetDirectoryName(args[0]), "Output");
|
// string outputDir = Path.Combine(Path.GetDirectoryName(args[0]), "Output");
|
||||||
if (!Directory.Exists(outputDir))
|
// if (!Directory.Exists(outputDir))
|
||||||
Directory.CreateDirectory(outputDir);
|
// Directory.CreateDirectory(outputDir);
|
||||||
var parallelOptions = new ParallelOptions
|
// var parallelOptions = new ParallelOptions
|
||||||
{
|
// {
|
||||||
MaxDegreeOfParallelism = 6 // 设置最大并行线程数
|
// MaxDegreeOfParallelism = 6 // 设置最大并行线程数
|
||||||
};
|
// };
|
||||||
|
|
||||||
// //ST //表情还要另取?
|
// //ST //表情还要另取?
|
||||||
// Parallel.ForEach(stts, parallelOptions, stt =>
|
// Parallel.ForEach(stts, parallelOptions, stt =>
|
||||||
// //foreach (StTable stt in stts)
|
// //foreach (StTable stt in stts)
|
||||||
// {
|
// {
|
||||||
// if (stt.order == 0) //仅提取鉴赏中有的ST
|
// if (stt.order == 0) //仅提取鉴赏中有的ST
|
||||||
// return;
|
// return;
|
||||||
// //continue;
|
// //continue;
|
||||||
// string targetFilename = Path.Combine(outputDir, stt.name); //最后保存可用的文件名
|
// string targetFilename = Path.Combine(outputDir, stt.name); //最后保存可用的文件名
|
||||||
// LsfData? lsfData = lm.FindLsfDataByName(stt.file) ?? throw new Exception($"错误,未找到与{stt.file}对应的lsf数据");
|
// LsfData? lsfData = lm.FindLsfDataByName(stt.file) ?? throw new Exception($"错误,未找到与{stt.file}对应的lsf数据");
|
||||||
// List<int> pendingList = [];
|
// List<int> pendingList = [];
|
||||||
// List<string> pendingListFn = [];
|
// List<string> pendingListFn = [];
|
||||||
// foreach (string o in stt.option)
|
// foreach (string o in stt.option)
|
||||||
// {
|
// {
|
||||||
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
// if (t.Count == 0)
|
// if (t.Count == 0)
|
||||||
// continue;
|
// continue;
|
||||||
// pendingList.AddRange(t);
|
// pendingList.AddRange(t);
|
||||||
// foreach (int i in t)
|
// foreach (int i in t)
|
||||||
// {
|
// {
|
||||||
// pendingListFn.Add(lsfData.lli[i].nameStr);
|
// pendingListFn.Add(lsfData.lli[i].nameStr);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// pendingList = TableManagercs.OrderLayer(pendingList, pendingListFn);
|
// pendingList = TableManagercs.OrderLayer(pendingList, pendingListFn);
|
||||||
// int n = 0;
|
// int n = 0;
|
||||||
// foreach (string o in faces[(int)stt.face].faceOptions)
|
// foreach (string o in faces[(int)stt.face].faceOptions)
|
||||||
// {
|
// {
|
||||||
// List<int> pendingListCopy = new(pendingList);
|
// List<int> pendingListCopy = new(pendingList);
|
||||||
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
// if (t.Count == 0)
|
// if (t.Count == 0)
|
||||||
// continue;
|
// continue;
|
||||||
// pendingListCopy.AddRange(t);
|
// pendingListCopy.AddRange(t);
|
||||||
// if (!ImageManager.Process(lsfData, [.. pendingListCopy], targetFilename + $"_{n++}.png"))
|
// if (!ImageManager.Process(lsfData, [.. pendingListCopy], targetFilename + $"_{n++}.png"))
|
||||||
// throw new Exception("Process Fail");
|
// throw new Exception("Process Fail");
|
||||||
// else
|
// else
|
||||||
// Console.WriteLine($"Export {stt.name}_{n - 1} Success");
|
// Console.WriteLine($"Export {stt.name}_{n - 1} Success");
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
////}
|
////}
|
||||||
|
|
||||||
//EV
|
// //EV
|
||||||
Parallel.ForEach(evts, parallelOptions, evt =>
|
// Parallel.ForEach(evts, parallelOptions, evt =>
|
||||||
//foreach (EvTable evt in evts)
|
// //foreach (EvTable evt in evts)
|
||||||
{
|
// {
|
||||||
if (evt.order == 0) //仅提取鉴赏中有的CG
|
// if (evt.order == 0) //仅提取鉴赏中有的CG
|
||||||
return;
|
// return;
|
||||||
//continue;
|
// //continue;
|
||||||
string targetFilename = Path.Combine(outputDir, evt.name + ".png"); //最后保存可用的文件名
|
// string targetFilename = Path.Combine(outputDir, evt.name + ".png"); //最后保存可用的文件名
|
||||||
LsfData lsfData = lm.FindLsfDataByName(evt.file) ?? throw new Exception("Something Wrong");
|
// LsfData lsfData = lm.FindLsfDataByName(evt.file) ?? throw new Exception("Something Wrong");
|
||||||
List<int> pendingList = [];
|
// List<int> pendingList = [];
|
||||||
List<string> pendingListFn = [];
|
// List<string> pendingListFn = [];
|
||||||
foreach (string o in evt.option)
|
// foreach (string o in evt.option)
|
||||||
{
|
// {
|
||||||
List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
if (t.Count == 0)
|
// if (t.Count == 0)
|
||||||
continue;
|
// continue;
|
||||||
pendingList.AddRange(t);
|
// pendingList.AddRange(t);
|
||||||
foreach (int i in t)
|
// foreach (int i in t)
|
||||||
{
|
// {
|
||||||
pendingListFn.Add(lsfData.lli[i].nameStr);
|
// pendingListFn.Add(lsfData.lli[i].nameStr);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
pendingList = TableManagercs.OrderLayer(pendingList, pendingListFn);
|
// pendingList = TableManagercs.OrderLayer(pendingList, pendingListFn);
|
||||||
if (pendingList[0] != 0)
|
// if (pendingList[0] != 0)
|
||||||
pendingList.Insert(0, 0);
|
// pendingList.Insert(0, 0);
|
||||||
if (!ImageManager.Process(lsfData, [.. pendingList], targetFilename))
|
// if (!ImageManager.Process(lsfData, [.. pendingList], targetFilename))
|
||||||
throw new Exception("Process Fail");
|
// throw new Exception("Process Fail");
|
||||||
else
|
// else
|
||||||
Console.WriteLine($"Export {evt.name} Success");
|
// Console.WriteLine($"Export {evt.name} Success");
|
||||||
});
|
// });
|
||||||
//}
|
// //}
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -328,23 +328,22 @@ namespace EscudeTools
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
////Batch Repack ESC-ARC Package
|
//Batch Repack ESC-ARC Package
|
||||||
//if (Directory.Exists(args[0]) && Directory.Exists(args[1]))
|
if (Directory.Exists(args[0]))// && Directory.Exists(args[1])
|
||||||
//{
|
{
|
||||||
// string[] directories = Directory.GetDirectories(args[0]);
|
string[] directories = Directory.GetDirectories(args[0]);
|
||||||
// foreach (string directory in directories)
|
foreach (string directory in directories)
|
||||||
// {
|
{
|
||||||
// PackManager pm = new();
|
PackManager pm = new();
|
||||||
// string providerFilePath = Path.Combine(args[1], Path.GetFileName(directory) + ".bin");
|
//string providerFilePath = Path.Combine(args[1], Path.GetFileName(directory) + ".bin");
|
||||||
// if (pm.Repack(directory, 2,true, providerFilePath))
|
if (pm.Repack(directory, 2, true))
|
||||||
// Console.WriteLine("Repack Package Success");
|
Console.WriteLine("Repack Package Success");
|
||||||
// else
|
else
|
||||||
// {
|
{
|
||||||
// Console.WriteLine("Repack Package Failed");
|
Console.WriteLine("Repack Package Failed");
|
||||||
// return;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
////Batch Unpack Script(Full, Text, Mess)
|
////Batch Unpack Script(Full, Text, Mess)
|
||||||
@ -413,7 +412,7 @@ namespace EscudeTools
|
|||||||
//smr.ExportMessDatabase(Path.GetDirectoryName(args[0]));
|
//smr.ExportMessDatabase(Path.GetDirectoryName(args[0]));
|
||||||
//return;
|
//return;
|
||||||
|
|
||||||
|
////repack sqlite to bin
|
||||||
//if (Directory.Exists(args[0]))
|
//if (Directory.Exists(args[0]))
|
||||||
//{
|
//{
|
||||||
// string[] files = Directory.GetFiles(args[0], "*.db");
|
// string[] files = Directory.GetFiles(args[0], "*.db");
|
||||||
@ -425,6 +424,7 @@ namespace EscudeTools
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
//if (Directory.Exists(args[0]))
|
//if (Directory.Exists(args[0]))
|
||||||
//{
|
//{
|
||||||
// string[] files = Directory.GetFiles(args[0], "*.bin");
|
// string[] files = Directory.GetFiles(args[0], "*.bin");
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"EscudeTools": {
|
"EscudeTools": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "G:\\x221.local\\lab2\\ev\\1\r\n\"G:\\x221.local\\lab2\\db_graphics.db\""
|
"commandLineArgs": "G:\\x221.local\\lab3\\Haison\\output\\output"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Reflection;
|
//部分代码来自Garbro
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace EscudeTools
|
namespace EscudeTools
|
||||||
@ -139,5 +140,29 @@ namespace EscudeTools
|
|||||||
count &= 7;
|
count &= 7;
|
||||||
return (byte)(v << count | v >> (8 - count));
|
return (byte)(v << count | v >> (8 - count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int ToBigEndian(int value)
|
||||||
|
{
|
||||||
|
byte[] bytes = BitConverter.GetBytes(value);
|
||||||
|
Array.Reverse(bytes);
|
||||||
|
return BitConverter.ToInt32(bytes, 0);
|
||||||
|
}
|
||||||
|
public static void CopyOverlapped(byte[] data, int src, int dst, int count)
|
||||||
|
{
|
||||||
|
if (dst > src)
|
||||||
|
{
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
int preceding = Math.Min(dst - src, count);
|
||||||
|
Buffer.BlockCopy(data, src, data, dst, preceding);
|
||||||
|
dst += preceding;
|
||||||
|
count -= preceding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(data, src, data, dst, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user