diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 43254c225..509dcfe40 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -311,7 +311,7 @@ public static bool InitializeSteam3(string username, string password) if (username != null && Config.RememberPassword) { - _ = AccountSettingsStore.Instance.LoginTokens.TryGetValue(username, out loginToken); + _ = AccountSettingsStore.Instance.LoginTokens.TryGetValue(username.ToLowerInvariant(), out loginToken); } steam3 = new Steam3Session( @@ -350,21 +350,31 @@ public static void ShutdownSteam3() steam3.Disconnect(); } - public static async Task DownloadPubfileAsync(uint appId, ulong publishedFileId) + public static async Task DownloadPubfileAsync(uint appId, ulong[] publishedFileIds) { - var details = steam3.GetPublishedFileDetails(appId, publishedFileId); + var list = steam3.GetPublishedFileDetails(appId, publishedFileIds); - if (!string.IsNullOrEmpty(details?.file_url)) - { - await DownloadWebFile(appId, details.filename, details.file_url); - } - else if (details?.hcontent_file > 0) + var hcontentFiles = new HashSet(); + + foreach (var details in list) { - await DownloadAppAsync(appId, new List<(uint, ulong)> { (appId, details.hcontent_file) }, DEFAULT_BRANCH, null, null, null, false, true); + if (!string.IsNullOrEmpty(details?.file_url)) + { + await DownloadWebFile(appId, details.filename, details.file_url); + } + else if (details?.hcontent_file > 0) + { + hcontentFiles.Add(details.hcontent_file); + } + else + { + Console.WriteLine("Unable to locate manifest ID for published file {0}", details?.publishedfileid); + } } - else + + if (hcontentFiles.Count > 0) { - Console.WriteLine("Unable to locate manifest ID for published file {0}", publishedFileId); + await DownloadAppAsync(appId, hcontentFiles.Select(f => (appId, f)).ToList(), DEFAULT_BRANCH, null, null, null, false, true); } } @@ -423,7 +433,7 @@ private static async Task DownloadWebFile(uint appId, string fileName, string ur File.Move(fileStagingPath, fileFinalPath); } - public static async Task DownloadAppAsync(uint appId, List<(uint depotId, ulong manifestId)> depotManifestIds, string branch, string os, string arch, string language, bool lv, bool isUgc) + public static async Task DownloadAppAsync(uint appId, List<(uint depotId, ulong manifestId)> depotManifestIds, string branch, string[] os, string[] arch, string[] language, bool lv, bool isUgc) { cdnPool = new CDNClientPool(steam3, appId); @@ -500,7 +510,7 @@ public static async Task DownloadAppAsync(uint appId, List<(uint depotId, ulong !string.IsNullOrWhiteSpace(depotConfig["oslist"].Value)) { var oslist = depotConfig["oslist"].Value.Split(','); - if (Array.IndexOf(oslist, os ?? Util.GetSteamOS()) == -1) + if (!os.Any(x => oslist.Contains(x))) continue; } @@ -508,7 +518,7 @@ public static async Task DownloadAppAsync(uint appId, List<(uint depotId, ulong !string.IsNullOrWhiteSpace(depotConfig["osarch"].Value)) { var depotArch = depotConfig["osarch"].Value; - if (depotArch != (arch ?? Util.GetSteamArch())) + if (!arch.Contains(depotArch)) continue; } @@ -517,7 +527,7 @@ public static async Task DownloadAppAsync(uint appId, List<(uint depotId, ulong !string.IsNullOrWhiteSpace(depotConfig["language"].Value)) { var depotLang = depotConfig["language"].Value; - if (depotLang != (language ?? "english")) + if (!language.Contains(depotLang)) continue; } diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index 98ded2a76..8bbc1735d 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -12,8 +12,10 @@ - - - + + + + + diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index eb396051a..c70b4ce8f 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -1,6 +1,11 @@ +#nullable enable using System; using System.Collections.Generic; -using System.ComponentModel; +using System.CommandLine; +using System.CommandLine.Builder; +using System.CommandLine.Help; +using System.CommandLine.NamingConventionBinder; +using System.CommandLine.Parsing; using System.IO; using System.Linq; using System.Reflection; @@ -11,28 +16,124 @@ namespace DepotDownloader { - class Program + internal class Program { - static int Main(string[] args) - => MainAsync(args).GetAwaiter().GetResult(); - - static async Task MainAsync(string[] args) + public static Task Main(string[] args) { - if (args.Length == 0) + var rootCommand = new RootCommand { - PrintUsage(); - return 1; - } + new Option("--debug") { IsHidden = true }, + + new Option("--app", "The AppID to download") { IsRequired = true, ArgumentHelpName = "id", Name = "AppId" }, + + new Option("--depot", "The DepotID to download") { ArgumentHelpName = "id", Name = "Depots" }, + new Option("--manifest", "Manifest id of content to download (requires --depot, default: current for branch)") { ArgumentHelpName = "id", Name = "Manifests" }, + + new Option("--ugc", "The UGC ID to download") { ArgumentHelpName = "id" }, + new Option("--pubfile", "The PublishedFileId to download (will automatically resolve to UGC id)") { ArgumentHelpName = "id", Name = "PublishedFileIds" }, + + new Option(new[] { "--branch", "--beta" }, "Download from specified branch if available"), + new Option(new[] { "--branch-password", "--betapassword" }, "Branch password if applicable"), + + new Option("--os", () => new[] { Util.GetSteamOS() }, "The operating system for which to download the game") { Name = "OperatingSystems" }.FromAmong("all", "windows", "macos", "linux"), + new Option("--arch", () => new[] { Util.GetSteamArch() }, "The architecture for which to download the game") { Name = "Architectures" }.FromAmong("64", "32"), + new Option("--language", () => new[] { "english" }, "The language for which to download the game") { ArgumentHelpName = "language", Name = "Languages" }, + new Option("--lowviolence", "Download low violence depots"), + + new Option("--username", "The username of the account to login to for restricted content"), + new Option("--password", "The password of the account to login to for restricted content"), + new Option("--remember-password", "If set, remember the password for subsequent logins of this user"), + new Option("--qr", "If set, allows logging in with a QR code generate by the Steam Mobile App"), - DebugLog.Enabled = false; + new Option(new[] { "--directory", "--dir" }, "The directory in which to place downloaded files"), + new Option("--filelist", "A list of files to download (from the manifest). Prefix file path with 'regex:' if you want to match with regex").ExistingOnly(), + new Option(new[] { "--validate", "--verify-all" }, "Include checksum verification of files already downloaded") { Name = "Validate" }, + new Option("--manifest-only", "Downloads a human readable manifest for any depots that would be downloaded"), + new Option("--cellid", "The overridden CellID of the content server to download from"), + new Option("--max-servers", () => 20, "Maximum number of content servers to use"), + new Option("--max-downloads", () => 8, "Maximum number of chunks to download concurrently"), + new Option("--loginid", "A unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently"), + }; + + rootCommand.Handler = CommandHandler.Create(DownloadAsync); + + return new CommandLineBuilder(rootCommand) + .UseDefaults() + .UseHelp(help => + { + // Workaround https://github.com/dotnet/command-line-api/issues/1550 + foreach (var option in rootCommand.Options) + { + if (option.HasAlias(option.Name)) + { + help.HelpBuilder.CustomizeSymbol(option, context => HelpBuilder.Default.GetIdentifierSymbolUsageLabel(option, context)[(option.Name.Length + 2)..]); + } + } + + help.HelpBuilder.CustomizeLayout(GetHelpLayout); + }) + .Build().InvokeAsync(args); + } + + private static IEnumerable GetHelpLayout(HelpContext _) + { + yield return ctx => { ctx.Output.WriteLine("DepotDownloader"); }; + yield return ctx => + { + ctx.Output.WriteLine(@$"Examples: + - downloading one or all depots for an app: + {ctx.Command.Name} --app [--depot [--manifest ]] [--username [--password ]] + + - downloading a workshop item using pubfile id: + {ctx.Command.Name} --app --pubfile [--username [--password ]] + + - downloading a workshop item using ugc id: + {ctx.Command.Name} --app --ugc [--username [--password ]]" + ); + }; + yield return HelpBuilder.Default.CommandArgumentsSection(); + yield return HelpBuilder.Default.OptionsSection(); + yield return HelpBuilder.Default.SubcommandsSection(); + yield return HelpBuilder.Default.AdditionalArgumentsSection(); + } + + public record InputModel( + bool Debug, + uint AppId, + uint[] Depots, + ulong[] Manifests, + ulong? UgcId, + ulong[] PublishedFileIds, + string? Branch, + string? BranchPassword, + string[] OperatingSystems, + string[] Architectures, + string[] Languages, + bool LowViolence, + string? Username, + string? Password, + bool RememberPassword, + bool Qr, + DirectoryInfo? Directory, + FileInfo? FileList, + bool Validate, + bool ManifestOnly, + int? CellId, + int MaxServers, + int MaxDownloads, + uint? LoginId + ); + + public static async Task DownloadAsync(InputModel input) + { AccountSettingsStore.LoadFromFile("account.config"); #region Common Options - if (HasParameter(args, "-debug")) + DebugLog.Enabled = input.Debug; + if (input.Debug) { - DebugLog.Enabled = true; DebugLog.AddListener((category, message) => { Console.WriteLine("[{0}] {1}", category, message); @@ -44,29 +145,16 @@ static async Task MainAsync(string[] args) DebugLog.WriteLine("DepotDownloader", "Runtime: {0}", RuntimeInformation.FrameworkDescription); } - 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.UseQrCode = HasParameter(args, "-qr"); - - ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); + ContentDownloader.Config.RememberPassword = input.RememberPassword; + ContentDownloader.Config.UseQrCode = input.Qr; + ContentDownloader.Config.DownloadManifestOnly = input.ManifestOnly; + ContentDownloader.Config.CellID = input.CellId ?? 0; - var cellId = GetParameter(args, "-cellid", -1); - if (cellId == -1) - { - cellId = 0; - } - - ContentDownloader.Config.CellID = cellId; - - var fileList = GetParameter(args, "-filelist"); - - if (fileList != null) + if (input.FileList != null) { try { - var fileListData = await File.ReadAllTextAsync(fileList); - var files = fileListData.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + var files = await File.ReadAllLinesAsync(input.FileList.FullName); ContentDownloader.Config.UsingFileList = true; ContentDownloader.Config.FilesToDownload = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -76,8 +164,8 @@ static async Task MainAsync(string[] args) { if (fileEntry.StartsWith("regex:")) { - var rgx = new Regex(fileEntry.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase); - ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); + var regex = new Regex(fileEntry[6..], RegexOptions.Compiled | RegexOptions.IgnoreCase); + ContentDownloader.Config.FilesToDownloadRegex.Add(regex); } else { @@ -85,206 +173,103 @@ static async Task MainAsync(string[] args) } } - Console.WriteLine("Using filelist: '{0}'.", fileList); + Console.WriteLine("Using filelist: '{0}'.", input.FileList); } catch (Exception ex) { - Console.WriteLine("Warning: Unable to load filelist: {0}", ex); + Console.WriteLine("Error: Unable to load filelist: {0}", ex); + 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; - - #endregion - - var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID); - if (appId == ContentDownloader.INVALID_APP_ID) + if (input.Directory != null) { - Console.WriteLine("Error: -app not specified!"); - return 1; + ContentDownloader.Config.InstallDirectory = input.Directory.FullName; } - var pubFile = GetParameter(args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID); - var ugcId = GetParameter(args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID); - if (pubFile != ContentDownloader.INVALID_MANIFEST_ID) - { - #region Pubfile Downloading + ContentDownloader.Config.VerifyAll = input.Validate; + ContentDownloader.Config.MaxDownloads = input.MaxDownloads; + ContentDownloader.Config.MaxServers = Math.Max(input.MaxServers, ContentDownloader.Config.MaxDownloads); + ContentDownloader.Config.LoginID = input.LoginId; - 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 - { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; - } + #endregion - #endregion - } - else if (ugcId != ContentDownloader.INVALID_MANIFEST_ID) + if (InitializeSteam(input.Username, input.Password)) { - #region UGC Downloading - - if (InitializeSteam(username, password)) + try { - try - { - await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) + if (input.PublishedFileIds.Any()) { - Console.WriteLine(ex.Message); - return 1; + await ContentDownloader.DownloadPubfileAsync(input.AppId, input.PublishedFileIds).ConfigureAwait(false); } - catch (Exception e) + else if (input.UgcId != null) { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; + await ContentDownloader.DownloadUGCAsync(input.AppId, input.UgcId.Value).ConfigureAwait(false); } - finally + else { - ContentDownloader.ShutdownSteam3(); - } - } - else - { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; - } - - #endregion - } - else - { - #region App downloading - - var branch = GetParameter(args, "-branch") ?? GetParameter(args, "-beta") ?? ContentDownloader.DEFAULT_BRANCH; - ContentDownloader.Config.BetaPassword = GetParameter(args, "-betapassword"); - - ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms"); - var os = GetParameter(args, "-os"); - - if (ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty(os)) - { - Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); - return 1; - } - - var arch = GetParameter(args, "-osarch"); + ContentDownloader.Config.BetaPassword = input.BranchPassword; - ContentDownloader.Config.DownloadAllLanguages = HasParameter(args, "-all-languages"); - var language = GetParameter(args, "-language"); + ContentDownloader.Config.DownloadAllPlatforms = input.OperatingSystems.Contains("all"); + ContentDownloader.Config.DownloadAllLanguages = input.Languages.Contains("all"); - if (ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty(language)) - { - Console.WriteLine("Error: Cannot specify -language when -all-languages is specified."); - return 1; - } + var depotManifestIds = new List<(uint, ulong)>(); - var lv = HasParameter(args, "-lowviolence"); - - var depotManifestIds = new List<(uint, ulong)>(); - var isUGC = false; + if (input.Manifests.Length > 0) + { + if (input.Depots.Length != input.Manifests.Length) + { + Console.WriteLine("Error: --manifest requires one id for every --depot specified"); + return 1; + } + + var zippedDepotManifest = input.Depots.Zip(input.Manifests, (depotId, manifestId) => (depotId, manifestId)); + depotManifestIds.AddRange(zippedDepotManifest); + } + else + { + depotManifestIds.AddRange(input.Depots.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); + } - var depotIdList = GetParameterList(args, "-depot"); - var manifestIdList = GetParameterList(args, "-manifest"); - if (manifestIdList.Count > 0) - { - if (depotIdList.Count != manifestIdList.Count) - { - Console.WriteLine("Error: -manifest requires one id for every -depot specified"); - return 1; + await ContentDownloader.DownloadAppAsync(input.AppId, depotManifestIds, input.Branch ?? ContentDownloader.DEFAULT_BRANCH, input.OperatingSystems, input.Architectures, input.Languages, input.LowViolence, false).ConfigureAwait(false); } - - var zippedDepotManifest = depotIdList.Zip(manifestIdList, (depotId, manifestId) => (depotId, manifestId)); - depotManifestIds.AddRange(zippedDepotManifest); } - else + catch (Exception ex) when (ex is ContentDownloaderException or OperationCanceledException) { - depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); + Console.WriteLine(ex.Message); + return 1; } - - if (InitializeSteam(username, password)) + catch (Exception e) { - 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(); - } + Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + throw; } - else + finally { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; + ContentDownloader.ShutdownSteam3(); } - - #endregion + } + else + { + Console.WriteLine("Error: InitializeSteam failed"); + return 1; } return 0; } - static bool InitializeSteam(string username, string password) + private static bool InitializeSteam(string? username, string? password) { if (!ContentDownloader.Config.UseQrCode) { - if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username))) + if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username.ToLowerInvariant()))) { do { Console.Write("Enter account password for \"{0}\": ", username); - if (Console.IsInputRedirected) - { - password = Console.ReadLine(); - } - else - { - // Avoid console echoing of password - password = Util.ReadPassword(); - } + password = Console.IsInputRedirected + ? Console.ReadLine() + : Util.ReadPassword(); Console.WriteLine(); } while (string.Empty == password); @@ -297,110 +282,5 @@ static bool InitializeSteam(string username, string password) return ContentDownloader.InitializeSteam3(username, password); } - - static int IndexOfParam(string[] args, string param) - { - for (var x = 0; x < args.Length; ++x) - { - 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]; - - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - return (T)converter.ConvertFromString(strParam); - } - - 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) - { - var strParam = args[index]; - - if (strParam[0] == '-') break; - - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - list.Add((T)converter.ConvertFromString(strParam)); - } - - index++; - } - - return list; - } - - static void PrintUsage() - { - 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(); - 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(); - 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: {ContentDownloader.DEFAULT_BRANCH})."); - 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(); - 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(); - 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. (Use -username -remember-password as login credentials)"); - 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(); - 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."); - } } } diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index 206b3b561..15eabe6a5 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -353,13 +353,13 @@ public void CheckAppBetaPassword(uint appid, string password) }, () => { return completed; }); } - public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID pubFile) + public List GetPublishedFileDetails(uint appId, ulong[] publishedFileIds) { var pubFileRequest = new CPublishedFile_GetDetails_Request { appid = appId }; - pubFileRequest.publishedfileids.Add(pubFile); + pubFileRequest.publishedfileids.AddRange(publishedFileIds); var completed = false; - PublishedFileDetails details = null; + List details = null; Action cbMethod = callback => { @@ -367,11 +367,11 @@ public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID if (callback.Result == EResult.OK) { var response = callback.GetDeserializedResponse(); - details = response.publishedfiledetails.FirstOrDefault(); + details = response.publishedfiledetails; } else { - throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving file details for pubfile {pubFile}."); + throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving file details for pubfiles [{string.Join(", ", publishedFileIds)}]."); } };