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;}
+ }
+ }
+}