Merge branch 'AssetStudioMod' into ArknightsStudio

This commit is contained in:
VaDiM
2023-12-16 12:48:02 +03:00
42 changed files with 1502 additions and 410 deletions

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
<AssemblyTitle>ArknightsStudio by aelurum</AssemblyTitle>
<AssemblyName>ArknightsStudioCLI</AssemblyName>
<Version>1.0.1</Version>

View File

@ -29,6 +29,7 @@ namespace AssetStudioCLI
LogName = $"{appAssembly.Name}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log";
LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogName);
var arch = Environment.Is64BitProcess ? "x64" : "x32";
Console.OutputEncoding = System.Text.Encoding.UTF8;
LogToFile(LoggerEvent.Verbose, $"---{appAssembly.Name} v{appAssembly.Version} [{arch}] | Logger launched---\n" +
$"CMD Args: {string.Join(" ", CLIOptions.cliArgs)}");
@ -36,15 +37,15 @@ namespace AssetStudioCLI
private static string ColorLogLevel(LoggerEvent logLevel)
{
string formattedLevel = $"[{logLevel}]";
var formattedLevel = $"[{logLevel}]";
switch (logLevel)
{
case LoggerEvent.Info:
return $"{formattedLevel.Color(CLIAnsiColors.BrightCyan)}";
return $"{formattedLevel.Color(ColorConsole.BrightCyan)}";
case LoggerEvent.Warning:
return $"{formattedLevel.Color(CLIAnsiColors.BrightYellow)}";
return $"{formattedLevel.Color(ColorConsole.BrightYellow)}";
case LoggerEvent.Error:
return $"{formattedLevel.Color(CLIAnsiColors.BrightRed)}";
return $"{formattedLevel.Color(ColorConsole.BrightRed)}";
default:
return formattedLevel;
}
@ -59,7 +60,7 @@ namespace AssetStudioCLI
string formattedMessage;
if (consoleMode)
{
string colorLogLevel = ColorLogLevel(logMsgLevel);
var colorLogLevel = ColorLogLevel(logMsgLevel);
formattedMessage = $"{colorLogLevel} {message}";
if (multiLine)
{

View File

@ -56,14 +56,14 @@ namespace Arknights
{
var faceImage = m_Texture2D.ConvertToImage(true);
var faceAlpha = avgSprite.FaceSpriteAlphaTexture.ConvertToImage(true);
if (faceImage.Size() != avgSprite.FaceSize)
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
{
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
faceAlpha.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
}
tex = avgSprite.FullTexture.ConvertToImage(true);
tex.Mutate(x => x.DrawImage(faceImage, location: avgSprite.FacePos, opacity: 1f));
alphaTex.Mutate(x => x.DrawImage(faceAlpha, location: avgSprite.FacePos, opacity: 1f));
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
alphaTex.Mutate(x => x.DrawImage(faceAlpha, avgSprite.FacePos, opacity: 1f));
}
else
{
@ -87,11 +87,11 @@ namespace Arknights
var faceImage = m_Texture2D.ConvertToImage(true);
var tex = avgSprite.FullTexture.ConvertToImage(true);
if (faceImage.Size() != avgSprite.FaceSize)
if (new Size(faceImage.Width, faceImage.Height) != avgSprite.FaceSize)
{
faceImage.Mutate(x => x.Resize(new ResizeOptions { Size = avgSprite.FaceSize, Sampler = KnownResamplers.Lanczos3, Mode = ResizeMode.Stretch }));
}
tex.Mutate(x => x.DrawImage(faceImage, location: avgSprite.FacePos, opacity: 1f));
tex.Mutate(x => x.DrawImage(faceImage, avgSprite.FacePos, opacity: 1f));
return tex;
}
@ -201,7 +201,7 @@ namespace Arknights
{
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
{
var newSize = (Size)(originalImage.Size() / downscaleMultiplier);
var newSize = (Size)(new Size(originalImage.Width, originalImage.Height) / downscaleMultiplier);
originalImage.Mutate(x => x.Resize(newSize, KnownResamplers.Lanczos3, compand: true));
}
var rectX = (int)Math.Floor(textureRect.x);

View File

@ -1,49 +0,0 @@
using System;
namespace AssetStudioCLI
{
// Represents set with 16 base colors using ANSI escape codes, which should be supported in most terminals
// (well, except for windows editions before windows 10)
public static class CLIAnsiColors
{
public static readonly string
Black = "\u001b[30m",
Red = "\u001b[31m",
Green = "\u001b[32m",
Yellow = "\u001b[33m", //remapped to ~BrightWhite in Windows PowerShell 6
Blue = "\u001b[34m",
Magenta = "\u001b[35m", //remapped to ~Blue in Windows PowerShell 6
Cyan = "\u001b[36m",
White = "\u001b[37m",
BrightBlack = "\u001b[30;1m",
BrightRed = "\u001b[31;1m",
BrightGreen = "\u001b[32;1m",
BrightYellow = "\u001b[33;1m",
BrightBlue = "\u001b[34;1m",
BrightMagenta = "\u001b[35;1m",
BrightCyan = "\u001b[36;1m",
BrightWhite = "\u001b[37;1m";
private static readonly string Reset = "\u001b[0m";
public static string Color(this string str, string ansiColor)
{
if (!CLIWinAnsiFix.isAnsiSupported)
{
return str;
}
return $"{ansiColor}{str}{Reset}";
}
public static void ANSICodesTest()
{
Console.WriteLine("ANSI escape codes test");
Console.WriteLine($"Supported: {CLIWinAnsiFix.isAnsiSupported}");
Console.WriteLine("\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m");
Console.WriteLine("\u001b[34m E \u001b[35m F \u001b[36m G \u001b[37m H \u001b[0m");
Console.WriteLine("\u001b[30;1m A \u001b[31;1m B \u001b[32;1m C \u001b[33;1m D \u001b[0m");
Console.WriteLine("\u001b[34;1m E \u001b[35;1m F \u001b[36;1m G \u001b[37;1m H \u001b[0m");
}
}
}

View File

@ -1,62 +0,0 @@
// Based on code by tomzorz (https://gist.github.com/tomzorz/6142d69852f831fb5393654c90a1f22e)
using System;
using System.Runtime.InteropServices;
namespace AssetStudioCLI
{
static class CLIWinAnsiFix
{
public static readonly bool isAnsiSupported;
private const int STD_OUTPUT_HANDLE = -11;
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
[DllImport("kernel32.dll")]
private static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
private static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(int nStdHandle);
static CLIWinAnsiFix()
{
bool isWin = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
if (isWin)
{
isAnsiSupported = TryEnableVTMode();
if (!isAnsiSupported)
{
//Check for bash terminal emulator. E.g., Git Bash, Cmder
isAnsiSupported = Environment.GetEnvironmentVariable("TERM") != null;
}
}
else
{
isAnsiSupported = true;
}
}
// Enable support for ANSI escape codes
// (but probably only suitable for windows 10+)
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
private static bool TryEnableVTMode()
{
var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (!GetConsoleMode(iStdOut, out uint outConsoleMode))
{
return false;
}
outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(iStdOut, outConsoleMode))
{
return false;
}
return true;
}
}
}

View File

@ -409,7 +409,7 @@ namespace AssetStudioCLI
Directory.CreateDirectory(dir);
return true;
}
Logger.Error($"Export error. File \"{fullPath.Color(CLIAnsiColors.BrightRed)}\" already exist");
Logger.Error($"Export error. File \"{fullPath.Color(ColorConsole.BrightRed)}\" already exist");
return false;
}

View File

@ -14,6 +14,7 @@ namespace AssetStudioCLI.Options
General,
Convert,
Logger,
Live2D,
FBX,
Filter,
Arknights,
@ -93,6 +94,9 @@ namespace AssetStudioCLI.Options
public static bool convertTexture;
public static Option<ImageFormat> o_imageFormat;
public static Option<AudioFormat> o_audioFormat;
//live2d
public static Option<CubismLive2DExtractor.Live2DMotionMode> o_l2dMotionMode;
public static Option<bool> f_l2dForceBezier;
//fbx
public static Option<float> o_fbxScaleFactor;
public static Option<int> o_fbxBoneSize;
@ -222,7 +226,7 @@ namespace AssetStudioCLI.Options
optionDefaultValue: "ASExport",
optionName: "-o, --output <path>",
optionDescription: "Specify path to the output folder\n" +
"If path isn't specifyed, 'ASExport' folder will be created in the program's work folder\n",
"If path isn't specified, 'ASExport' folder will be created in the program's work folder\n",
optionHelpGroup: HelpGroups.General
);
o_displayHelp = new GroupedOption<bool>
@ -279,6 +283,30 @@ namespace AssetStudioCLI.Options
);
#endregion
#region Init Cubism Live2D Options
o_l2dMotionMode = new GroupedOption<CubismLive2DExtractor.Live2DMotionMode>
(
optionDefaultValue: CubismLive2DExtractor.Live2DMotionMode.MonoBehaviour,
optionName: "--l2d-motion-mode <value>",
optionDescription: "Specify Live2D motion export mode\n" +
"<Value: monoBehaviour(default) | animationClip>\n" +
"MonoBehaviour - Try to export motions from MonoBehaviour Fade motions\n" +
"If no Fade motions are found, the AnimationClip method will be used\n" +
"AnimationClip - Try to export motions using AnimationClip assets\n" +
"Example: \"--l2d-motion-mode animationClip\"\n",
optionHelpGroup: HelpGroups.Live2D
);
f_l2dForceBezier = new GroupedOption<bool>
(
optionDefaultValue: false,
optionName: "--l2d-force-bezier",
optionDescription: "(Flag) If specified, Linear motion segments will be calculated as Bezier segments\n" +
"(May help if the exported motions look jerky/not smooth enough)",
optionHelpGroup: HelpGroups.Live2D,
isFlag: true
);
#endregion
#region Init FBX Options
o_fbxScaleFactor = new GroupedOption<float>
(
@ -340,7 +368,7 @@ namespace AssetStudioCLI.Options
);
#endregion
#region Arknights Options
#region Init Arknights Options
akResizedOnly = true;
o_akSpriteAlphaMode = new GroupedOption<AkSpriteAlphaMode>
(
@ -385,7 +413,7 @@ namespace AssetStudioCLI.Options
(
optionDefaultValue: false,
optionName: "--original-avg-names",
optionDescription: "(Flag) If specified, names of avg character sprites will not be fixed\n",
optionDescription: "(Flag) If specified, names of avg character sprites will not be restored\n",
optionHelpGroup: HelpGroups.Arknights,
isFlag: true
);
@ -447,8 +475,8 @@ namespace AssetStudioCLI.Options
{
cliArgs = args;
var brightYellow = CLIAnsiColors.BrightYellow;
var brightRed = CLIAnsiColors.BrightRed;
var brightYellow = ColorConsole.BrightYellow;
var brightRed = ColorConsole.BrightRed;
if (args.Length == 0 || args.Any(x => x.ToLower() == "-h" || x.ToLower() == "--help" || x.ToLower() == "-?"))
{
@ -489,6 +517,7 @@ namespace AssetStudioCLI.Options
}
};
#region Parse "Working Mode" Option
var workModeOptionIndex = resplittedArgs.FindIndex(x => x.ToLower() == "-m" || x.ToLower() == "--mode");
if (workModeOptionIndex >= 0)
{
@ -515,6 +544,7 @@ namespace AssetStudioCLI.Options
case "info":
o_workMode.Value = WorkMode.Info;
break;
case "l2d":
case "live2d":
o_workMode.Value = WorkMode.ExportLive2D;
o_exportAssetTypes.Value = new List<ClassIDType>()
@ -546,6 +576,7 @@ namespace AssetStudioCLI.Options
}
resplittedArgs.RemoveRange(workModeOptionIndex, 2);
}
#endregion
#region Parse Flags
for (int i = 0; i < resplittedArgs.Count; i++)
@ -581,6 +612,16 @@ namespace AssetStudioCLI.Options
f_akAddAliases.Value = true;
resplittedArgs.RemoveAt(i);
break;
case "--l2d-force-bezier":
if (o_workMode.Value != WorkMode.ExportLive2D)
{
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n");
ShowOptionDescription(o_workMode.Description);
return;
}
f_l2dForceBezier.Value = true;
resplittedArgs.RemoveAt(i);
break;
}
}
#endregion
@ -782,6 +823,28 @@ namespace AssetStudioCLI.Options
return;
}
break;
case "--l2d-motion-mode":
if (o_workMode.Value != WorkMode.ExportLive2D)
{
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. This option is not suitable for the current working mode [{o_workMode.Value}].\n");
ShowOptionDescription(o_workMode.Description);
return;
}
switch (value.ToLower())
{
case "fade":
case "monobehaviour":
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.MonoBehaviour;
break;
case "animationclip":
o_l2dMotionMode.Value = CubismLive2DExtractor.Live2DMotionMode.AnimationClip;
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported Live2D motion mode: [{value.Color(brightRed)}].\n");
ShowOptionDescription(o_l2dMotionMode.Description);
return;
}
break;
case "--fbx-scale-factor":
{
var isFloat = float.TryParse(value, out float floatValue);
@ -949,7 +1012,7 @@ namespace AssetStudioCLI.Options
}
catch (Exception ex)
{
Console.WriteLine("Unknown Error.".Color(CLIAnsiColors.Red));
Console.WriteLine("Unknown Error.".Color(ColorConsole.Red));
Console.WriteLine(ex);
return;
}
@ -986,13 +1049,13 @@ namespace AssetStudioCLI.Options
private static bool TryFindOptionDescription(string option, Dictionary<string, string> dict, bool isFlag = false)
{
var optionDesc = dict.Where(x => x.Key.Contains(option));
var optionDesc = dict.Where(x => x.Key.Contains(option)).ToArray();
if (optionDesc.Any())
{
var arg = isFlag ? "flag" : "option";
var rand = new Random();
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Count()));
Console.WriteLine($"Did you mean [{ $"{rndOption.Key}".Color(CLIAnsiColors.BrightYellow) }] {arg}?");
var rndOption = optionDesc.ElementAt(rand.Next(0, optionDesc.Length));
Console.WriteLine($"Did you mean [{$"{rndOption.Key}".Color(ColorConsole.BrightYellow)}] {arg}?");
Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}");
return true;
@ -1034,7 +1097,7 @@ namespace AssetStudioCLI.Options
else
{
var arch = Environment.Is64BitProcess ? "x64" : "x32";
Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# v{appAssembly.Version}\n# Based on AssetStudioMod v0.17.3\n");
Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# v{appAssembly.Version}\n# Based on AssetStudioMod v0.17.4\n");
Console.WriteLine($"{usage}\n\n{helpMessage}");
}
}
@ -1105,7 +1168,7 @@ namespace AssetStudioCLI.Options
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
if (o_workMode.Value == WorkMode.Export)
{
@ -1134,7 +1197,9 @@ namespace AssetStudioCLI.Options
}
else
{
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Live2D Motion Export Method: {o_l2dMotionMode}");
sb.AppendLine($"# Force Bezier: {f_l2dForceBezier }");
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");
}
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break;

