From bbe55d4c562f865a27504973b986cbf783632d33 Mon Sep 17 00:00:00 2001 From: Chenx221 Date: Fri, 1 Nov 2024 16:57:49 +0800 Subject: [PATCH] update --- ArtemisFgTools/FgHelper.cs | 91 ++++++-- ArtemisFgTools/Program.cs | 197 +++++++++++------- ArtemisFgTools/Properties/launchSettings.json | 8 + 3 files changed, 195 insertions(+), 101 deletions(-) create mode 100644 ArtemisFgTools/Properties/launchSettings.json diff --git a/ArtemisFgTools/FgHelper.cs b/ArtemisFgTools/FgHelper.cs index 8f03c41..203f1bc 100644 --- a/ArtemisFgTools/FgHelper.cs +++ b/ArtemisFgTools/FgHelper.cs @@ -1,4 +1,7 @@ -namespace ArtemisFgTools +using System.Reflection; +using System.Xml.Linq; + +namespace ArtemisFgTools { public class FgHelper { @@ -10,10 +13,35 @@ public List> Pose { get; set; } = pose; public Dictionary> Face { get; set; } = face; } - - public static List FetchFgObjectsFromScript(string path) + //{"fg",ch="零",size="z1",mx=40,mode=3,resize=1,path=":fg/rei/z1/",file="rei_z1a0200",face="a0001",head="rei_z1a",lv=4,id=11}, + public class FgRecord : IEquatable + { + public string ChName { get; set; } + public string Size { get; set; } + public string File { get; set; } + public string Face { get; set; } + + public override bool Equals(object? obj) + { + return Equals(obj as FgRecord); + } + + public bool Equals(FgRecord? other) + { + return other != null && + ChName == other.ChName && + Size == other.Size && + File == other.File && + Face == other.Face; + } + + public override int GetHashCode() + { + return HashCode.Combine(ChName, Size, File, Face); + } + } + public static HashSet FetchFgObjectsFromScript(string path) { - //检查path是否存在,然后foreach获得文件夹下所有.ast文件,循环 if (!Directory.Exists(path)) throw new DirectoryNotFoundException("The path does not exist."); else @@ -33,45 +61,64 @@ throw new Exception("No valid fg script line found."); else { - List fgObjects = []; + HashSet fgRecords = []; foreach (var line in fgScriptLine) { - FgObject fgObject = ParseScriptFGLine(line); - // TODO:如果fgObject未被添加到fgObjects中,添加 - fgObjects.Add(fgObject); + FgRecord? fgRecord = ParseScriptFGLine(line); + if (fgRecord != null) + fgRecords.Add(fgRecord); + } - if (fgObjects.Count == 0) + if (fgRecords.Count == 0) throw new Exception("No valid fg object found."); else - return fgObjects; + return fgRecords; } } } - public static FgObject ParseScriptFGLine(string input) + public static FgRecord? ParseScriptFGLine(string input) { + FgRecord fgRecord = new(); input = input.Trim('{', '}'); - var pairs = input.Split(','); - - var result = new Dictionary(); - foreach (var pair in pairs) { var keyValue = pair.Split(['=', '='], 2); if (keyValue.Length == 2) { string key = keyValue[0].Trim(); - string value = keyValue[1].Trim().Trim('"'); // 去掉引号 - result[key] = value; + string value = keyValue[1].Trim().Trim('"').Trim('}'); + PropertyInfo? property = typeof(FgRecord).GetProperty(char.ToUpper(key[0]) + key[1..]); + if (property != null) + { + if (property.PropertyType == typeof(int)) + { + if (int.TryParse(value, out int intValue)) + property.SetValue(fgRecord, intValue); + else + throw new Exception($"Invalid integer value '{value}' for property '{key}'."); + } + else if (property.PropertyType == typeof(string)) + { + property.SetValue(fgRecord, value); + } + // 其他类型的处理可以在这里添加 + } } } - foreach (var kv in result) - { - Console.WriteLine($"{kv.Key}: {kv.Value}"); - } + //补个chname + if (fgRecord.File != null) + fgRecord.ChName = GetCharacterEngName(fgRecord.File); + return fgRecord.Size == null ? null : fgRecord; + } - throw new NotImplementedException(); + public static string GetCharacterEngName(string input) + { + int underscoreIndex = input.IndexOf('_'); + if (underscoreIndex > 0) + return input[..underscoreIndex]; + throw new Exception("Not supported character name format."); } } } diff --git a/ArtemisFgTools/Program.cs b/ArtemisFgTools/Program.cs index 98b1dd2..86bea7a 100644 --- a/ArtemisFgTools/Program.cs +++ b/ArtemisFgTools/Program.cs @@ -6,93 +6,121 @@ namespace ArtemisFgTools { internal class Program { - - static void Main() + static void Main(string[] args) { - Console.WriteLine("请输入立绘fg文件夹的所在路径(无需\"\"):"); - string? fgImagePath = Console.ReadLine(); - - Console.WriteLine("有找到exlist吗?(y/n) "); - string spModeStr = Console.ReadLine() ?? throw new Exception("Invalid input"); - bool spMode = (spModeStr == "n") || (spModeStr == "y" ? false : throw new Exception("Invalid input")); - - Console.WriteLine("请输入保存位置:"); - string? savePath = Console.ReadLine(); - if (!Directory.Exists(savePath)) - Directory.CreateDirectory(savePath); - - if (spMode) + if (args.Length == 1) { - Console.WriteLine("请输入script文件夹的所在路径:"); - string? scriptPath = Console.ReadLine(); - if (string.IsNullOrEmpty(fgImagePath) || string.IsNullOrEmpty(savePath) || string.IsNullOrEmpty(scriptPath)) - { - Console.WriteLine("路径不能为空"); - return; - } - if (!Directory.Exists(fgImagePath) || !Directory.Exists(scriptPath)) - { - Console.WriteLine("路径不存在"); - return; - } - List fgObjects = FetchFgObjectsFromScript(scriptPath); - Process(fgImagePath, savePath, size, fgObjects); - //我忘了size是干啥的了... - return; + if (args[0] == "-h") + Console.WriteLine("Usage: tools.exe -c (-s | -t ) -o "); + else + Console.WriteLine("Invalid arguments, Please check the usage via -h"); } - - Console.WriteLine("请输入exlist的文件路径:"); - string? luaFilePath = Console.ReadLine(); - - if (string.IsNullOrEmpty(fgImagePath) || string.IsNullOrEmpty(savePath) || string.IsNullOrEmpty(luaFilePath)) + else if (args.Length != 6) + Console.WriteLine("Invalid arguments, Please check the usage via -h"); + else if (args[0] != "-c" || !(args[2] == "-s" || args[2] == "-t") || args[4] != "-o") + Console.WriteLine("Invalid arguments, Please check the usage via -h"); + else { - Console.WriteLine("路径不能为空"); - return; - } - if (!Directory.Exists(fgImagePath) || !File.Exists(luaFilePath)) - { - Console.WriteLine("路径不存在"); - return; - } - - Dictionary? dictionary = ParseLuaTable(luaFilePath); - - if (dictionary != null) - { - if (dictionary["fg"] is Dictionary fgDictionary) + if (!Directory.Exists(args[5])) + Directory.CreateDirectory(args[5]); + if (!Directory.Exists(args[1])) + Console.WriteLine("Invalid fg path"); + else if (args[2] == "-s") { - if (fgDictionary["size"] is not List size || size.Count == 0) - throw new Exception("size not found or empty"); - fgDictionary.Remove("size"); - - //convert to FgObject - List fgObjects = []; - foreach (var fg in fgDictionary) - { - if (fg.Value is Dictionary fgValue) - { - var fuku = ConvertToStringList(fgValue["fuku"] as List); - var pose = ConvertToNestedStringList(fgValue["pose"] as List); - var face = ConvertToStringDictionary(fgValue["face"] as Dictionary); - //check null - if (fgValue["path"] is not string path || fgValue["head"] is not string head || fuku == null || pose == null || face == null) - { - Console.WriteLine("fg object has null value"); - continue; - } - path = path[4..]; // remove :fg/../ - fgObjects.Add(new FgObject(path, head, fuku, pose, face)); - } - } - //jmp - Process(fgImagePath, savePath, size, fgObjects); + if (!Directory.Exists(args[3])) + Console.WriteLine("Invalid script path"); + else + PreProcess2(args[1], args[5], args[3]); } else - Console.WriteLine("fg not found"); + { + if (!File.Exists(args[3])) + Console.WriteLine("Invalid lua table path"); + else + PreProcess(args[1], args[5], args[3]); + } } + + Console.WriteLine("Press any key to exit..."); + Console.ReadKey(); } - private static void Process(string? fgImagePath, string? savePath, List size, List fgObjects) + private static void PreProcess2(string fgImagePath, string savePath, string scriptPath) + { + HashSet fgRecords = FetchFgObjectsFromScript(scriptPath); + if (fgRecords.Count == 0) + throw new Exception("No valid fg object found."); + //重新写个,我也懒得将FgRecord转FgObject了 + //Tips:如果有单独饰品素材,可能前面的解析会有遗漏 //反正遥かなるニライカナイ里没有戴眼镜的角色 (笑 + Process2(fgImagePath, savePath, fgRecords); + } + + private static void PreProcess(string fgImagePath, string savePath, string luaFilePath) + { + Dictionary dictionary = ParseLuaTable(luaFilePath) ?? throw new Exception("Lua table parsing failed"); + + if (dictionary["fg"] is Dictionary fgDictionary) + { + if (fgDictionary["size"] is not List size || size.Count == 0) + throw new Exception("size not found or empty"); + fgDictionary.Remove("size"); + + List fgObjects = []; + foreach (var fg in fgDictionary) + { + if (fg.Value is Dictionary fgValue) + { + var fuku = ConvertToStringList(fgValue["fuku"] as List); + var pose = ConvertToNestedStringList(fgValue["pose"] as List); + var face = ConvertToStringDictionary(fgValue["face"] as Dictionary); + //check null + if (fgValue["path"] is not string path || fgValue["head"] is not string head || fuku == null || pose == null || face == null) + throw new Exception("fg object has null value"); + path = path[4..]; // remove :fg/../ + fgObjects.Add(new FgObject(path, head, fuku, pose, face)); + } + } + Process(fgImagePath, savePath, size, fgObjects); + } + else + Console.WriteLine("fg not found"); + } + + private static void Process2(string fgImagePath, string savePath, HashSet fgRecords) + { + var parallelOptions = new ParallelOptions + { + MaxDegreeOfParallelism = 6 // 设置最大并行度 + }; + + Parallel.ForEach(fgRecords, parallelOptions, fgRecord => + { + string originImageBase = Path.Combine(fgImagePath, fgRecord.ChName, fgRecord.Size, fgRecord.File + ".png"); + string originImageFace = Path.Combine(fgImagePath, fgRecord.ChName, fgRecord.Size, fgRecord.Face + ".png"); + string targetFilename = $"{fgRecord.File}_{fgRecord.Face}.png"; + string targetPath = Path.Combine(savePath, fgRecord.Size); + + if (!File.Exists(originImageBase) || !File.Exists(originImageFace)) + { + Console.WriteLine("ERROR, Image not found. Details:"); + Console.WriteLine($"Base: {originImageBase}"); + Console.WriteLine($"Face: {originImageFace}"); + return; + } + if (!Directory.Exists(targetPath)) + Directory.CreateDirectory(targetPath); + targetPath = Path.Combine(targetPath, targetFilename); + if (File.Exists(targetPath)) + { + Console.WriteLine("File already exists, skipping..."); + return; + } + ProcessAndSaveLite(originImageBase, originImageFace, targetPath); + }); + + } + + private static void Process(string fgImagePath, string savePath, List size, List fgObjects) { foreach (var fgObject in fgObjects) { @@ -174,6 +202,19 @@ namespace ArtemisFgTools } } + private static void ProcessAndSaveLite(string baseImg, string faceImg, string target) + { + using MagickImage firstImage = new(baseImg); + List comment1 = ReadPngComment(firstImage.Comment); //base + using MagickImage secondImage = new(faceImg); + List comment2 = ReadPngComment(secondImage.Comment); //face + int x = comment2[0] - comment1[0]; // face x - base x + int y = comment2[1] - comment1[1]; // face y - base y + firstImage.Composite(secondImage, x, y, CompositeOperator.Over); + firstImage.Write(target); + Console.WriteLine($"Image {Path.GetFileName(target)} processing completed."); + } + private static void ProcessAndSave(string baseImg, string layerImg, string layer2Img, string target, bool special) { if (File.Exists(target)) @@ -223,9 +264,7 @@ namespace ArtemisFgTools LuaTable luaTable = lua.GetTable("exfgtable"); if (luaTable != null) - { return (Dictionary?)LuaTableToSs(luaTable); - } else { Console.WriteLine("Lua table not found"); diff --git a/ArtemisFgTools/Properties/launchSettings.json b/ArtemisFgTools/Properties/launchSettings.json new file mode 100644 index 0000000..bc604ae --- /dev/null +++ b/ArtemisFgTools/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "ArtemisFgTools": { + "commandName": "Project", + "commandLineArgs": "-c\r\nG:\\x221.local\\1\\lab\\fg\r\n-s\r\nG:\\x221.local\\1\\lab\\script\r\n-o\r\nG:\\x221.local\\1\\lab\\output" + } + } +} \ No newline at end of file