diff --git a/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj index fc533b6..59938d8 100644 --- a/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj +++ b/AssetStudio.PInvoke/AssetStudio.PInvoke.csproj @@ -3,7 +3,7 @@ net472;net6.0;net6.0-windows;net7.0;net7.0-windows true - 0.17.2.0 + 1.0.0 Copyright © Perfare 2020-2022; Copyright © hozuki 2020 embedded diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index 90b4762..cb2ed75 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -2,7 +2,7 @@ net472;net6.0;net6.0-windows;net7.0;net7.0-windows - 0.17.2.0 + 1.0.0 Copyright © Perfare 2018-2022; Copyright © aelurum 2023 embedded diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index af90baf..d9cadfb 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -91,7 +91,14 @@ namespace AssetStudio } if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up { - var m_IgnoreMasterTextureLimit = reader.ReadBoolean(); + if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up + { + var m_IgnoreMipmapLimit = reader.ReadBoolean(); + } + else + { + var m_IgnoreMasterTextureLimit = reader.ReadBoolean(); + } } if (version[0] >= 3) //3.0.0 - 5.4 { @@ -100,6 +107,11 @@ namespace AssetStudio var m_ReadAllowed = reader.ReadBoolean(); } } + if (version[0] > 2022 || (version[0] == 2022 && version[1] >= 2)) //2022.2 and up + { + var m_MipmapLimitGroupName = reader.ReadAlignedString(); + reader.AlignStream(); + } if (version[0] > 2018 || (version[0] == 2018 && version[1] >= 2)) //2018.2 and up { var m_StreamingMipmaps = reader.ReadBoolean(); diff --git a/AssetStudioCLI/AssetStudioCLI.csproj b/AssetStudioCLI/AssetStudioCLI.csproj index 326ad44..7aca31b 100644 --- a/AssetStudioCLI/AssetStudioCLI.csproj +++ b/AssetStudioCLI/AssetStudioCLI.csproj @@ -3,9 +3,9 @@ Exe net472;net6.0;net7.0 - AssetStudioMod by aelurum - AssetStudioModCLI - 0.17.2.0 + ArknightsStudio by aelurum + ArknightsStudioCLI + 1.0.0 Copyright © Perfare; Copyright © aelurum 2023 AnyCPU embedded @@ -24,6 +24,8 @@ + + @@ -32,6 +34,9 @@ + + + @@ -42,6 +47,8 @@ The dll is cross-platform while the executable isn't --> + + @@ -50,6 +57,9 @@ + + + @@ -58,39 +68,46 @@ + + + + + + + diff --git a/AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs b/AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs index b86ff28..5d8ef2b 100644 --- a/AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs +++ b/AssetStudioCLI/Components/Arknights/AkSpriteHelper.cs @@ -148,11 +148,11 @@ namespace Arknights bool resized = false; if (tex.Width != texMask.Width || tex.Height != texMask.Height) { - texMask.Mutate(x => x.Resize(tex.Width, tex.Height, CLIOptions.o_akAlphaMaskResampler.Value)); + texMask.Mutate(x => x.Resize(tex.Width, tex.Height, CLIOptions.o_akAlphaTexResampler.Value)); resized = true; } - var invGamma = 1.0 / (1.0 + CLIOptions.o_akAlphaMaskGamma.Value / 10.0); + var invGamma = 1.0 / (1.0 + CLIOptions.o_akShadowGamma.Value / 10.0); if (CLIOptions.akResizedOnly && !resized) { invGamma = 1.0; diff --git a/AssetStudioCLI/Components/AssetItem.cs b/AssetStudioCLI/Components/AssetItem.cs index acb25ac..47f1cbb 100644 --- a/AssetStudioCLI/Components/AssetItem.cs +++ b/AssetStudioCLI/Components/AssetItem.cs @@ -14,6 +14,7 @@ namespace AssetStudioCLI public ClassIDType Type; public string Text; public string UniqueID; + public GameObjectNode Node; public PortraitSprite AkPortraitSprite; public AssetItem(Object asset) diff --git a/AssetStudioCLI/Components/BaseNode.cs b/AssetStudioCLI/Components/BaseNode.cs new file mode 100644 index 0000000..ecc3a60 --- /dev/null +++ b/AssetStudioCLI/Components/BaseNode.cs @@ -0,0 +1,16 @@ +using AssetStudio; +using System.Collections.Generic; + +namespace AssetStudioCLI +{ + internal class BaseNode + { + public List nodes = new List(); + + public BaseNode() + { + + } + + } +} diff --git a/AssetStudioCLI/Components/GameObjectNode.cs b/AssetStudioCLI/Components/GameObjectNode.cs new file mode 100644 index 0000000..4d88ee0 --- /dev/null +++ b/AssetStudioCLI/Components/GameObjectNode.cs @@ -0,0 +1,16 @@ +using AssetStudio; +using System.Collections.Generic; + +namespace AssetStudioCLI +{ + internal class GameObjectNode : BaseNode + { + public GameObject gameObject; + + public GameObjectNode(GameObject gameObject) + { + this.gameObject = gameObject; + } + + } +} diff --git a/AssetStudioCLI/Exporter.cs b/AssetStudioCLI/Exporter.cs index ba09615..b55d327 100644 --- a/AssetStudioCLI/Exporter.cs +++ b/AssetStudioCLI/Exporter.cs @@ -4,6 +4,7 @@ using AssetStudioCLI.Options; using Newtonsoft.Json; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; @@ -243,7 +244,7 @@ namespace AssetStudioCLI var alias = ""; var m_Sprite = (Sprite)item.Asset; var type = CLIOptions.o_imageFormat.Value; - var spriteMaskMode = CLIOptions.o_akSpriteMaskMode.Value != AkSpriteMaskMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off; + var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off; var isCharAvgSprite = item.Container.Contains("avg/characters"); var isCharArt = item.Container.Contains("arts/characters"); @@ -271,7 +272,7 @@ namespace AssetStudioCLI if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath, alias)) return false; - if (CLIOptions.o_akSpriteMaskMode.Value == AkSpriteMaskMode.External && (isCharAvgSprite || isCharArt)) + if (CLIOptions.o_akSpriteAlphaMode.Value == AkSpriteAlphaMode.SearchExternal && (isCharAvgSprite || isCharArt)) { if (m_Sprite.m_RD.alphaTexture.IsNull) { @@ -307,7 +308,7 @@ namespace AssetStudioCLI public static bool ExportPortraitSprite(AssetItem item, string exportPath) { var type = CLIOptions.o_imageFormat.Value; - var spriteMaskMode = CLIOptions.o_akSpriteMaskMode.Value != AkSpriteMaskMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off; + var spriteMaskMode = CLIOptions.o_akSpriteAlphaMode.Value != AkSpriteAlphaMode.None ? SpriteMaskMode.Export : SpriteMaskMode.Off; if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) return false; @@ -342,6 +343,33 @@ namespace AssetStudioCLI return true; } + public static void ExportGameObject(GameObject gameObject, string exportPath, List animationList = null) + { + var convert = animationList != null + ? new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value, animationList.Select(x => (AnimationClip)x.Asset).ToArray()) + : new ModelConverter(gameObject, CLIOptions.o_imageFormat.Value); + exportPath = exportPath + FixFileName(gameObject.m_Name) + ".fbx"; + ExportFbx(convert, exportPath); + } + + private static void ExportFbx(IImported convert, string exportPath) + { + var eulerFilter = true; + var filterPrecision = (float)0.25f; + var exportAllNodes = true; + var exportSkins = true; + var exportAnimations = true; + var exportBlendShape = true; + var castToBone = false; + var boneSize = CLIOptions.o_fbxBoneSize.Value; + var exportAllUvsAsDiffuseMaps = false; + var scaleFactor = CLIOptions.o_fbxScaleFactor.Value; + var fbxVersion = 3; + var fbxFormat = 0; + ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision, + exportAllNodes, exportSkins, exportAnimations, exportBlendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, fbxVersion, fbxFormat == 1); + } + public static bool ExportDumpFile(AssetItem item, string exportPath) { if (item.Asset == null) diff --git a/AssetStudioCLI/Libraries/linux-x64/libAssetStudioFBXNative.so b/AssetStudioCLI/Libraries/linux-x64/libAssetStudioFBXNative.so new file mode 100644 index 0000000..6ee1b36 Binary files /dev/null and b/AssetStudioCLI/Libraries/linux-x64/libAssetStudioFBXNative.so differ diff --git a/AssetStudioCLI/Libraries/osx-arm64/libAssetStudioFBXNative.dylib b/AssetStudioCLI/Libraries/osx-arm64/libAssetStudioFBXNative.dylib new file mode 100644 index 0000000..85263f5 Binary files /dev/null and b/AssetStudioCLI/Libraries/osx-arm64/libAssetStudioFBXNative.dylib differ diff --git a/AssetStudioCLI/Libraries/osx-x64/libAssetStudioFBXNative.dylib b/AssetStudioCLI/Libraries/osx-x64/libAssetStudioFBXNative.dylib new file mode 100644 index 0000000..8d4d6ca Binary files /dev/null and b/AssetStudioCLI/Libraries/osx-x64/libAssetStudioFBXNative.dylib differ diff --git a/AssetStudioCLI/Options/CLIOptions.cs b/AssetStudioCLI/Options/CLIOptions.cs index 28d127b..0f8dab7 100644 --- a/AssetStudioCLI/Options/CLIOptions.cs +++ b/AssetStudioCLI/Options/CLIOptions.cs @@ -14,6 +14,8 @@ namespace AssetStudioCLI.Options General, Convert, Logger, + FBX, + Filter, Arknights, Advanced, } @@ -25,6 +27,7 @@ namespace AssetStudioCLI.Options Dump, Info, ExportLive2D, + SplitObjects, } internal enum AssetGroupOption @@ -58,11 +61,11 @@ namespace AssetStudioCLI.Options NameAndContainer, } - internal enum AkSpriteMaskMode + internal enum AkSpriteAlphaMode { None, - Internal, - External + InternalOnly, + SearchExternal } internal static class CLIOptions @@ -75,7 +78,8 @@ namespace AssetStudioCLI.Options private static Dictionary optionsDict; private static Dictionary flagsDict; private static Dictionary> optionGroups; - private static List supportedAssetTypes; + private static List exportableAssetTypes; + private static Dictionary knownAssetTypesDict; //general public static Option o_workMode; public static Option> o_exportAssetTypes; @@ -89,23 +93,28 @@ namespace AssetStudioCLI.Options public static bool convertTexture; public static Option o_imageFormat; public static Option o_audioFormat; - //arknights - public static bool akResizedOnly; - public static Option o_akSpriteMaskMode; - public static Option o_akAlphaMaskResampler; - private static string resamplerName; - public static Option o_akAlphaMaskGamma; - public static Option f_akOriginalAvgNames; - public static Option f_akAddAliases; - //advanced - public static Option o_exportAssetList; + //fbx + public static Option o_fbxScaleFactor; + public static Option o_fbxBoneSize; + //filter public static Option> o_filterByName; public static Option> o_filterByContainer; public static Option> o_filterByPathID; public static Option> o_filterByText; + //arknights + public static bool akResizedOnly; + public static Option o_akSpriteAlphaMode; + public static Option o_akAlphaTexResampler; + private static string resamplerName; + public static Option o_akShadowGamma; + public static Option f_akOriginalAvgNames; + public static Option f_akAddAliases; + //advanced + public static Option o_exportAssetList; public static Option o_assemblyPath; public static Option o_unityVersion; public static Option f_notRestoreExtensionName; + public static Option f_loadAllAssets; static CLIOptions() { @@ -150,7 +159,7 @@ namespace AssetStudioCLI.Options optionsDict = new Dictionary(); flagsDict = new Dictionary(); optionGroups = new Dictionary>(); - supportedAssetTypes = new List + exportableAssetTypes = new List { ClassIDType.Texture2D, ClassIDType.Sprite, @@ -164,6 +173,7 @@ namespace AssetStudioCLI.Options ClassIDType.MovieTexture, ClassIDType.Mesh, }; + knownAssetTypesDict = ((ClassIDType[])Enum.GetValues(typeof(ClassIDType))).ToHashSet().ToDictionary(x => x.ToString().ToLower(), y => y); #region Init General Options o_workMode = new GroupedOption @@ -171,18 +181,19 @@ namespace AssetStudioCLI.Options optionDefaultValue: WorkMode.Export, optionName: "-m, --mode ", optionDescription: "Specify working mode\n" + - "\n" + + "\n" + "Export - Exports converted assets\n" + "ExportRaw - Exports raw data\n" + "Dump - Makes asset dumps\n" + "Info - Loads file(s), shows the number of available for export assets and exits\n" + "Live2D - Exports Live2D Cubism 3 models\n" + + "SplitObjects - Exports split objects (fbx)\n" + "Example: \"-m info\"\n", optionHelpGroup: HelpGroups.General ); o_exportAssetTypes = new GroupedOption> ( - optionDefaultValue: supportedAssetTypes, + optionDefaultValue: exportableAssetTypes, optionName: "-t, --asset-type ", optionDescription: "Specify asset type(s) to export\n" + " ( - optionDefaultValue: "", + optionDefaultValue: "ASExport", optionName: "-o, --output ", 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", @@ -268,52 +279,113 @@ namespace AssetStudioCLI.Options ); #endregion + #region Init FBX Options + o_fbxScaleFactor = new GroupedOption + ( + optionDefaultValue: 1f, + optionName: "--fbx-scale-factor ", + optionDescription: "Specify the FBX Scale Factor\n" + + " + ( + optionDefaultValue: 10, + optionName: "--fbx-bone-size ", + optionDescription: "Specify the FBX Bone Size\n" + + "> + ( + optionDefaultValue: new List(), + optionName: "--filter-by-name ", + optionDescription: "Specify the name by which assets should be filtered\n" + + "*To specify multiple names write them separated by ',' or ';' without spaces\n" + + "Example: \"--filter-by-name char\" or \"--filter-by-name char,bg\"\n", + optionHelpGroup: HelpGroups.Filter + ); + o_filterByContainer = new GroupedOption> + ( + optionDefaultValue: new List(), + optionName: "--filter-by-container ", + optionDescription: "Specify the container by which assets should be filtered\n" + + "*To specify multiple containers write them separated by ',' or ';' without spaces\n" + + "Example: \"--filter-by-container arts\" or \"--filter-by-container arts,icons\"\n", + optionHelpGroup: HelpGroups.Filter + ); + o_filterByPathID = new GroupedOption> + ( + optionDefaultValue: new List(), + optionName: "--filter-by-pathid ", + optionDescription: "Specify the PathID by which assets should be filtered\n" + + "*To specify multiple PathIDs write them separated by ',' or ';' without spaces\n" + + "Example: \"--filter-by-pathid 7238605633795851352,-2430306240205277265\"\n", + optionHelpGroup: HelpGroups.Filter + ); + o_filterByText = new GroupedOption> + ( + optionDefaultValue: new List(), + optionName: "--filter-by-text ", + optionDescription: "Specify the text by which assets should be filtered\n" + + "Looks for assets that contain the specified text in their names or containers\n" + + "*To specify multiple values write them separated by ',' or ';' without spaces\n" + + "Example: \"--filter-by-text portrait\" or \"--filter-by-text portrait,art\"\n", + optionHelpGroup: HelpGroups.Filter + ); + #endregion + #region Arknights Options akResizedOnly = true; - o_akSpriteMaskMode = new GroupedOption + o_akSpriteAlphaMode = new GroupedOption ( - optionDefaultValue: AkSpriteMaskMode.External, - optionName: "--spritemask-mode ", - optionDescription: "Specify the mode in which you want to export sprites with alpha mask\n" + + optionDefaultValue: AkSpriteAlphaMode.SearchExternal, + optionName: "--spritealpha-mode ", + optionDescription: "Specify the mode in which you want to export sprites with alpha texture\n" + "\n" + - "None - Export sprites without alpha mask applied\n" + - "Internal - Export sprites with internal alpha mask applied (if exist)\n" + - "SearchExternal - Export sprites with internal alpha mask applied,\n" + - "and in case it doesn't exist, Studio will try to find an external alpha mask\n" + - "Example: \"--spritemask-mode internalOnly\"\n", + "None - Export sprites without alpha texture applied\n" + + "InternalOnly - Export sprites with internal alpha texture applied (if exist)\n" + + "SearchExternal - Export sprites with internal alpha texture applied,\n" + + "and in case it doesn't exist, Studio will try to find an external alpha texture\n" + + "Example: \"--spritealpha-mode internalOnly\"\n", optionHelpGroup: HelpGroups.Arknights ); - o_akAlphaMaskResampler = new GroupedOption + o_akAlphaTexResampler = new GroupedOption ( optionDefaultValue: KnownResamplers.MitchellNetravali, - optionName: "--alphamask-resampler ", - optionDescription: "Specify the alpha mask upscale algorithm for 2048x2048 sprites\n" + + optionName: "--alphatex-resampler ", + optionDescription: "Specify the alpha texture upscale algorithm for 2048x2048 sprites\n" + "\n" + "Mitchell - Mitchell Netravali algorithm. Yields good equilibrium between \n" + - "sharpness and smoothness (produces less artifacts than bicubic in case of alpha masks)\n" + + "sharpness and smoothness (produces less artifacts than bicubic in the current use case)\n" + "Spline - Similar to Mitchell Netravali but yielding smoother results\n" + "Welch - A high speed algorithm that delivers very sharpened results\n" + - "Example: \"--alphamask-resampler bicubic\"\n", + "Example: \"--alphatex-resampler bicubic\"\n", optionHelpGroup: HelpGroups.Arknights ); resamplerName = "Mitchell"; - o_akAlphaMaskGamma = new GroupedOption + o_akShadowGamma = new GroupedOption ( optionDefaultValue: 2, - optionName: "--alphamask-gamma ", - optionDescription: "Specify the alpha mask gamma correction for 2048x2048 sprites\n" + + optionName: "--shadow-gamma ", + optionDescription: "Specify the gamma correction of semi-transparent shadow for 2048x2048 sprites\n" + "\n" + - "<0 - Make the alpha mask darker\n" + - "0 - Do not change the gamma of alpha mask\n" + - ">0 - Make the alpha mask lighter\n" + - "Example: \"--alphamask-gamma 0\"\n", + "<0 - Make the shadow darker\n" + + "0 - Do not change the brightness of the shadow\n" + + ">0 - Make the shadow lighter\n" + + "Example: \"--shadow-gamma 0\"\n", optionHelpGroup: HelpGroups.Arknights ); f_akOriginalAvgNames = new GroupedOption ( optionDefaultValue: false, optionName: "--original-avg-names", - optionDescription: "(Flag) If specified, names of avg sprites with faces will not be fixed\n", + optionDescription: "(Flag) If specified, names of avg character sprites will not be fixed\n", optionHelpGroup: HelpGroups.Arknights, isFlag: true ); @@ -321,7 +393,7 @@ namespace AssetStudioCLI.Options ( optionDefaultValue: false, optionName: "--add-aliases", - optionDescription: "(Flag) If specified, aliases will be added to avg sprite names (if exist)", + optionDescription: "(Flag) If specified, aliases will be added to avg character sprite names (if exist)", optionHelpGroup: HelpGroups.Arknights, isFlag: true ); @@ -338,62 +410,33 @@ namespace AssetStudioCLI.Options "Example: \"--export-asset-list xml\"\n", optionHelpGroup: HelpGroups.Advanced ); - o_filterByName = new GroupedOption> - ( - optionDefaultValue: new List(), - optionName: "--filter-by-name ", - optionDescription: "Specify the name by which assets should be filtered\n" + - "*To specify multiple names write them separated by ',' or ';' without spaces\n" + - "Example: \"--filter-by-name char\" or \"--filter-by-name char,bg\"\n", - optionHelpGroup: HelpGroups.Advanced - ); - o_filterByContainer = new GroupedOption> - ( - optionDefaultValue: new List(), - optionName: "--filter-by-container ", - optionDescription: "Specify the container by which assets should be filtered\n" + - "*To specify multiple containers write them separated by ',' or ';' without spaces\n" + - "Example: \"--filter-by-container arts\" or \"--filter-by-container arts,icons\"\n", - optionHelpGroup: HelpGroups.Advanced - ); - o_filterByPathID = new GroupedOption> - ( - optionDefaultValue: new List(), - optionName: "--filter-by-pathid ", - optionDescription: "Specify the PathID by which assets should be filtered\n" + - "*To specify multiple PathIDs write them separated by ',' or ';' without spaces\n" + - "Example: \"--filter-by-pathid 7238605633795851352,-2430306240205277265\"\n", - optionHelpGroup: HelpGroups.Advanced - ); - o_filterByText = new GroupedOption> - ( - optionDefaultValue: new List(), - optionName: "--filter-by-text ", - optionDescription: "Specify the text by which assets should be filtered\n" + - "Looks for assets that contain the specified text in their names or containers\n" + - "*To specify multiple values write them separated by ',' or ';' without spaces\n" + - "Example: \"--filter-by-text portrait\" or \"--filter-by-text portrait,art\"\n", - optionHelpGroup: HelpGroups.Advanced - ); o_assemblyPath = new GroupedOption ( optionDefaultValue: "", optionName: "--assembly-folder ", - optionDescription: "Specify the path to the assembly folder", + optionDescription: "Specify the path to the assembly folder\n", optionHelpGroup: HelpGroups.Advanced ); o_unityVersion = new GroupedOption ( optionDefaultValue: "", optionName: "--unity-version ", - optionDescription: "Specify Unity version. Example: \"--unity-version 2017.4.39f1\"", + optionDescription: "Specify Unity version\nExample: \"--unity-version 2017.4.39f1\"\n", optionHelpGroup: HelpGroups.Advanced ); f_notRestoreExtensionName = new GroupedOption ( optionDefaultValue: false, optionName: "--not-restore-extension", - optionDescription: "(Flag) If specified, Studio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension", + optionDescription: "(Flag) If specified, Studio will not try to use/restore original TextAsset\nextension name, and will just export all TextAssets with the \".txt\" extension\n", + optionHelpGroup: HelpGroups.Advanced, + isFlag: true + ); + f_loadAllAssets = new GroupedOption + ( + optionDefaultValue: false, + optionName: "--load-all", + optionDescription: "(Flag) If specified, Studio will load assets of all types\n(Only for Dump, Info and ExportRaw modes)", optionHelpGroup: HelpGroups.Advanced, isFlag: true ); @@ -407,7 +450,7 @@ namespace AssetStudioCLI.Options var brightYellow = CLIAnsiColors.BrightYellow; var brightRed = CLIAnsiColors.BrightRed; - if (args.Length == 0 || args.Any(x => x == "-h" || x == "--help")) + if (args.Length == 0 || args.Any(x => x.ToLower() == "-h" || x.ToLower() == "--help" || x.ToLower() == "-?")) { showHelp = true; return; @@ -422,7 +465,6 @@ namespace AssetStudioCLI.Options $"Specified file or folder was not found. The input path must be specified as the first argument."); return; } - o_outputFolder.Value = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ASExport"); } else { @@ -447,6 +489,64 @@ namespace AssetStudioCLI.Options } }; + var workModeOptionIndex = resplittedArgs.FindIndex(x => x.ToLower() == "-m" || x.ToLower() == "--mode"); + if (workModeOptionIndex >= 0) + { + var option = resplittedArgs[workModeOptionIndex]; + if (workModeOptionIndex + 1 >= resplittedArgs.Count) + { + Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n"); + TryFindOptionDescription(option, optionsDict); + return; + } + var value = resplittedArgs[workModeOptionIndex + 1]; + switch (value.ToLower()) + { + case "export": + o_workMode.Value = WorkMode.Export; + break; + case "raw": + case "exportraw": + o_workMode.Value = WorkMode.ExportRaw; + break; + case "dump": + o_workMode.Value = WorkMode.Dump; + break; + case "info": + o_workMode.Value = WorkMode.Info; + break; + case "live2d": + o_workMode.Value = WorkMode.ExportLive2D; + o_exportAssetTypes.Value = new List() + { + ClassIDType.AnimationClip, + ClassIDType.GameObject, + ClassIDType.MonoBehaviour, + ClassIDType.Texture2D, + ClassIDType.Transform, + }; + break; + case "splitobjects": + o_workMode.Value = WorkMode.SplitObjects; + o_exportAssetTypes.Value = new List() + { + ClassIDType.GameObject, + ClassIDType.Texture2D, + ClassIDType.Material, + ClassIDType.Transform, + ClassIDType.Mesh, + ClassIDType.MeshRenderer, + ClassIDType.MeshFilter, + }; + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported working mode: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_workMode.Description); + return; + } + resplittedArgs.RemoveRange(workModeOptionIndex, 2); + } + #region Parse Flags for (int i = 0; i < resplittedArgs.Count; i++) { @@ -458,6 +558,21 @@ namespace AssetStudioCLI.Options f_notRestoreExtensionName.Value = true; resplittedArgs.RemoveAt(i); break; + case "--load-all": + switch (o_workMode.Value) + { + case WorkMode.ExportRaw: + case WorkMode.Dump: + case WorkMode.Info: + f_loadAllAssets.Value = true; + resplittedArgs.RemoveAt(i); + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{flag}] flag. This flag is not suitable for the current working mode [{o_workMode.Value}].\n"); + ShowOptionDescription(f_loadAllAssets.Description, isFlag: true); + return; + } + break; case "--original-avg-names": f_akOriginalAvgNames.Value = true; resplittedArgs.RemoveAt(i); @@ -479,43 +594,9 @@ namespace AssetStudioCLI.Options var value = resplittedArgs[i + 1].Replace("\"", ""); switch (option) { - case "-m": - case "--mode": - switch (value.ToLower()) - { - case "export": - o_workMode.Value = WorkMode.Export; - break; - case "raw": - case "exportraw": - o_workMode.Value = WorkMode.ExportRaw; - break; - case "dump": - o_workMode.Value = WorkMode.Dump; - break; - case "info": - o_workMode.Value = WorkMode.Info; - break; - case "live2d": - o_workMode.Value = WorkMode.ExportLive2D; - o_exportAssetTypes.Value = new List() - { - 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); - return; - } - break; case "-t": case "--asset-type": - if (o_workMode.Value == WorkMode.ExportLive2D) + if (o_workMode.Value == WorkMode.ExportLive2D || o_workMode.Value == WorkMode.SplitObjects) { i++; continue; @@ -527,47 +608,38 @@ namespace AssetStudioCLI.Options switch (type.ToLower()) { case "tex2d": - case "texture2d": o_exportAssetTypes.Value.Add(ClassIDType.Texture2D); break; - case "sprite": - o_exportAssetTypes.Value.Add(ClassIDType.Sprite); - break; case "akportrait": o_exportAssetTypes.Value.Add(ClassIDType.AkPortraitSprite); break; - case "textasset": - o_exportAssetTypes.Value.Add(ClassIDType.TextAsset); - break; - case "monobehaviour": - o_exportAssetTypes.Value.Add(ClassIDType.MonoBehaviour); - break; - case "font": - o_exportAssetTypes.Value.Add(ClassIDType.Font); - break; - case "shader": - o_exportAssetTypes.Value.Add(ClassIDType.Shader); - break; case "audio": - case "audioclip": o_exportAssetTypes.Value.Add(ClassIDType.AudioClip); break; case "video": - case "videoclip": o_exportAssetTypes.Value.Add(ClassIDType.VideoClip); break; - case "movietexture": - o_exportAssetTypes.Value.Add(ClassIDType.MovieTexture); - break; - case "mesh": - o_exportAssetTypes.Value.Add(ClassIDType.Mesh); - break; case "all": - o_exportAssetTypes.Value = supportedAssetTypes; + o_exportAssetTypes.Value = exportableAssetTypes; break; default: - Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset type: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_exportAssetTypes.Description); + var isKnownType = knownAssetTypesDict.TryGetValue(type.ToLower(), out var assetType); + if (isKnownType) + { + if (f_loadAllAssets.Value || exportableAssetTypes.Contains(assetType)) + { + o_exportAssetTypes.Value.Add(assetType); + break; + } + } + else + { + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unknown asset type specified [{type.Color(brightRed)}].\n"); + ShowOptionDescription(o_exportAssetTypes.Description); + return; + } + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Asset type [{type.Color(brightRed)}] is not supported for exporting.\n"); + ShowOptionDescription(o_exportAssetTypes.Description); return; } } @@ -593,7 +665,7 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported grouping option: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_groupAssetsBy.Description); + ShowOptionDescription(o_groupAssetsBy.Description); return; } break; @@ -606,6 +678,10 @@ namespace AssetStudioCLI.Options { Directory.CreateDirectory(value); } + if (!value.EndsWith($"{Path.DirectorySeparatorChar}")) + { + value += Path.DirectorySeparatorChar; + } o_outputFolder.Value = value; } catch (Exception ex) @@ -640,7 +716,7 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log level value: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_logLevel.Description); + ShowOptionDescription(o_logLevel.Description); return; } break; @@ -658,7 +734,7 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported log output mode: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_logOutput.Description); + ShowOptionDescription(o_logOutput.Description); return; } break; @@ -686,7 +762,7 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported image format: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_imageFormat.Description); + ShowOptionDescription(o_imageFormat.Description); return; } break; @@ -702,84 +778,40 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported audio format: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_audioFormat.Description); + ShowOptionDescription(o_audioFormat.Description); return; } break; - case "--spritemask-mode": - switch (value.ToLower()) + case "--fbx-scale-factor": { - case "none": - o_akSpriteMaskMode.Value = AkSpriteMaskMode.None; - break; - case "internal": - o_akSpriteMaskMode.Value = AkSpriteMaskMode.Internal; - break; - case "searchexternal": - o_akSpriteMaskMode.Value = AkSpriteMaskMode.External; - break; - default: - Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported sprite mask mode: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_akSpriteMaskMode.Description); + var isFloat = float.TryParse(value, out float floatValue); + if (isFloat && floatValue >= 0 && floatValue <= 100) + { + o_fbxScaleFactor.Value = floatValue; + } + else + { + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported scale factor value: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_fbxScaleFactor.Description); return; + } + break; } - break; - case "--alphamask-resampler": - switch (value.ToLower()) + case "--fbx-bone-size": { - case "nearest": - o_akAlphaMaskResampler.Value = KnownResamplers.NearestNeighbor; - break; - case "bilinear": - o_akAlphaMaskResampler.Value = KnownResamplers.Triangle; - break; - case "bicubic": - o_akAlphaMaskResampler.Value = KnownResamplers.Bicubic; - break; - case "mitchell": - o_akAlphaMaskResampler.Value = KnownResamplers.MitchellNetravali; - break; - case "spline": - o_akAlphaMaskResampler.Value = KnownResamplers.Spline; - break; - case "welch": - o_akAlphaMaskResampler.Value = KnownResamplers.Welch; - break; - default: - Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported alpha mask resampler: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_akAlphaMaskResampler.Description); + var isInt = int.TryParse(value, out int intValue); + if (isInt && intValue >= 0 && intValue <= 100) + { + o_fbxBoneSize.Value = intValue; + } + else + { + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported bone size value: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_fbxBoneSize.Description); return; + } + break; } - resamplerName = value.ToLower(); - break; - case "--alphamask-gamma": - var isInt = int.TryParse(value, out int intValue); - if (isInt && intValue >= -5 && intValue <= 5) - { - o_akAlphaMaskGamma.Value = intValue; - } - else - { - Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported gamma correction value: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_akAlphaMaskGamma.Description); - return; - } - break; - case "--export-asset-list": - switch (value.ToLower()) - { - case "xml": - o_exportAssetList.Value = ExportListType.XML; - break; - case "none": - o_exportAssetList.Value = ExportListType.None; - break; - default: - Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset list export option: [{value.Color(brightRed)}].\n"); - Console.WriteLine(o_exportAssetList.Description); - return; - } - break; case "--filter-by-name": o_filterByName.Value.AddRange(ValueSplitter(value)); filterBy = filterBy == FilterBy.None ? FilterBy.Name : filterBy == FilterBy.Container ? FilterBy.NameAndContainer : filterBy; @@ -796,6 +828,82 @@ namespace AssetStudioCLI.Options o_filterByText.Value.AddRange(ValueSplitter(value)); filterBy = FilterBy.NameOrContainer; break; + case "--spritealpha-mode": + switch (value.ToLower()) + { + case "none": + o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.None; + break; + case "internalonly": + o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.InternalOnly; + break; + case "searchexternal": + o_akSpriteAlphaMode.Value = AkSpriteAlphaMode.SearchExternal; + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported sprite alpha mode: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_akSpriteAlphaMode.Description); + return; + } + break; + case "--alphatex-resampler": + switch (value.ToLower()) + { + case "nearest": + o_akAlphaTexResampler.Value = KnownResamplers.NearestNeighbor; + break; + case "bilinear": + o_akAlphaTexResampler.Value = KnownResamplers.Triangle; + break; + case "bicubic": + o_akAlphaTexResampler.Value = KnownResamplers.Bicubic; + break; + case "mitchell": + o_akAlphaTexResampler.Value = KnownResamplers.MitchellNetravali; + break; + case "spline": + o_akAlphaTexResampler.Value = KnownResamplers.Spline; + break; + case "welch": + o_akAlphaTexResampler.Value = KnownResamplers.Welch; + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported alpha texture resampler: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_akAlphaTexResampler.Description); + return; + } + resamplerName = value.ToLower(); + break; + case "--shadow-gamma": + { + var isInt = int.TryParse(value, out int intValue); + if (isInt && intValue >= -5 && intValue <= 5) + { + o_akShadowGamma.Value = intValue; + } + else + { + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported gamma correction value: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_akShadowGamma.Description); + return; + } + break; + } + case "--export-asset-list": + switch (value.ToLower()) + { + case "xml": + o_exportAssetList.Value = ExportListType.XML; + break; + case "none": + o_exportAssetList.Value = ExportListType.None; + break; + default: + Console.WriteLine($"{"Error".Color(brightRed)} during parsing [{option}] option. Unsupported asset list export option: [{value.Color(brightRed)}].\n"); + ShowOptionDescription(o_exportAssetList.Description); + return; + } + break; case "--assembly-folder": if (Directory.Exists(value)) { @@ -813,9 +921,9 @@ namespace AssetStudioCLI.Options break; default: Console.WriteLine($"{"Error:".Color(brightRed)} Unknown option [{option.Color(brightRed)}].\n"); - if (!TryShowOptionDescription(option, optionsDict)) + if (!TryFindOptionDescription(option, optionsDict)) { - TryShowOptionDescription(option, flagsDict); + TryFindOptionDescription(option, flagsDict, isFlag: true); } return; } @@ -826,12 +934,12 @@ namespace AssetStudioCLI.Options if (optionsDict.Any(x => x.Key.Contains(option))) { Console.WriteLine($"{"Error during parsing options:".Color(brightRed)} Value for [{option.Color(brightRed)}] option was not found.\n"); - TryShowOptionDescription(option, optionsDict); + TryFindOptionDescription(option, optionsDict); } else if (flagsDict.Any(x => x.Key.Contains(option))) { Console.WriteLine($"{"Error:".Color(brightRed)} Unknown flag [{option.Color(brightRed)}].\n"); - TryShowOptionDescription(option, flagsDict); + TryFindOptionDescription(option, flagsDict, isFlag: true); } else { @@ -852,6 +960,15 @@ namespace AssetStudioCLI.Options { Studio.assemblyLoader.Loaded = true; } + if (o_outputFolder.Value == o_outputFolder.DefaultValue) + { + var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, o_outputFolder.DefaultValue + Path.DirectorySeparatorChar); + if (!Directory.Exists(fullPath)) + { + Directory.CreateDirectory(fullPath); + } + o_outputFolder.Value = fullPath; + } isParsed = true; } @@ -861,14 +978,21 @@ namespace AssetStudioCLI.Options return value.Split(separator); } - private static bool TryShowOptionDescription(string option, Dictionary descDict) + private static void ShowOptionDescription(string desc, bool isFlag = false) { - var optionDesc = descDict.Where(x => x.Key.Contains(option)); + var arg = isFlag ? "Flag" : "Option"; + Console.WriteLine($"{arg} description:\n{desc}"); + } + + private static bool TryFindOptionDescription(string option, Dictionary dict, bool isFlag = false) + { + var optionDesc = dict.Where(x => x.Key.Contains(option)); 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) }] option?"); + Console.WriteLine($"Did you mean [{ $"{rndOption.Key}".Color(CLIAnsiColors.BrightYellow) }] {arg}?"); Console.WriteLine($"Here's a description of it: \n\n{rndOption.Value}"); return true; @@ -910,7 +1034,7 @@ namespace AssetStudioCLI.Options else { var arch = Environment.Is64BitProcess ? "x64" : "x32"; - Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# Based on AssetStudioMod v{appAssembly.Version}\n"); + Console.WriteLine($"# {appAssembly.Name} [{arch}]\n# v{appAssembly.Version}\n# Based on AssetStudioMod v0.17.3\n"); Console.WriteLine($"{usage}\n\n{helpMessage}"); } } @@ -934,6 +1058,21 @@ namespace AssetStudioCLI.Options } } + private static string ShowExportTypes() + { + switch (o_workMode.Value) + { + case WorkMode.ExportRaw: + case WorkMode.Dump: + case WorkMode.Info: + return f_loadAllAssets.Value && o_exportAssetTypes.Value == o_exportAssetTypes.DefaultValue + ? $"# Export Asset Type(s): All" + : $"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}"; + default: + return $"# Export Asset Type(s): {string.Join(", ", o_exportAssetTypes.Value)}"; + } + } + public static void ShowCurrentOptions() { var sb = new StringBuilder(); @@ -942,31 +1081,24 @@ namespace AssetStudioCLI.Options sb.AppendLine($"# Input Path: \"{inputPath}\""); switch (o_workMode.Value) { - 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: + case WorkMode.Export: + case WorkMode.ExportRaw: + case WorkMode.Dump: 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($"# Assebmly Path: \"{o_assemblyPath}\""); - 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)}"); + if (o_workMode.Value != WorkMode.Export) + { + sb.AppendLine($"# Load All Assets: {f_loadAllAssets}"); + } + sb.AppendLine(ShowExportTypes()); sb.AppendLine($"# Asset Group Option: {o_groupAssetsBy}"); - sb.AppendLine($"# Export Image Format: {o_imageFormat}"); - sb.AppendLine($"# Export Audio Format: {o_audioFormat}"); - sb.AppendLine($"# [Arkingths] Sprite Mask Mode: {o_akSpriteMaskMode}"); - sb.AppendLine($"# [Arknights] Mask Resampler: {resamplerName}"); - sb.AppendLine($"# [Arknights] Mask Gamma Correction: {o_akAlphaMaskGamma.Value * 10:+#;-#;0}%"); + if (o_workMode.Value == WorkMode.Export) + { + sb.AppendLine($"# Export Image Format: {o_imageFormat}"); + sb.AppendLine($"# Export Audio Format: {o_audioFormat}"); + sb.AppendLine($"# [Arkingths] Sprite Alpha Mode: {o_akSpriteAlphaMode}"); + sb.AppendLine($"# [Arknights] Alpha Texture Resampler: {resamplerName}"); + sb.AppendLine($"# [Arknights] Shadow Gamma Correction: {o_akShadowGamma.Value * 10:+#;-#;0}%"); + } sb.AppendLine($"# [Arknights] Don't Fix Avg Names: {f_akOriginalAvgNames}"); sb.AppendLine($"# [Arknights] Add Aliases: {f_akAddAliases}"); sb.AppendLine($"# Log Level: {o_logLevel}"); @@ -975,7 +1107,36 @@ namespace AssetStudioCLI.Options sb.AppendLine(ShowCurrentFilter()); sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\""); sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); - sb.AppendLine($"# Restore TextAsset Extension: {!f_notRestoreExtensionName.Value}"); + if (o_workMode.Value == WorkMode.Export) + { + sb.AppendLine($"# Restore TextAsset Extension: {!f_notRestoreExtensionName.Value}"); + } + break; + case WorkMode.Info: + sb.AppendLine($"# Load All Assets: {f_loadAllAssets}"); + sb.AppendLine(ShowExportTypes()); + 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: + case WorkMode.SplitObjects: + 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}"); + if (o_workMode.Value == WorkMode.SplitObjects) + { + sb.AppendLine($"# Export Image Format: {o_imageFormat}"); + sb.AppendLine($"# Filter by Name(s): \"{string.Join("\", \"", o_filterByName.Value)}\""); + } + else + { + sb.AppendLine($"# Assebmly Path: \"{o_assemblyPath}\""); + } + sb.AppendLine($"# Unity Version: \"{o_unityVersion}\""); break; } sb.AppendLine("======"); diff --git a/AssetStudioCLI/Program.cs b/AssetStudioCLI/Program.cs index a7d6f01..40de9d5 100644 --- a/AssetStudioCLI/Program.cs +++ b/AssetStudioCLI/Program.cs @@ -35,9 +35,9 @@ namespace AssetStudioCLI if (Studio.LoadAssets()) { Studio.ParseAssets(); - if (CLIOptions.filterBy != FilterBy.None && CLIOptions.o_workMode.Value != WorkMode.ExportLive2D) + if (CLIOptions.filterBy != FilterBy.None) { - Studio.FilterAssets(); + Studio.Filter(); } if (CLIOptions.o_exportAssetList.Value != ExportListType.None) { @@ -51,6 +51,9 @@ namespace AssetStudioCLI case WorkMode.ExportLive2D: Studio.ExportLive2D(); break; + case WorkMode.SplitObjects: + Studio.ExportSplitObjects(); + break; default: Studio.ExportAssets(); break; diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index c12bb55..ecd6c8f 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -16,6 +16,7 @@ namespace AssetStudioCLI public static AssetsManager assetsManager = new AssetsManager(); public static List exportableAssetsList = new List(); public static List loadedAssetsList = new List(); + public static List gameObjectTree = new List(); public static AssemblyLoader assemblyLoader = new AssemblyLoader(); private static Dictionary containers = new Dictionary(); @@ -33,8 +34,10 @@ namespace AssetStudioCLI { var isLoaded = false; assetsManager.SpecifyUnityVersion = CLIOptions.o_unityVersion.Value; - assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value); - + if (!CLIOptions.f_loadAllAssets.Value) + { + assetsManager.SetAssetFilter(CLIOptions.o_exportAssetTypes.Value); + } assetsManager.LoadFilesAndFolders(CLIOptions.inputPath); if (assetsManager.assetsFileList.Count == 0) { @@ -53,6 +56,7 @@ namespace AssetStudioCLI Logger.Info("Parse assets..."); var objectCount = assetsManager.assetsFileList.Sum(x => x.Objects.Count); + var objectAssetItemDic = new Dictionary(objectCount); Progress.Reset(); var i = 0; @@ -61,6 +65,7 @@ namespace AssetStudioCLI foreach (var asset in assetsFile.Objects) { var assetItem = new AssetItem(asset); + objectAssetItemDic.Add(asset, assetItem); assetItem.UniqueID = "_#" + i; var isExportable = false; switch (asset) @@ -80,6 +85,7 @@ namespace AssetStudioCLI } } } + assetItem.Text = m_AssetBundle.m_Name; break; case ResourceManager m_ResourceManager: foreach (var m_Container in m_ResourceManager.m_Container) @@ -104,14 +110,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; - case Mesh _: - case MovieTexture _: - case TextAsset _: - case Font _: - case Sprite _: - assetItem.Text = ((NamedObject)asset).m_Name; - break; + break; case Shader m_Shader: assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name; break; @@ -125,15 +124,27 @@ namespace AssetStudioCLI assetItem.Text = m_MonoBehaviour.m_Name; } break; + case GameObject m_GameObject: + assetItem.Text = m_GameObject.m_Name; + break; + case Animator m_Animator: + if (m_Animator.m_GameObject.TryGet(out var gameObject)) + { + assetItem.Text = gameObject.m_Name; + } + break; + case NamedObject m_NamedObject: + assetItem.Text = m_NamedObject.m_Name; + break; } - if (assetItem.Text == "") + if (string.IsNullOrEmpty(assetItem.Text)) { assetItem.Text = assetItem.TypeString + assetItem.UniqueID; } loadedAssetsList.Add(assetItem); isExportable = CLIOptions.o_exportAssetTypes.Value.Contains(asset.type); - if (isExportable) + if (isExportable || (CLIOptions.f_loadAllAssets.Value && CLIOptions.o_exportAssetTypes.Value == CLIOptions.o_exportAssetTypes.DefaultValue)) { exportableAssetsList.Add(assetItem); } @@ -161,6 +172,11 @@ namespace AssetStudioCLI containers.Clear(); } } + + if (CLIOptions.o_workMode.Value == WorkMode.SplitObjects) + { + BuildTreeStructure(objectAssetItemDic); + } var log = $"Finished loading {assetsManager.assetsFileList.Count} files with {exportableAssetsList.Count} exportable assets"; var unityVer = assetsManager.assetsFileList[0].version; long m_ObjectsCount; @@ -183,6 +199,85 @@ namespace AssetStudioCLI Logger.Info(log); } + public static void BuildTreeStructure(Dictionary objectAssetItemDic) + { + Logger.Info("Building tree structure..."); + + var treeNodeDictionary = new Dictionary(); + var assetsFileCount = assetsManager.assetsFileList.Count; + int j = 0; + Progress.Reset(); + foreach (var assetsFile in assetsManager.assetsFileList) + { + var fileNode = new BaseNode(); //RootNode + + foreach (var obj in assetsFile.Objects) + { + if (obj is GameObject m_GameObject) + { + if (!treeNodeDictionary.TryGetValue(m_GameObject, out var currentNode)) + { + currentNode = new GameObjectNode(m_GameObject); + treeNodeDictionary.Add(m_GameObject, currentNode); + } + + foreach (var pptr in m_GameObject.m_Components) + { + if (pptr.TryGet(out var m_Component)) + { + objectAssetItemDic[m_Component].Node = currentNode; + if (m_Component is MeshFilter m_MeshFilter) + { + if (m_MeshFilter.m_Mesh.TryGet(out var m_Mesh)) + { + objectAssetItemDic[m_Mesh].Node = currentNode; + } + } + else if (m_Component is SkinnedMeshRenderer m_SkinnedMeshRenderer) + { + if (m_SkinnedMeshRenderer.m_Mesh.TryGet(out var m_Mesh)) + { + objectAssetItemDic[m_Mesh].Node = currentNode; + } + } + } + } + + var parentNode = fileNode; + + if (m_GameObject.m_Transform != null) + { + if (m_GameObject.m_Transform.m_Father.TryGet(out var m_Father)) + { + if (m_Father.m_GameObject.TryGet(out var parentGameObject)) + { + if (!treeNodeDictionary.TryGetValue(parentGameObject, out var parentGameObjectNode)) + { + parentGameObjectNode = new GameObjectNode(parentGameObject); + treeNodeDictionary.Add(parentGameObject, parentGameObjectNode); + } + parentNode = parentGameObjectNode; + } + } + } + + parentNode.nodes.Add(currentNode); + + } + } + + if (fileNode.nodes.Count > 0) + { + gameObjectTree.Add(fileNode); + } + + Progress.Report(++j, assetsFileCount); + } + + treeNodeDictionary.Clear(); + objectAssetItemDic.Clear(); + } + public static void ShowExportableAssetsInfo() { var exportableAssetsCountDict = new Dictionary(); @@ -226,7 +321,20 @@ namespace AssetStudioCLI } } - public static void FilterAssets() + public static void Filter() + { + switch (CLIOptions.o_workMode.Value) + { + case WorkMode.ExportLive2D: + case WorkMode.SplitObjects: + break; + default: + FilterAssets(); + break; + } + } + + private static void FilterAssets() { var assetsCount = exportableAssetsList.Count; var filteredAssets = new List(); @@ -234,14 +342,14 @@ namespace AssetStudioCLI switch(CLIOptions.filterBy) { case FilterBy.Name: - filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); + filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); Logger.Info( $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names." ); break; case FilterBy.Container: - filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); + filteredAssets = exportableAssetsList.FindAll(x => CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0)); Logger.Info( $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByContainer.Value)}\"".Color(Ansi.BrightYellow)} in their Containers." @@ -256,8 +364,8 @@ namespace AssetStudioCLI break; case FilterBy.NameOrContainer: filteredAssets = exportableAssetsList.FindAll(x => - CLIOptions.o_filterByText.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) || - CLIOptions.o_filterByText.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) + CLIOptions.o_filterByText.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) || + CLIOptions.o_filterByText.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ); Logger.Info( $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + @@ -266,8 +374,8 @@ namespace AssetStudioCLI break; case FilterBy.NameAndContainer: filteredAssets = exportableAssetsList.FindAll(x => - CLIOptions.o_filterByName.Value.Any(y => x.Text.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) && - CLIOptions.o_filterByContainer.Value.Any(y => x.Container.ToString().IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) + CLIOptions.o_filterByName.Value.Any(y => x.Text.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) && + CLIOptions.o_filterByContainer.Value.Any(y => x.Container.IndexOf(y, StringComparison.OrdinalIgnoreCase) >= 0) ); Logger.Info( $"Found [{filteredAssets.Count}/{assetsCount}] asset(s) " + @@ -411,6 +519,101 @@ namespace AssetStudioCLI Logger.Info($"Finished exporting asset list with {exportableAssetsList.Count} items."); } + public static void ExportSplitObjects() + { + var savePath = CLIOptions.o_outputFolder.Value; + var searchList = CLIOptions.o_filterByName.Value; + var isFiltered = CLIOptions.filterBy == FilterBy.Name; + + var exportableObjects = new List(); + var exportedCount = 0; + var k = 0; + + Logger.Info($"Searching for objects to export.."); + Progress.Reset(); + var count = gameObjectTree.Sum(x => x.nodes.Count); + foreach (var node in gameObjectTree) + { + foreach (GameObjectNode j in node.nodes) + { + if (isFiltered) + { + if (!searchList.Any(searchText => j.gameObject.m_Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0)) + continue; + } + var gameObjects = new List(); + CollectNode(j, gameObjects); + + if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) + { + Progress.Report(++k, count); + continue; + } + exportableObjects.Add(j); + } + } + gameObjectTree.Clear(); + var exportableCount = exportableObjects.Count; + var log = $"Found {exportableCount} exportable object(s) "; + if (isFiltered) + { + log += $"that contain {$"\"{string.Join("\", \"", CLIOptions.o_filterByName.Value)}\"".Color(Ansi.BrightYellow)} in their Names"; + } + Logger.Info(log); + if (exportableCount > 0) + { + Progress.Reset(); + k = 0; + + foreach (var gameObjectNode in exportableObjects) + { + var gameObject = gameObjectNode.gameObject; + var filename = FixFileName(gameObject.m_Name); + var targetPath = $"{savePath}{filename}{Path.DirectorySeparatorChar}"; + //重名文件处理 + for (int i = 1; ; i++) + { + if (Directory.Exists(targetPath)) + { + targetPath = $"{savePath}{filename} ({i}){Path.DirectorySeparatorChar}"; + } + else + { + break; + } + } + Directory.CreateDirectory(targetPath); + //导出FBX + Logger.Info($"Exporting {filename}.fbx"); + Progress.Report(k, exportableCount); + try + { + ExportGameObject(gameObject, targetPath); + Logger.Debug($"{gameObject.type} \"{filename}\" saved to \"{targetPath}\""); + exportedCount++; + } + catch (Exception ex) + { + Logger.Error($"Export GameObject:{gameObject.m_Name} error", ex); + } + k++; + } + } + var status = exportedCount > 0 + ? $"Finished exporting [{exportedCount}/{exportableCount}] object(s) to \"{CLIOptions.o_outputFolder.Value.Color(Ansi.BrightCyan)}\"" + : "Nothing exported"; + Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true); + } + + private static void CollectNode(GameObjectNode node, List gameObjects) + { + gameObjects.Add(node.gameObject); + foreach (GameObjectNode i in node.nodes) + { + CollectNode(i, gameObjects); + } + } + public static void ExportLive2D() { var baseDestPath = Path.Combine(CLIOptions.o_outputFolder.Value, "Live2DOutput"); diff --git a/AssetStudioFBXNative/CMakeLists.txt b/AssetStudioFBXNative/CMakeLists.txt new file mode 100644 index 0000000..f90011d --- /dev/null +++ b/AssetStudioFBXNative/CMakeLists.txt @@ -0,0 +1,22 @@ +# Set the minimum version of CMake that can be used +cmake_minimum_required (VERSION 3.8) + +# Set the project name +project("AssetStudioFBXNative") + +# Set the C++ standard to C++ 14 +set(CMAKE_CXX_STANDARD 14) + +# Generate the shared library from the library sources +add_library(AssetStudioFBXNative SHARED + asfbx_skin_context.cpp + asfbx_morph_context.cpp + api.cpp + utils.cpp + asfbx_context.cpp + asfbx_anim_context.cpp) + +# Add the given directories to those the compiler uses to search for include files +target_include_directories(AssetStudioFBXNative PRIVATE .) + +target_link_libraries(AssetStudioFBXNative PRIVATE fbxsdk xml2) \ No newline at end of file diff --git a/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj index 816445e..5c50fe8 100644 --- a/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj +++ b/AssetStudioFBXWrapper/AssetStudioFBXWrapper.csproj @@ -3,7 +3,7 @@ net472;net6.0;net6.0-windows;net7.0;net7.0-windows true - 0.17.2.0 + 1.0.0 Copyright © Perfare 2018-2022; Copyright © hozuki 2020 embedded diff --git a/AssetStudioGUI/AboutForm.Designer.cs b/AssetStudioGUI/AboutForm.Designer.cs index c54f55a..e2b5997 100644 --- a/AssetStudioGUI/AboutForm.Designer.cs +++ b/AssetStudioGUI/AboutForm.Designer.cs @@ -49,7 +49,7 @@ this.label7 = new System.Windows.Forms.Label(); this.modVersionLabel = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); - this.label8 = new System.Windows.Forms.Label(); + this.basedOnLabel = new System.Windows.Forms.Label(); this.checkUpdatesLinkLabel = new System.Windows.Forms.LinkLabel(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.licenseRichTextBox = new System.Windows.Forms.RichTextBox(); @@ -116,8 +116,7 @@ this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(347, 46); this.label2.TabIndex = 0; - this.label2.Text = "AssetStudio is a tool for exploring, extracting, and exporting assets and asset b" + - "undles."; + this.label2.Text = "ArknightsStudio is a modified version of AssetStudio designed for Arknights."; this.label2.UseCompatibleTextRendering = true; // // textBox2 @@ -164,7 +163,7 @@ this.tableLayoutPanel2.ColumnCount = 3; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F)); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F)); - this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 111F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 113F)); this.tableLayoutPanel2.Controls.Add(this.label16, 0, 0); this.tableLayoutPanel2.Controls.Add(this.label17, 1, 0); this.tableLayoutPanel2.Controls.Add(this.gitPerfareLinkLabel, 2, 0); @@ -194,7 +193,7 @@ // this.label17.AutoSize = true; this.label17.BackColor = System.Drawing.Color.Transparent; - this.label17.Location = new System.Drawing.Point(102, 2); + this.label17.Location = new System.Drawing.Point(101, 2); this.label17.Name = "label17"; this.label17.Size = new System.Drawing.Size(110, 13); this.label17.TabIndex = 10; @@ -204,7 +203,7 @@ // this.gitPerfareLinkLabel.AutoSize = true; this.gitPerfareLinkLabel.BackColor = System.Drawing.Color.Transparent; - this.gitPerfareLinkLabel.Location = new System.Drawing.Point(239, 2); + this.gitPerfareLinkLabel.Location = new System.Drawing.Point(237, 2); this.gitPerfareLinkLabel.Name = "gitPerfareLinkLabel"; this.gitPerfareLinkLabel.Size = new System.Drawing.Size(67, 13); this.gitPerfareLinkLabel.TabIndex = 11; @@ -226,7 +225,7 @@ // this.label19.AutoSize = true; this.label19.BackColor = System.Drawing.Color.Transparent; - this.label19.Location = new System.Drawing.Point(102, 20); + this.label19.Location = new System.Drawing.Point(101, 20); this.label19.Name = "label19"; this.label19.Size = new System.Drawing.Size(113, 13); this.label19.TabIndex = 13; @@ -236,7 +235,7 @@ // this.gitAelurumLinkLabel.AutoSize = true; this.gitAelurumLinkLabel.BackColor = System.Drawing.Color.Transparent; - this.gitAelurumLinkLabel.Location = new System.Drawing.Point(239, 20); + this.gitAelurumLinkLabel.Location = new System.Drawing.Point(237, 20); this.gitAelurumLinkLabel.Name = "gitAelurumLinkLabel"; this.gitAelurumLinkLabel.Size = new System.Drawing.Size(67, 13); this.gitAelurumLinkLabel.TabIndex = 14; @@ -250,13 +249,13 @@ this.tableLayoutPanel1.ColumnCount = 3; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 41.37931F)); this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 58.62069F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 111F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 113F)); this.tableLayoutPanel1.Controls.Add(this.label5, 0, 0); this.tableLayoutPanel1.Controls.Add(this.productNamelabel, 1, 0); this.tableLayoutPanel1.Controls.Add(this.label7, 0, 1); this.tableLayoutPanel1.Controls.Add(this.modVersionLabel, 1, 1); this.tableLayoutPanel1.Controls.Add(this.label4, 0, 2); - this.tableLayoutPanel1.Controls.Add(this.label8, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.basedOnLabel, 1, 2); this.tableLayoutPanel1.Controls.Add(this.checkUpdatesLinkLabel, 2, 1); this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 80); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; @@ -285,11 +284,11 @@ // this.productNamelabel.AutoSize = true; this.productNamelabel.BackColor = System.Drawing.Color.Transparent; - this.productNamelabel.Location = new System.Drawing.Point(102, 2); + this.productNamelabel.Location = new System.Drawing.Point(101, 2); this.productNamelabel.Name = "productNamelabel"; - this.productNamelabel.Size = new System.Drawing.Size(103, 13); + this.productNamelabel.Size = new System.Drawing.Size(100, 13); this.productNamelabel.TabIndex = 1; - this.productNamelabel.Text = "AssetStudioModGUI"; + this.productNamelabel.Text = "ArknightsStudioGUI"; // // label7 // @@ -297,16 +296,16 @@ this.label7.BackColor = System.Drawing.Color.Transparent; this.label7.Location = new System.Drawing.Point(5, 20); this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(68, 13); + this.label7.Size = new System.Drawing.Size(45, 13); this.label7.TabIndex = 2; - this.label7.Text = "Mod version:"; + this.label7.Text = "Version:"; // // modVersionLabel // this.modVersionLabel.AutoSize = true; this.modVersionLabel.BackColor = System.Drawing.Color.Transparent; this.modVersionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.modVersionLabel.Location = new System.Drawing.Point(102, 20); + this.modVersionLabel.Location = new System.Drawing.Point(101, 20); this.modVersionLabel.Name = "modVersionLabel"; this.modVersionLabel.Size = new System.Drawing.Size(52, 13); this.modVersionLabel.TabIndex = 3; @@ -323,21 +322,21 @@ this.label4.TabIndex = 4; this.label4.Text = "Based on:"; // - // label8 + // basedOnLabel // - this.label8.AutoSize = true; - this.label8.BackColor = System.Drawing.Color.Transparent; - this.label8.Location = new System.Drawing.Point(102, 38); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(108, 13); - this.label8.TabIndex = 5; - this.label8.Text = "AssetStudio v0.16.47"; + this.basedOnLabel.AutoSize = true; + this.basedOnLabel.BackColor = System.Drawing.Color.Transparent; + this.basedOnLabel.Location = new System.Drawing.Point(101, 38); + this.basedOnLabel.Name = "basedOnLabel"; + this.basedOnLabel.Size = new System.Drawing.Size(123, 13); + this.basedOnLabel.TabIndex = 5; + this.basedOnLabel.Text = "AssetStudioMod v0.17.0"; // // checkUpdatesLinkLabel // this.checkUpdatesLinkLabel.AutoSize = true; this.checkUpdatesLinkLabel.BackColor = System.Drawing.Color.Transparent; - this.checkUpdatesLinkLabel.Location = new System.Drawing.Point(239, 20); + this.checkUpdatesLinkLabel.Location = new System.Drawing.Point(237, 20); this.checkUpdatesLinkLabel.Name = "checkUpdatesLinkLabel"; this.checkUpdatesLinkLabel.Size = new System.Drawing.Size(96, 13); this.checkUpdatesLinkLabel.TabIndex = 6; @@ -391,7 +390,7 @@ this.productTitleLabel.Name = "productTitleLabel"; this.productTitleLabel.Size = new System.Drawing.Size(384, 30); this.productTitleLabel.TabIndex = 1; - this.productTitleLabel.Text = "AssetStudioModGUI"; + this.productTitleLabel.Text = "ArknightsStudioGUI"; this.productTitleLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // CloseButton @@ -492,7 +491,7 @@ private System.Windows.Forms.Label label7; private System.Windows.Forms.Label modVersionLabel; private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label basedOnLabel; private System.Windows.Forms.LinkLabel checkUpdatesLinkLabel; private System.Windows.Forms.RichTextBox licenseRichTextBox; private System.Windows.Forms.TextBox textBox2; diff --git a/AssetStudioGUI/AboutForm.cs b/AssetStudioGUI/AboutForm.cs index 307a60e..d3467e5 100644 --- a/AssetStudioGUI/AboutForm.cs +++ b/AssetStudioGUI/AboutForm.cs @@ -17,6 +17,7 @@ namespace AssetStudioGUI productVersionLabel.Text = $"v{Application.ProductVersion} [{arch}]"; productNamelabel.Text = productName; modVersionLabel.Text = Application.ProductVersion; + basedOnLabel.Text = "AssetStudioMod v0.17.3"; licenseRichTextBox.Text = GetLicenseText(); } @@ -41,7 +42,7 @@ namespace AssetStudioGUI private void checkUpdatesLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { - var ps = new ProcessStartInfo("https://github.com/aelurum/AssetStudio/releases") + var ps = new ProcessStartInfo("https://github.com/aelurum/AssetStudio/tags") { UseShellExecute = true }; diff --git a/AssetStudioGUI/AssetStudioGUI.csproj b/AssetStudioGUI/AssetStudioGUI.csproj index 9ea2e5d..6436eac 100644 --- a/AssetStudioGUI/AssetStudioGUI.csproj +++ b/AssetStudioGUI/AssetStudioGUI.csproj @@ -5,9 +5,9 @@ net472;net6.0-windows;net7.0-windows true Resources\as.ico - AssetStudioMod by aelurum - AssetStudioModGUI - 0.17.2.0 + ArknightsStudio by aelurum + ArknightsStudioGUI + 1.0.0 Copyright © Perfare 2018-2022; Copyright © aelurum 2021-2023 embedded diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index 7c339d3..0653839 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -293,7 +293,7 @@ this.akTitleMenuItem.Name = "akTitleMenuItem"; this.akTitleMenuItem.ShowShortcutKeys = false; this.akTitleMenuItem.Size = new System.Drawing.Size(265, 22); - this.akTitleMenuItem.Text = "Arknighs"; + this.akTitleMenuItem.Text = "Arknights"; // // akFixFaceSpriteNamesToolStripMenuItem // @@ -302,7 +302,7 @@ this.akFixFaceSpriteNamesToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked; this.akFixFaceSpriteNamesToolStripMenuItem.Name = "akFixFaceSpriteNamesToolStripMenuItem"; this.akFixFaceSpriteNamesToolStripMenuItem.Size = new System.Drawing.Size(265, 22); - this.akFixFaceSpriteNamesToolStripMenuItem.Text = "Fix names of avg face sprites"; + this.akFixFaceSpriteNamesToolStripMenuItem.Text = "Fix names of avg character sprites"; this.akFixFaceSpriteNamesToolStripMenuItem.ToolTipText = "Rename face sprites with numeric names to correct ones"; this.akFixFaceSpriteNamesToolStripMenuItem.CheckedChanged += new System.EventHandler(this.akFixFaceSpriteNamesToolStripMenuItem_Check); // diff --git a/AssetStudioGUI/ExportOptions.Designer.cs b/AssetStudioGUI/ExportOptions.Designer.cs index b068685..3bb6da3 100644 --- a/AssetStudioGUI/ExportOptions.Designer.cs +++ b/AssetStudioGUI/ExportOptions.Designer.cs @@ -512,9 +512,9 @@ this.akResamplerLabel.AutoSize = true; this.akResamplerLabel.Location = new System.Drawing.Point(6, 21); this.akResamplerLabel.Name = "akResamplerLabel"; - this.akResamplerLabel.Size = new System.Drawing.Size(113, 13); + this.akResamplerLabel.Size = new System.Drawing.Size(120, 13); this.akResamplerLabel.TabIndex = 5; - this.akResamplerLabel.Text = "Alpha mask resampler:"; + this.akResamplerLabel.Text = "Alpha texture resampler:"; this.exportUvsTooltip.SetToolTip(this.akResamplerLabel, "Only affects exported images"); // // akResamplerComboBox @@ -528,9 +528,9 @@ "Mitchell-Netravali", "Spline", "Welch"}); - this.akResamplerComboBox.Location = new System.Drawing.Point(125, 18); + this.akResamplerComboBox.Location = new System.Drawing.Point(132, 18); this.akResamplerComboBox.Name = "akResamplerComboBox"; - this.akResamplerComboBox.Size = new System.Drawing.Size(169, 21); + this.akResamplerComboBox.Size = new System.Drawing.Size(162, 21); this.akResamplerComboBox.TabIndex = 4; this.exportUvsTooltip.SetToolTip(this.akResamplerComboBox, "Only affects exported images"); // @@ -549,7 +549,7 @@ this.akSpritesAlphaGroupBox.Size = new System.Drawing.Size(300, 178); this.akSpritesAlphaGroupBox.TabIndex = 12; this.akSpritesAlphaGroupBox.TabStop = false; - this.akSpritesAlphaGroupBox.Text = "Sprites: Alpha Mask [Arknights]"; + this.akSpritesAlphaGroupBox.Text = "Sprites: Alpha Texture [Arknights]"; // // akGammaNoteLabel // @@ -567,9 +567,9 @@ this.akResamplerDescLabel.ForeColor = System.Drawing.SystemColors.GrayText; this.akResamplerDescLabel.Location = new System.Drawing.Point(6, 43); this.akResamplerDescLabel.Name = "akResamplerDescLabel"; - this.akResamplerDescLabel.Size = new System.Drawing.Size(244, 13); + this.akResamplerDescLabel.Size = new System.Drawing.Size(251, 13); this.akResamplerDescLabel.TabIndex = 6; - this.akResamplerDescLabel.Text = "Alpha mask upscale method for 2048x2048 sprites"; + this.akResamplerDescLabel.Text = "Alpha texture upscale method for 2048x2048 sprites"; // // akResizedOnlyCheckBox // @@ -597,9 +597,9 @@ this.akGammaLabel.AutoSize = true; this.akGammaLabel.Location = new System.Drawing.Point(6, 86); this.akGammaLabel.Name = "akGammaLabel"; - this.akGammaLabel.Size = new System.Drawing.Size(102, 13); + this.akGammaLabel.Size = new System.Drawing.Size(86, 13); this.akGammaLabel.TabIndex = 1; - this.akGammaLabel.Text = "Alpha mask gamma:"; + this.akGammaLabel.Text = "Shadow gamma:"; // // akAlphaMaskGammaTrackBar // @@ -627,9 +627,9 @@ this.akAddAliasesCheckBox.AutoSize = true; this.akAddAliasesCheckBox.Location = new System.Drawing.Point(6, 28); this.akAddAliasesCheckBox.Name = "akAddAliasesCheckBox"; - this.akAddAliasesCheckBox.Size = new System.Drawing.Size(213, 17); + this.akAddAliasesCheckBox.Size = new System.Drawing.Size(261, 17); this.akAddAliasesCheckBox.TabIndex = 0; - this.akAddAliasesCheckBox.Text = "Add aliases to avg sprite names (if exist)"; + this.akAddAliasesCheckBox.Text = "Add aliases to avg character sprite names (if exist)"; this.akAddAliasesCheckBox.UseVisualStyleBackColor = true; // // ExportOptions diff --git a/AssetStudioUtility/AssemblyLoader.cs b/AssetStudioUtility/AssemblyLoader.cs index 021b713..d2a7d0a 100644 --- a/AssetStudioUtility/AssemblyLoader.cs +++ b/AssetStudioUtility/AssemblyLoader.cs @@ -33,7 +33,12 @@ namespace AssetStudio public TypeDefinition GetTypeDefinition(string assemblyName, string fullName) { - if (moduleDic.TryGetValue(assemblyName, out var module)) + moduleDic.TryGetValue(assemblyName, out var module); + if (module == null && !assemblyName.Contains(".dll")) + { + moduleDic.TryGetValue(assemblyName + ".dll", out module); + } + if (module != null) { var typeDef = module.GetType(fullName); if (typeDef == null && assemblyName == "UnityEngine.dll") diff --git a/AssetStudioUtility/AssetStudioUtility.csproj b/AssetStudioUtility/AssetStudioUtility.csproj index 5fc9266..2c8a47e 100644 --- a/AssetStudioUtility/AssetStudioUtility.csproj +++ b/AssetStudioUtility/AssetStudioUtility.csproj @@ -2,7 +2,7 @@ net472;net6.0;net6.0-windows;net7.0;net7.0-windows - 0.17.2.0 + 1.0.0 Copyright © Perfare 2018-2022; Copyright © aelurum 2023 embedded diff --git a/CHANGELOG.md b/CHANGELOG.md index d280287..9b31626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v0.17.3.0 [13-09-2023] +- [CLI] Added support for exporting split objects (fbx) (https://github.com/aelurum/AssetStudio/pull/10) +- [CLI] Fixed display of asset names in the exported asset list in some working modes +- [CLI] Fixed a bug where the default output folder might not exist +- Added support of Texture2D assets from Unity 2022.2+ +- Fixed AssemblyLoader (https://github.com/aelurum/AssetStudio/issues/6) +- [CLI] Added --load-all flag to load assets of all types +- [CLI] Improved option grouping on the help screen + ## v0.17.2.0 [27-08-2023] - [GUI] Improved Scene Hierarchy tab - Added "Related assets" item to the context menu (https://github.com/aelurum/AssetStudio/issues/7) diff --git a/README.md b/README.md index d345c5d..ccbad86 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ You can read CLI readme [here](https://github.com/aelurum/AssetStudio/blob/Asset ``` AssetStudioModCLI -m info ``` -- Export assets of all supported types +- Export assets of all supported for export types ``` AssetStudioModCLI ``` @@ -89,12 +89,22 @@ AssetStudioModCLI -g type ``` AssetStudioModCLI -o ``` +- Dump assets to a specified output folder +``` +AssetStudioModCLI -m dump -o +``` - Export Live2D Cubism models ``` AssetStudioModCLI -m live2d ``` > When running in live2d mode you can only specify `-o`, `--log-level`, `--log-output`, `--export-asset-list`, `--unity-version` and `--assembly-folder` options. Any other options will be ignored. +- Export all FBX objects (similar to "Export all objects (split)" option in the GUI) +``` +AssetStudioModCLI -m splitObjects +``` +> When running in splitObjects mode you can only specify `-o`, `--log-level`, `--log-output`, `--export-asset-list`, `--image-format`, `--filter-by-name` and `--unity-version` options. +Any other options will be ignored. ### Advanced Samples - Export image assets converted to webp format to a specified output folder @@ -109,6 +119,13 @@ AssetStudioModCLI -m info -t audio --filter-by-name voice ``` AssetStudioModCLI -t audio --filter-by-name voice ``` +- Export audio assets that have "music" or "voice" in their names +``` +AssetStudioModCLI -t audio --filter-by-name music,voice +``` +``` +AssetStudioModCLI -t audio --filter-by-name music --filter-by-name voice +``` - Export audio assets that have "char" in their names **or** containers ``` AssetStudioModCLI -t audio --filter-by-text char @@ -117,6 +134,10 @@ AssetStudioModCLI -t audio --filter-by-text char ``` AssetStudioModCLI -t audio --filter-by-name voice --filter-by-container char ``` +- Export FBX objects that have "model" or "scene" in their names and set the scale factor to 10 +``` +AssetStudioModCLI -m splitObjects --filter-by-name model,scene --fbx-scale-factor 10 +``` - Export MonoBehaviour assets that require an assembly folder to read and create a log file ``` AssetStudioModCLI -t monobehaviour --assembly-folder --log-output both @@ -125,6 +146,14 @@ AssetStudioModCLI -t monobehaviour --assembly-folder --unity-version 2017.4.39f1 ``` +- Load assets of all types and show them (similar to "Display all assets" option in the GUI) +``` +AssetStudioModCLI -m info --load-all +``` +- Load assets of all types and dump Material assets +``` +AssetStudioModCLI -m dump -t material --load-all +``` ## GUI Usage @@ -140,7 +169,7 @@ Use **File->Extract file** or **File->Extract folder**. ### Export Assets, Live2D models -use **Export** menu. +Use **Export** menu. ### Export Model diff --git a/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj b/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj index fba7713..43a1d72 100644 --- a/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj +++ b/Texture2DDecoderWrapper/Texture2DDecoderWrapper.csproj @@ -3,7 +3,7 @@ net472 true - 0.17.2.0 + 1.0.0 Copyright © Perfare 2020-2022; Copyright © hozuki 2020 embedded