From f957ef5da9fed84269b3016bfb9a946e45e9879e Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Sat, 23 Mar 2019 17:17:10 -0400 Subject: [PATCH] Add -pubfile support, dynamically getting appId + ugcId. --- DepotDownloader/ContentDownloader.cs | 14 ++- DepotDownloader/Program.cs | 150 ++++++++++++++++----------- DepotDownloader/Steam3Session.cs | 34 ++++++ 3 files changed, 136 insertions(+), 62 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index f8aaca7..15381b9 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -16,6 +16,7 @@ namespace DepotDownloader public const uint INVALID_APP_ID = uint.MaxValue; public const uint INVALID_DEPOT_ID = uint.MaxValue; public const ulong INVALID_MANIFEST_ID = ulong.MaxValue; + public const string DEFAULT_BRANCH = "Public"; public static DownloadConfig Config = new DownloadConfig(); @@ -373,6 +374,12 @@ namespace DepotDownloader steam3.Disconnect(); } + public static async Task DownloadPubfileAsync( ulong publishedFileId ) + { + var details = steam3.GetPubfileDetails(publishedFileId); + await DownloadAppAsync( details.consumer_appid, details.consumer_appid, details.hcontent_file, DEFAULT_BRANCH, null, true ); + } + public static async Task DownloadAppAsync( uint appId, uint depotId, ulong manifestId, string branch, string os, bool isUgc ) { if ( steam3 != null ) @@ -392,12 +399,9 @@ namespace DepotDownloader } } - Console.WriteLine( "Using app branch: '{0}'.", branch ); - var depotIDs = new List(); KeyValue depots = GetSteam3AppSection( appId, EAppInfoSection.Depots ); - if ( isUgc ) { var workshopDepot = depots["workshopdepot"].AsUnsignedInteger(); @@ -408,6 +412,8 @@ namespace DepotDownloader } else { + Console.WriteLine( "Using app branch: '{0}'.", branch ); + if ( depots != null ) { foreach ( var depotSection in depots.Children ) @@ -918,7 +924,7 @@ namespace DepotDownloader ConfigStore.TheConfig.LastManifests[ depot.id ] = depot.manifestId; ConfigStore.Save(); - Console.WriteLine( "Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, DepotBytesCompressed, DepotBytesUncompressed ); + Console.WriteLine( "Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, DepotBytesCompressed, DepotBytesUncompressed ); } Console.WriteLine( "Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", TotalBytesCompressed, TotalBytesUncompressed, depots.Count ); diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index 1c9e3f5..71496a3 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -25,34 +25,13 @@ namespace DepotDownloader ConfigStore.LoadFromFile( Path.Combine( Directory.GetCurrentDirectory(), "DepotDownloader.config" ) ); - uint appId = GetParameter( args, "-app", ContentDownloader.INVALID_APP_ID ); - if (appId == ContentDownloader.INVALID_APP_ID) - { - Console.WriteLine("Error: -app not specified!"); - return; - } + #region Common Options - uint depotId; - bool isUGC = false; + string username = GetParameter( args, "-username" ) ?? GetParameter( args, "-user" ); + string password = GetParameter( args, "-password" ) ?? GetParameter( args, "-pass" ); + ContentDownloader.Config.RememberPassword = HasParameter( args, "-remember-password" ); - ulong manifestId = GetParameter(args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID); - if (manifestId != ContentDownloader.INVALID_MANIFEST_ID) - { - depotId = appId; - isUGC = true; - } - else - { - depotId = GetParameter(args, "-depot", ContentDownloader.INVALID_DEPOT_ID); - manifestId = GetParameter(args, "-manifest", ContentDownloader.INVALID_MANIFEST_ID); - if (depotId == ContentDownloader.INVALID_DEPOT_ID && manifestId != ContentDownloader.INVALID_MANIFEST_ID) - { - Console.WriteLine("Error: -manifest requires -depot to be specified"); - return; - } - } - - ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); + ContentDownloader.Config.DownloadManifestOnly = HasParameter( args, "-manifest-only" ); int cellId = GetParameter( args, "-cellid", -1 ); if ( cellId == -1 ) @@ -61,7 +40,6 @@ namespace DepotDownloader } ContentDownloader.Config.CellID = cellId; - ContentDownloader.Config.BetaPassword = GetParameter( args, "-betapassword" ); string fileList = GetParameter( args, "-filelist" ); string[] files = null; @@ -70,7 +48,7 @@ namespace DepotDownloader { try { - string fileListData = File.ReadAllText( fileList ); + string fileListData = File.ReadAllText(fileList); files = fileListData.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ); ContentDownloader.Config.UsingFileList = true; @@ -93,36 +71,94 @@ namespace DepotDownloader Console.WriteLine( "Using filelist: '{0}'.", fileList ); } - catch ( Exception ex ) + catch (Exception ex) { Console.WriteLine( "Warning: Unable to load filelist: {0}", ex.ToString() ); } } - string username = GetParameter( args, "-username" ) ?? GetParameter( args, "-user" ); - string password = GetParameter( args, "-password" ) ?? GetParameter( args, "-pass" ); - ContentDownloader.Config.RememberPassword = HasParameter( args, "-remember-password" ); ContentDownloader.Config.InstallDirectory = GetParameter( args, "-dir" ); - ContentDownloader.Config.DownloadAllPlatforms = HasParameter( args, "-all-platforms" ); + 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", 4 ); - string branch = GetParameter( args, "-branch" ) ?? GetParameter( args, "-beta" ) ?? "Public"; - string os = GetParameter( args, "-os", null ); - - if ( ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty( os ) ) - { - Console.WriteLine( "Error: Cannot specify -os when -all-platforms is specified." ); - return; - } - ContentDownloader.Config.MaxServers = Math.Max( ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads ); + #endregion + + ulong pubFile = GetParameter( args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID ); + if ( pubFile != ContentDownloader.INVALID_MANIFEST_ID ) + { + #region Pubfile Downloading + + if ( InitializeSteam( username, password ) ) + { + await ContentDownloader.DownloadPubfileAsync( pubFile ).ConfigureAwait( false ); + ContentDownloader.ShutdownSteam3(); + } + + #endregion + } + else + { + #region App downloading + + string branch = GetParameter( args, "-branch" ) ?? GetParameter( args, "-beta" ) ?? ContentDownloader.DEFAULT_BRANCH; + ContentDownloader.Config.BetaPassword = GetParameter( args, "-betapassword" ); + + ContentDownloader.Config.DownloadAllPlatforms = HasParameter( args, "-all-platforms" ); + string os = GetParameter( args, "-os", null ); + + if ( ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty( os ) ) + { + Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); + return; + } + + uint appId = GetParameter( args, "-app", ContentDownloader.INVALID_APP_ID ); + if ( appId == ContentDownloader.INVALID_APP_ID ) + { + Console.WriteLine( "Error: -app not specified!" ); + return; + } + + uint depotId; + bool isUGC = false; + + ulong manifestId = GetParameter( args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID ); + if ( manifestId != ContentDownloader.INVALID_MANIFEST_ID ) + { + depotId = appId; + isUGC = true; + } + else + { + depotId = GetParameter( args, "-depot", ContentDownloader.INVALID_DEPOT_ID ); + manifestId = GetParameter( args, "-manifest", ContentDownloader.INVALID_MANIFEST_ID ); + if ( depotId == ContentDownloader.INVALID_DEPOT_ID && manifestId != ContentDownloader.INVALID_MANIFEST_ID ) + { + Console.WriteLine( "Error: -manifest requires -depot to be specified" ); + return; + } + } + + if ( InitializeSteam( username, password ) ) + { + await ContentDownloader.DownloadAppAsync( appId, depotId, manifestId, branch, os, isUGC ).ConfigureAwait( false ); + ContentDownloader.ShutdownSteam3(); + } + + #endregion + } + } + + static bool InitializeSteam( string username, string password ) + { if ( username != null && password == null && ( !ContentDownloader.Config.RememberPassword || !ConfigStore.TheConfig.LoginKeys.ContainsKey( username ) ) ) { do { - Console.Write("Enter account password for \"{0}\": ", username); + Console.Write( "Enter account password for \"{0}\": ", username ); password = Util.ReadPassword(); Console.WriteLine(); } while ( String.Empty == password ); @@ -135,11 +171,7 @@ namespace DepotDownloader // capture the supplied password in case we need to re-use it after checking the login key ContentDownloader.Config.SuppliedPassword = password; - if ( ContentDownloader.InitializeSteam3( username, password ) ) - { - await ContentDownloader.DownloadAppAsync( appId, depotId, manifestId, branch, os, isUGC ).ConfigureAwait( false ); - ContentDownloader.ShutdownSteam3(); - } + return ContentDownloader.InitializeSteam3( username, password ); } static int IndexOfParam( string[] args, string param ) @@ -177,28 +209,30 @@ namespace DepotDownloader static void PrintUsage() { Console.WriteLine(); - Console.WriteLine( "Usage: depotdownloader -app [-depot [-manifest ] | -ugc ]" ); + Console.WriteLine( "Usage - downloading one or all depots for an app:" ); + Console.WriteLine( "\tdepotdownloader -app [-depot [-manifest ] | [-ugc ]]" ); Console.WriteLine( "\t\t[-username [-password ]] [other options]" ); Console.WriteLine(); + Console.WriteLine( "Usage - downloading a Workshop item published via SteamUGC" ); + Console.WriteLine( "\tdepotdownloader -pubfile [-username [-password ]]" ); + Console.WriteLine(); Console.WriteLine( "Parameters:" ); Console.WriteLine( "\t-app <#>\t\t\t\t- the AppID to download." ); - Console.WriteLine(); - Console.WriteLine( "Optional Parameters:" ); 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(); Console.WriteLine( "\t-ugc <#>\t\t\t\t- the UGC ID to download." ); + 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(); - Console.WriteLine( "\t-username \t\t- the username of the account to login to for restricted content. "); + 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." ); Console.WriteLine(); - 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(); Console.WriteLine( "\t-dir \t\t- the directory in which to place downloaded files." ); - 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-filelist \t- a list of files to download (from the manifest). Can optionally use regex to download only certain files." ); Console.WriteLine( "\t-validate\t\t\t\t- Include checksum verification of files already downloaded" ); Console.WriteLine(); diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index 9c1cca1..9bb17ab 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -1,4 +1,5 @@ using SteamKit2; +using SteamKit2.Unified.Internal; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -40,6 +41,7 @@ namespace DepotDownloader public SteamClient steamClient; public SteamUser steamUser; SteamApps steamApps; + SteamUnifiedMessages.UnifiedService steamPublishedFile; CallbackManager callbacks; @@ -85,6 +87,8 @@ namespace DepotDownloader this.steamUser = this.steamClient.GetHandler(); this.steamApps = this.steamClient.GetHandler(); + var steamUnifiedMessages = this.steamClient.GetHandler(); + this.steamPublishedFile = steamUnifiedMessages.CreateService(); this.callbacks = new CallbackManager( this.steamClient ); @@ -202,6 +206,36 @@ namespace DepotDownloader }, () => { return completed; } ); } + public PublishedFileDetails GetPubfileDetails( PublishedFileID pubFile ) + { + var pubFileRequest = new CPublishedFile_GetDetails_Request(); + pubFileRequest.publishedfileids.Add( pubFile ); + + bool completed = false; + PublishedFileDetails details = null; + + Action cbMethod = callback => + { + completed = true; + if ( callback.Result == EResult.OK ) + { + var response = callback.GetDeserializedResponse(); + details = response.publishedfiledetails[0]; + } + else + { + throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving UGC id for pubfile {pubFile}."); + } + }; + + WaitUntilCallback(() => + { + callbacks.Subscribe( steamPublishedFile.SendMessage( api => api.GetDetails( pubFileRequest ) ), cbMethod ); + }, () => { return completed; }); + + return details; + } + public void RequestPackageInfo( IEnumerable packageIds ) { List packages = packageIds.ToList();