From 341b8b05e1983350b231a470988604f1a97e4690 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:28:35 -0600 Subject: [PATCH 01/11] Add support for multiple appids. --- DepotDownloader/ContentDownloader.cs | 9 +- DepotDownloader/Program.cs | 192 ++++++++++++++++++--------- 2 files changed, 132 insertions(+), 69 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index a089f122..efb4e623 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -47,7 +47,7 @@ namespace DepotDownloader public byte[] DepotKey { get; } = depotKey; } - static bool CreateDirectories(uint depotId, uint depotVersion, out string installDir) + static bool CreateDirectories(uint appId, uint depotId, uint depotVersion, out string installDir) { installDir = null; try @@ -56,6 +56,9 @@ namespace DepotDownloader { Directory.CreateDirectory(DEFAULT_DOWNLOAD_DIR); + var appPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, appId.ToString()); + Directory.CreateDirectory(appPath); + var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); Directory.CreateDirectory(depotPath); @@ -388,7 +391,7 @@ namespace DepotDownloader private static async Task DownloadWebFile(uint appId, string fileName, string url) { - if (!CreateDirectories(appId, 0, out var installDir)) + if (!CreateDirectories(appId, appId, 0, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return; @@ -603,7 +606,7 @@ namespace DepotDownloader var uVersion = GetSteam3AppBuildNumber(appId, branch); - if (!CreateDirectories(depotId, uVersion, out var installDir)) + if (!CreateDirectories(appId, depotId, uVersion, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return null; diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index ea65385b..9cb99c30 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -124,24 +124,36 @@ namespace DepotDownloader #endregion - var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID); - if (appId == ContentDownloader.INVALID_APP_ID) + var appIdList = GetParameterList(args, "-app"); + if (appIdList.Count == 0) { Console.WriteLine("Error: -app not 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) + + var pubFileList = GetParameterList(args, "-pubfile"); + var ugcIdList = GetParameterList(args, "-ugc"); + + if (pubFileList.Count > 0) { - #region Pubfile Downloading + if (pubFileList.Count != appIdList.Count) + { + Console.WriteLine("Error: Number of -pubfile arguments does not match number of -app arguments."); + return 1; + } if (InitializeSteam(username, password)) { try { - await ContentDownloader.DownloadPubfileAsync(appId, pubFile).ConfigureAwait(false); + for (var i = 0; i < appIdList.Count; i++) + { + var appId = appIdList[i]; + var pubFile = pubFileList[i]; + + await ContentDownloader.DownloadPubfileAsync(appId, pubFile).ConfigureAwait(false); + } } catch (Exception ex) when ( ex is ContentDownloaderException @@ -152,7 +164,7 @@ namespace DepotDownloader } catch (Exception e) { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + Console.WriteLine("Download failed due to an unhandled exception: {0}", e.Message); throw; } finally @@ -165,18 +177,27 @@ namespace DepotDownloader Console.WriteLine("Error: InitializeSteam failed"); return 1; } - - #endregion } - else if (ugcId != ContentDownloader.INVALID_MANIFEST_ID) + + else if (ugcIdList.Count > 0) { - #region UGC Downloading + if (ugcIdList.Count != appIdList.Count) + { + Console.WriteLine("Error: Number of -ugc arguments does not match number of -app arguments."); + return 1; + } if (InitializeSteam(username, password)) { try { - await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false); + for (var i = 0; i < appIdList.Count; i++) + { + var appId = appIdList[i]; + var ugcId = ugcIdList[i]; + + await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false); + } } catch (Exception ex) when ( ex is ContentDownloaderException @@ -187,7 +208,7 @@ namespace DepotDownloader } catch (Exception e) { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); + Console.WriteLine("Download failed due to an unhandled exception: {0}", e.Message); throw; } finally @@ -200,9 +221,8 @@ namespace DepotDownloader Console.WriteLine("Error: InitializeSteam failed"); return 1; } - - #endregion } + else { #region App downloading @@ -241,32 +261,60 @@ namespace DepotDownloader 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) - { - 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); - } - else - { - depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); - } if (InitializeSteam(username, password)) { try { - await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); + foreach (var appId in appIdList) + + { + + var depotManifestIds = new List<(uint, ulong)>(); + + var isUGC = false; + + + + if (manifestIdList.Count > 0) + + { + + 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); + + } + + else + + { + + depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); + + } + + + + await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); + + } } catch (Exception ex) when ( ex is ContentDownloaderException @@ -394,47 +442,59 @@ namespace DepotDownloader { // Do not use tabs to align parameters here because tab size may differ Console.WriteLine(); - Console.WriteLine("Usage: downloading one or all depots for an app:"); - Console.WriteLine(" depotdownloader -app [-depot [-manifest ]]"); + Console.WriteLine("Usage: downloading depots for one or more apps:"); + Console.WriteLine(" depotdownloader -app [-depot [-manifest ]]"); Console.WriteLine(" [-username [-password ]] [other options]"); Console.WriteLine(); - Console.WriteLine("Usage: downloading a workshop item using pubfile id"); - Console.WriteLine(" depotdownloader -app -pubfile [-username [-password ]]"); - Console.WriteLine("Usage: downloading a workshop item using ugc id"); - Console.WriteLine(" depotdownloader -app -ugc [-username [-password ]]"); + Console.WriteLine("Examples:"); + Console.WriteLine(" Single list format:"); + Console.WriteLine(" depotdownloader -app \"10 20 30\" -depot \"11 22 33\" -manifest \"1111 2222 3333\""); + Console.WriteLine(); + Console.WriteLine(" Group list format:"); + Console.WriteLine(" depotdownloader -app 10 -depot 11 -manifest 1111 -app 20 -depot 22 -manifest 2222"); + Console.WriteLine(" -app 30 -depot 33 -manifest 3333"); + Console.WriteLine(); + Console.WriteLine("Usage: downloading a workshop item using pubfile id:"); + Console.WriteLine(" depotdownloader -app -pubfile [-username [-password ]]"); + Console.WriteLine("Usage: downloading a workshop item using ugc id:"); + Console.WriteLine(" depotdownloader -app -ugc [-username [-password ]]"); Console.WriteLine(); Console.WriteLine("Parameters:"); - Console.WriteLine(" -app <#> - the AppID to download."); - Console.WriteLine(" -depot <#> - the DepotID to download."); - Console.WriteLine(" -manifest - manifest id of content to download (requires -depot, default: current for branch)."); - Console.WriteLine($" -beta - download from specified branch if available (default: {ContentDownloader.DEFAULT_BRANCH})."); - Console.WriteLine(" -betapassword - branch password if applicable."); - Console.WriteLine(" -all-platforms - downloads all platform-specific depots when -app is used."); - Console.WriteLine(" -all-archs - download all architecture-specific depots when -app is used."); - Console.WriteLine(" -os - the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)"); - Console.WriteLine(" -osarch - the architecture for which to download the game (32 or 64, default: the host's architecture)"); - Console.WriteLine(" -all-languages - download all language-specific depots when -app is used."); - Console.WriteLine(" -language - the language for which to download the game (default: english)"); - Console.WriteLine(" -lowviolence - download low violence depots when -app is used."); + Console.WriteLine(" -app <# or \"# # ...\"> - the AppID(s) to download. Provide multiple IDs separated by spaces."); + Console.WriteLine(" -depot <# or \"# # ...\"> - the DepotID(s) to download. Must correspond to the provided AppIDs."); + Console.WriteLine(" -manifest <# or \"# # ...\"> - manifest ID(s) of content to download (requires -depot). Must correspond to the provided DepotIDs."); + Console.WriteLine($" -beta - download from specified branch if available (default: {ContentDownloader.DEFAULT_BRANCH})."); + Console.WriteLine(" -betapassword - branch password if applicable."); + Console.WriteLine(" -all-platforms - downloads all platform-specific depots when -app is used."); + Console.WriteLine(" -all-archs - download all architecture-specific depots when -app is used."); + Console.WriteLine(" -os - the operating system for which to download the game (windows, macos, or linux)."); + Console.WriteLine(" (default: OS the program is currently running on)"); + Console.WriteLine(" -osarch - the architecture for which to download the game (32 or 64)."); + Console.WriteLine(" (default: the host's architecture)"); + Console.WriteLine(" -all-languages - download all language-specific depots when -app is used."); + Console.WriteLine(" -language - the language for which to download the game (default: english)"); + Console.WriteLine(" -lowviolence - download low violence depots when -app is used."); Console.WriteLine(); - Console.WriteLine(" -ugc <#> - the UGC ID to download."); - Console.WriteLine(" -pubfile <#> - the PublishedFileId to download. (Will automatically resolve to UGC id)"); + Console.WriteLine(" -ugc <# or \"# # ...\"> - the UGC ID(s) to download. Must correspond to the provided AppIDs."); + Console.WriteLine(" -pubfile <# or \"# # ...\"> - the PublishedFileId(s) to download. Will automatically resolve to UGC IDs."); Console.WriteLine(); - Console.WriteLine(" -username - the username of the account to login to for restricted content."); - Console.WriteLine(" -password - the password of the account to login to for restricted content."); - Console.WriteLine(" -remember-password - if set, remember the password for subsequent logins of this user."); - Console.WriteLine(" use -username -remember-password as login credentials."); + Console.WriteLine(" -username - the username of the account to login to for restricted content."); + Console.WriteLine(" -password - the password of the account to login to for restricted content."); + Console.WriteLine(" -remember-password - if set, remember the password for subsequent logins of this user."); + Console.WriteLine(" Use -username -remember-password as login credentials."); Console.WriteLine(); - Console.WriteLine(" -dir - the directory in which to place downloaded files."); - Console.WriteLine(" -filelist - the name of a local file that contains a list of files to download (from the manifest)."); - Console.WriteLine(" prefix file path with `regex:` if you want to match with regex. each file path should be on their own line."); + Console.WriteLine(" -dir - the directory in which to place downloaded files."); + Console.WriteLine(" -filelist - the name of a local file that contains a list of files to download (from the manifest)."); + Console.WriteLine(" Prefix file path with `regex:` if you want to match with regex."); + Console.WriteLine(" Each file path should be on its own line."); Console.WriteLine(); - Console.WriteLine(" -validate - include checksum verification of files already downloaded"); - Console.WriteLine(" -manifest-only - downloads a human readable manifest for any depots that would be downloaded."); - Console.WriteLine(" -cellid <#> - the overridden CellID of the content server to download from."); - Console.WriteLine(" -max-servers <#> - maximum number of content servers to use. (default: 20)."); - Console.WriteLine(" -max-downloads <#> - maximum number of chunks to download concurrently. (default: 8)."); - Console.WriteLine(" -loginid <#> - a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently."); + Console.WriteLine(" -validate - include checksum verification of files already downloaded."); + Console.WriteLine(" -manifest-only - downloads a human-readable manifest for any depots that would be downloaded."); + Console.WriteLine(" -cellid <#> - the overridden CellID of the content server to download from."); + Console.WriteLine(" -max-servers <#> - maximum number of content servers to use (default: 20)."); + Console.WriteLine(" -max-downloads <#> - maximum number of chunks to download concurrently (default: 8)."); + Console.WriteLine(" -loginid <#> - a unique 32-bit integer Steam LogonID in decimal, required if running"); + Console.WriteLine(" multiple instances of DepotDownloader concurrently."); } static void PrintVersion(bool printExtra = false) From e600f92182db0b670f91efc50459f53deae33c1a Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:49:06 -0600 Subject: [PATCH 02/11] Add support for multiple ids. --- DepotDownloader/ContentDownloader.cs | 31 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index efb4e623..c55ef215 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -47,7 +47,7 @@ namespace DepotDownloader public byte[] DepotKey { get; } = depotKey; } - static bool CreateDirectories(uint appId, uint depotId, uint depotVersion, out string installDir) + static bool CreateDirectories(uint appId, uint depotId, ulong manifestId, out string installDir) { installDir = null; try @@ -56,14 +56,28 @@ namespace DepotDownloader { Directory.CreateDirectory(DEFAULT_DOWNLOAD_DIR); - var appPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, appId.ToString()); + var baseDir = DEFAULT_DOWNLOAD_DIR; + + // Create appId directory + var appPath = Path.Combine(baseDir, appId.ToString()); Directory.CreateDirectory(appPath); - var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); - Directory.CreateDirectory(depotPath); + if (depotId != ContentDownloader.INVALID_DEPOT_ID) + { + // Create depotId directory under appId + var depotPath = Path.Combine(appPath, depotId.ToString()); + Directory.CreateDirectory(depotPath); - installDir = Path.Combine(depotPath, depotVersion.ToString()); - Directory.CreateDirectory(installDir); + // Create manifestId directory under depotId + installDir = Path.Combine(depotPath, manifestId.ToString()); + Directory.CreateDirectory(installDir); + } + else + { + // For cases where depotId is not applicable (e.g., DownloadWebFile), use appId directory directly + installDir = appPath; + Directory.CreateDirectory(installDir); + } Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); Directory.CreateDirectory(Path.Combine(installDir, STAGING_DIR)); @@ -86,6 +100,7 @@ namespace DepotDownloader return true; } + static bool TestIsFileIncluded(string filename) { if (!Config.UsingFileList) @@ -604,9 +619,7 @@ namespace DepotDownloader return null; } - var uVersion = GetSteam3AppBuildNumber(appId, branch); - - if (!CreateDirectories(appId, depotId, uVersion, out var installDir)) + if (!CreateDirectories(appId, depotId, manifestId, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return null; From 73329330d3ecc40d06f40d95e467c6e941927c74 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:40:58 -0600 Subject: [PATCH 03/11] Address Config already loaded error. --- DepotDownloader/ContentDownloader.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index c55ef215..7f1ce472 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -440,14 +440,17 @@ namespace DepotDownloader cdnPool = new CDNClientPool(steam3, appId); // Load our configuration data containing the depots currently installed - var configPath = Config.InstallDirectory; - if (string.IsNullOrWhiteSpace(configPath)) + if (DepotConfigStore.Instance == null) { - configPath = DEFAULT_DOWNLOAD_DIR; - } + var configPath = Config.InstallDirectory; + if (string.IsNullOrWhiteSpace(configPath)) + { + configPath = DEFAULT_DOWNLOAD_DIR; + } - Directory.CreateDirectory(Path.Combine(configPath, CONFIG_DIR)); - DepotConfigStore.LoadFromFile(Path.Combine(configPath, CONFIG_DIR, "depot.config")); + Directory.CreateDirectory(Path.Combine(configPath, CONFIG_DIR)); + DepotConfigStore.LoadFromFile(Path.Combine(configPath, CONFIG_DIR, "depot.config")); + } await steam3?.RequestAppInfo(appId); From 78e6141e970479b667edde66a209db092cd7f0b1 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:07:32 -0600 Subject: [PATCH 04/11] Fix Argument exception. --- DepotDownloader/Program.cs | 67 +++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index 9cb99c30..e7f8a36d 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -413,31 +413,76 @@ namespace DepotDownloader static List GetParameterList(string[] args, string param) { var list = new List(); - var index = IndexOfParam(args, param); - if (index == -1 || index == (args.Length - 1)) + var converter = TypeDescriptor.GetConverter(typeof(T)); + if (converter == null) + { + Console.WriteLine($"Warning: No type converter available for type {typeof(T)}"); return list; + } - index++; - + int index = 0; while (index < args.Length) { - var strParam = args[index]; + // Find the next occurrence of the parameter + if (args[index].Equals(param, StringComparison.OrdinalIgnoreCase)) + { + index++; // Move to the value(s) after the parameter - if (strParam[0] == '-') break; + // Process values following the parameter + while (index < args.Length && !args[index].StartsWith("-")) + { + var strParam = args[index]; - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) + // Handle space-separated values within a single argument + if (strParam.Contains(" ")) + { + var values = strParam.Split(' ', StringSplitOptions.RemoveEmptyEntries); + foreach (var val in values) + { + try + { + var convertedValue = converter.ConvertFromString(val); + if (convertedValue != null) + { + list.Add((T)convertedValue); + } + } + catch (Exception ex) + { + Console.WriteLine($"Warning: Unable to convert value '{val}' to type {typeof(T)}. Exception: {ex.Message}"); + } + } + } + else + { + try + { + var convertedValue = converter.ConvertFromString(strParam); + if (convertedValue != null) + { + list.Add((T)convertedValue); + } + } + catch (Exception ex) + { + Console.WriteLine($"Warning: Unable to convert value '{strParam}' to type {typeof(T)}. Exception: {ex.Message}"); + } + } + + index++; + } + } + else { - list.Add((T)converter.ConvertFromString(strParam)); + index++; } - - index++; } return list; } + static void PrintUsage() { // Do not use tabs to align parameters here because tab size may differ From 88b92bed4b50344de5dc16091ccc1628a514372d Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Wed, 4 Dec 2024 02:22:18 -0600 Subject: [PATCH 05/11] Change creating directories --- DepotDownloader/ContentDownloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 7f1ce472..46661f98 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -406,7 +406,7 @@ namespace DepotDownloader private static async Task DownloadWebFile(uint appId, string fileName, string url) { - if (!CreateDirectories(appId, appId, 0, out var installDir)) + if (!CreateDirectories(appId, 0, 0, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return; From 140a10fe771aa3cf8f9c45531dede87c05465f30 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Wed, 4 Dec 2024 14:10:09 -0600 Subject: [PATCH 06/11] Add OS fallback. --- DepotDownloader/ContentDownloader.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 46661f98..9b086b43 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -514,8 +514,21 @@ namespace DepotDownloader !string.IsNullOrWhiteSpace(depotConfig["oslist"].Value)) { var oslist = depotConfig["oslist"].Value.Split(','); - if (Array.IndexOf(oslist, os ?? Util.GetSteamOS()) == -1) - continue; + var osMatches = oslist.Contains(os ?? Util.GetSteamOS(), StringComparer.OrdinalIgnoreCase); + + // Attempt fallback to Windows if no initial match found + if (!osMatches) + { + osMatches = oslist.Contains("windows", StringComparer.OrdinalIgnoreCase); + } + + // Print warning only if no match was found after fallback + if (!osMatches) + { + Console.WriteLine($"No matching OS found for depot {depotSection.Name} under AppID: {appId}."); + continue; // Skip this depot + } + } if (!Config.DownloadAllArchs && From 8b8d6d79deaa4a34c5e54f165c3cb70379625646 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:03:13 -0600 Subject: [PATCH 07/11] Skip if appid in list fails. --- DepotDownloader/Program.cs | 46 +++++++++++++------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index e7f8a36d..42b18ab4 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -271,63 +271,49 @@ namespace DepotDownloader try { foreach (var appId in appIdList) - { - var depotManifestIds = new List<(uint, ulong)>(); var isUGC = false; - if (manifestIdList.Count > 0) - { - 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); - } - else - { - depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); - } - - - await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); - + try + { + await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); + } + catch (ContentDownloaderException ex) + { + Console.WriteLine($"Warning: {ex.Message}"); + // Continue with the next appId + } + catch (OperationCanceledException ex) + { + Console.WriteLine($"Warning: Operation canceled for AppID {appId}: {ex.Message}"); + // Decide whether to continue or break. Here, we continue. + } } } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) + catch (Exception ex) // Handle other unforeseen exceptions { - Console.WriteLine(ex.Message); + Console.WriteLine("An unexpected error occurred: " + ex.Message); return 1; } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } finally { ContentDownloader.ShutdownSteam3(); From 44b9a2c91312047673d7d7bbc04d892c7a78a569 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:52:28 -0600 Subject: [PATCH 08/11] Partial revert --- DepotDownloader/ContentDownloader.cs | 31 +++++++--------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 9b086b43..52345907 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -47,7 +47,7 @@ namespace DepotDownloader public byte[] DepotKey { get; } = depotKey; } - static bool CreateDirectories(uint appId, uint depotId, ulong manifestId, out string installDir) + static bool CreateDirectories(uint depotId, ulong depotVersion, out string installDir) { installDir = null; try @@ -56,28 +56,11 @@ namespace DepotDownloader { Directory.CreateDirectory(DEFAULT_DOWNLOAD_DIR); - var baseDir = DEFAULT_DOWNLOAD_DIR; + var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); + Directory.CreateDirectory(depotPath); - // Create appId directory - var appPath = Path.Combine(baseDir, appId.ToString()); - Directory.CreateDirectory(appPath); - - if (depotId != ContentDownloader.INVALID_DEPOT_ID) - { - // Create depotId directory under appId - var depotPath = Path.Combine(appPath, depotId.ToString()); - Directory.CreateDirectory(depotPath); - - // Create manifestId directory under depotId - installDir = Path.Combine(depotPath, manifestId.ToString()); - Directory.CreateDirectory(installDir); - } - else - { - // For cases where depotId is not applicable (e.g., DownloadWebFile), use appId directory directly - installDir = appPath; - Directory.CreateDirectory(installDir); - } + installDir = Path.Combine(depotPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); Directory.CreateDirectory(Path.Combine(installDir, STAGING_DIR)); @@ -406,7 +389,7 @@ namespace DepotDownloader private static async Task DownloadWebFile(uint appId, string fileName, string url) { - if (!CreateDirectories(appId, 0, 0, out var installDir)) + if (!CreateDirectories( 0, 0, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return; @@ -635,7 +618,7 @@ namespace DepotDownloader return null; } - if (!CreateDirectories(appId, depotId, manifestId, out var installDir)) + if (!CreateDirectories( depotId, manifestId, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return null; From dab93017d31cd078879b4b60ead286df1841f49b Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:45:49 -0600 Subject: [PATCH 09/11] Change directory structure. Use value for args. --- DepotDownloader/ContentDownloader.cs | 66 +++++++++++++++++++++++++--- DepotDownloader/DownloadConfig.cs | 7 +++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 52345907..c0a9ada7 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -56,11 +56,45 @@ namespace DepotDownloader { Directory.CreateDirectory(DEFAULT_DOWNLOAD_DIR); - var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); - Directory.CreateDirectory(depotPath); + // Check if we should use publishedFileId or ugcId directories + if (Config.UsePubOrUgcDirectories) + { + // We need an appId to structure this properly. + // The appId can be retrieved from Config.AppId if you choose to store it there, + // or we can rely on it being set previously. + // If needed, add a field in Config for the current appId being processed. + // Assume we have a Config.AppId that stores the current app ID. + var appId = Config.AppId; + var appPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, appId.ToString()); + Directory.CreateDirectory(appPath); + + if (Config.PublishedFileId != 0) + { + var pubPath = Path.Combine(appPath, Config.PublishedFileId.ToString()); + Directory.CreateDirectory(pubPath); + + // Use depotVersion as the final directory level + installDir = Path.Combine(pubPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + else if (Config.UgcId != 0) + { + var ugcPath = Path.Combine(appPath, Config.UgcId.ToString()); + Directory.CreateDirectory(ugcPath); + + // Use depotVersion as the final directory level + installDir = Path.Combine(ugcPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + } + else + { + var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); + Directory.CreateDirectory(depotPath); - installDir = Path.Combine(depotPath, depotVersion.ToString()); - Directory.CreateDirectory(installDir); + installDir = Path.Combine(depotPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); Directory.CreateDirectory(Path.Combine(installDir, STAGING_DIR)); @@ -68,7 +102,6 @@ namespace DepotDownloader else { Directory.CreateDirectory(Config.InstallDirectory); - installDir = Config.InstallDirectory; Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); @@ -84,6 +117,7 @@ namespace DepotDownloader } + static bool TestIsFileIncluded(string filename) { if (!Config.UsingFileList) @@ -350,6 +384,11 @@ namespace DepotDownloader { var details = await steam3.GetPublishedFileDetails(appId, publishedFileId); + // Set the current appId and publishedFileId in the config + Config.AppId = appId; // Make sure AppId is a field in Config as well + Config.PublishedFileId = publishedFileId; + Config.UgcId = 0; // Reset ugcId + if (!string.IsNullOrEmpty(details?.file_url)) { await DownloadWebFile(appId, details.filename, details.file_url); @@ -362,12 +401,21 @@ namespace DepotDownloader { Console.WriteLine("Unable to locate manifest ID for published file {0}", publishedFileId); } + + // Reset after download if you prefer a clean slate + Config.PublishedFileId = 0; } + public static async Task DownloadUGCAsync(uint appId, ulong ugcId) { SteamCloud.UGCDetailsCallback details = null; + // Set the current appId and ugcId in the config + Config.AppId = appId; + Config.UgcId = ugcId; + Config.PublishedFileId = 0; // Reset publishedFileId + if (steam3.steamUser.SteamID.AccountType != EAccountType.AnonUser) { details = await steam3.GetUGCDetails(ugcId); @@ -385,11 +433,15 @@ namespace DepotDownloader { await DownloadAppAsync(appId, [(appId, ugcId)], DEFAULT_BRANCH, null, null, null, false, true); } + + // Reset after download if you prefer a clean slate + Config.UgcId = 0; } + private static async Task DownloadWebFile(uint appId, string fileName, string url) { - if (!CreateDirectories( 0, 0, out var installDir)) + if (!CreateDirectories(0, 0, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return; @@ -618,7 +670,7 @@ namespace DepotDownloader return null; } - if (!CreateDirectories( depotId, manifestId, out var installDir)) + if (!CreateDirectories(depotId, manifestId, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); return null; diff --git a/DepotDownloader/DownloadConfig.cs b/DepotDownloader/DownloadConfig.cs index c0d4f6cb..45968cd8 100644 --- a/DepotDownloader/DownloadConfig.cs +++ b/DepotDownloader/DownloadConfig.cs @@ -32,5 +32,12 @@ namespace DepotDownloader public uint? LoginID { get; set; } public bool UseQrCode { get; set; } + + public ulong PublishedFileId { get; set; } = 0; + public ulong UgcId { get; set; } = 0; + public uint AppId { get; set; } = 0; + + // Helper property to determine if we're using pubfile/ugc directories + public bool UsePubOrUgcDirectories => (PublishedFileId != 0 || UgcId != 0); } } From d9ced51fd6faf882e1833026b83b2ed320d14e35 Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Sat, 7 Dec 2024 21:04:19 -0600 Subject: [PATCH 10/11] Add support for -dir --- DepotDownloader/ContentDownloader.cs | 80 ++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index c0a9ada7..e17a7550 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -52,19 +52,19 @@ namespace DepotDownloader installDir = null; try { + if (Config.AppId == 0 || Config.AppId == INVALID_APP_ID) + { + return false; + } + + uint appId = Config.AppId; + if (string.IsNullOrWhiteSpace(Config.InstallDirectory)) { Directory.CreateDirectory(DEFAULT_DOWNLOAD_DIR); - // Check if we should use publishedFileId or ugcId directories if (Config.UsePubOrUgcDirectories) { - // We need an appId to structure this properly. - // The appId can be retrieved from Config.AppId if you choose to store it there, - // or we can rely on it being set previously. - // If needed, add a field in Config for the current appId being processed. - // Assume we have a Config.AppId that stores the current app ID. - var appId = Config.AppId; var appPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, appId.ToString()); Directory.CreateDirectory(appPath); @@ -73,7 +73,6 @@ namespace DepotDownloader var pubPath = Path.Combine(appPath, Config.PublishedFileId.ToString()); Directory.CreateDirectory(pubPath); - // Use depotVersion as the final directory level installDir = Path.Combine(pubPath, depotVersion.ToString()); Directory.CreateDirectory(installDir); } @@ -82,28 +81,86 @@ namespace DepotDownloader var ugcPath = Path.Combine(appPath, Config.UgcId.ToString()); Directory.CreateDirectory(ugcPath); - // Use depotVersion as the final directory level installDir = Path.Combine(ugcPath, depotVersion.ToString()); Directory.CreateDirectory(installDir); } + else + { + // No publishedFileId or ugcId + var depotPath = Path.Combine(appPath, depotId.ToString()); + Directory.CreateDirectory(depotPath); + + installDir = Path.Combine(depotPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } } else { - var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString()); + // No pub/ugc directories, just create app/depot/version structure + var appPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, appId.ToString()); + Directory.CreateDirectory(appPath); + + var depotPath = Path.Combine(appPath, depotId.ToString()); Directory.CreateDirectory(depotPath); installDir = Path.Combine(depotPath, depotVersion.ToString()); Directory.CreateDirectory(installDir); } + // Config and staging directories Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); Directory.CreateDirectory(Path.Combine(installDir, STAGING_DIR)); } else { + // Using custom installation directory provided via -dir Directory.CreateDirectory(Config.InstallDirectory); - installDir = Config.InstallDirectory; + if (Config.UsePubOrUgcDirectories) + { + var appPath = Path.Combine(Config.InstallDirectory, appId.ToString()); + Directory.CreateDirectory(appPath); + + if (Config.PublishedFileId != 0) + { + var pubPath = Path.Combine(appPath, Config.PublishedFileId.ToString()); + Directory.CreateDirectory(pubPath); + + installDir = Path.Combine(pubPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + else if (Config.UgcId != 0) + { + var ugcPath = Path.Combine(appPath, Config.UgcId.ToString()); + Directory.CreateDirectory(ugcPath); + + installDir = Path.Combine(ugcPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + else + { + // No publishedFileId or ugcId + var depotPath = Path.Combine(appPath, depotId.ToString()); + Directory.CreateDirectory(depotPath); + + installDir = Path.Combine(depotPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + } + else + { + // Just create an app/depot/version hierarchy under the custom dir + var appPath = Path.Combine(Config.InstallDirectory, appId.ToString()); + Directory.CreateDirectory(appPath); + + var depotPath = Path.Combine(appPath, depotId.ToString()); + Directory.CreateDirectory(depotPath); + + installDir = Path.Combine(depotPath, depotVersion.ToString()); + Directory.CreateDirectory(installDir); + } + + // Config and staging directories Directory.CreateDirectory(Path.Combine(installDir, CONFIG_DIR)); Directory.CreateDirectory(Path.Combine(installDir, STAGING_DIR)); } @@ -670,6 +727,7 @@ namespace DepotDownloader return null; } + Config.AppId = appId; if (!CreateDirectories(depotId, manifestId, out var installDir)) { Console.WriteLine("Error: Unable to create install directories!"); From 7deefe0d42cc167cd21d3235f35ecff0d36cc01c Mon Sep 17 00:00:00 2001 From: Alstruit <34786806+Alstruit@users.noreply.github.com> Date: Sun, 8 Dec 2024 00:19:54 -0600 Subject: [PATCH 11/11] Change to address os cases. --- DepotDownloader/ContentDownloader.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index e17a7550..a4767713 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -606,19 +606,25 @@ namespace DepotDownloader !string.IsNullOrWhiteSpace(depotConfig["oslist"].Value)) { var oslist = depotConfig["oslist"].Value.Split(','); - var osMatches = oslist.Contains(os ?? Util.GetSteamOS(), StringComparer.OrdinalIgnoreCase); - // Attempt fallback to Windows if no initial match found - if (!osMatches) + // Special case: empty oslist and appId + 1 + if (oslist.Length == 0 && id == appId + 1) { - osMatches = oslist.Contains("windows", StringComparer.OrdinalIgnoreCase); + Console.WriteLine($"Warning: Depot {id} has an empty oslist and is being downloaded."); + depotIdsFound.Add(id); + depotManifestIds.Add((id, INVALID_MANIFEST_ID)); + continue; } - // Print warning only if no match was found after fallback + var osMatches = os != null + ? oslist.Contains(os, StringComparer.OrdinalIgnoreCase) + : oslist.Contains(Util.GetSteamOS(), StringComparer.OrdinalIgnoreCase); + + // If no match, skip this depot if (!osMatches) { Console.WriteLine($"No matching OS found for depot {depotSection.Name} under AppID: {appId}."); - continue; // Skip this depot + continue; } }