Add option to export Live2D Cubism models

This commit is contained in:
VaDiM
2023-07-02 03:10:11 +03:00
parent 6d41693b85
commit aea6cbc97f
17 changed files with 1256 additions and 31 deletions

View File

@ -21,6 +21,7 @@ namespace AssetStudioCLI.Options
ExportRaw,
Dump,
Info,
ExportLive2D,
}
internal enum AssetGroupOption
@ -132,11 +133,12 @@ namespace AssetStudioCLI.Options
optionDefaultValue: WorkMode.Export,
optionName: "-m, --mode <value>",
optionDescription: "Specify working mode\n" +
"<Value: export(default) | exportRaw | dump | info>\n" +
"<Value: export(default) | exportRaw | dump | info | live2d>\n" +
"Export - Exports converted assets\n" +
"ExportRaw - Exports raw data\n" +
"Dump - Makes asset dumps\n" +
"Info - Loads file(s), shows the number of supported for export assets and exits\n" +
"Live2D - Exports Live2D Cubism 3 models\n" +
"Example: \"-m info\"\n",
optionHelpGroup: HelpGroups.General
);
@ -414,6 +416,17 @@ namespace AssetStudioCLI.Options
case "info":
o_workMode.Value = WorkMode.Info;
break;
case "live2d":
o_workMode.Value = WorkMode.ExportLive2D;
o_exportAssetTypes.Value = new List<ClassIDType>()
{
ClassIDType.AnimationClip,
ClassIDType.GameObject,
ClassIDType.MonoBehaviour,
ClassIDType.Texture2D,
ClassIDType.Transform,
};
break;
default:
Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n");
Console.WriteLine(o_workMode.Description);
@ -422,6 +435,11 @@ namespace AssetStudioCLI.Options
break;
case "-t":
case "--asset-type":
if (o_workMode.Value == WorkMode.ExportLive2D)
{
i++;
continue;
}
var splittedTypes = ValueSplitter(value);
o_exportAssetTypes.Value = new List<ClassIDType>();
foreach (var type in splittedTypes)
@ -773,29 +791,37 @@ namespace AssetStudioCLI.Options
sb.AppendLine("[Current Options]");
sb.AppendLine($"# Working Mode: {o_workMode}");
sb.AppendLine($"# Input Path: \"{inputPath}\"");
if (o_workMode.Value != WorkMode.Info)
switch (o_workMode.Value)
{
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
sb.AppendLine($"# Restore TextAsset extension: {!f_notRestoreExtensionName.Value}");
}
else
{
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
case WorkMode.Info:
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break;
case WorkMode.ExportLive2D:
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
break;
default:
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
sb.AppendLine($"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}");
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
sb.AppendLine($"# Export Image Format: {o_imageFormat}");
sb.AppendLine($"# Export Audio Format: {o_audioFormat}");
sb.AppendLine($"# Log Level: {o_logLevel}");
sb.AppendLine($"# Log Output: {o_logOutput}");
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
sb.AppendLine(ShowCurrentFilter());
sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\"");
sb.AppendLine($"# Unity Version: \"{o_unityVersion}\"");
sb.AppendLine($"# Restore TextAsset extension: {!f_notRestoreExtensionName.Value}");
break;
}
sb.AppendLine("======");
Logger.Info(sb.ToString());

View File

@ -36,7 +36,7 @@ namespace AssetStudioCLI
if (studio.LoadAssets())
{
studio.ParseAssets();
if (options.filterBy != FilterBy.None)
if (options.filterBy != FilterBy.None && options.o_workMode.Value != WorkMode.ExportLive2D)
{
studio.FilterAssets();
}
@ -44,12 +44,18 @@ namespace AssetStudioCLI
{
studio.ExportAssetList();
}
if (options.o_workMode.Value == WorkMode.Info)
switch (options.o_workMode.Value)
{
studio.ShowExportableAssetsInfo();
return;
case WorkMode.Info:
studio.ShowExportableAssetsInfo();
break;
case WorkMode.ExportLive2D:
studio.ExportLive2D();
break;
default:
studio.ExportAssets();
break;
}
studio.ExportAssets();
}
}
catch (Exception ex)

View File

@ -6,6 +6,7 @@ using System.IO;
using System.Linq;
using System.Xml.Linq;
using static AssetStudioCLI.Exporter;
using static CubismLive2DExtractor.Live2DExtractor;
using Ansi = AssetStudioCLI.CLIAnsiColors;
namespace AssetStudioCLI
@ -14,6 +15,7 @@ namespace AssetStudioCLI
{
public AssetsManager assetsManager = new AssetsManager();
public List<AssetItem> parsedAssetsList = new List<AssetItem>();
private static Dictionary<AssetStudio.Object, string> containers = new Dictionary<AssetStudio.Object, string>();
private readonly CLIOptions options;
public Studio(CLIOptions cliOptions)
@ -51,7 +53,6 @@ namespace AssetStudioCLI
Logger.Info("Parse assets...");
var fileAssetsList = new List<AssetItem>();
var containers = new Dictionary<AssetStudio.Object, string>();
var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count);
Progress.Reset();
@ -147,7 +148,6 @@ namespace AssetStudioCLI
}
}
parsedAssetsList.AddRange(fileAssetsList);
containers.Clear();
fileAssetsList.Clear();
}
}
@ -379,5 +379,78 @@ namespace AssetStudioCLI
}
Logger.Info($"Finished exporting asset list with {parsedAssetsList.Count} items.");
}
public void ExportLive2D()
{
var baseDestPath = Path.Combine(options.o_outputFolder.Value, "Live2DOutput");
var useFullContainerPath = false;
Progress.Reset();
Logger.Info($"Searching for Live2D files...");
var cubismMocs = parsedAssetsList.Where(x =>
{
if (x.Type == ClassIDType.MonoBehaviour)
{
((MonoBehaviour)x.Asset).m_Script.TryGet(out var m_Script);
return m_Script?.m_ClassName == "CubismMoc";
}
return false;
}).Select(x => x.Asset).ToArray();
if (cubismMocs.Length == 0)
{
Logger.Default.Log(LoggerEvent.Info, "Live2D Cubism models were not found.", ignoreLevel: true);
return;
}
if (cubismMocs.Length > 1)
{
var basePathSet = cubismMocs.Select(x => containers[x].Substring(0, containers[x].LastIndexOf("/"))).ToHashSet();
if (basePathSet.Count != cubismMocs.Length)
{
useFullContainerPath = true;
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 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
);
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)
continue;
name = container;
Logger.Info($"[{modelCounter + 1}/{totalModelCount}] Exporting Live2D: \"{container.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);
modelCounter++;
}
catch (Exception ex)
{
Logger.Error($"Live2D model export error: \"{name}\"", ex);
}
Progress.Report(modelCounter, (int)totalModelCount);
}
var status = modelCounter > 0 ?
$"Finished exporting [{modelCounter}/{totalModelCount}] Live2D model(s) to \"{options.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" :
"Nothing exported.";
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
}
}
}