From 42b8584edf722c0c7fd4e1f0e7cca46a6a5d9c75 Mon Sep 17 00:00:00 2001 From: Scott Ehlert Date: Sun, 6 Nov 2022 20:22:07 -0500 Subject: [PATCH 01/28] Simplify/refactor PlatformUtilities.SetExecutable and add support for using stat on ARM versions of Linux. --- DepotDownloader/PlatformUtilities.cs | 90 ++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/DepotDownloader/PlatformUtilities.cs b/DepotDownloader/PlatformUtilities.cs index 9e3d2b6c..5ecdc8d3 100644 --- a/DepotDownloader/PlatformUtilities.cs +++ b/DepotDownloader/PlatformUtilities.cs @@ -11,11 +11,23 @@ namespace DepotDownloader private const int ModeExecute = ModeExecuteOwner | ModeExecuteGroup | ModeExecuteOther; [StructLayout(LayoutKind.Explicit, Size = 144)] - private readonly struct StatLinux + private readonly struct StatLinuxX64 { [FieldOffset(24)] public readonly uint st_mode; } + [StructLayout(LayoutKind.Explicit, Size = 104)] + private readonly struct StatLinuxArm32 + { + [FieldOffset(16)] public readonly uint st_mode; + } + + [StructLayout(LayoutKind.Explicit, Size = 128)] + private readonly struct StatLinuxArm64 + { + [FieldOffset(16)] public readonly uint st_mode; + } + [StructLayout(LayoutKind.Explicit, Size = 144)] private readonly struct StatOSX { @@ -23,7 +35,13 @@ namespace DepotDownloader } [DllImport("libc", EntryPoint = "__xstat", SetLastError = true)] - private static extern int statLinux(int version, string path, out StatLinux statLinux); + private static extern int statLinuxX64(int version, string path, out StatLinuxX64 statLinux); + + [DllImport("libc", EntryPoint = "__xstat", SetLastError = true)] + private static extern int statLinuxArm32(int version, string path, out StatLinuxArm32 statLinux); + + [DllImport("libc", EntryPoint = "__xstat", SetLastError = true)] + private static extern int statLinuxArm64(int version, string path, out StatLinuxArm64 statLinux); [DllImport("libc", EntryPoint = "stat", SetLastError = true)] private static extern int statOSX(string path, out StatOSX stat); @@ -34,9 +52,6 @@ namespace DepotDownloader [DllImport("libc", SetLastError = true)] private static extern int chmod(string path, uint mode); - [DllImport("libc", SetLastError = true)] - private static extern int chmod(string path, ushort mode); - [DllImport("libc", CallingConvention = CallingConvention.Cdecl, SetLastError = true)] private static extern IntPtr strerror(int errno); @@ -49,36 +64,63 @@ namespace DepotDownloader } } - public static void SetExecutable(string path, bool value) + private static uint GetFileMode(string path) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - ThrowIf(statLinux(1, path, out var stat)); - - var hasExecuteMask = (stat.st_mode & ModeExecute) == ModeExecute; - if (hasExecuteMask != value) + switch (RuntimeInformation.ProcessArchitecture) { - ThrowIf(chmod(path, (uint)(value - ? stat.st_mode | ModeExecute - : stat.st_mode & ~ModeExecute))); + case Architecture.X64: + { + ThrowIf(statLinuxX64(1, path, out var stat)); + return stat.st_mode; + } + case Architecture.Arm: + { + ThrowIf(statLinuxArm32(3, path, out var stat)); + return stat.st_mode; + } + case Architecture.Arm64: + { + ThrowIf(statLinuxArm64(0, path, out var stat)); + return stat.st_mode; + } } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - StatOSX stat; - - ThrowIf(RuntimeInformation.ProcessArchitecture == Architecture.Arm64 - ? statOSX(path, out stat) - : statOSXCompat(path, out stat)); - - var hasExecuteMask = (stat.st_mode & ModeExecute) == ModeExecute; - if (hasExecuteMask != value) + switch (RuntimeInformation.ProcessArchitecture) { - ThrowIf(chmod(path, (ushort)(value - ? stat.st_mode | ModeExecute - : stat.st_mode & ~ModeExecute))); + case Architecture.X64: + { + ThrowIf(statOSXCompat(path, out var stat)); + return stat.st_mode; + } + case Architecture.Arm64: + { + ThrowIf(statOSX(path, out var stat)); + return stat.st_mode; + } } } + throw new PlatformNotSupportedException(); + } + + public static void SetExecutable(string path, bool value) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + var mode = GetFileMode(path); + var hasExecuteMask = (mode & ModeExecute) == ModeExecute; + if (hasExecuteMask != value) + { + ThrowIf(chmod(path, (uint)(value + ? mode | ModeExecute + : mode & ~ModeExecute))); + } } } } From ced92a112859187f4e1bd65073cec7bf17c7dde3 Mon Sep 17 00:00:00 2001 From: Margen67 Date: Tue, 17 May 2022 19:55:46 -0700 Subject: [PATCH 02/28] CI updates Rename matrix.os to runs-on. Opt out of dotnet telemetry. Upgrade checkout and upload-artifact to v3. Upgrade setup-dotnet to v2. --- .github/workflows/build.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5dbc0a5d..71f12a55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,26 +15,28 @@ on: jobs: build: - name: .NET on ${{ matrix.os }} (${{ matrix.configuration }}) - runs-on: ${{ matrix.os }} + name: .NET on ${{ matrix.runs-on }} (${{ matrix.configuration }}) + runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + runs-on: [macos-latest, ubuntu-latest, windows-latest] configuration: [Release, Debug] + env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup .NET Core - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: - dotnet-version: '6.0.x' + dotnet-version: 6.0.x - name: Build run: dotnet publish -c ${{ matrix.configuration }} -o artifacts - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: matrix.configuration == 'Release' with: name: DepotDownloader-${{ runner.os }} From 10cc4c913109fceb941d624042abeb575e68825d Mon Sep 17 00:00:00 2001 From: Yaakov Date: Tue, 21 Feb 2023 20:51:50 +1100 Subject: [PATCH 03/28] Add global.json, lock to .NET 6.0 SDK --- global.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 00000000..1b8195c4 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "6.0.100", + "rollForward": "latestMinor" + } +} From bf4df71d64ff3260fc85f1299bda555b876a1454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 19:57:36 +0000 Subject: [PATCH 04/28] Bump protobuf-net from 3.1.17 to 3.2.0 Bumps [protobuf-net](https://github.com/protobuf-net/protobuf-net) from 3.1.17 to 3.2.0. - [Release notes](https://github.com/protobuf-net/protobuf-net/releases) - [Changelog](https://github.com/protobuf-net/protobuf-net/blob/main/docs/releasenotes.md) - [Commits](https://github.com/protobuf-net/protobuf-net/commits) --- updated-dependencies: - dependency-name: protobuf-net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- DepotDownloader/DepotDownloader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index c9fbf234..7e8a9d2c 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -11,7 +11,7 @@ - + From 3b060a663218b99f52ff75af859bace8a82ece84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 19:57:18 +0000 Subject: [PATCH 05/28] Bump protobuf-net from 3.2.0 to 3.2.16 Bumps [protobuf-net](https://github.com/protobuf-net/protobuf-net) from 3.2.0 to 3.2.16. - [Release notes](https://github.com/protobuf-net/protobuf-net/releases) - [Changelog](https://github.com/protobuf-net/protobuf-net/blob/main/docs/releasenotes.md) - [Commits](https://github.com/protobuf-net/protobuf-net/commits/3.2.16) --- updated-dependencies: - dependency-name: protobuf-net dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- DepotDownloader/DepotDownloader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index 7e8a9d2c..3fadc13d 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -11,7 +11,7 @@ - + From 5e414bc48b32b72f09760c2f2df8a6cf8efe3484 Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Sun, 19 Mar 2023 23:10:40 -0400 Subject: [PATCH 06/28] Remove printing of depot name. It's no longer sent. --- DepotDownloader/ContentDownloader.cs | 43 +++++++--------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 7cee83f7..98e5b44a 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -41,21 +41,18 @@ namespace DepotDownloader public string branch { get; private set; } public string installDir { get; private set; } - public string contentName { get; private set; } public byte[] depotKey { get; private set; } public DepotDownloadInfo( uint depotid, uint appId, ulong manifestId, string branch, - string installDir, string contentName, - byte[] depotKey) + string installDir, byte[] depotKey) { this.id = depotid; this.appId = appId; this.manifestId = manifestId; this.branch = branch; this.installDir = installDir; - this.contentName = contentName; this.depotKey = depotKey; } } @@ -314,29 +311,13 @@ namespace DepotDownloader return UInt64.Parse(node.Value); } - static string GetAppOrDepotName(uint depotId, uint appId) + static string GetAppName(uint appId) { - if (depotId == INVALID_DEPOT_ID) - { - var info = GetSteam3AppSection(appId, EAppInfoSection.Common); - - if (info == null) - return String.Empty; - - return info["name"].AsString(); - } - - var depots = GetSteam3AppSection(appId, EAppInfoSection.Depots); - - if (depots == null) - return String.Empty; - - var depotChild = depots[depotId.ToString()]; - - if (depotChild == null) + var info = GetSteam3AppSection(appId, EAppInfoSection.Common); + if (info == null) return String.Empty; - return depotChild["name"].AsString(); + return info["name"].AsString(); } public static bool InitializeSteam3(string username, string password) @@ -486,7 +467,7 @@ namespace DepotDownloader } else { - var contentName = GetAppOrDepotName(INVALID_DEPOT_ID, appId); + var contentName = GetAppName(appId); throw new ContentDownloaderException(String.Format("App {0} ({1}) is not available from this account.", appId, contentName)); } } @@ -609,11 +590,9 @@ namespace DepotDownloader if (steam3 != null && appId != INVALID_APP_ID) steam3.RequestAppInfo(appId); - var contentName = GetAppOrDepotName(depotId, appId); - if (!AccountHasAccess(depotId)) { - Console.WriteLine("Depot {0} ({1}) is not available from this account.", depotId, contentName); + Console.WriteLine("Depot {0}) is not available from this account.", depotId); return null; } @@ -630,7 +609,7 @@ namespace DepotDownloader if (manifestId == INVALID_MANIFEST_ID) { - Console.WriteLine("Depot {0} ({1}) missing public subsection or manifest section.", depotId, contentName); + Console.WriteLine("Depot {0} missing public subsection or manifest section.", depotId); return null; } } @@ -653,7 +632,7 @@ namespace DepotDownloader var depotKey = steam3.DepotKeys[depotId]; - return new DepotDownloadInfo(depotId, appId, manifestId, branch, installDir, contentName, depotKey); + return new DepotDownloadInfo(depotId, appId, manifestId, branch, installDir, depotKey); } private class ChunkMatch @@ -752,7 +731,7 @@ namespace DepotDownloader { var depotCounter = new DepotDownloadCounter(); - Console.WriteLine("Processing depot {0} - {1}", depot.id, depot.contentName); + Console.WriteLine("Processing depot {0} - {1}", depot.id); ProtoManifest oldProtoManifest = null; ProtoManifest newProtoManifest = null; @@ -991,7 +970,7 @@ namespace DepotDownloader var depot = depotFilesData.depotDownloadInfo; var depotCounter = depotFilesData.depotCounter; - Console.WriteLine("Downloading depot {0} - {1}", depot.id, depot.contentName); + Console.WriteLine("Downloading depot {0}", depot.id); var files = depotFilesData.filteredFiles.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)).ToArray(); var networkChunkQueue = new ConcurrentQueue<(FileStreamData fileStreamData, ProtoManifest.FileData fileData, ProtoManifest.ChunkData chunk)>(); From 790b938d76ededccecfa6bc6bf5e9b9910343c2b Mon Sep 17 00:00:00 2001 From: Yaakov Date: Wed, 22 Mar 2023 20:01:58 +1100 Subject: [PATCH 07/28] Add scheduled workflow for nightly builds against latest SK2 --- .github/workflows/sk2-ci.yml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/sk2-ci.yml diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml new file mode 100644 index 00000000..3bf9da92 --- /dev/null +++ b/.github/workflows/sk2-ci.yml @@ -0,0 +1,41 @@ +name: SteamKit2 Continuous Integration + +on: + schedule: + - cron: '0 1 * * *' + workflow_dispatch: + +jobs: + build: + name: .NET on ${{ matrix.runs-on }} (${{ matrix.configuration }}) + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + runs-on: [ macos-latest, ubuntu-latest, windows-latest ] + configuration: [ Release, Debug ] + env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + steps: + - uses: actions/checkout@v3 + + - name: Setup .NET Core + uses: actions/setup-dotnet@v2 + with: + dotnet-version: 6.0.x + + - name: Configure NuGet + run: | + dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/SteamRE/index.json" + dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease + + - name: Build + run: dotnet publish -c ${{ matrix.configuration }} -o artifacts + + - name: Upload artifact + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' + with: + name: DepotDownloader-${{ runner.os }} + path: artifacts + if-no-files-found: error From f2fe08211264ee29b5c8922857d4e7d5a0c60fba Mon Sep 17 00:00:00 2001 From: Yaakov Date: Wed, 22 Mar 2023 20:02:46 +1100 Subject: [PATCH 08/28] fix yaml? --- .github/workflows/sk2-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml index 3bf9da92..3992a4f1 100644 --- a/.github/workflows/sk2-ci.yml +++ b/.github/workflows/sk2-ci.yml @@ -26,8 +26,8 @@ jobs: - name: Configure NuGet run: | - dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/SteamRE/index.json" - dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease + dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/SteamRE/index.json" + dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease - name: Build run: dotnet publish -c ${{ matrix.configuration }} -o artifacts From 7b0f3135ff1a294305433bd5e975839e96348ba2 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Wed, 22 Mar 2023 20:07:54 +1100 Subject: [PATCH 09/28] be a little more specific when updating the pkg --- .github/workflows/sk2-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml index 3992a4f1..3281dd54 100644 --- a/.github/workflows/sk2-ci.yml +++ b/.github/workflows/sk2-ci.yml @@ -26,8 +26,8 @@ jobs: - name: Configure NuGet run: | - dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/SteamRE/index.json" - dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease + dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github https://nuget.pkg.github.com/SteamRE/index.json + dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease --source https://nuget.pkg.github.com/SteamRE/index.json - name: Build run: dotnet publish -c ${{ matrix.configuration }} -o artifacts From b31d79f556662333c515749eee0b4219354da242 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Wed, 22 Mar 2023 20:13:06 +1100 Subject: [PATCH 10/28] Revert "be a little more specific when updating the pkg" This reverts commit 7b0f3135ff1a294305433bd5e975839e96348ba2. --- .github/workflows/sk2-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml index 3281dd54..3992a4f1 100644 --- a/.github/workflows/sk2-ci.yml +++ b/.github/workflows/sk2-ci.yml @@ -26,8 +26,8 @@ jobs: - name: Configure NuGet run: | - dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github https://nuget.pkg.github.com/SteamRE/index.json - dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease --source https://nuget.pkg.github.com/SteamRE/index.json + dotnet nuget add source --username USERNAME --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/SteamRE/index.json" + dotnet add DepotDownloader/DepotDownloader.csproj package SteamKit2 --prerelease - name: Build run: dotnet publish -c ${{ matrix.configuration }} -o artifacts From 0112ca749d88f9d02f2740495386aa609f6e2729 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Thu, 23 Mar 2023 23:20:53 +1100 Subject: [PATCH 11/28] Rudimentary support for new access tokens --- DepotDownloader/AccountSettingsStore.cs | 8 ++- DepotDownloader/ContentDownloader.cs | 9 ++- DepotDownloader/DepotDownloader.csproj | 2 +- DepotDownloader/DownloadConfig.cs | 1 - DepotDownloader/Program.cs | 5 +- DepotDownloader/Steam3Session.cs | 94 +++++++++++-------------- 6 files changed, 53 insertions(+), 66 deletions(-) diff --git a/DepotDownloader/AccountSettingsStore.cs b/DepotDownloader/AccountSettingsStore.cs index c6c9a251..8bed9622 100644 --- a/DepotDownloader/AccountSettingsStore.cs +++ b/DepotDownloader/AccountSettingsStore.cs @@ -17,8 +17,10 @@ namespace DepotDownloader [ProtoMember(2, IsRequired = false)] public ConcurrentDictionary ContentServerPenalty { get; private set; } - [ProtoMember(3, IsRequired = false)] - public Dictionary LoginKeys { get; private set; } + // Member 3 was a Dictionary for LoginKeys. + + [ProtoMember(4, IsRequired = false)] + public Dictionary LoginTokens { get; private set; } string FileName; @@ -26,7 +28,7 @@ namespace DepotDownloader { SentryData = new Dictionary(); ContentServerPenalty = new ConcurrentDictionary(); - LoginKeys = new Dictionary(); + LoginTokens = new Dictionary(); } static bool Loaded diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 7cee83f7..1a57dc6e 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -341,20 +341,20 @@ namespace DepotDownloader public static bool InitializeSteam3(string username, string password) { - string loginKey = null; + string loginToken = null; if (username != null && Config.RememberPassword) { - _ = AccountSettingsStore.Instance.LoginKeys.TryGetValue(username, out loginKey); + _ = AccountSettingsStore.Instance.LoginTokens.TryGetValue(username, out loginToken); } steam3 = new Steam3Session( new SteamUser.LogOnDetails { Username = username, - Password = loginKey == null ? password : null, + Password = loginToken == null ? password : null, ShouldRememberPassword = Config.RememberPassword, - LoginKey = loginKey, + AccessToken = loginToken, LoginID = Config.LoginID ?? 0x534B32, // "SK2" } ); @@ -381,7 +381,6 @@ namespace DepotDownloader if (steam3 == null) return; - steam3.TryWaitForLoginKey(); steam3.Disconnect(); } diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index 3fadc13d..c64d0054 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -12,6 +12,6 @@ - + diff --git a/DepotDownloader/DownloadConfig.cs b/DepotDownloader/DownloadConfig.cs index 448e589f..bcb46cb1 100644 --- a/DepotDownloader/DownloadConfig.cs +++ b/DepotDownloader/DownloadConfig.cs @@ -22,7 +22,6 @@ namespace DepotDownloader public int MaxServers { get; set; } public int MaxDownloads { get; set; } - public string SuppliedPassword { get; set; } public bool RememberPassword { get; set; } // A Steam LoginID to allow multiple concurrent connections diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index e4ceb006..9c07fbbd 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -268,7 +268,7 @@ namespace DepotDownloader static bool InitializeSteam(string username, string password) { - if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey(username))) + if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username))) { do { @@ -291,9 +291,6 @@ namespace DepotDownloader Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); } - // capture the supplied password in case we need to re-use it after checking the login key - ContentDownloader.Config.SuppliedPassword = password; - return ContentDownloader.InitializeSteam3(username, password); } diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index 071981fb..d88feccc 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using SteamKit2; +using SteamKit2.Authentication; using SteamKit2.Internal; namespace DepotDownloader @@ -53,7 +54,6 @@ namespace DepotDownloader bool bAborted; bool bExpectingDisconnectRemote; bool bDidDisconnect; - bool bDidReceiveLoginKey; bool bIsConnectionRecovery; int connectionBackoff; int seq; // more hack fixes @@ -79,7 +79,6 @@ namespace DepotDownloader this.bAborted = false; this.bExpectingDisconnectRemote = false; this.bDidDisconnect = false; - this.bDidReceiveLoginKey = false; this.seq = 0; this.AppTokens = new Dictionary(); @@ -112,7 +111,6 @@ namespace DepotDownloader this.callbacks.Subscribe(SessionTokenCallback); this.callbacks.Subscribe(LicenseListCallback); this.callbacks.Subscribe(UpdateMachineAuthCallback); - this.callbacks.Subscribe(LoginKeyCallback); Console.Write("Connecting to Steam3..."); @@ -419,7 +417,6 @@ namespace DepotDownloader bExpectingDisconnectRemote = false; bDidDisconnect = false; bIsConnectionRecovery = false; - bDidReceiveLoginKey = false; } void Connect() @@ -466,23 +463,6 @@ namespace DepotDownloader steamClient.Disconnect(); } - public void TryWaitForLoginKey() - { - if (logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword) return; - - var totalWaitPeriod = DateTime.Now.AddSeconds(3); - - while (true) - { - var now = DateTime.Now; - if (now >= totalWaitPeriod) break; - - if (bDidReceiveLoginKey) break; - - callbacks.RunWaitAllCallbacks(TimeSpan.FromMilliseconds(100)); - } - } - private void WaitForCallbacks() { callbacks.RunWaitCallbacks(TimeSpan.FromSeconds(1)); @@ -496,7 +476,7 @@ namespace DepotDownloader } } - private void ConnectedCallback(SteamClient.ConnectedCallback connected) + private async void ConnectedCallback(SteamClient.ConnectedCallback connected) { Console.WriteLine(" Done!"); bConnecting = false; @@ -508,7 +488,37 @@ namespace DepotDownloader } else { - Console.Write("Logging '{0}' into Steam3...", logonDetails.Username); + Console.WriteLine("Logging '{0}' into Steam3...", logonDetails.Username); + + if (logonDetails.Username != null && logonDetails.Password != null && logonDetails.AccessToken is null) + { + try + { + var session = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new SteamKit2.Authentication.AuthSessionDetails + { + Username = logonDetails.Username, + Password = logonDetails.Password, + IsPersistentSession = ContentDownloader.Config.RememberPassword, + Authenticator = new UserConsoleAuthenticator(), + }); + + var result = await session.PollingWaitForResultAsync(); + + DebugLog.WriteLine(nameof(Steam3Session), "Completed authentication initialization, got access token."); + + // Assume that we get back the same username, no need to reset it. + logonDetails.Password = null; + logonDetails.AccessToken = result.RefreshToken; + + AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken; + AccountSettingsStore.Save(); + } + catch (Exception ex) + { + Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); + Abort(false); + } + } steamUser.LogOn(logonDetails); } } @@ -553,14 +563,14 @@ namespace DepotDownloader { var isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied; var is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor; - var isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword; + var isAccessToken = ContentDownloader.Config.RememberPassword && logonDetails.AccessToken != null && loggedOn.Result == EResult.InvalidPassword; // TODO: Get EResult for bad access token - if (isSteamGuard || is2FA || isLoginKey) + if (isSteamGuard || is2FA || isAccessToken) { bExpectingDisconnectRemote = true; Abort(false); - if (!isLoginKey) + if (!isAccessToken) { Console.WriteLine("This account is protected by Steam Guard."); } @@ -573,23 +583,15 @@ namespace DepotDownloader logonDetails.TwoFactorCode = Console.ReadLine(); } while (String.Empty == logonDetails.TwoFactorCode); } - else if (isLoginKey) + else if (isAccessToken) { - AccountSettingsStore.Instance.LoginKeys.Remove(logonDetails.Username); + AccountSettingsStore.Instance.LoginTokens.Remove(logonDetails.Username); AccountSettingsStore.Save(); - logonDetails.LoginKey = null; - - if (ContentDownloader.Config.SuppliedPassword != null) - { - Console.WriteLine("Login key was expired. Connecting with supplied password."); - logonDetails.Password = ContentDownloader.Config.SuppliedPassword; - } - else - { - Console.Write("Login key was expired. Please enter your password: "); - logonDetails.Password = Util.ReadPassword(); - } + // TODO: Handle gracefully by falling back to password prompt? + Console.WriteLine("Access token was rejected."); + Abort(false); + return; } else { @@ -699,17 +701,5 @@ namespace DepotDownloader // send off our response steamUser.SendMachineAuthResponse(authResponse); } - - private void LoginKeyCallback(SteamUser.LoginKeyCallback loginKey) - { - Console.WriteLine("Accepted new login key for account {0}", logonDetails.Username); - - AccountSettingsStore.Instance.LoginKeys[logonDetails.Username] = loginKey.LoginKey; - AccountSettingsStore.Save(); - - steamUser.AcceptNewLoginKey(loginKey); - - bDidReceiveLoginKey = true; - } } } From 6ffa0948a494798a138d3689dff0e3e9500525c5 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Fri, 24 Mar 2023 15:00:27 +1100 Subject: [PATCH 12/28] Implement -qr CLI arg to log in with a QR code --- DepotDownloader/DepotDownloader.csproj | 1 + DepotDownloader/DownloadConfig.cs | 2 + DepotDownloader/Program.cs | 38 ++++++++------- DepotDownloader/Steam3Session.cs | 64 ++++++++++++++++++++++++-- 4 files changed, 83 insertions(+), 22 deletions(-) diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index c64d0054..a2e07dd3 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -12,6 +12,7 @@ + diff --git a/DepotDownloader/DownloadConfig.cs b/DepotDownloader/DownloadConfig.cs index bcb46cb1..1022aac4 100644 --- a/DepotDownloader/DownloadConfig.cs +++ b/DepotDownloader/DownloadConfig.cs @@ -26,5 +26,7 @@ namespace DepotDownloader // A Steam LoginID to allow multiple concurrent connections public uint? LoginID { get; set; } + + public bool UseQrCode { get; set; } } } diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index 9c07fbbd..baae3e8e 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -47,6 +47,7 @@ namespace DepotDownloader 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"); @@ -268,27 +269,30 @@ namespace DepotDownloader static bool InitializeSteam(string username, string password) { - if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username))) + if (!ContentDownloader.Config.UseQrCode) { - do + if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username))) { - Console.Write("Enter account password for \"{0}\": ", username); - if (Console.IsInputRedirected) + do { - password = Console.ReadLine(); - } - else - { - // Avoid console echoing of password - password = Util.ReadPassword(); - } + Console.Write("Enter account password for \"{0}\": ", username); + if (Console.IsInputRedirected) + { + password = Console.ReadLine(); + } + else + { + // Avoid console echoing of password + password = Util.ReadPassword(); + } - Console.WriteLine(); - } while (string.Empty == password); - } - else if (username == null) - { - Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); + Console.WriteLine(); + } while (string.Empty == password); + } + else if (username == null) + { + Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); + } } return ContentDownloader.InitializeSteam3(username, password); diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index d88feccc..e30ff64e 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using QRCoder; using SteamKit2; using SteamKit2.Authentication; using SteamKit2.Internal; @@ -72,7 +73,7 @@ namespace DepotDownloader { this.logonDetails = details; - this.authenticatedUser = details.Username != null; + this.authenticatedUser = details.Username != null || ContentDownloader.Config.UseQrCode; this.credentials = new Credentials(); this.bConnected = false; this.bConnecting = false; @@ -114,7 +115,7 @@ namespace DepotDownloader Console.Write("Connecting to Steam3..."); - if (authenticatedUser) + if (details.Username != null) { var fi = new FileInfo(String.Format("{0}.sentryFile", logonDetails.Username)); if (AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey(logonDetails.Username)) @@ -488,7 +489,10 @@ namespace DepotDownloader } else { - Console.WriteLine("Logging '{0}' into Steam3...", logonDetails.Username); + if (logonDetails.Username != null) + { + Console.WriteLine("Logging '{0}' into Steam3...", logonDetails.Username); + } if (logonDetails.Username != null && logonDetails.Password != null && logonDetails.AccessToken is null) { @@ -504,8 +508,6 @@ namespace DepotDownloader var result = await session.PollingWaitForResultAsync(); - DebugLog.WriteLine(nameof(Steam3Session), "Completed authentication initialization, got access token."); - // Assume that we get back the same username, no need to reset it. logonDetails.Password = null; logonDetails.AccessToken = result.RefreshToken; @@ -519,6 +521,46 @@ namespace DepotDownloader Abort(false); } } + else if (ContentDownloader.Config.UseQrCode) + { + Console.WriteLine("Logging in with QR code..."); + + try + { + var session = await steamClient.Authentication.BeginAuthSessionViaQRAsync(new AuthSessionDetails + { + IsPersistentSession = ContentDownloader.Config.RememberPassword, + Authenticator = new UserConsoleAuthenticator(), + }); + + // Steam will periodically refresh the challenge url, so we need a new QR code. + session.ChallengeURLChanged = () => + { + Console.WriteLine(); + Console.WriteLine("The QR code has changed:"); + + DisplayQrCode(session.ChallengeURL); + }; + + // Draw initial QR code immediately + DisplayQrCode(session.ChallengeURL); + + var result = await session.PollingWaitForResultAsync(); + + logonDetails.Username = result.AccountName; + logonDetails.Password = null; + logonDetails.AccessToken = result.RefreshToken; + + AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken; + AccountSettingsStore.Save(); + } + catch (Exception ex) + { + Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); + Abort(false); + } + } + steamUser.LogOn(logonDetails); } } @@ -701,5 +743,17 @@ namespace DepotDownloader // send off our response steamUser.SendMachineAuthResponse(authResponse); } + + private static void DisplayQrCode(string challengeUrl) + { + // Encode the link as a QR code + var qrGenerator = new QRCodeGenerator(); + var qrCodeData = qrGenerator.CreateQrCode(challengeUrl, QRCodeGenerator.ECCLevel.L); + var qrCode = new AsciiQRCode(qrCodeData); + var qrCodeAsAsciiArt = qrCode.GetGraphic(1, drawQuietZones: false); + + Console.WriteLine("Use the Steam Mobile App to sign in with this QR code:"); + Console.WriteLine(qrCodeAsAsciiArt); + } } } From 8d6c09e779d26bcbf5a218d977368b8073f43c8b Mon Sep 17 00:00:00 2001 From: Yaakov Date: Fri, 24 Mar 2023 15:00:36 +1100 Subject: [PATCH 13/28] dispose of disposables --- DepotDownloader/Steam3Session.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index e30ff64e..db42df8d 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -747,9 +747,9 @@ namespace DepotDownloader private static void DisplayQrCode(string challengeUrl) { // Encode the link as a QR code - var qrGenerator = new QRCodeGenerator(); + using var qrGenerator = new QRCodeGenerator(); var qrCodeData = qrGenerator.CreateQrCode(challengeUrl, QRCodeGenerator.ECCLevel.L); - var qrCode = new AsciiQRCode(qrCodeData); + using var qrCode = new AsciiQRCode(qrCodeData); var qrCodeAsAsciiArt = qrCode.GetGraphic(1, drawQuietZones: false); Console.WriteLine("Use the Steam Mobile App to sign in with this QR code:"); From c36ddcc065034ed52e308b3ae54373b928d1a120 Mon Sep 17 00:00:00 2001 From: Yaakov Date: Fri, 24 Mar 2023 15:57:46 +1100 Subject: [PATCH 14/28] handle reconnects more gracefully --- DepotDownloader/Steam3Session.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index db42df8d..976bd833 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -482,6 +482,12 @@ namespace DepotDownloader Console.WriteLine(" Done!"); bConnecting = false; bConnected = true; + + // Update our tracking so that we don't time out, even if we need to reconnect multiple times, + // e.g. if the authentication phase takes a while and therefore multiple connections. + connectTime = DateTime.Now; + connectionBackoff = 0; + if (!authenticatedUser) { Console.Write("Logging anonymously into Steam3..."); @@ -515,10 +521,15 @@ namespace DepotDownloader AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken; AccountSettingsStore.Save(); } + catch (TaskCanceledException) + { + return; + } catch (Exception ex) { Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); Abort(false); + return; } } else if (ContentDownloader.Config.UseQrCode) @@ -554,10 +565,15 @@ namespace DepotDownloader AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken; AccountSettingsStore.Save(); } + catch (TaskCanceledException) + { + return; + } catch (Exception ex) { Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); Abort(false); + return; } } @@ -569,6 +585,8 @@ namespace DepotDownloader { bDidDisconnect = true; + DebugLog.WriteLine(nameof(Steam3Session), $"Disconnected: bIsConnectionRecovery = {bIsConnectionRecovery}, UserInitiated = {disconnected.UserInitiated}, bExpectingDisconnectRemote = {bExpectingDisconnectRemote}"); + // When recovering the connection, we want to reconnect even if the remote disconnects us if (!bIsConnectionRecovery && (disconnected.UserInitiated || bExpectingDisconnectRemote)) { From d73462e077f6e7ba1251734577a3cb11d6581a0b Mon Sep 17 00:00:00 2001 From: Yaakov Date: Fri, 24 Mar 2023 16:12:14 +1100 Subject: [PATCH 15/28] improve pre-login auth handling (I think?) --- DepotDownloader/Steam3Session.cs | 108 ++++++++++++++++++------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index 976bd833..bc1aa0d7 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -59,6 +59,7 @@ namespace DepotDownloader int connectionBackoff; int seq; // more hack fixes DateTime connectTime; + AuthSession authSession; // input readonly SteamUser.LogOnDetails logonDetails; @@ -426,6 +427,7 @@ namespace DepotDownloader bConnected = false; bConnecting = true; connectionBackoff = 0; + authSession = null; ResetConnectionFlags(); @@ -500,63 +502,75 @@ namespace DepotDownloader Console.WriteLine("Logging '{0}' into Steam3...", logonDetails.Username); } - if (logonDetails.Username != null && logonDetails.Password != null && logonDetails.AccessToken is null) + if (authSession is null) { - try + if (logonDetails.Username != null && logonDetails.Password != null && logonDetails.AccessToken is null) { - var session = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new SteamKit2.Authentication.AuthSessionDetails + try { - Username = logonDetails.Username, - Password = logonDetails.Password, - IsPersistentSession = ContentDownloader.Config.RememberPassword, - Authenticator = new UserConsoleAuthenticator(), - }); - - var result = await session.PollingWaitForResultAsync(); - - // Assume that we get back the same username, no need to reset it. - logonDetails.Password = null; - logonDetails.AccessToken = result.RefreshToken; - - AccountSettingsStore.Instance.LoginTokens[result.AccountName] = result.RefreshToken; - AccountSettingsStore.Save(); - } - catch (TaskCanceledException) - { - return; + authSession = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new SteamKit2.Authentication.AuthSessionDetails + { + Username = logonDetails.Username, + Password = logonDetails.Password, + IsPersistentSession = ContentDownloader.Config.RememberPassword, + Authenticator = new UserConsoleAuthenticator(), + }); + } + catch (TaskCanceledException) + { + return; + } + catch (Exception ex) + { + Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); + Abort(false); + return; + } } - catch (Exception ex) + else if (logonDetails.AccessToken is null && ContentDownloader.Config.UseQrCode) { - Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); - Abort(false); - return; - } - } - else if (ContentDownloader.Config.UseQrCode) - { - Console.WriteLine("Logging in with QR code..."); + Console.WriteLine("Logging in with QR code..."); - try - { - var session = await steamClient.Authentication.BeginAuthSessionViaQRAsync(new AuthSessionDetails + try { - IsPersistentSession = ContentDownloader.Config.RememberPassword, - Authenticator = new UserConsoleAuthenticator(), - }); + var session = await steamClient.Authentication.BeginAuthSessionViaQRAsync(new AuthSessionDetails + { + IsPersistentSession = ContentDownloader.Config.RememberPassword, + Authenticator = new UserConsoleAuthenticator(), + }); - // Steam will periodically refresh the challenge url, so we need a new QR code. - session.ChallengeURLChanged = () => - { - Console.WriteLine(); - Console.WriteLine("The QR code has changed:"); + authSession = session; - DisplayQrCode(session.ChallengeURL); - }; + // Steam will periodically refresh the challenge url, so we need a new QR code. + session.ChallengeURLChanged = () => + { + Console.WriteLine(); + Console.WriteLine("The QR code has changed:"); - // Draw initial QR code immediately - DisplayQrCode(session.ChallengeURL); + DisplayQrCode(session.ChallengeURL); + }; - var result = await session.PollingWaitForResultAsync(); + // Draw initial QR code immediately + DisplayQrCode(session.ChallengeURL); + } + catch (TaskCanceledException) + { + return; + } + catch (Exception ex) + { + Console.Error.WriteLine("Failed to authenticate with Steam: " + ex.Message); + Abort(false); + return; + } + } + } + + if (authSession != null) + { + try + { + var result = await authSession.PollingWaitForResultAsync(); logonDetails.Username = result.AccountName; logonDetails.Password = null; @@ -575,6 +589,8 @@ namespace DepotDownloader Abort(false); return; } + + authSession = null; } steamUser.LogOn(logonDetails); From dea7a632c1d5b0000c430a51e7514a9f253479f9 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Mon, 3 Apr 2023 11:32:50 +0300 Subject: [PATCH 16/28] Add issue templates --- .github/ISSUE_TEMPLATE/bug-report.yml | 53 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 ++ .github/ISSUE_TEMPLATE/feature-request.yml | 36 +++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..d9e2ae9c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,53 @@ +name: Bug Report +description: File a bug report +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: what-should-have-happened + attributes: + label: What did you expect to happen? + placeholder: I expected that... + validations: + required: true + - type: textarea + id: what-actually-happened + attributes: + label: Instead of that, what actually happened? + placeholder: ... but instead, what happened was... + validations: + required: true + - type: dropdown + id: operating-system + attributes: + label: Which operating system are you running on? + options: + - Linux + - macOS + - Windows + - Other + validations: + required: true + - type: input + id: dotnet-version + attributes: + label: Version + description: What version of DepotDownloader are you running on? + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: textarea + id: additional-info + attributes: + label: Additional Information + description: Is there anything else that you think we should know? + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..47e6660c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Discussions + url: https://github.com/SteamRE/DepotDownloader/discussions/new + about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 00000000..72cbe5db --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,36 @@ +name: Feature Request +description: Suggest an idea for this project +labels: [enhancement] +body: + - type: markdown + attributes: + value: | + Thanks, we appreciate good ideas! + - type: textarea + id: problem-area + attributes: + label: What problem is this feature trying to solve? + placeholder: I'm really frustrated when... + validations: + required: true + - type: textarea + id: proposed-solution + attributes: + label: How would you like it to be solved? + placeholder: I think that it could be solved by... + validations: + required: true + - type: textarea + id: alternative-solutions + attributes: + label: Have you considered any alternative solutions + placeholder: I did think that that it also could be solved by ..., but... + validations: + required: true + - type: textarea + id: additional-info + attributes: + label: Additional Information + description: Is there anything else that you think we should know? + validations: + required: false From 347806b53b8e7b4f89d0c73b257e9f0107bc970e Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Thu, 6 Apr 2023 00:27:48 +0300 Subject: [PATCH 17/28] Add icon (#417) --- DepotDownloader/DepotDownloader.csproj | 1 + Icon/DepotDownloader.ico | Bin 0 -> 46542 bytes Icon/DepotDownloader.png | Bin 0 -> 11088 bytes Icon/DepotDownloader.svg | 16 ++++++++++++++++ 4 files changed, 17 insertions(+) create mode 100644 Icon/DepotDownloader.ico create mode 100644 Icon/DepotDownloader.png create mode 100644 Icon/DepotDownloader.svg diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index 3fadc13d..d60c0bfc 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -8,6 +8,7 @@ Steam Downloading Utility SteamRE Team Copyright © SteamRE Team 2021 + ..\Icon\DepotDownloader.ico diff --git a/Icon/DepotDownloader.ico b/Icon/DepotDownloader.ico new file mode 100644 index 0000000000000000000000000000000000000000..6b0c58ce4de5167df6a0167f8cfeaabd62eb7f7c GIT binary patch literal 46542 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 eb`dRyGxZqp0a+Btds7x`~rTw0ZpogWpO z^8bSWBfm}fKZugL!5d;1kQPxm@Fx6s01O5LODeyuJ2E;^H}_gK{U|P{RWtiiF?lB} zu2l2uU-{45lCcXlBL@|I+uZJ5T}y9K8Ff>~V0m>&UftGZmIljw%`o&+qq5tTwduFBmvXZCv>G0*fvG@yS2c=+hSk zO$%GU6l43q%BctU$RcH38zmiURRg=WWtjb!jFVef-#V;w1=hF#i!K@Ya|T;Hf=%qg zMz&!0|6o@Su)RxINKWUkeVB$WXXhgi5 zXMoKoH;ARfoArjnU0YpR_|b=Xx4AyoU+eVYVq~zt zJJx2j~S9(vy(1 zGM6N<)?3g~Z>lBF^XLDPnQhyZ;8WWsW*gf!@~@|kWQUNi=ixwri{4a2+w0)$FspV$ zFH`ycrnk}$iX(-hkqla70BT5qkDSAK;&#m-tQzE`Q@#v-7|%au=>6c`-=FkVjyl+vT-aoYARYPD4P3I z`H}1+Nkcn2^rAFF$&K~7#$%Ydox1K zJz?`UeTio=1#<{Ch(VkhOhT52FcUrR%pVaH$A=Vhf3NP!pdM7o-un}}7IV!B?m1XxuPXQH?avPsuHtX7U8c;n{A zzC2Y1aSU7Q^Pc1NwvBK}$VS-T{OD#T0(5^J#r#S*4=U&hRV6DIz;5Du)xM>q$)2lw zZ$}kO?OS-|vYFbSFg|e>JG=Dg0PZR8zL9W#4w3v{g=jE#eHg6lZmg+|C8jTM4y^vuRqDu0pSgc&rSxTyX2Qo(J;Yeae{aG zf2V&FB(6eFdc8cmXvZ8-MFY=1P}QOF!O+wZ4fqZp+e*jSXNu3nC2fLYXK!B7dC;z+ z)L&^5{X;A@j$Vl?lv-Cq8z+(?t;TdnqqSc2cxy1OW)AlSE_r=xQ?g}tD7`XCfQss{ zJkfnCNi^Qqj~Sr^-U^|KOV->=byC;jJA0Ll647uH_Qtcc3r!?Zxv^npWs1B23xNbm z2bsmd@ab}6X8`Y>(tD< zByqa&MZC*pCsK(0cGr&^onEuFD*rPHsdKInFfn0*r!5L)taVc^zAl1x1Yiu(T9wOWsh7>o4= zKI7j)4czY=#(%A@vLvOwkHpcoN4fcRc}owmTun?f#gV9iNSJ_l9y|RAmS>10Sv=sH zkLEG8B2_2_sPI`lY=hpL#ef{RWtU-CSqgBF4k?xV&)Au8-Q7@S44|H`_Q*e;QvJL( z8u%d4rY%aEt3@}{3Q3A%KHBrak9okV@vHS3QU~)4*$dMCxdY_2x855vsgr8{u8$Zq zvRiLRW4?iSz9DvJX@sRW_8u0oF4VoBx|qclGm7nJi^dUX-)XdQhQbrbiT7}iFri&p~F&p_E$mz$M(tDGu6=k z72FOiT5L&09LMeZh(dJ6faqH6ZuR1TgxwgFkN}Tdh4g~`1)_;)O+cZ|K?iPL7}_bz zo^%7+4XcTKBO>E+p{Mgu3QEv7lJOYFpPqRfZTrMeN>hEiQZ+5$4X$~4v?pfutB#Us z%kx418q#Es%^xm32;xzO=YH|Dgz^QuG!xOf{TCBu3UYVz%ZR*t1b2CgNwF2cvAHbl zHPYY-s!{&noEx^>oXDi_o{a6HcJSM6QUj%-HZK`Os|riL1VWg|o`Zq~RI{GJcm-QrsjZ;n1MNZLma7OoJtbz6**L zV8t{Ix*U4dDz#JR((Lln399jy6{5n*P?WfSHGw(`T6V!CFa)q99!oW z{?Jk*`DgN6))AmAJ;Y`d4(fK@Zbd;%(2o`iJq(Xp)kf?fh<=`ft5UCD*L1^I&d{ef zUFW|c6_n~ymCQbxjYR(0i`yC^Sn^}0NWEaRVYV7TD7KIA&%`_q>UI;B_0xX-?y%T zSDzH}BbPKRUd%#~g2Hu1A~4Noh+QIGihWazWa>=oWYX{`8aJZ=Ojgc{Y)Y{5lt{Nn zlX=!<=xtLds=5}hy?L&gE=Vgtt>hPTLBnb6CtV`G+5=X2&%JJW@%sm%+&?PkiR<*x zh}d3&GaOfi)`5gMEjo?mVZ7%Xm*Hu=-R^gua8EALS0$N2gvnf{Nz-y((tlcsl$NgZ z`dmbZ%%_49eh7|}=7eE)rYvn2C3=5u#%ncYXt<3V7W75sda`)O&EI01_d$LpL;P^Q_cG-D;8~$|W@C$Z zZ9-!8j}D2M@sdGipAQB(*M-`JpI2}yR!P#Bl}eDP&iietBUd|&5lcfn1RazJOCzYy zM@CtJY$F>Od6D=^c2P-S-TLVuJuyXAe07(PH)xDL+w6s^_Q||i8A8SuN{QY|+Q`qn zgG0$I{Z-rw9klYBe54HNA?j4>_-j~mDkG8BWTsL4vkaY7t_U7d{qf}(RNNSeDt$EF zQo@Az7y2rA(hdYyt)gbGc3gopV}w!f)Hx1+vn6331qCw{eM*Em;7HqHfJ&@qS+s&i zrIJa6^EWFF?8X8elOK9-cn`f~{54c?(OTVVvAQrvj_K0Lz;ar}CLoZtMOG;9Sx;hu zd6(xU#1Q#VV5@HuguzW3*;bfF1sg+U2qH=*fZ}d(9tVS`qGzNz{~%tbu4 zaXCl~D`~LcZ0H_P`^+nDsU zIeu4899u;BfU`CmLXf(fX*S?W`oXk?#5C+tHM-~1w5Ch=mRB$dp8BQX4FaHt=p`vY zwOJq8k4b?ic={`Qx$mOg_8)Q_IM=sr2^N}WhFQfEhxaKkM<3`i)oSY7mV3{X?Q-iCHdA zm5xpR!G5kT{C&h`xB|esfAOS_QQx}1l!`ps3GEUy2;7oiv=%6qDY>;XzeYmP4ca%w ziHF=Ujlh-V!<$7LU}-nAtQm>5E1S5>&a5Xv%^ES*$T$si}>UwDL!f+c(TOo zVL?T`(5^=rg$;S$w2u z7!D)87Ln5-2BMvEwe_tCd8Jd&#+Bu>4JV*T$Rc|A1p69b53K4(27E(xq&5o^G`3n} zuUpIrj`{&{WL(8vaH^NqtlK*!C}#gYspa0gr0%80=o|^uZ5pz?tSp7QN57+dF_&Iu zNC;~r?jwEAuD6-4pb6#Q;+LF!agbhjs+v+IaTdQ6!b(6zn-WAoCJt;XaLUm6tCD#p zmGfXlF=A;@M*6FNc*CLm$`RukBi!hi))etOn}*9(VOqo-zxJb3v=2r2*Q7v>K?1}* zD2*V|#S4i1sI@iJiA#Y^aZPuVOGWZ9d~<2GP8hE5qjJ~qg;RdF>~@9`S4R0Du*eZgo&aYG72yx^WvGY<(5j8h{~po`LX zn_xU-0`Vz)Av7s}Jf}lz3&9!&Kx^1>e4-`o0bcC~W;3Kh@2r3}c_qL+HmY+_zn{YV z-;4UHVhB4Bg`M_AS`W}gT8O#zB81nHgA{y@`3+g-oco`N9>AZhQ05{!yqm(MRH3aB zbIa&Z^%xB*FmR1K!_wKwbb#6b@}c6M z6LDhiQ|1q$>ObW>HS~?+CMx2RtQ&Ud0I>7Rh7sp)$>bD+Fg@%;4^mS;i^r|zTEK-a zyjV~Hn(H2IZd*Rd(rG`7%+C*rZ@BS$Zvl*!1;xhgmkT#l+>jqHTNxHZSDhn(CBo>6 zdeXEF#v_c_6wq&td7;5egc%ZmTIg2m1-@c3aaDmToxE@{TDcZ|Ddf%;16n3sPnxey zOPKp6vD3oGa$g~&vh(!CTT-9 z*!Z6S_BIV!S3s|{qSOJO1QpLb=M8^fA15cL|287GIk)s9j^Wr$xLbU4wXu~^dZ)v3 z`e%;Do%FHvT)_s4wcH`aoJ1*Kym!dMf6|&)((Y=f*0=5fuZ@9qQ|+J6y4`W~G(78K zv!wr*s&O?FUh?NBfXCNvsIg>!(}M8=U%h5!)odzkEr8>M4fAG@3tZ=O97-lZ>GKQl}{ z&>5E4Yj@~k3Qs$m67}@Fw}}fy)*o7o%!K3uTW8c`!B(x%b*)>ra+%>vSz72sX0)}S&-Yw%OmCqREmHT8SRLhLUDk|W#SLH zwm~_PsL6jZI%Xnyu$+KC8WeyQ*k$2CS@3Ky%(1mijGwa^of@Bk3em& zxDT7cQ;t~)5jCIT77$O3QEWfhB!@B(DD_9B<&>@wtC~IS>P`7{Qbl_dx2%YVjd$#B zzJrb$4BeNj>C!Ft)T@o~P8^Bplf83bx=C*#y34^z$I@oCV4iw)G6CG>I~5Qm2bfE9 zt^ADMesq6DeL(#!bBz9C%M*>1_!;*Bp6va-)-gKqj7>Tr57pXgU!8_Zbx(Z&!RcO( z1rQ38npw2X`KM2*?n-cKZSeT~$boo1P@IY}dw^~*8x_(~b#WuDx~tR}e<=02mV zJAX?Fo9_R|={stalc=l7u=2qJHXTT1MOpg3tr=zZfOq9b$-&?0Kw1ZmQn@ooB9)42 z$Be2)4eXi{Z9RtRE16S>$Jm)1xzK%tG<>Hq3m@PIjhlt0fpYmG5nBu5E{Kaf~j8}c({ps{|{uqab3be-s!_n*!dwrO4y2!$t zMy=cUX#hm95nV7keOYfC?!T7~0;W*l2BMhlaYRtRONjZHNB!|Cvpt|-J~UXw{`vB{ zlQZ~K@-_PFYCrF9#%6coX~EbXZ)=RzFkKhSSg4RHiYS81-4C_ECwLbTqc&nTO)dyP zG3Dr5?&+zkrY41uu{sT1Z{-Ub&<9%c?;Tr;))%+wg9Q0HVC_dHiDO?PGTt>}sdaOX@m?sx0g@TI z)@q2%p@_?N;zry8S#D_P7}Dkl`4Y2KUWl@w%X{OrFrsXykkB_PO^B?;@k-D8qWF7b ziCi&*u{_`}2#jPe>?JD6V}3Bt;8B2PLf%-lwx1OvvUPzvAmAn0xFz;7QyxeO05}?K zH>d-WfZUj})Vr+ScpmTH`0%Q?{j7m-1Z=e3!RNNCuYW2chmI}d6o&_6U?f_Hl!nP^ zD}KI}X9cVpU_kOY?J_zfCXBh9Wb&VAh@iO+aeX}0S@PZa1wHVK8s)FHh?}LK4Ol4) z)IU`6P}Rno@$u1(K0$?6SfR_(kJb}RJeW#D7Qm~PA#hqe*rgl zv*43Y9LA4r7lu$NE(~0nYIZ~v)L_3lTyw%MS9p|z$uuD_z2y^fW^06*&R|!c0C#MV zAtqUJ9&$wy88pKHP7Q)=FNcJZJ7E43rCiW`2Koa|f%Glf$MAgNUgs8!W2KG|`2ts} z#JPb>19;0AiwaG1Qr}j@-?w1LDjijgtPv9o3nqb2%YPgC3(z&? zvFs{3l+-=LAqP3Q3ewCg`M)zv-}n-3fBkAJ17w9J^Wg7X6GJP?-_k`SOUyZ#2%>~C zejW0m$NE3%4W7DLL0GwBCQfPLt`4 zNSmtJ-#?m7Mtpc?2gfDxUnp3+Ks$h$Quu9Xx@q%tUmD7}MBLPhvE zE~o;1dmgjTztM*NL^w5nxNz)I_gaAhr_2GM={BC-X_i-4mx6BwuZ-qXtddyj1h7fg zSx!{%YKQJr|Dil)Kfo!jAcv+|{iaL&Z#`dS%GohCMZemhsq zo8vsx-6o;p0_saGsD$M#1dJ}hC3WNQ%7Do;N*w_7NfgUCyW-suJB5_=q7Jig=0`|o zbjuA$jbwr#{-Ki?wlGd>`1-_?ECda{cgV=*{I<2}5uBdziE?+vCDOBBPrCYqGE*Dz zko%)h`ji{(xo8oumt9{X+uxZY8~i|`df{(mS%g>2J!zKUvTzE&lSu6V>MP$(Mid8} zG^1taTBh)iY4QV@=Eizj6mgn^C|a*Me|9VvlNP?Q6Gv-Kitu>6tnty$%!@Hk9_=K8 z(HeepoCjdc@PdRufECvjptOWxQ5cb04o(kb$E`-y9(+jkEzoY(#?Zt zX1|dhPwuKPT5I@3m8tKzZcjp*o-~E_;-26Hm8)!KB&<;}CvHkX)Hg4FTOA`+(xNVH z`Et_vt|?iCZPF;-BR$suvaxVus^Yk39$UfRhYQz@Q~c(vUzr331SPYES>KuDuY^PIAHMJ`n|Q0bnUv?3_=8G!t3KNT@RRpLqz(K@3}? zkU*gy02sStc4PYZu5FoDJAs{Q#{9r_;FhZ${*{?sW{*_V`3U+k#_lIPQhBP_=Q zfMwDr+ezKfu-!Z!`Bpj2<36GbcC=EZ<|_kc^-`=!vl6v_4;2Y&arGBG+Qs+~Sg+GUvlQv4P^3q*~2{K(%8zBC*RgU;O6fH zf+ecMX^w|iTcMzxZ8Z>hc*lxK1v<+LC46!bK?rdCz7AioyqAbsHAh|U!ZYep-C4wq zl9G%W;7sU>@uHJ>leOh{zg^gJR2>U{zVGfJ8?sK=X?Oh8>gC^6RajaPG-}>NSqS1c zJ{L1iE!F!!Q90bp5oz-RYX!d(uq@!{8&E zF{VW+y$uq(&*t1x;Hq)u$!&o5H){Mq*L=AE3#LP5IHX zRj6@O^1E~{(vGKf^|_`jl<^$Nc%@FH4$bSDix?Ottx(*ynKiw~ZA8Jh(G;9y;kfPh zY@{6>X65t_uPJ33Im$|5wLJp!R}Z2nSvg9sDI&xjnk+yyKOAm>kZs0ezk{Q$g7`O% z@?>3G?ogKjT%NbEwkiTsSzmlO)#Ofe_|V6w_ct}BSZC}L!@T0(SlQ6 zyf@9i0n%)lD$0*dDlt)arQ}!Lg)(gFU`+JH;tf@upI%_YQM{MB&J)PRvzf9uU~WM5 z0S&UZa0l}Ba_q9hp(bX+XgTN>{12Ej>)HD=I2{Ty@Emo}c&({{v)8?LSag5L966Ac zzVNc*_hAYZhaaxIzN>Cjr>EzKk?Ul^#BC(Ch<*$e?+5Z# zSlQv-SWyGt->PM**(uK5w$z7lb45KuR4cP;j@J|4lz+F7CIwOV+VF1{=~3g;fuu~I zf=WM1HB=R+*1+&u<>u0W5%^}cFu%Cew5mfbN%^YcpSKWn1@k$+ZV)eB-}qZ(*?p}% z{M|=lZ4tc~sA$xQV;F5nzaRa?;aYVACc@BB6cTz16tuY;e{N6~plv=dYQms&FR2d_ z-ZeUK)cbakLf=<}*NPqAUF{_UT$i5fwxKW`DD#pr!8+R@N7beLQ|A_E-EDN-`_4Vgrful&x*=>Yv*h}=10>k1TKsyP1)F$= zIh{Ls&xQ{G;>AHmD9tDYBD*GtNz_z5+PKjiRPG}cUsrjRh&U`Es$GW8R+$bvg4+2r zcVNfBjRz@(DSWE3GT2=U;Sqg81kYGtZgZdFKrM5u80h_EV3tDpUHND5H<~w5ksYzz z{#jT)8^*0;wI~Wh5;vuOgOzG=j_^L}>0d+k?S|JIMg8wk?O!LXvQk6p2$U3U;Mg&% z#fcv|nkx7roUlM;i25Zk|L?rXFt^vIdsJd9FmG%1Uv9f$&#um&;Z6OVxJMsGT-@cD1ALw>mT_YNyY!t2T;4IBE+>xsuQK=gA4n=q5u z9dhKXV~C^@9uF|{8p5B{0$0%bL+6G>S<*&ROzNuug@Z-9yYc>Pyey!MXbuwtXBg3y#KZRdOVrMcEFkQ$RXBzRbgcrG?l8Y@VIKgDc>#`;poxF z6Q^&qxUGc^CxoVpHDF}Tnbx6@C!GV^jxUg;>UlA1*gu!)Cp+S{-kJ(@WhPp-JK9QI zs9DhSNYIv Au>b%7 literal 0 HcmV?d00001 diff --git a/Icon/DepotDownloader.svg b/Icon/DepotDownloader.svg new file mode 100644 index 00000000..ab1d371a --- /dev/null +++ b/Icon/DepotDownloader.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + From 196e99b30d9c85ca7ac1726db263c3dc6a4ad52f Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Tue, 25 Apr 2023 10:33:19 -0400 Subject: [PATCH 18/28] Add support for appinfo depots section with expanded branch info. --- DepotDownloader/ContentDownloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 7cee83f7..f098d988 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -244,7 +244,7 @@ namespace DepotDownloader if (manifests.Children.Count == 0 && manifests_encrypted.Children.Count == 0) return INVALID_MANIFEST_ID; - var node = manifests[branch]; + var node = manifests[branch].Children.Count > 0 ? manifests[branch]["gid"] : manifests[branch]; if (branch != "Public" && node == KeyValue.Invalid) { From 792e77d91f9254eb67019a4c814df5e9b52bd90a Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Wed, 26 Apr 2023 12:21:20 +0300 Subject: [PATCH 19/28] Add self-contained builds (#418) --- .github/workflows/build.yml | 86 ++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 71f12a55..e161f553 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,90 @@ jobs: uses: actions/upload-artifact@v3 if: matrix.configuration == 'Release' with: - name: DepotDownloader-${{ runner.os }} + name: DepotDownloader-${{ runner.os }}-framework path: artifacts if-no-files-found: error + + - name: Publish Windows-x64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime win-x64 --output selfcontained-win-x64 + + - name: Publish Windows-arm64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime win-arm64 --output selfcontained-win-arm64 + + - name: Publish Linux-x64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime linux-x64 --output selfcontained-linux-x64 + + - name: Publish Linux-arm + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime linux-arm --output selfcontained-linux-arm + + - name: Publish Linux-arm64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime linux-arm64 --output selfcontained-linux-arm64 + + - name: Publish macOS-x64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'macos-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime osx-x64 --output selfcontained-osx-x64 + + - name: Publish macOS-arm64 + if: matrix.configuration == 'Release' && matrix.runs-on == 'macos-latest' + run: dotnet publish --configuration Release -p:PublishSingleFile=true -p:DebugType=embedded --self-contained --runtime osx-arm64 --output selfcontained-osx-arm64 + + - name: Upload Windows-x64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' + with: + name: DepotDownloader-windows-x64 + path: selfcontained-win-x64 + if-no-files-found: error + + - name: Upload Windows-arm64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' + with: + name: DepotDownloader-windows-arm64 + path: selfcontained-win-arm64 + if-no-files-found: error + + - name: Upload Linux-x64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + with: + name: DepotDownloader-linux-x64 + path: selfcontained-linux-x64 + if-no-files-found: error + + - name: Upload Linux-arm + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + with: + name: DepotDownloader-linux-arm + path: selfcontained-linux-arm + if-no-files-found: error + + - name: Upload Linux-arm64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'ubuntu-latest' + with: + name: DepotDownloader-linux-arm64 + path: selfcontained-linux-arm64 + if-no-files-found: error + + - name: Upload macOS-x64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'macos-latest' + with: + name: DepotDownloader-macos-x64 + path: selfcontained-osx-x64 + if-no-files-found: error + + - name: Upload macOS-arm64 + uses: actions/upload-artifact@v3 + if: matrix.configuration == 'Release' && matrix.runs-on == 'macos-latest' + with: + name: DepotDownloader-macos-arm64 + path: selfcontained-osx-arm64 + if-no-files-found: error From 33f78dfff51e34b9e7f627e632247ac51fb8f5a2 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Thu, 27 Apr 2023 20:44:52 +0300 Subject: [PATCH 20/28] Fix formatting Fixes #426 --- DepotDownloader/ContentDownloader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index a46eb3a4..18e19bbe 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -731,7 +731,7 @@ namespace DepotDownloader { var depotCounter = new DepotDownloadCounter(); - Console.WriteLine("Processing depot {0} - {1}", depot.id); + Console.WriteLine("Processing depot {0}", depot.id); ProtoManifest oldProtoManifest = null; ProtoManifest newProtoManifest = null; From 4f7b601e2c4f1c58040b7013bbe7c2a470b75417 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Wed, 3 May 2023 22:56:24 +0300 Subject: [PATCH 21/28] Remove support for encrypted_gid (v1) --- DepotDownloader/ContentDownloader.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 18e19bbe..92171dce 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -255,23 +255,8 @@ namespace DepotDownloader Config.BetaPassword = password = Console.ReadLine(); } - var encrypted_v1 = node_encrypted["encrypted_gid"]; var encrypted_v2 = node_encrypted["encrypted_gid_2"]; - if (encrypted_v1 != KeyValue.Invalid) - { - var input = Util.DecodeHexString(encrypted_v1.Value); - var manifest_bytes = CryptoHelper.VerifyAndDecryptPassword(input, password); - - if (manifest_bytes == null) - { - Console.WriteLine("Password was invalid for branch {0}", branch); - return INVALID_MANIFEST_ID; - } - - return BitConverter.ToUInt64(manifest_bytes, 0); - } - if (encrypted_v2 != KeyValue.Invalid) { // Submit the password to Steam now to get encryption keys From 2cfee72f69a702a883e9db7cc3a529e73c10d981 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Wed, 3 May 2023 22:57:09 +0300 Subject: [PATCH 22/28] Support new `gid` field in encrypted manifests --- DepotDownloader/ContentDownloader.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 92171dce..19de60ba 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -255,9 +255,14 @@ namespace DepotDownloader Config.BetaPassword = password = Console.ReadLine(); } - var encrypted_v2 = node_encrypted["encrypted_gid_2"]; + var encrypted_gid = node_encrypted["gid"]; - if (encrypted_v2 != KeyValue.Invalid) + if (encrypted_gid == KeyValue.Invalid) + { + encrypted_gid = node_encrypted["encrypted_gid_2"]; + } + + if (encrypted_gid != KeyValue.Invalid) { // Submit the password to Steam now to get encryption keys steam3.CheckAppBetaPassword(appId, Config.BetaPassword); @@ -268,7 +273,7 @@ namespace DepotDownloader return INVALID_MANIFEST_ID; } - var input = Util.DecodeHexString(encrypted_v2.Value); + var input = Util.DecodeHexString(encrypted_gid.Value); byte[] manifest_bytes; try { From 8d31a80c98e3b62870003958d21e2e063d3eac40 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 2 Jun 2023 11:16:54 +0300 Subject: [PATCH 23/28] Remove code to handle older style of gids --- DepotDownloader/ContentDownloader.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index 19de60ba..705ad051 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -241,7 +241,7 @@ namespace DepotDownloader if (manifests.Children.Count == 0 && manifests_encrypted.Children.Count == 0) return INVALID_MANIFEST_ID; - var node = manifests[branch].Children.Count > 0 ? manifests[branch]["gid"] : manifests[branch]; + var node = manifests[branch]["gid"]; if (branch != "Public" && node == KeyValue.Invalid) { @@ -257,11 +257,6 @@ namespace DepotDownloader var encrypted_gid = node_encrypted["gid"]; - if (encrypted_gid == KeyValue.Invalid) - { - encrypted_gid = node_encrypted["encrypted_gid_2"]; - } - if (encrypted_gid != KeyValue.Invalid) { // Submit the password to Steam now to get encryption keys From 0e8b6627159cb5ae378d0dac3688b0585d3740d1 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 2 Jun 2023 11:20:42 +0300 Subject: [PATCH 24/28] Do not try to print machine auth hash --- DepotDownloader/Steam3Session.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index bc1aa0d7..206b3b56 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -752,7 +752,7 @@ namespace DepotDownloader private void UpdateMachineAuthCallback(SteamUser.UpdateMachineAuthCallback machineAuth) { var hash = Util.SHAHash(machineAuth.Data); - Console.WriteLine("Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash); + Console.WriteLine("Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length); AccountSettingsStore.Instance.SentryData[logonDetails.Username] = machineAuth.Data; AccountSettingsStore.Save(); From cc5da551f9ba67de5610e05eb5f155bb01e91324 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 2 Jun 2023 11:28:31 +0300 Subject: [PATCH 25/28] Case-insensitive comparison of `public` branch Fixes #429 Fixes #428 --- DepotDownloader/ContentDownloader.cs | 10 +++++----- DepotDownloader/Program.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index a4f8e854..1560bf4e 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -21,7 +21,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 const string DEFAULT_BRANCH = "public"; public static DownloadConfig Config = new DownloadConfig(); @@ -243,7 +243,7 @@ namespace DepotDownloader var node = manifests[branch].Children.Count > 0 ? manifests[branch]["gid"] : manifests[branch]; - if (branch != "Public" && node == KeyValue.Invalid) + if (node == KeyValue.Invalid && !string.Equals(branch, DEFAULT_BRANCH, StringComparison.OrdinalIgnoreCase)) { var node_encrypted = manifests_encrypted[branch]; if (node_encrypted != KeyValue.Invalid) @@ -589,10 +589,10 @@ namespace DepotDownloader if (manifestId == INVALID_MANIFEST_ID) { manifestId = GetSteam3DepotManifest(depotId, appId, branch); - if (manifestId == INVALID_MANIFEST_ID && branch != "public") + if (manifestId == INVALID_MANIFEST_ID && !string.Equals(branch, DEFAULT_BRANCH, StringComparison.OrdinalIgnoreCase)) { - Console.WriteLine("Warning: Depot {0} does not have branch named \"{1}\". Trying public branch.", depotId, branch); - branch = "public"; + Console.WriteLine("Warning: Depot {0} does not have branch named \"{1}\". Trying {2} branch.", depotId, branch, DEFAULT_BRANCH); + branch = DEFAULT_BRANCH; manifestId = GetSteam3DepotManifest(depotId, appId, branch); } diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs index baae3e8e..eb396051 100644 --- a/DepotDownloader/Program.cs +++ b/DepotDownloader/Program.cs @@ -376,7 +376,7 @@ namespace DepotDownloader Console.WriteLine("\t-app <#>\t\t\t\t- the AppID to download."); Console.WriteLine("\t-depot <#>\t\t\t\t- the DepotID to download."); Console.WriteLine("\t-manifest \t\t\t- manifest id of content to download (requires -depot, default: current for branch)."); - Console.WriteLine("\t-beta \t\t\t- download from specified branch if available (default: Public)."); + Console.WriteLine($"\t-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)"); From 0122f0f63fc5124284e406fc7ad0846271191ee3 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 9 Jun 2023 15:36:27 +0300 Subject: [PATCH 26/28] Run automatic CI only on Sundays --- .github/workflows/sk2-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml index 3992a4f1..185be88c 100644 --- a/.github/workflows/sk2-ci.yml +++ b/.github/workflows/sk2-ci.yml @@ -2,7 +2,7 @@ name: SteamKit2 Continuous Integration on: schedule: - - cron: '0 1 * * *' + - cron: '0 1 * * SUN' workflow_dispatch: jobs: From ec692a8d9eaa9da4d966287e14fbc76d8ebe1081 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Tue, 13 Jun 2023 13:32:38 +0300 Subject: [PATCH 27/28] Bump version to 2.5.0 --- DepotDownloader/DepotDownloader.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index eab9ca4d..98ded2a7 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -4,7 +4,7 @@ net6.0 true LatestMajor - 2.4.7 + 2.5.0 Steam Downloading Utility SteamRE Team Copyright © SteamRE Team 2021 From 4c52ad3a4c608612c28fd186884815fad103694d Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Tue, 13 Jun 2023 13:46:58 +0300 Subject: [PATCH 28/28] Upload only a single framework build --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e161f553..265a1562 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,9 +37,9 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v3 - if: matrix.configuration == 'Release' + if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' with: - name: DepotDownloader-${{ runner.os }}-framework + name: DepotDownloader-framework path: artifacts if-no-files-found: error