From ce1d61f9104e4f5539c6cf3cce15404b3c20d1f1 Mon Sep 17 00:00:00 2001 From: BxNiom Date: Thu, 30 Sep 2021 22:15:08 +0200 Subject: [PATCH] Cleanup, Arguments - Rewritten the Program.cs to get it clean and readable - Rewritten the usage section - Added ArgumentParser (!!! ATTENTION !!! some arguments have changed) - ProgramArgs contains all possible parameters for the command line Feel free to add some ;) --- .gitignore | 5 +- DepotDownloader/ArgumentParser.cs | 227 ++++++++++++ DepotDownloader/DepotDownloader.csproj | 1 + DepotDownloader/Program.cs | 471 ++++++++++--------------- DepotDownloader/ProgramArgs.cs | 141 ++++++++ 5 files changed, 562 insertions(+), 283 deletions(-) create mode 100644 DepotDownloader/ArgumentParser.cs create mode 100644 DepotDownloader/ProgramArgs.cs diff --git a/.gitignore b/.gitignore index 8772e0fa..155de13c 100644 --- a/.gitignore +++ b/.gitignore @@ -117,4 +117,7 @@ protobuf/ cryptopp/ # misc -Thumbs.db \ No newline at end of file +Thumbs.db + +# IntelliJ Rider +.idea/ diff --git a/DepotDownloader/ArgumentParser.cs b/DepotDownloader/ArgumentParser.cs new file mode 100644 index 00000000..34d92278 --- /dev/null +++ b/DepotDownloader/ArgumentParser.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace DepotDownloader +{ + /// + /// Quick argument parser + /// Need some optimisation, but it is functional ;) + /// + public static class ArgumentParser + { + /// + /// Parse command line arguments and set Properties in TContainer object + /// + /// Command line arguments + /// Type implementing IArgumentContainer + /// TContainer object with properties set + /// + public static TContainer Parse(string[] args) where TContainer : IArgumentContainer + { + var containerType = typeof(TContainer); + var container = (TContainer)Activator.CreateInstance(containerType); + + if (container == null) + { + throw new ArgumentException($"Type {containerType} has no empty constructor"); + } + + var options = GetContainerOptions(containerType); + + for (var i = 0; i < args.Length; i++) + { + if (!args[i].StartsWith("-")) + { + throw new ArgumentException($"Unknown argument: {args[i]}"); + } + + var arg = args[i].StartsWith("--") ? args[i].Substring(2) : args[i].Substring(1); + + var (option, property) = (from op in options + where + arg.Length == 1 && op.Item1.ShortOption == arg[0] + || op.Item1.LongOption.Equals(arg, StringComparison.Ordinal) + select op).FirstOrDefault(); + + if (option == null || property == null) + { + throw new ArgumentException($"Unknown argument: '{arg}'"); + } + + if (option.ParameterName != null || option.AllowMultiple) + { + if (i == args.Length - 1) + { + throw new ArgumentException($"No parameter for option '{arg}' found"); + } + + if (!option.AllowMultiple) + { + var parameter = args[++i]; + if (parameter.StartsWith("-")) + { + throw new ArgumentException($"No parameter for option '{arg}' found"); + } + + property.SetValue(container, + property.PropertyType == typeof(string) + ? parameter + : TypeDescriptor.GetConverter(property.PropertyType).ConvertFromString(parameter)); + } + else + { + var converter = property.PropertyType.IsGenericType + ? TypeDescriptor.GetConverter(property.PropertyType.GenericTypeArguments[0]) + : null; + + var list = (IList)property.GetValue(container); + if (list == null) + { + throw new ArgumentException("Initialize List properties first!"); + } + + while (i < args.Length && !args[i + 1].StartsWith("-")) + { + var parameter = converter != null ? converter.ConvertFromString(args[++i]) : args[++i]; + list.Add(parameter); + } + } + } + } + + return container; + } + + // TODO wrap + /// + /// Creates a parameter table for given container type + /// + /// number of whitespaces at the line beginning + /// wrap long descriptions (not implemented yet) + /// Container type + /// + public static string GetHelpList(int ident = 4, bool wrap = false) where T : IArgumentContainer + { + var optionList = GetContainerOptions(typeof(T)); + var sb = new StringBuilder(); + + var lines = new List<(string, string)>(); + var maxOpLength = 0; + foreach (var (option, _) in optionList) + { + var opStr = option.ShortOption != '\0' ? $"-{option.ShortOption}" : ""; + if (!string.IsNullOrEmpty(option.LongOption)) + { + opStr += (option.ShortOption != '\0' ? ", " : " ") + $"--{option.LongOption}"; + } + + if (option.ParameterName != null) + { + opStr += $" <{option.ParameterName}>"; + } + + lines.Add((opStr, option.Description)); + maxOpLength = Math.Max(maxOpLength, opStr.Length); + } + + var identStr = "".PadRight(ident); + foreach (var (op, desc) in lines) + { + sb.AppendLine(identStr + op.PadRight(maxOpLength + 4) + desc); + } + + return sb.ToString(); + } + + private static List<(OptionAttribute, PropertyInfo)> GetContainerOptions(Type containerType) + { + var resultList = new List<(OptionAttribute, PropertyInfo)>(); + + foreach (var prop in containerType.GetProperties()) + { + // try to get OptionAttribute from property + var a = prop.GetCustomAttribute(typeof(OptionAttribute)); + + if (a == null) + { + continue; + } + + // Check some things + var option = (OptionAttribute)a; + if (prop.SetMethod == null) + { + throw new ArgumentException($"No setter found for '{prop.Name}'"); + } + + // Only options with descriptions are allowed! + if (string.IsNullOrEmpty(option.Description)) + { + throw new ArgumentException($"No description found for '{prop.Name}'"); + } + + // We need a short option or a long option + if (option.ShortOption == '\0' && string.IsNullOrEmpty(option.LongOption)) + { + throw new ArgumentException( + $"You must at least permit ShortOption or LongOption. Property: '{prop.Name}"); + } + + // AllowMultiple only allowed on list properties + if (option.AllowMultiple && !prop.PropertyType.IsAssignableTo(typeof(IList))) + { + throw new ArgumentException( + $"Options with AllowMultiple must be assignable to IList type. Property: '{prop.Name}"); + } + + if (!option.AllowMultiple && option.ParameterName == null && prop.PropertyType != typeof(bool)) + { + throw new ArgumentException( + $"Property must be bool if there is no parameter required. Property: '{prop.Name}"); + } + + // if everything is ok, add it to the result list + resultList.Add((option, prop)); + } + + return resultList; + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class OptionAttribute : Attribute + { + /// + /// Used for lists of parameters, seperated by whitespaces + /// + public bool AllowMultiple = false; + + /// + /// + public string Description = null; + + /// + /// long option name (e.g. --username) + /// + public string LongOption = null; + + /// + /// Name of parameter if parameter is needed (e.g. ) + /// + public string ParameterName = null; + + /// + /// single character option (e.g. -u) + /// + public char ShortOption = '\0'; + } + + public interface IArgumentContainer + { + } +} diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index a5f4e912..0931bbd9 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -8,6 +8,7 @@ Steam Downloading Utility SteamRE Team Copyright © SteamRE Team 2021 + 2.4.3 diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index 748ee5dc..17098b6c 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -9,12 +8,14 @@ using SteamKit2; namespace DepotDownloader { - class Program + internal partial class Program { - static int Main(string[] args) - => MainAsync(args).GetAwaiter().GetResult(); + private static int Main(string[] args) + { + return MainAsync(args).GetAwaiter().GetResult(); + } - static async Task MainAsync(string[] args) + private static async Task MainAsync(string[] args) { if (args.Length == 0) { @@ -23,378 +24,284 @@ namespace DepotDownloader } DebugLog.Enabled = false; + AccountSettingsStore.LoadFromFile("account.config"); + + // Use ArgumentParser to read args + ProgramArgs arguments = null; + try + { + arguments = ArgumentParser.Parse(args); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + PrintUsage(); + return 2; + } - AccountSettingsStore.LoadFromFile("account.config"); - - #region Common Options + #region Arguments -> ContentDownload.Config - if (HasParameter(args, "-debug")) + if (arguments.Debug) { + // Verbose DebugLog.Enabled = true; DebugLog.AddListener((category, message) => { Console.WriteLine("[{0}] {1}", category, message); }); - var httpEventListener = new HttpDiagnosticEventListener(); + // why? + //var httpEventListener = new HttpDiagnosticEventListener(); } - var username = GetParameter(args, "-username") ?? GetParameter(args, "-user"); - var password = GetParameter(args, "-password") ?? GetParameter(args, "-pass"); - ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password"); + ContentDownloader.Config.RememberPassword = arguments.RememberPassword; + ContentDownloader.Config.DownloadManifestOnly = arguments.ManifestOnly; + ContentDownloader.Config.CellID = arguments.CellID; + ContentDownloader.Config.InstallDirectory = arguments.InstallDir; + ContentDownloader.Config.VerifyAll = arguments.Validate; + ContentDownloader.Config.MaxDownloads = arguments.MaxDownloads; + ContentDownloader.Config.MaxServers = Math.Max(arguments.MaxServers, arguments.MaxDownloads); + ContentDownloader.Config.LoginID = arguments.LoginID; + ContentDownloader.Config.DownloadAllPlatforms = arguments.AllPlatforms; + ContentDownloader.Config.DownloadAllLanguages = arguments.AllLanguages; + ContentDownloader.Config.BetaPassword = arguments.BetaPassword; - ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); + #endregion - var cellId = GetParameter(args, "-cellid", -1); - if (cellId == -1) + if (!string.IsNullOrEmpty(arguments.FileList)) { - cellId = 0; + await LoadFileListAsync(arguments.FileList); } - ContentDownloader.Config.CellID = cellId; - - var fileList = GetParameter(args, "-filelist"); - - if (fileList != null) + if (arguments.AppID == ContentDownloader.INVALID_APP_ID) { - try - { - var fileListData = await File.ReadAllTextAsync(fileList); - var files = fileListData.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); - - ContentDownloader.Config.UsingFileList = true; - ContentDownloader.Config.FilesToDownload = new HashSet(StringComparer.OrdinalIgnoreCase); - ContentDownloader.Config.FilesToDownloadRegex = new List(); - - foreach (var fileEntry in files) - { - if (fileEntry.StartsWith("regex:")) - { - var rgx = new Regex(fileEntry.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase); - ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); - } - else - { - ContentDownloader.Config.FilesToDownload.Add(fileEntry.Replace('\\', '/')); - } - } - - Console.WriteLine("Using filelist: '{0}'.", fileList); - } - catch (Exception ex) - { - Console.WriteLine("Warning: Unable to load filelist: {0}", ex); - } + Console.WriteLine("Error: App not specified"); + PrintUsage(); + return 1; } - ContentDownloader.Config.InstallDirectory = GetParameter(args, "-dir"); - - ContentDownloader.Config.VerifyAll = HasParameter(args, "-verify-all") || HasParameter(args, "-verify_all") || HasParameter(args, "-validate"); - ContentDownloader.Config.MaxServers = GetParameter(args, "-max-servers", 20); - ContentDownloader.Config.MaxDownloads = GetParameter(args, "-max-downloads", 8); - ContentDownloader.Config.MaxServers = Math.Max(ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads); - ContentDownloader.Config.LoginID = HasParameter(args, "-loginid") ? GetParameter(args, "-loginid") : null; + // PubFile download + if (arguments.PubFile != ContentDownloader.INVALID_MANIFEST_ID) + { + return await DownloadPubFileAsync(arguments); + } - #endregion + // UGC download + if (arguments.UGC != ContentDownloader.INVALID_MANIFEST_ID) + { + return await DownloadUGCAsync(arguments); + } - var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID); - if (appId == ContentDownloader.INVALID_APP_ID) + // App download + if (ContentDownloader.Config.DownloadAllPlatforms && arguments.OperatingSystem != null) { - Console.WriteLine("Error: -app not specified!"); + Console.WriteLine("Error: Cannot specify --os when --all-platforms is specified."); return 1; } - var pubFile = GetParameter(args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID); - var ugcId = GetParameter(args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID); - if (pubFile != ContentDownloader.INVALID_MANIFEST_ID) + if (ContentDownloader.Config.DownloadAllLanguages && arguments.Language != null) { - #region Pubfile Downloading + Console.WriteLine("Error: Cannot specify --language when --all-languages is specified."); + return 1; + } - if (InitializeSteam(username, password)) - { - try - { - await ContentDownloader.DownloadPubfileAsync(appId, pubFile).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } - } - else + var depotManifestIds = new List<(uint, ulong)>(); + if (arguments.ManifestIDs.Count > 0) + { + if (arguments.DepotIDs.Count != arguments.ManifestIDs.Count) { - Console.WriteLine("Error: InitializeSteam failed"); + Console.WriteLine("Error: --manifest requires one id for every --depot specified"); return 1; } - #endregion + var zippedDepotManifest = arguments.DepotIDs.Zip(arguments.ManifestIDs, (d, m) => (d, m)); + depotManifestIds.AddRange(zippedDepotManifest); } - else if (ugcId != ContentDownloader.INVALID_MANIFEST_ID) + else { - #region UGC Downloading + depotManifestIds.AddRange(arguments.DepotIDs.Select(d => (d, ContentDownloader.INVALID_MANIFEST_ID))); + } - if (InitializeSteam(username, password)) + if (InitializeSteam(arguments.Username, arguments.Password)) + { + try { - try - { - await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } + await ContentDownloader.DownloadAppAsync(arguments.AppID, + depotManifestIds, + arguments.BetaBranchName, + arguments.OperatingSystem, + arguments.Architecture, + arguments.Language, + arguments.LowViolence, + false).ConfigureAwait(false); } - else + catch (Exception ex) when (ex is ContentDownloaderException or OperationCanceledException) { - Console.WriteLine("Error: InitializeSteam failed"); + Console.WriteLine(ex.Message); return 1; } - - #endregion + catch (Exception e) + { + Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + throw; + } + finally + { + ContentDownloader.ShutdownSteam3(); + } } else { - #region App downloading - - var branch = GetParameter(args, "-branch") ?? GetParameter(args, "-beta") ?? ContentDownloader.DEFAULT_BRANCH; - ContentDownloader.Config.BetaPassword = GetParameter(args, "-betapassword"); + Console.WriteLine("Error: InitializeSteam failed"); + return 1; + } - ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms"); - var os = GetParameter(args, "-os"); + return 0; + } - if (ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty(os)) + private static async Task DownloadPubFileAsync(ProgramArgs args) + { + if (InitializeSteam(args.Username, args.Password)) + { + try { - Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); - return 1; + await ContentDownloader.DownloadPubfileAsync(args.AppID, args.PubFile).ConfigureAwait(false); } - - var arch = GetParameter(args, "-osarch"); - - ContentDownloader.Config.DownloadAllLanguages = HasParameter(args, "-all-languages"); - var language = GetParameter(args, "-language"); - - if (ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty(language)) + catch (Exception ex) when (ex is ContentDownloaderException or OperationCanceledException) { - Console.WriteLine("Error: Cannot specify -language when -all-languages is specified."); + Console.WriteLine(ex.Message); return 1; } - - var lv = HasParameter(args, "-lowviolence"); - - var depotManifestIds = new List<(uint, ulong)>(); - var isUGC = false; - - var depotIdList = GetParameterList(args, "-depot"); - var manifestIdList = GetParameterList(args, "-manifest"); - if (manifestIdList.Count > 0) + catch (Exception e) { - if (depotIdList.Count != manifestIdList.Count) - { - Console.WriteLine("Error: -manifest requires one id for every -depot specified"); - return 1; - } - - var zippedDepotManifest = depotIdList.Zip(manifestIdList, (depotId, manifestId) => (depotId, manifestId)); - depotManifestIds.AddRange(zippedDepotManifest); + Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + throw; } - else + finally { - depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); + ContentDownloader.ShutdownSteam3(); } + } + else + { + Console.WriteLine("Error: InitializeSteam failed"); + return 1; + } - if (InitializeSteam(username, password)) + return 0; + } + + private static async Task DownloadUGCAsync(ProgramArgs args) + { + if (InitializeSteam(args.Username, args.Password)) + { + try { - try - { - await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } + await ContentDownloader.DownloadUGCAsync(args.AppID, args.UGC).ConfigureAwait(false); } - else + catch (Exception ex) when (ex is ContentDownloaderException or OperationCanceledException) { - Console.WriteLine("Error: InitializeSteam failed"); + Console.WriteLine(ex.Message); return 1; } - - #endregion + catch (Exception e) + { + Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + throw; + } + finally + { + ContentDownloader.ShutdownSteam3(); + } + } + else + { + Console.WriteLine("Error: InitializeSteam failed"); + return 1; } return 0; } - static bool InitializeSteam(string username, string password) + private static async Task LoadFileListAsync(string fileList) { - if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey(username))) + try { - do + var fileListData = await File.ReadAllTextAsync(fileList); + var files = fileListData.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries); + + ContentDownloader.Config.UsingFileList = true; + ContentDownloader.Config.FilesToDownload = new HashSet(StringComparer.OrdinalIgnoreCase); + ContentDownloader.Config.FilesToDownloadRegex = new List(); + + foreach (var fileEntry in files) { - Console.Write("Enter account password for \"{0}\": ", username); - if (Console.IsInputRedirected) + if (fileEntry.StartsWith("regex:")) { - password = Console.ReadLine(); + var rgx = new Regex(fileEntry.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase); + ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); } else { - // Avoid console echoing of password - password = Util.ReadPassword(); + ContentDownloader.Config.FilesToDownload.Add(fileEntry.Replace('\\', '/')); } + } - Console.WriteLine(); - } while (String.Empty == password); + Console.WriteLine("Using file list: '{0}'.", fileList); } - else if (username == null) + catch (Exception ex) { - Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); + Console.WriteLine("Warning: Unable to load file list: {0}", ex); } - - // capture the supplied password in case we need to re-use it after checking the login key - ContentDownloader.Config.SuppliedPassword = password; - - return ContentDownloader.InitializeSteam3(username, password); } - static int IndexOfParam(string[] args, string param) + private static bool InitializeSteam(string username, string password) { - for (var x = 0; x < args.Length; ++x) + if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || + !AccountSettingsStore.Instance.LoginKeys + .ContainsKey(username))) { - if (args[x].Equals(param, StringComparison.OrdinalIgnoreCase)) - return x; - } - - return -1; - } - - static bool HasParameter(string[] args, string param) - { - return IndexOfParam(args, param) > -1; - } - - static T GetParameter(string[] args, string param, T defaultValue = default(T)) - { - var index = IndexOfParam(args, param); - - if (index == -1 || index == (args.Length - 1)) - return defaultValue; - - var strParam = args[index + 1]; + do + { + Console.Write("Enter account password for \"{0}\": ", username); + password = Console.IsInputRedirected ? Console.ReadLine() : Util.ReadPassword(); - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - return (T)converter.ConvertFromString(strParam); + Console.WriteLine(); + } while (string.Empty == password); } - - return default(T); - } - - static List GetParameterList(string[] args, string param) - { - var list = new List(); - var index = IndexOfParam(args, param); - - if (index == -1 || index == (args.Length - 1)) - return list; - - index++; - - while (index < args.Length) + else if (username == null) { - var strParam = args[index]; - - if (strParam[0] == '-') break; - - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - list.Add((T)converter.ConvertFromString(strParam)); - } - - index++; + Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); } - return list; + // capture the supplied password in case we need to re-use it after checking the login key + ContentDownloader.Config.SuppliedPassword = password; + + return ContentDownloader.InitializeSteam3(username, password); } - static void PrintUsage() + private static void PrintUsage() { + Console.WriteLine($"DepotDownloader version {typeof(Program).Assembly.GetName().Version?.ToString(3)} - copyright SteamRE Team 2021"); Console.WriteLine(); - Console.WriteLine("Usage - downloading one or all depots for an app:"); - Console.WriteLine("\tdepotdownloader -app [-depot [-manifest ]]"); - Console.WriteLine("\t\t[-username [-password ]] [other options]"); + Console.WriteLine(" Steam depot downloader utilizing the SteamKit2 library."); + Console.WriteLine(" https://github.com/SteamRE/DepotDownloader"); Console.WriteLine(); - Console.WriteLine("Usage - downloading a workshop item using pubfile id"); - Console.WriteLine("\tdepotdownloader -app -pubfile [-username [-password ]]"); - Console.WriteLine("Usage - downloading a workshop item using ugc id"); - Console.WriteLine("\tdepotdownloader -app -ugc [-username [-password ]]"); + Console.WriteLine("Usage:"); Console.WriteLine(); - Console.WriteLine("Parameters:"); - Console.WriteLine("\t-app <#>\t\t\t\t- the AppID to download."); - Console.WriteLine("\t-depot <#>\t\t\t\t- the DepotID to download."); - Console.WriteLine("\t-manifest \t\t\t- manifest id of content to download (requires -depot, default: current for branch)."); - Console.WriteLine("\t-beta \t\t\t- download from specified branch if available (default: Public)."); - Console.WriteLine("\t-betapassword \t\t- branch password if applicable."); - Console.WriteLine("\t-all-platforms\t\t\t- downloads all platform-specific depots when -app is used."); - Console.WriteLine("\t-os \t\t\t\t- the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)"); - Console.WriteLine("\t-osarch \t\t\t\t- the architecture for which to download the game (32 or 64, default: the host's architecture)"); - Console.WriteLine("\t-all-languages\t\t\t\t- download all language-specific depots when -app is used."); - Console.WriteLine("\t-language \t\t\t\t- the language for which to download the game (default: english)"); - Console.WriteLine("\t-lowviolence\t\t\t\t- download low violence depots when -app is used."); + Console.WriteLine(" Downloading one or all depots for an app:"); + Console.WriteLine(" depotdownloader --app [--depot [--manifest ]]"); + Console.WriteLine(" [--username [--password ]] [other options]"); Console.WriteLine(); - Console.WriteLine("\t-ugc <#>\t\t\t\t- the UGC ID to download."); - Console.WriteLine("\t-pubfile <#>\t\t\t- the PublishedFileId to download. (Will automatically resolve to UGC id)"); + Console.WriteLine(" Downloading a workshop item using pubfile id"); + Console.WriteLine(" depotdownloader --app --pub-file [--username [--password ]]"); Console.WriteLine(); - Console.WriteLine("\t-username \t\t- the username of the account to login to for restricted content."); - Console.WriteLine("\t-password \t\t- the password of the account to login to for restricted content."); - Console.WriteLine("\t-remember-password\t\t- if set, remember the password for subsequent logins of this user."); + Console.WriteLine(" Downloading a workshop item using ugc id"); + Console.WriteLine(" depotdownloader --app --ugc [--username [--password ]]"); Console.WriteLine(); - Console.WriteLine("\t-dir \t\t- the directory in which to place downloaded files."); - Console.WriteLine("\t-filelist \t- a list of files to download (from the manifest). Prefix file path with 'regex:' if you want to match with regex."); - Console.WriteLine("\t-validate\t\t\t\t- Include checksum verification of files already downloaded"); + Console.WriteLine("Parameters:"); Console.WriteLine(); - Console.WriteLine("\t-manifest-only\t\t\t- downloads a human readable manifest for any depots that would be downloaded."); - Console.WriteLine("\t-cellid <#>\t\t\t\t- the overridden CellID of the content server to download from."); - Console.WriteLine("\t-max-servers <#>\t\t- maximum number of content servers to use. (default: 20)."); - Console.WriteLine("\t-max-downloads <#>\t\t- maximum number of chunks to download concurrently. (default: 8)."); - Console.WriteLine("\t-loginid <#>\t\t- a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently."); + Console.WriteLine(ArgumentParser.GetHelpList(2)); } } } diff --git a/DepotDownloader/ProgramArgs.cs b/DepotDownloader/ProgramArgs.cs new file mode 100644 index 00000000..9c31e28e --- /dev/null +++ b/DepotDownloader/ProgramArgs.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; + +namespace DepotDownloader +{ + internal partial class Program + { + private class ProgramArgs : IArgumentContainer + { + public ProgramArgs() + { + AppID = ContentDownloader.INVALID_APP_ID; + DepotIDs = new List(); + ManifestIDs = new List(); + ManifestOnly = false; + UGC = ContentDownloader.INVALID_MANIFEST_ID; + PubFile = ContentDownloader.INVALID_MANIFEST_ID; + Validate = false; + CellID = 0; + Username = ""; + Password = ""; + RememberPassword = false; + LoginID = null; + BetaBranchName = ContentDownloader.DEFAULT_BRANCH; + BetaPassword = ""; + AllPlatforms = false; + OperatingSystem = ""; + Architecture = Environment.Is64BitOperatingSystem ? "64" : "32"; + AllLanguages = false; + Language = ""; + LowViolence = false; + InstallDir = ""; + FileList = null; + MaxServers = 20; + MaxDownloads = 8; + Debug = false; + } + + [Option(ShortOption = 'a', LongOption = "app", ParameterName = "id", + Description = "The AppID to download")] + public uint AppID { get; set; } + + [Option(ShortOption = 'd', LongOption = "depot", ParameterName = "id", AllowMultiple = true, + Description = "The DepotID to download, separate multiple ids with whitespace")] + public List DepotIDs { get; set;} + + [Option(ShortOption = 'm', LongOption = "manifest", ParameterName = "id", AllowMultiple = true, + Description = "manifest id of content to download (requires -d|--depot, default: current for branch)")] + public List ManifestIDs { get; set;} + + [Option(LongOption = "manifest-only", + Description = "Downloads a human readable manifest for any depots that would be downloaded")] + public bool ManifestOnly { get; set;} + + [Option(ShortOption = 'g', LongOption = "ugc", ParameterName = "id", + Description = "The UGC ID to download")] + public ulong UGC { get; set;} + + [Option(LongOption = "pub-file", ParameterName = "id", + Description = "The Published-File-ID to download. (Will automatically resolve to UGC id)\n")] + public ulong PubFile { get; set;} + + [Option(LongOption = "validate", Description = "Include checksum verification of files already downloaded")] + public bool Validate { get; set;} + + [Option(ShortOption = 'c', LongOption = "cell-id", ParameterName = "id", + Description = "The overridden CellID of the content server to download from.")] + public int CellID { get; set;} + + [Option(ShortOption = 'u', LongOption = "username", ParameterName = "user", + Description = "The username of the account to login to for restricted content")] + public string Username { get; set;} + + [Option(ShortOption = 'p', LongOption = "password", ParameterName = "pass", + Description = "The password of the account to login to for restricted content.")] + public string Password { get;set; } + + [Option(LongOption = "remember-password", + Description = "If set, remember the password for subsequent logins of this user\n")] + public bool RememberPassword { get; set;} + + [Option(LongOption = "login-id", ParameterName = "id", + Description = "A unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently")] + public uint? LoginID { get; set;} + + [Option(LongOption = "beta", ParameterName = "branch", + Description = "Download from specified beta branch if available (default: Public)")] + public string BetaBranchName { get; set;} + + [Option(LongOption = "beta-password", ParameterName = "pass", + Description = "Beta branch password if applicable\n")] + public string BetaPassword { get; set;} + + [Option(LongOption = "all-platforms", + Description = "Downloads all platform-specific depots when -a|--app is used")] + public bool AllPlatforms { get; set;} + + [Option(ShortOption = 'o', LongOption = "os", ParameterName = "os", + Description = "The operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)")] + public string OperatingSystem { get; set;} + + [Option(LongOption = "os-arch", + Description = + "The architecture for which to download the game (32 or 64, default: the host's architecture)\n", + ParameterName = "arch")] + public string Architecture { get; set;} + + [Option(LongOption = "all-languages", + Description = "Download all language-specific depots when -a|--app is used")] + public bool AllLanguages { get; set;} + + [Option(ShortOption = 'l', LongOption = "language", ParameterName = "lang", + Description = "The language for which to download the game (default: english)\n")] + public string Language { get; set;} + + [Option(LongOption = "low-violence", + Description = "Download low violence depots when -a|--app is used")] + public bool LowViolence { get; set;} + + [Option(LongOption = "install-dir", ParameterName = "dir", + Description = "The directory in which to place downloaded files")] + public string InstallDir { get; set;} + + [Option(LongOption = "file-list", ParameterName = "file", + Description = "a list of files to download (from the manifest). Prefix file path with 'regex:' if you want to match with regex")] + public string FileList { get; set;} + + [Option(LongOption = "max-servers", ParameterName = "count", + Description = "Maximum number of content servers to use (default: 20)")] + public int MaxServers { get; set;} + + [Option(LongOption = "max-downloads", ParameterName = "count", + Description = "Maximum number of chunks to download concurrently (default: 8)")] + public int MaxDownloads { get; set;} + + [Option(LongOption = "verbose", + Description = "Makes the DepotDownload more verbose/talkative. Mostly useful for debugging")] + public bool Debug { get; set;} + } + } +}