View File

@ -10,6 +10,7 @@ ArknightsStudioCLI <input path to asset file/folder> [-m, --mode <value>]
[-o, --output <path>] [-h, --help]
[--log-level <value>] [--log-output <value>]
[--image-format <value>] [--audio-format <value>]
[--l2d-motion-mode <value>] [--l2d-force-bezier]
[--fbx-scale-factor <value>] [--fbx-bone-size <value>]
[--filter-by-name <text>] [--filter-by-container <text>]
[--filter-by-pathid <text>] [--filter-by-text <text>]
@ -48,7 +49,7 @@ General Options:
Example: "-g container"
-o, --output <path> Specify path to the output folder
If path isn't specifyed, 'ASExport' folder will be created in the program's work folder
If path isn't specified, 'ASExport' folder will be created in the program's work folder
-h, --help Display help and exit
@ -72,6 +73,17 @@ Convert Options:
None - Do not convert audios and export them in their own format
Example: "--audio-format wav"
Live2D Options:
--l2d-motion-mode <value> Specify Live2D motion export mode
<Value: monoBehaviour(default) | animationClip>
MonoBehaviour - Try to export motions from MonoBehaviour Fade motions
If no Fade motions are found, the AnimationClip method will be used
AnimationClip - Try to export motions using AnimationClip assets
Example: "--l2d-motion-mode animationClip"
--l2d-force-bezier (Flag) If specified, Linear motion segments will be calculated as Bezier segments
(May help if the exported motions look jerky/not smooth enough)
FBX Options:
--fbx-scale-factor <value> Specify the FBX Scale Factor
<Value: float number from 0 to 100 (default=1)
@ -124,7 +136,7 @@ Arknights Options:
>0 - Make the shadow lighter
Example: "--shadow-gamma 0"
--original-avg-names (Flag) If specified, names of avg character sprites will not be fixed
--original-avg-names (Flag) If specified, names of avg character sprites will not be restored
--add-aliases (Flag) If specified, aliases will be added to avg character sprite names (if exist)

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Xml.Linq;
using static AssetStudioCLI.Exporter;
using static CubismLive2DExtractor.Live2DExtractor;
using Ansi = AssetStudioCLI.CLIAnsiColors;
using Ansi = AssetStudio.ColorConsole;
namespace AssetStudioCLI
{
@ -62,6 +62,7 @@ namespace AssetStudioCLI
var i = 0;
foreach (var assetsFile in assetsManager.assetsFileList)
{
var preloadTable = Array.Empty<PPtr<AssetStudio.Object>>();
foreach (var asset in assetsFile.Objects)
{
var assetItem = new AssetItem(asset);
@ -70,22 +71,31 @@ namespace AssetStudioCLI
var isExportable = false;
switch (asset)
{
case PreloadData m_PreloadData:
preloadTable = m_PreloadData.m_Assets;
break;
case AssetBundle m_AssetBundle:
var isStreamedSceneAssetBundle = m_AssetBundle.m_IsStreamedSceneAssetBundle;
if (!isStreamedSceneAssetBundle)
{
preloadTable = m_AssetBundle.m_PreloadTable;
}
assetItem.Text = string.IsNullOrEmpty(m_AssetBundle.m_AssetBundleName) ? m_AssetBundle.m_Name : m_AssetBundle.m_AssetBundleName;
foreach (var m_Container in m_AssetBundle.m_Container)
{
var preloadIndex = m_Container.Value.preloadIndex;
var preloadSize = m_Container.Value.preloadSize;
var preloadSize = isStreamedSceneAssetBundle ? preloadTable.Length : m_Container.Value.preloadSize;
var preloadEnd = preloadIndex + preloadSize;
for (int k = preloadIndex; k < preloadEnd; k++)
for (var k = preloadIndex; k < preloadEnd; k++)
{
var pptr = m_AssetBundle.m_PreloadTable[k];
var pptr = preloadTable[k];
if (pptr.TryGet(out var obj))
{
containers[obj] = m_Container.Key;
}
}
}
assetItem.Text = m_AssetBundle.m_Name;
break;
case ResourceManager m_ResourceManager:
foreach (var m_Container in m_ResourceManager.m_Container)
@ -110,7 +120,7 @@ namespace AssetStudioCLI
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
assetItem.FullSize = asset.byteSize + m_VideoClip.m_ExternalResources.m_Size;
assetItem.Text = m_VideoClip.m_Name;
break;
break;
case Shader m_Shader:
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
break;
@ -153,11 +163,11 @@ namespace AssetStudioCLI
}
foreach (var asset in loadedAssetsList)
{
if (containers.ContainsKey(asset.Asset))
if (containers.TryGetValue(asset.Asset, out var container))
{
asset.Container = containers[asset.Asset];
asset.Container = container;
if (asset.Type == ClassIDType.MonoBehaviour && asset.Container.Contains("/arts/charportraits/portraits"))
if (asset.Type == ClassIDType.MonoBehaviour && container.Contains("/arts/charportraits/portraits"))
{
var portraitsList = Arknights.AkSpriteHelper.GeneratePortraits(asset);
foreach (var portrait in portraitsList)
@ -618,6 +628,8 @@ namespace AssetStudioCLI
{
var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput");
var useFullContainerPath = false;
var motionMode = CLIOptions.o_l2dMotionMode.Value;
var forceBezier = CLIOptions.f_l2dForceBezier.Value;
Progress.Reset();
Logger.Info($"Searching for Live2D files...");
@ -631,6 +643,7 @@ namespace AssetStudioCLI
}
return false;
}).Select(x => x.Asset).ToArray();
if (cubismMocs.Length == 0)
{
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
@ -638,7 +651,18 @@ namespace AssetStudioCLI
}
if (cubismMocs.Length > 1)
{
var basePathSet = cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToHashSet();
var basePathSet = cubismMocs.Select(x =>
{
var pathLen = containers.TryGetValue(x, out var itemContainer) ? itemContainer.LastIndexOf("/") : 0;
pathLen = pathLen < 0 ? containers[x].Length : pathLen;
return itemContainer?.Substring(0, pathLen);
}).ToHashSet();
if (basePathSet.All(x => x == null))
{
Logger.Error($"Live2D Cubism export error: Cannot find any model related files.");
return;
}
if (basePathSet.Count != cubismMocs.Length)
{
@ -646,9 +670,16 @@ namespace AssetStudioCLI
Logger.Debug($"useFullContainerPath: {useFullContainerPath}");
}
}
var basePathList = useFullContainerPath ?
cubismMocs.Select(x => containers[x]).ToList() :
cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToList();
var basePathList = cubismMocs.Select(x =>
{
containers.TryGetValue(x, out var container);
container = useFullContainerPath
? container
: container?.Substring(0, container.LastIndexOf("/"));
return container;
}).Where(x => x != null).ToList();
var lookup = containers.ToLookup(
x => basePathList.Find(b => x.Value.Contains(b) && x.Value.Split('/').Any(y => y == b.Substring(b.LastIndexOf("/") + 1))),
x => x.Key
@ -656,31 +687,31 @@ namespace AssetStudioCLI
var totalModelCount = lookup.LongCount(x => x.Key != null);
Logger.Info($"Found {totalModelCount} model(s).");
var name = "";
var modelCounter = 0;
foreach (var assets in lookup)
{
var container = assets.Key;
if (container == null)
var srcContainer = assets.Key;
if (srcContainer == null)
continue;
name = container;
var container = srcContainer;
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{container.Color(Ansi.BrightCyan)}\"");
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{srcContainer.Color(Ansi.BrightCyan)}\"");
try
{
var modelName = useFullContainerPath ? Path.GetFileNameWithoutExtension(container) : container.Substring(container.LastIndexOf('/') + 1);
container = Path.HasExtension(container) ? container.Replace(Path.GetExtension(container), "") : container;
var destPath = Path.Combine(baseDestPath, container) + Path.DirectorySeparatorChar;
ExtractLive2D(assets, destPath, modelName, assemblyLoader);
ExtractLive2D(assets, destPath, modelName, assemblyLoader, motionMode, forceBezier);
modelCounter++;
}
catch (Exception ex)
{
Logger.Error($"Live2D model export error: \"{name}\"", ex);
Logger.Error($"Live2D model export error: \"{srcContainer}\"", ex);
}
Progress.Report(modelCounter, (int)totalModelCount);
}
var status = modelCounter > 0 ?
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
"Nothing exported.";