pull/583/merge
Alstruit 6 months ago committed by GitHub
commit 93a582a719
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -47,30 +47,120 @@ namespace DepotDownloader
public byte[] DepotKey { get; } = depotKey;
}
static bool CreateDirectories(uint depotId, uint depotVersion, out string installDir)
static bool CreateDirectories(uint depotId, ulong depotVersion, out string installDir)
{
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);
var depotPath = Path.Combine(DEFAULT_DOWNLOAD_DIR, depotId.ToString());
Directory.CreateDirectory(depotPath);
if (Config.UsePubOrUgcDirectories)
{
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);
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(depotPath, depotVersion.ToString());
Directory.CreateDirectory(installDir);
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
{
// 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));
}
@ -83,6 +173,8 @@ namespace DepotDownloader
return true;
}
static bool TestIsFileIncluded(string filename)
{
if (!Config.UsingFileList)
@ -343,6 +435,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);
@ -355,12 +452,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);
@ -378,11 +484,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(appId, 0, out var installDir))
if (!CreateDirectories(0, 0, out var installDir))
{
Console.WriteLine("Error: Unable to create install directories!");
return;
@ -416,14 +526,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);
@ -487,8 +600,27 @@ namespace DepotDownloader
!string.IsNullOrWhiteSpace(depotConfig["oslist"].Value))
{
var oslist = depotConfig["oslist"].Value.Split(',');
if (Array.IndexOf(oslist, os ?? Util.GetSteamOS()) == -1)
// Special case: empty oslist and appId + 1
if (oslist.Length == 0 && id == appId + 1)
{
Console.WriteLine($"Warning: Depot {id} has an empty oslist and is being downloaded.");
depotIdsFound.Add(id);
depotManifestIds.Add((id, INVALID_MANIFEST_ID));
continue;
}
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;
}
}
if (!Config.DownloadAllArchs &&
@ -595,9 +727,8 @@ namespace DepotDownloader
return null;
}
var uVersion = GetSteam3AppBuildNumber(appId, branch);
if (!CreateDirectories(depotId, uVersion, out var installDir))
Config.AppId = appId;
if (!CreateDirectories(depotId, manifestId, out var installDir))
{
Console.WriteLine("Error: Unable to create install directories!");
return null;

@ -31,5 +31,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);
}
}

@ -155,24 +155,36 @@ namespace DepotDownloader
#endregion
var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID);
if (appId == ContentDownloader.INVALID_APP_ID)
var appIdList = GetParameterList<uint>(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<ulong>(args, "-pubfile");
var ugcIdList = GetParameterList<ulong>(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
@ -183,7 +195,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
@ -196,18 +208,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
@ -218,7 +239,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
@ -231,9 +252,8 @@ namespace DepotDownloader
Console.WriteLine("Error: InitializeSteam failed");
return 1;
}
#endregion
}
else
{
#region App downloading
@ -272,45 +292,59 @@ namespace DepotDownloader
var lv = HasParameter(args, "-lowviolence");
var depotManifestIds = new List<(uint, ulong)>();
var isUGC = false;
var depotIdList = GetParameterList<uint>(args, "-depot");
var manifestIdList = GetParameterList<ulong>(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)));
}
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();
@ -411,69 +445,125 @@ namespace DepotDownloader
static List<T> GetParameterList<T>(string[] args, string param)
{
var list = new List<T>();
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];
// 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}");
}
}
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter != null)
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
Console.WriteLine();
Console.WriteLine("Usage: downloading one or all depots for an app:");
Console.WriteLine(" depotdownloader -app <id> [-depot <id> [-manifest <id>]]");
Console.WriteLine("Usage: downloading depots for one or more apps:");
Console.WriteLine(" depotdownloader -app <id(s)> [-depot <id(s)> [-manifest <id(s)>]]");
Console.WriteLine(" [-username <username> [-password <password>]] [other options]");
Console.WriteLine();
Console.WriteLine("Usage: downloading a workshop item using pubfile id");
Console.WriteLine(" depotdownloader -app <id> -pubfile <id> [-username <username> [-password <password>]]");
Console.WriteLine("Usage: downloading a workshop item using ugc id");
Console.WriteLine(" depotdownloader -app <id> -ugc <id> [-username <username> [-password <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 <id(s)> -pubfile <id(s)> [-username <username> [-password <password>]]");
Console.WriteLine("Usage: downloading a workshop item using ugc id:");
Console.WriteLine(" depotdownloader -app <id(s)> -ugc <id(s)> [-username <username> [-password <password>]]");
Console.WriteLine();
Console.WriteLine("Parameters:");
Console.WriteLine(" -app <#> - the AppID to download.");
Console.WriteLine(" -depot <#> - the DepotID to download.");
Console.WriteLine(" -manifest <id> - manifest id of content to download (requires -depot, default: current for branch).");
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($" -branch <branchname> - download from specified branch if available (default: {ContentDownloader.DEFAULT_BRANCH}).");
Console.WriteLine(" -branchpassword <pass> - 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 <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 <arch> - 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 <lang> - the language for which to download the game (default: english)");
Console.WriteLine(" -lowviolence - download low violence depots when -app is used.");
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 <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 <arch> - 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 <lang> - 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 <user> - the username of the account to login to for restricted content.");
Console.WriteLine(" -password <pass> - 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 <username> -remember-password as login credentials.");
Console.WriteLine(" -username <user> - the username of the account to login to for restricted content.");
Console.WriteLine(" -password <pass> - 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 <username> -remember-password as login credentials.");
Console.WriteLine();
Console.WriteLine(" -dir <installdir> - the directory in which to place downloaded files.");
Console.WriteLine(" -filelist <file.txt> - 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 <installdir> - the directory in which to place downloaded files.");
Console.WriteLine(" -filelist <file.txt> - 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.");

Loading…
Cancel
Save