实现立绘合成功能
*初步测试通过
This commit is contained in:
parent
22ca8d0b34
commit
0ea149e129
@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
|
||||||
namespace EscudeTools
|
namespace EscudeTools
|
||||||
{
|
{
|
||||||
@ -169,7 +170,7 @@ namespace EscudeTools
|
|||||||
// Add columns to the create table query
|
// Add columns to the create table query
|
||||||
foreach (var column in sheet.col)
|
foreach (var column in sheet.col)
|
||||||
{
|
{
|
||||||
createTableQuery.Append($"{column.name} {Utils.GetSQLiteColumnType(column.type)}, ");
|
createTableQuery.Append($"{column.name} {Utils.GetSQLiteColumnType(column.type, column.size)}, ");
|
||||||
}
|
}
|
||||||
|
|
||||||
createTableQuery.Remove(createTableQuery.Length - 2, 2); // Remove the last comma and space
|
createTableQuery.Remove(createTableQuery.Length - 2, 2); // Remove the last comma and space
|
||||||
@ -177,6 +178,7 @@ namespace EscudeTools
|
|||||||
|
|
||||||
createTableCommand.CommandText = createTableQuery.ToString();
|
createTableCommand.CommandText = createTableQuery.ToString();
|
||||||
createTableCommand.ExecuteNonQuery();
|
createTableCommand.ExecuteNonQuery();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using SqliteCommand insertDataCommand = connection.CreateCommand();
|
using SqliteCommand insertDataCommand = connection.CreateCommand();
|
||||||
|
@ -4,7 +4,7 @@ namespace EscudeTools
|
|||||||
{
|
{
|
||||||
public class ImageManager
|
public class ImageManager
|
||||||
{
|
{
|
||||||
public static bool EvProcess(LsfData ld, int[] n, string target)
|
public static bool Process(LsfData ld, int[] n, string target)
|
||||||
{
|
{
|
||||||
//get base size
|
//get base size
|
||||||
int height = ld.lfh.height, width = ld.lfh.width;
|
int height = ld.lfh.height, width = ld.lfh.width;
|
||||||
@ -16,15 +16,28 @@ namespace EscudeTools
|
|||||||
int offsetX = ld.lli[n[i]].rect.left;
|
int offsetX = ld.lli[n[i]].rect.left;
|
||||||
int offsetY = ld.lli[n[i]].rect.top;
|
int offsetY = ld.lli[n[i]].rect.top;
|
||||||
int mode = ld.lli[n[i]].mode;
|
int mode = ld.lli[n[i]].mode;
|
||||||
baseImage.Composite(overlayImage, offsetX, offsetY, (mode == 3) ? CompositeOperator.Multiply : ((mode == 10) ? CompositeOperator.Plus : CompositeOperator.Over));
|
if (mode == 3)
|
||||||
|
{
|
||||||
|
overlayImage.Composite(baseImage, -1 * offsetX, -1 * offsetY, CompositeOperator.DstIn);
|
||||||
|
baseImage.Composite(overlayImage, offsetX, offsetY, CompositeOperator.Multiply);//原先就一条这个,发现处理透明时会有问题
|
||||||
|
}
|
||||||
|
else if (mode == 10)
|
||||||
|
{
|
||||||
|
baseImage.Composite(overlayImage, offsetX, offsetY, CompositeOperator.Plus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
baseImage.Composite(overlayImage, offsetX, offsetY, CompositeOperator.Over);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
baseImage.Write(target);
|
baseImage.Write(target);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool STProcess()
|
//上面足够给Ev、St用了
|
||||||
{
|
//public static bool StProcess()
|
||||||
throw new NotImplementedException();
|
//{
|
||||||
}
|
// throw new NotImplementedException();
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,13 @@ namespace EscudeTools
|
|||||||
public LsfData? FindLsfDataByName(string name)
|
public LsfData? FindLsfDataByName(string name)
|
||||||
{
|
{
|
||||||
lsfDataLookup.TryGetValue(name.ToLower(), out var lsfData);
|
lsfDataLookup.TryGetValue(name.ToLower(), out var lsfData);
|
||||||
|
|
||||||
|
|
||||||
|
//c**,为什么会有错字?
|
||||||
|
if (name == "08_Syuichi" && lsfData == null)
|
||||||
|
lsfDataLookup.TryGetValue("08_syuuichi", out lsfData);
|
||||||
|
|
||||||
|
|
||||||
return lsfData; // 如果未找到,则返回 null
|
return lsfData; // 如果未找到,则返回 null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ namespace EscudeTools
|
|||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
//批量处理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]))
|
||||||
{
|
{
|
||||||
@ -13,9 +14,9 @@ namespace EscudeTools
|
|||||||
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[2];
|
string[] foundTN = new string[3];
|
||||||
List<int> tableIds = [];
|
List<int> tableIds = [];
|
||||||
bool found1 = false, found2 = 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())
|
||||||
{
|
{
|
||||||
@ -33,11 +34,16 @@ namespace EscudeTools
|
|||||||
foundTN[1] = tableName;
|
foundTN[1] = tableName;
|
||||||
found2 = true;
|
found2 = true;
|
||||||
}
|
}
|
||||||
|
else if (tableName.StartsWith("表情"))
|
||||||
|
{
|
||||||
|
foundTN[2] = tableName;
|
||||||
|
found3 = true;
|
||||||
|
}
|
||||||
tableNames.Add(tableName);
|
tableNames.Add(tableName);
|
||||||
tableIds.Add(id++);
|
tableIds.Add(id++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!(found1 && found2))
|
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]}");
|
||||||
@ -85,10 +91,33 @@ namespace EscudeTools
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found3)
|
||||||
|
{
|
||||||
|
Console.WriteLine("自动识别失败,请选择存放表情信息的数据表ID: ");
|
||||||
|
string? input = Console.ReadLine();
|
||||||
|
if (int.TryParse(input, out int userInputId))
|
||||||
|
{
|
||||||
|
if (userInputId >= 0 && userInputId < tableIds.Count)
|
||||||
|
{
|
||||||
|
foundTN[2] = tableNames[userInputId];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Invalid input. Please enter a valid number.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
List<EvTable> evts = [];
|
List<EvTable> evts = [];
|
||||||
List<StTable> stts = [];
|
List<StTable> stts = [];
|
||||||
|
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();
|
||||||
@ -125,7 +154,7 @@ namespace EscudeTools
|
|||||||
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),
|
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),
|
||||||
@ -133,6 +162,22 @@ namespace EscudeTools
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
using (var command = new SqliteCommand($"SELECT * FROM {foundTN[2]};", connection))
|
||||||
|
{
|
||||||
|
using var reader = command.ExecuteReader();
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.IsDBNull(0) || string.IsNullOrEmpty(reader.GetString(0)))
|
||||||
|
continue;
|
||||||
|
for (int i = 0; i < faces.Length; i++)
|
||||||
|
{
|
||||||
|
if (faces[i] == null)
|
||||||
|
faces[i] = new Face();
|
||||||
|
if (reader.GetInt32(2 + i) == 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();
|
||||||
@ -153,38 +198,82 @@ namespace EscudeTools
|
|||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = 6 // 设置最大并行线程数
|
MaxDegreeOfParallelism = 6 // 设置最大并行线程数
|
||||||
};
|
};
|
||||||
Parallel.ForEach(evts, parallelOptions, evt =>
|
|
||||||
//foreach (EvTable evt in evts)
|
//ST //表情还要另取?
|
||||||
|
Parallel.ForEach(stts, parallelOptions, stt =>
|
||||||
|
//foreach (StTable stt in stts)
|
||||||
{
|
{
|
||||||
if (evt.order == 0) //仅提取鉴赏中有的CG
|
if (stt.order == 0) //仅提取鉴赏中有的ST
|
||||||
return;
|
return;
|
||||||
//continue;
|
//continue;
|
||||||
string targetFilename = Path.Combine(outputDir, evt.name + ".png"); //最后保存可用的文件名(这里还没有后缀)
|
string targetFilename = Path.Combine(outputDir, stt.name); //最后保存可用的文件名
|
||||||
LsfData lsfData = lm.FindLsfDataByName(evt.file) ?? throw new Exception("Something Wrong");
|
LsfData? lsfData = lm.FindLsfDataByName(stt.file) ?? throw new Exception($"错误,未找到与{stt.file}对应的lsf数据");
|
||||||
List<int> pendingList = [];
|
List<int> pendingList = [];
|
||||||
foreach (string o in evt.option)
|
List<string> pendingListFn = [];
|
||||||
|
foreach (string o in stt.option)
|
||||||
{
|
{
|
||||||
int t = TableManagercs.ParseOptions(lsfData, o);
|
List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
if (t == -1)
|
if (t.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
pendingList.Add(t);
|
pendingList.AddRange(t);
|
||||||
|
foreach (int i in t)
|
||||||
|
{
|
||||||
|
pendingListFn.Add(lsfData.lli[i].nameStr);
|
||||||
}
|
}
|
||||||
if (pendingList[0] != 0)
|
}
|
||||||
pendingList.Insert(0, 0);
|
pendingList = TableManagercs.OrderLayer(pendingList, pendingListFn);
|
||||||
if (!ImageManager.EvProcess(lsfData, [.. pendingList], targetFilename))
|
int n = 0;
|
||||||
|
foreach (string o in faces[(int)stt.face].faceOptions)
|
||||||
|
{
|
||||||
|
List<int> pendingListCopy = new(pendingList);
|
||||||
|
List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
|
if (t.Count == 0)
|
||||||
|
continue;
|
||||||
|
pendingListCopy.AddRange(t);
|
||||||
|
if (File.Exists(targetFilename + $"_{n++}.png"))
|
||||||
|
continue;
|
||||||
|
if (!ImageManager.Process(lsfData, [.. pendingListCopy], targetFilename + $"_{n++}.png"))
|
||||||
throw new Exception("Process Fail");
|
throw new Exception("Process Fail");
|
||||||
else
|
else
|
||||||
Console.WriteLine($"Export {evt.name} Success");
|
Console.WriteLine($"Export {stt.name}_{n - 1} Success");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
////EV
|
||||||
|
////Parallel.ForEach(evts, parallelOptions, evt =>
|
||||||
|
//foreach (EvTable evt in evts)
|
||||||
|
//{
|
||||||
|
// if (evt.order == 0) //仅提取鉴赏中有的CG
|
||||||
|
// //return;
|
||||||
|
// continue;
|
||||||
|
// string targetFilename = Path.Combine(outputDir, evt.name + ".png"); //最后保存可用的文件名
|
||||||
|
// LsfData lsfData = lm.FindLsfDataByName(evt.file) ?? throw new Exception("Something Wrong");
|
||||||
|
// List<int> pendingList = [];
|
||||||
|
// foreach (string o in evt.option)
|
||||||
|
// {
|
||||||
|
// List<int> t = TableManagercs.ParseOptions(lsfData, o);
|
||||||
|
// if (t.Count == 0)
|
||||||
|
// continue;
|
||||||
|
// pendingList.AddRange(t);
|
||||||
|
// }
|
||||||
|
// if (pendingList[0] != 0)
|
||||||
|
// pendingList.Insert(0, 0);
|
||||||
|
// if (!ImageManager.Process(lsfData, [.. pendingList], targetFilename))
|
||||||
|
// throw new Exception("Process Fail");
|
||||||
|
// else
|
||||||
|
// Console.WriteLine($"Export {evt.name} Success");
|
||||||
|
// //});
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//// 批量读取lsf文件
|
//// 批量读取lsf文件
|
||||||
//if (Directory.Exists(args[0]))
|
//if (Directory.Exists(args[0]))
|
||||||
//{
|
//{
|
||||||
@ -345,7 +434,7 @@ namespace EscudeTools
|
|||||||
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
////导出db_*.bin
|
||||||
//if (Directory.Exists(args[0]))
|
//if (Directory.Exists(args[0]))
|
||||||
//{
|
//{
|
||||||
// string[] files = Directory.GetFiles(args[0], "db_*.bin");
|
// string[] files = Directory.GetFiles(args[0], "db_*.bin");
|
||||||
@ -401,8 +490,7 @@ namespace EscudeTools
|
|||||||
// Console.WriteLine(" -s <filepath> Same as <filepath>");
|
// Console.WriteLine(" -s <filepath> Same as <filepath>");
|
||||||
// Console.WriteLine(" -h Display help info");
|
// Console.WriteLine(" -h Display help info");
|
||||||
//}
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -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\\lab2\\st\\3\r\n\"G:\\x221.local\\lab2\\db_graphics.db\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,15 +22,19 @@ namespace EscudeTools
|
|||||||
public string[] option;
|
public string[] option;
|
||||||
public uint coverd;
|
public uint coverd;
|
||||||
public uint filter;
|
public uint filter;
|
||||||
public uint color;
|
public uint face;
|
||||||
public uint id;
|
public uint id;
|
||||||
public uint loc;
|
public uint loc;
|
||||||
public int order;
|
public int order;
|
||||||
public uint link;
|
public uint link;
|
||||||
}
|
}
|
||||||
|
public class Face
|
||||||
|
{
|
||||||
|
public List<string> faceOptions = []; //face options str ↓需要解析
|
||||||
|
}
|
||||||
public class TableManagercs
|
public class TableManagercs
|
||||||
{
|
{
|
||||||
public static int ParseOptions(LsfData ld, string input)
|
public static List<int> ParseOptions(LsfData ld, string input)
|
||||||
{
|
{
|
||||||
// 正则表达式匹配 p*:* 格式
|
// 正则表达式匹配 p*:* 格式
|
||||||
Regex regex = new(@"p(\d+):(\d+)");
|
Regex regex = new(@"p(\d+):(\d+)");
|
||||||
@ -50,19 +54,52 @@ namespace EscudeTools
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
List<int> tmp = [];
|
||||||
|
List<string> tmpS = [];
|
||||||
for (int i = 0; i < ld.lli.Length; i++)
|
for (int i = 0; i < ld.lli.Length; i++)
|
||||||
{
|
{
|
||||||
if (ld.lli[i].index == 0 && ld.lli[i].state == 0)
|
if (ld.lli[i].index == 0 && ld.lli[i].state == 0)
|
||||||
{
|
{
|
||||||
if (ld.lli[i].index == results[0] && ld.lli[i].state + 1 == results[1])
|
if (ld.lli[i].index == results[0] && ld.lli[i].state + 1 == results[1])
|
||||||
return i;
|
{
|
||||||
|
tmpS.Add(ld.lli[i].nameStr);
|
||||||
|
tmp.Add(i);
|
||||||
}
|
}
|
||||||
if (ld.lli[i].index == results[0] && ld.lli[i].state == results[1])
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
//一般可以忽略这个警告
|
else if (ld.lli[i].index == results[0] && ld.lli[i].state == results[1])
|
||||||
Console.WriteLine($"[WARN] Found invalid index:state data {results[0]}:{results[1]} in {ld.lsfName}, may be a bug?");
|
{
|
||||||
return -1;
|
tmpS.Add(ld.lli[i].nameStr);
|
||||||
|
tmp.Add(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (tmp.Count == 0)
|
||||||
|
Console.WriteLine($"[WARN] Found invalid index:state data {results[0]}:{results[1]} in {ld.lsfName}, may be a bug?"); //一般可以忽略这个警告
|
||||||
|
return tmp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据文件名序号重新为组合顺序进行排序,解决某些长发角色合成的问题
|
||||||
|
public static List<int> OrderLayer(List<int> layer, List<string> layer_fn)
|
||||||
|
{
|
||||||
|
List<int> order = [];
|
||||||
|
foreach (string item in layer_fn)
|
||||||
|
{
|
||||||
|
string[] parts = item.Split("_");
|
||||||
|
if (parts.Length == 3)
|
||||||
|
{
|
||||||
|
if (int.TryParse(parts[2], out int number))
|
||||||
|
{
|
||||||
|
order.Add(number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<int> sortedTmp = layer.Select((value, index) => new { Value = value, Index = order[index] })
|
||||||
|
.OrderBy(x => x.Index)
|
||||||
|
.Select(x => x.Value)
|
||||||
|
.ToList();
|
||||||
|
return sortedTmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ParseCharactor(string input)
|
public static int ParseCharactor(string input)
|
||||||
|
@ -76,21 +76,26 @@ namespace EscudeTools
|
|||||||
stream.CopyTo(fileStream);
|
stream.CopyTo(fileStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetSQLiteColumnType(ushort type)
|
public static string GetSQLiteColumnType(ushort type, uint size)
|
||||||
{
|
{
|
||||||
return type switch
|
if (type == 1)
|
||||||
{
|
return "INTEGER";
|
||||||
// int
|
//else if (type == 4 && size == 4)
|
||||||
0x1 => "INTEGER",
|
// return "TEXT";
|
||||||
// float
|
else
|
||||||
0x2 => "REAL",
|
return "TEXT";
|
||||||
// string
|
//return type switch
|
||||||
0x3 => "TEXT",
|
//{
|
||||||
// bool
|
// // int
|
||||||
0x4 => "INTEGER",
|
// 0x1 => "INTEGER",
|
||||||
_ => throw new NotSupportedException($"Unsupported column type: {type}"),
|
// // float
|
||||||
};
|
// 0x2 => "REAL",
|
||||||
throw new NotImplementedException();
|
// // string
|
||||||
|
// 0x3 => "TEXT",
|
||||||
|
// // bool
|
||||||
|
// 0x4 => "INTEGER",
|
||||||
|
// _ => throw new NotSupportedException($"Unsupported column type: {type}"),
|
||||||
|
//};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool ISKANJI(byte x)
|
public static bool ISKANJI(byte x)
|
||||||
|
Loading…
Reference in New Issue
Block a user