using System; using System.IO; using System.Linq; using System.CommandLine; using System.CommandLine.Binding; using System.CommandLine.Parsing; using System.Text.RegularExpressions; namespace AssetStudio.CLI { public static class CommandLine { public static void Init(string[] args) { var rootCommand = RegisterOptions(); rootCommand.Invoke(args); } public static RootCommand RegisterOptions() { var optionsBinder = new OptionsBinder(); var rootCommand = new RootCommand() { optionsBinder.Silent, optionsBinder.Verbose, optionsBinder.TypeFilter, optionsBinder.NameFilter, optionsBinder.ContainerFilter, optionsBinder.GameName, optionsBinder.KeyIndex, optionsBinder.MapOp, optionsBinder.MapType, optionsBinder.MapName, optionsBinder.UnityVersion, optionsBinder.GroupAssetsType, optionsBinder.AssetExportType, optionsBinder.Model, optionsBinder.Key, optionsBinder.AIFile, optionsBinder.DummyDllFolder, optionsBinder.Input, optionsBinder.Output }; rootCommand.SetHandler(Program.Run, optionsBinder); return rootCommand; } } public class Options { public bool Silent { get; set; } public bool Verbose { get; set; } public ClassIDType[] TypeFilter { get; set; } public Regex[] NameFilter { get; set; } public Regex[] ContainerFilter { get; set; } public string GameName { get; set; } public int KeyIndex { get; set; } public MapOpType MapOp { get; set; } public ExportListType MapType { get; set; } public string MapName { get; set; } public string UnityVersion { get; set; } public AssetGroupOption GroupAssetsType { get; set; } public ExportType AssetExportType { get; set; } public bool Model { get; set; } public byte Key { get; set; } public FileInfo AIFile { get; set; } public DirectoryInfo DummyDllFolder { get; set; } public FileInfo Input { get; set; } public DirectoryInfo Output { get; set; } } public class OptionsBinder : BinderBase { public readonly Option Silent; public readonly Option Verbose; public readonly Option TypeFilter; public readonly Option NameFilter; public readonly Option ContainerFilter; public readonly Option GameName; public readonly Option KeyIndex; public readonly Option MapOp; public readonly Option MapType; public readonly Option MapName; public readonly Option UnityVersion; public readonly Option GroupAssetsType; public readonly Option AssetExportType; public readonly Option Model; public readonly Option Key; public readonly Option AIFile; public readonly Option DummyDllFolder; public readonly Argument Input; public readonly Argument Output; public OptionsBinder() { Silent = new Option("--silent", "Hide log messages."); Verbose = new Option("--verbose", "Enable verbose logging."); TypeFilter = new Option("--types", "Specify unity class type(s)") { AllowMultipleArgumentsPerToken = true, ArgumentHelpName = "Texture2D|Sprite|etc.." }; NameFilter = new Option("--names", result => result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray(), false, "Specify name regex filter(s).") { AllowMultipleArgumentsPerToken = true }; ContainerFilter = new Option("--containers", result => result.Tokens.Select(x => new Regex(x.Value, RegexOptions.IgnoreCase)).ToArray(), false, "Specify container regex filter(s).") { AllowMultipleArgumentsPerToken = true }; GameName = new Option("--game", $"Specify Game.") { IsRequired = true }; KeyIndex = new Option("--key_index", "Specify key index.") { ArgumentHelpName = UnityCNManager.ToString() }; MapOp = new Option("--map_op", "Specify which map to build."); MapType = new Option("--map_type", "AssetMap output type."); MapName = new Option("--map_name", () => "assets_map", "Specify AssetMap file name."); UnityVersion = new Option("--unity_version", "Specify Unity version."); GroupAssetsType = new Option("--group_assets", "Specify how exported assets should be grouped."); AssetExportType = new Option("--export_type", "Specify how assets should be exported."); Model = new Option("--models", "Enable to export models only"); AIFile = new Option("--ai_file", "Specify asset_index json file path (to recover GI containers).").LegalFilePathsOnly(); DummyDllFolder = new Option("--dummy_dlls", "Specify DummyDll path.").LegalFilePathsOnly(); Input = new Argument("input_path", "Input file/folder.").LegalFilePathsOnly(); Output = new Argument("output_path", "Output folder.").LegalFilePathsOnly(); Key = new Option("--key", result => { var value = result.Tokens.Single().Value; if (value.StartsWith("0x")) { value = value[2..]; return Convert.ToByte(value, 0x10); } else { return byte.Parse(value); } }, false, "XOR key to decrypt MiHoYoBinData."); TypeFilter.AddValidator(FilterValidator); NameFilter.AddValidator(FilterValidator); ContainerFilter.AddValidator(FilterValidator); Key.AddValidator(result => { var value = result.Tokens.Single().Value; try { if (value.StartsWith("0x")) { value = value.Substring(2); Convert.ToByte(value, 0x10); } else { byte.Parse(value); } } catch (Exception e) { result.ErrorMessage = "Invalid byte value.\n" + e.Message; } }); GameName.FromAmong(GameManager.GetGameNames()); GroupAssetsType.SetDefaultValue(AssetGroupOption.ByType); AssetExportType.SetDefaultValue(ExportType.Convert); MapOp.SetDefaultValue(MapOpType.None); MapType.SetDefaultValue(ExportListType.XML); KeyIndex.SetDefaultValue(0); } public void FilterValidator(OptionResult result) { var values = result.Tokens.Select(x => x.Value).ToArray(); foreach (var val in values) { if (string.IsNullOrWhiteSpace(val)) { result.ErrorMessage = "Empty string."; return; } try { Regex.Match("", val, RegexOptions.IgnoreCase); } catch (ArgumentException e) { result.ErrorMessage = "Invalid Regex.\n" + e.Message; return; } } } protected override Options GetBoundValue(BindingContext bindingContext) => new() { Silent = bindingContext.ParseResult.GetValueForOption(Silent), Verbose = bindingContext.ParseResult.GetValueForOption(Verbose), TypeFilter = bindingContext.ParseResult.GetValueForOption(TypeFilter), NameFilter = bindingContext.ParseResult.GetValueForOption(NameFilter), ContainerFilter = bindingContext.ParseResult.GetValueForOption(ContainerFilter), GameName = bindingContext.ParseResult.GetValueForOption(GameName), KeyIndex = bindingContext.ParseResult.GetValueForOption(KeyIndex), MapOp = bindingContext.ParseResult.GetValueForOption(MapOp), MapType = bindingContext.ParseResult.GetValueForOption(MapType), MapName = bindingContext.ParseResult.GetValueForOption(MapName), UnityVersion = bindingContext.ParseResult.GetValueForOption(UnityVersion), GroupAssetsType = bindingContext.ParseResult.GetValueForOption(GroupAssetsType), AssetExportType = bindingContext.ParseResult.GetValueForOption(AssetExportType), Model = bindingContext.ParseResult.GetValueForOption(Model), Key = bindingContext.ParseResult.GetValueForOption(Key), AIFile = bindingContext.ParseResult.GetValueForOption(AIFile), DummyDllFolder = bindingContext.ParseResult.GetValueForOption(DummyDllFolder), Input = bindingContext.ParseResult.GetValueForArgument(Input), Output = bindingContext.ParseResult.GetValueForArgument(Output) }; } }