diff --git a/AssetStudioCLI/Options/CLIOptions.cs b/AssetStudioCLI/Options/CLIOptions.cs index 37cd04451..f6c970ebc 100644 --- a/AssetStudioCLI/Options/CLIOptions.cs +++ b/AssetStudioCLI/Options/CLIOptions.cs @@ -118,12 +118,14 @@ internal static class CLIOptions public static Option> o_filterByPathID; public static Option> o_filterByText; public static Option f_filterWithRegex; + public static Option f_filterExcludeMode; //advanced public static Option o_bundleBlockInfoCompression; public static Option o_bundleBlockCompression; public static Option o_maxParallelExportTasks; public static Option o_exportAssetList; public static Option o_assemblyPath; + public static Option o_stripPathPrefix; public static Option o_unityVersion; public static Option f_decompressToDisk; public static Option f_notRestoreExtensionName; @@ -456,7 +458,17 @@ private static void InitOptions() optionDefaultValue: false, optionName: "--filter-with-regex", optionDescription: "(Flag) If specified, the filter options will handle the specified text\n" + - "as a regular expression (doesn't apply to --filter-by-pathid)", + "as a regular expression (doesn't apply to --filter-by-pathid)\n", + optionExample: "", + optionHelpGroup: HelpGroups.Filter, + isFlag: true + ); + f_filterExcludeMode = new GroupedOption + ( + optionDefaultValue: false, + optionName: "--filter-exclude-mode", + optionDescription: "(Flag) If specified, the filter options will work as an exclusion\n" + + "(i.e. assets that match the filter conditions will be excluded)", optionExample: "", optionHelpGroup: HelpGroups.Filter, isFlag: true @@ -523,6 +535,14 @@ private static void InitOptions() optionExample: "", optionHelpGroup: HelpGroups.Advanced ); + o_stripPathPrefix = new GroupedOption + ( + optionDefaultValue: "", + optionName: "--strip-path-prefix ", + optionDescription: "Specify a path prefix to be stripped from exported asset paths\n", + optionExample: "Example: \"--strip-path-prefix assets/models/char/\"\n", + optionHelpGroup: HelpGroups.Advanced + ); o_unityVersion = new GroupedOption ( optionDefaultValue: null, @@ -739,6 +759,10 @@ public static void ParseArgs(string[] args) f_filterWithRegex.Value = true; flagIndexes.Add(i); break; + case "--filter-exclude-mode": + f_filterExcludeMode.Value = true; + flagIndexes.Add(i); + break; case "--decompress-to-disk": f_decompressToDisk.Value = true; flagIndexes.Add(i); @@ -1230,6 +1254,13 @@ public static void ParseArgs(string[] args) return; } break; + case "--strip-path-prefix": + o_stripPathPrefix.Value = Path.Combine(Path.GetDirectoryName(value), Path.GetFileName(value)); + if (!o_stripPathPrefix.Value.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + o_stripPathPrefix.Value += Path.DirectorySeparatorChar; + } + break; case "--unity-version": try { @@ -1457,12 +1488,14 @@ public static void ShowCurrentOptions() } sb.AppendLine(ShowCurrentFilter()); sb.AppendLine($"# Filter With Regex: {f_filterWithRegex}"); + sb.AppendLine($"# Filter Exclusion Mode: {f_filterExcludeMode}"); sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\""); break; case WorkMode.Live2D: sb.AppendLine($"# [{o_workMode} Options]"); sb.AppendLine($"# Filter by Text: \"{string.Join("\", \"", o_filterByText.Value)}\""); sb.AppendLine($"# Filter With Regex: {f_filterWithRegex}"); + sb.AppendLine($"# Filter Exclusion Mode: {f_filterExcludeMode}"); sb.AppendLine($"# Model Group Option: {o_l2dGroupOption}"); sb.AppendFormat("# Search Model-related Assets by: {0}\n", f_l2dAssetSearchByFilename.Value ? "FileName" : "Container"); sb.AppendLine($"# Motion Export Method: {o_l2dMotionMode}"); @@ -1476,6 +1509,7 @@ public static void ShowCurrentOptions() ? ShowCurrentFilter() : $"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\""); sb.AppendLine($"# Filter With Regex: {f_filterWithRegex}"); + sb.AppendLine($"# Filter Exclusion Mode: {f_filterExcludeMode}"); sb.AppendLine($"# Export Image Format: {o_imageFormat}"); sb.AppendLine($"# FBX Scale Factor: {o_fbxScaleFactor}"); sb.AppendLine($"# FBX Bone Size: {o_fbxBoneSize}"); diff --git a/AssetStudioCLI/ReadMe.md b/AssetStudioCLI/ReadMe.md index 29f21aff9..d8986be6a 100644 --- a/AssetStudioCLI/ReadMe.md +++ b/AssetStudioCLI/ReadMe.md @@ -152,6 +152,9 @@ Filter Options: --filter-with-regex (Flag) If specified, the filter options will handle the specified text as a regular expression (doesn't apply to --filter-by-pathid) + --filter-exclude-mode (Flag) If specified, the filter options will work as an exclusion + (i.e. assets that match the filter conditions will be excluded) + Advanced Options: --blockinfo-comp Specify the compression type of bundle's blockInfo data @@ -183,6 +186,9 @@ Advanced Options: --assembly-folder Specify the path to the assembly folder + --strip-path-prefix Specify a path prefix to be stripped from exported asset paths + Example: "--strip-path-prefix assets/models/char/" + --unity-version Specify Unity version Example: "--unity-version 2017.4.39f1" diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index 9bb0a113e..47628881b 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -637,6 +637,14 @@ private static void FilterAssets() ); break; } + + if (CLIOptions.f_filterExcludeMode.Value) + { + var excludeCount = assetsCount - filteredAssets.Count; + Logger.Info($"Excluding {excludeCount} asset(s) that match the filter."); + filteredAssets = parsedAssetsList.Except(filteredAssets).ToList(); + } + parsedAssetsList.Clear(); parsedAssetsList = filteredAssets; } @@ -651,6 +659,31 @@ public static void ExportAssets() var parallelExportCount = CLIOptions.o_maxParallelExportTasks.Value; var toExportAssetDict = new ConcurrentDictionary(); var toParallelExportAssetDict = new ConcurrentDictionary(); + + if (CLIOptions.o_stripPathPrefix.Value != null) + { + foreach (var asset in parsedAssetsList) + { + var containerPath = asset.Container; + if (string.IsNullOrEmpty(containerPath)) + { + continue; + } + if (!Path.Combine(Path.GetDirectoryName(containerPath), Path.GetFileName(containerPath)).StartsWith(CLIOptions.o_stripPathPrefix.Value)) + { + Logger.Warning($"Asset container path \"{asset.Container}\" does not start with the specified path prefix \"{CLIOptions.o_stripPathPrefix.Value}\""); + Logger.Warning("strip path prefix option will be ignored."); + CLIOptions.o_stripPathPrefix.Value = null; + break; + } + } + } + + if (CLIOptions.o_stripPathPrefix.Value != null) + { + Logger.Info($"Asset container path prefix \"{CLIOptions.o_stripPathPrefix.Value}\" will be stripped off."); + } + Parallel.ForEach(parsedAssetsList, asset => { string exportPath; @@ -663,7 +696,12 @@ public static void ExportAssets() case AssetGroupOption.ContainerPathFull: if (!string.IsNullOrEmpty(asset.Container)) { - exportPath = Path.Combine(savePath, Path.GetDirectoryName(asset.Container)); + var containerPath = Path.GetDirectoryName(asset.Container); + if (CLIOptions.o_stripPathPrefix.Value != null) + { + containerPath = containerPath.Substring(CLIOptions.o_stripPathPrefix.Value.Length); + } + exportPath = Path.Combine(savePath, containerPath); if (groupOption == AssetGroupOption.ContainerPathFull) { exportPath = Path.Combine(exportPath, Path.GetFileNameWithoutExtension(asset.Container)); @@ -732,7 +770,6 @@ public static void ExportAssets() toExportAssetDict.TryAdd(asset, exportPath); } }); - foreach (var toExportAsset in toExportAssetDict) { var asset = toExportAsset.Key;