实现立绘合成功能

*初步测试通过
This commit is contained in:
Chenx221 2024-10-25 16:00:35 +08:00
parent 22ca8d0b34
commit 0ea149e129
Signed by: chenx221
GPG Key ID: D7A9EC07024C3021
7 changed files with 215 additions and 63 deletions

View File

@ -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();
@ -295,7 +297,7 @@ namespace EscudeTools
reader.Read(); reader.Read();
if (reader.GetFieldType(1) == typeof(int)) if (reader.GetFieldType(1) == typeof(int))
{ {
flag2 = reader.GetInt32(1)==0; flag2 = reader.GetInt32(1) == 0;
} }
else if (reader.GetFieldType(1) == typeof(string)) else if (reader.GetFieldType(1) == typeof(string))
{ {
@ -309,7 +311,7 @@ namespace EscudeTools
{ {
if (types[i] != 4) if (types[i] != 4)
continue; continue;
if (textOffset == 0&&flag2) if (textOffset == 0 && flag2)
{ {
textOffset++; textOffset++;
flag = false; flag = false;
@ -346,7 +348,7 @@ namespace EscudeTools
} }
int index1 = textMulti.IndexOf(tableName[..^3]); int index1 = textMulti.IndexOf(tableName[..^3]);
textMulti.Add(tableName[..^3]); textMulti.Add(tableName[..^3]);
if(index1 == -1) if (index1 == -1)
{ {
text.Add(tableName[..^3]);//表名 text.Add(tableName[..^3]);//表名
textOffset1.Add(textOffset); textOffset1.Add(textOffset);

View File

@ -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();
//}
} }
} }

View File

@ -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
} }

View File

@ -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 = TableManagercs.OrderLayer(pendingList, pendingListFn);
pendingList.Insert(0, 0); int n = 0;
if (!ImageManager.EvProcess(lsfData, [.. pendingList], targetFilename)) foreach (string o in faces[(int)stt.face].faceOptions)
throw new Exception("Process Fail"); {
else List<int> pendingListCopy = new(pendingList);
Console.WriteLine($"Export {evt.name} Success"); 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");
else
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");
//} //}
} }
} }
} }

View File

@ -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\""
} }
} }
} }

View File

@ -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]) else if (ld.lli[i].index == results[0] && ld.lli[i].state == results[1])
return i; {
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?"); Console.WriteLine($"[WARN] Found invalid index:state data {results[0]}:{results[1]} in {ld.lsfName}, may be a bug?"); //一般可以忽略这个警告
return -1; 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)

View File

@ -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)
@ -123,16 +128,16 @@ namespace EscudeTools
}; };
} }
public static byte RotByteR (byte v, int count) public static byte RotByteR(byte v, int count)
{ {
count &= 7; count &= 7;
return (byte)(v >> count | v << (8-count)); return (byte)(v >> count | v << (8 - count));
} }
public static byte RotByteL (byte v, int count) public static byte RotByteL(byte v, int count)
{ {
count &= 7; count &= 7;
return (byte)(v << count | v >> (8-count)); return (byte)(v << count | v >> (8 - count));
} }
} }
} }