Update asset export logic

- Disabled saving files with a unique id if they already exist. A unique id will only be added to files with identical names during export.
- Added an option to overwrite exisisting files.
- Fixed support of multiple model export using "Export selected objects (split)" option. (#43)
This commit is contained in:
VaDiM
2025-08-17 11:23:07 +03:00
parent 52b0a21181
commit f0a69025fe
11 changed files with 149 additions and 46 deletions

View File

@@ -10,10 +10,13 @@ namespace AssetStudioCLI
{
internal static class Exporter
{
private static readonly HashSet<string> ExportPathHashSet = new HashSet<string>(System.StringComparer.OrdinalIgnoreCase);
private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath, string mode = "Export")
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
var canOverwrite = CLIOptions.f_overwriteExisting.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
@@ -24,17 +27,18 @@ namespace AssetStudioCLI
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (!File.Exists(fullPath))
if (ExportPathHashSet.Add(fullPath))
{
Directory.CreateDirectory(dir);
return true;
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
}
}
if (filenameFormat == FilenameFormat.AssetName)
else if (filenameFormat == FilenameFormat.AssetName)
{
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
if (CanWrite(fullPath, dir, canOverwrite))
{
Directory.CreateDirectory(dir);
return true;
}
}
@@ -42,6 +46,14 @@ namespace AssetStudioCLI
return false;
}
private static bool CanWrite(string fullPath, string dir, bool canOverwrite)
{
if (!canOverwrite && File.Exists(fullPath))
return false;
Directory.CreateDirectory(dir);
return true;
}
private static bool ExportVideoClip(AssetItem item, string exportPath)
{
var m_VideoClip = (VideoClip)item.Asset;
@@ -387,5 +399,10 @@ namespace AssetStudioCLI
? Path.GetRandomFileName()
: Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
public static void ClearHash()
{
ExportPathHashSet.Clear();
}
}
}

View File

@@ -93,6 +93,7 @@ namespace AssetStudioCLI.Options
public static Option<AssetGroupOption> o_groupAssetsBy;
public static Option<FilenameFormat> o_filenameFormat;
public static Option<string> o_outputFolder;
public static Option<bool> f_overwriteExisting;
public static Option<bool> o_displayHelp;
//logger
public static Option<LoggerEvent> o_logLevel;
@@ -254,6 +255,15 @@ namespace AssetStudioCLI.Options
optionExample: "",
optionHelpGroup: HelpGroups.General
);
f_overwriteExisting = new GroupedOption<bool>
(
optionDefaultValue: false,
optionName: "-r, --overwrite-existing",
optionDescription: "(Flag) If specified, Studio will overwrite existing files during asset export/dump\n",
optionExample: "",
optionHelpGroup: HelpGroups.General,
isFlag: true
);
o_displayHelp = new GroupedOption<bool>
(
optionDefaultValue: false,
@@ -678,6 +688,11 @@ namespace AssetStudioCLI.Options
switch (flag)
{
case "-r":
case "--overwrite-existing":
f_overwriteExisting.Value = true;
flagIndexes.Add(i);
break;
case "--l2d-search-by-filename":
if (o_workMode.Value != WorkMode.Live2D)
{
@@ -1403,7 +1418,8 @@ namespace AssetStudioCLI.Options
if (o_workMode.Value != WorkMode.Info)
{
sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}");
sb.AppendLine($"# Filename format: {o_filenameFormat}");
sb.AppendLine($"# Filename Format: {o_filenameFormat}");
sb.AppendLine($"# Overwrite Existing Files: {f_overwriteExisting}");
}
if (o_workMode.Value == WorkMode.Export)
{

View File

@@ -10,7 +10,7 @@ namespace AssetStudioCLI
{
internal static class ParallelExporter
{
private static ConcurrentDictionary<string, bool> savePathHash = new ConcurrentDictionary<string, bool>();
private static readonly ConcurrentDictionary<string, bool> ExportPathDict = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
public static bool ExportTexture2D(AssetItem item, string exportPath, out string debugLog)
{
@@ -184,6 +184,7 @@ namespace AssetStudioCLI
{
var fileName = FixFileName(item.Text);
var filenameFormat = CLIOptions.o_filenameFormat.Value;
var canOverwrite = CLIOptions.f_overwriteExisting.Value;
switch (filenameFormat)
{
case FilenameFormat.AssetName_PathID:
@@ -194,17 +195,18 @@ namespace AssetStudioCLI
break;
}
fullPath = Path.Combine(dir, fileName + extension);
if (savePathHash.TryAdd(fullPath.ToLower(), true) && !File.Exists(fullPath))
if (ExportPathDict.TryAdd(fullPath, true))
{
Directory.CreateDirectory(dir);
return true;
if (CanWrite(fullPath, dir, canOverwrite))
{
return true;
}
}
if (filenameFormat == FilenameFormat.AssetName)
else if (filenameFormat == FilenameFormat.AssetName)
{
fullPath = Path.Combine(dir, fileName + item.UniqueID + extension);
if (!File.Exists(fullPath))
if (CanWrite(fullPath, dir, canOverwrite))
{
Directory.CreateDirectory(dir);
return true;
}
}
@@ -212,6 +214,14 @@ namespace AssetStudioCLI
return false;
}
private static bool CanWrite(string fullPath, string dir, bool canOverwrite)
{
if (!canOverwrite && File.Exists(fullPath))
return false;
Directory.CreateDirectory(dir);
return true;
}
public static bool ParallelExportConvertFile(AssetItem item, string exportPath, out string debugLog)
{
switch (item.Type)
@@ -237,7 +247,7 @@ namespace AssetStudioCLI
public static void ClearHash()
{
savePathHash.Clear();
ExportPathDict.Clear();
}
}
}

View File

@@ -761,6 +761,7 @@ namespace AssetStudioCLI
}
Console.Write($"Exported [{exportedCount}/{toExportCount}]\r");
}
Exporter.ClearHash();
Parallel.ForEach(toParallelExportAssetDict, new ParallelOptions { MaxDegreeOfParallelism = parallelExportCount }, toExportAsset =>
{