From ac29a5d9cf6b4cad3aa674542d0116311859e3bc Mon Sep 17 00:00:00 2001 From: Killers0992 <38152961+Killers0992@users.noreply.github.com> Date: Sat, 11 May 2024 18:12:07 +0200 Subject: [PATCH] Update --- .editorconfig | 230 ------------ .github/ISSUE_TEMPLATE/bug-report.yml | 53 --- .github/ISSUE_TEMPLATE/config.yml | 5 - .github/ISSUE_TEMPLATE/feature-request.yml | 36 -- .github/dependabot.yml | 15 - .github/workflows/build.yml | 128 ------- .github/workflows/sk2-ci.yml | 41 --- DepotDownloader.sln | 22 -- DepotDownloader/ContentDownloader.cs | 77 +--- DepotDownloader/DepotDownloader.csproj | 20 - DepotDownloader/Program.cs | 410 --------------------- DepotDownloader/Steam3Session.cs | 12 - Dockerfile | 13 + Icon/DepotDownloader.ico | Bin 46542 -> 0 bytes Icon/DepotDownloader.png | Bin 11088 -> 0 bytes Icon/DepotDownloader.svg | 16 - LICENSE | 339 ----------------- Program.cs | 53 +++ README.md | 67 ---- SCPSL.DownloadFiles.csproj | 19 + SCPSL.DownloadFiles.sln | 25 ++ action.yml | 20 + global.json | 6 - 23 files changed, 142 insertions(+), 1465 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/sk2-ci.yml delete mode 100644 DepotDownloader.sln delete mode 100644 DepotDownloader/DepotDownloader.csproj delete mode 100644 DepotDownloader/Program.cs create mode 100644 Dockerfile delete mode 100644 Icon/DepotDownloader.ico delete mode 100644 Icon/DepotDownloader.png delete mode 100644 Icon/DepotDownloader.svg delete mode 100644 LICENSE create mode 100644 Program.cs delete mode 100644 README.md create mode 100644 SCPSL.DownloadFiles.csproj create mode 100644 SCPSL.DownloadFiles.sln create mode 100644 action.yml delete mode 100644 global.json diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index d05495c2..00000000 --- a/.editorconfig +++ /dev/null @@ -1,230 +0,0 @@ -# top-most EditorConfig file -root = true - -[*] -indent_style = space -charset = utf-8 -end_of_line = lf -insert_final_newline = true - -# Code files -[*.{cs, csx, vb, vbx}] -indent_size = 4 - -# XML project files -[*.{csproj, vbproj, vcxproj, vcxproj.filters, proj, projitems, shproj}] -indent_size = 2 - -# XML config files -[*.{props, targets, ruleset, config, nuspec, resx, vsixmanifest, vsct}] -indent_size = 2 - -# Dotnet code style settings: -[*.{cs, vb}] - -# IDE0055: Fix formatting -dotnet_diagnostic.IDE0055.severity = warning - -# Sort using and Import directives with System.* appearing first -dotnet_sort_system_directives_first = true -dotnet_separate_import_directive_groups = false -# Avoid "this." and "Me." if not necessary -dotnet_style_qualification_for_field = false:refactoring -dotnet_style_qualification_for_property = false:refactoring -dotnet_style_qualification_for_method = false:refactoring -dotnet_style_qualification_for_event = false:refactoring - -# Use language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion - -# Suggest more modern language features when available -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion - -# Non-private static fields are PascalCase -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style - -dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field -dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected -dotnet_naming_symbols.non_private_static_fields.required_modifiers = static - -dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case - -# Non-private readonly fields are PascalCase -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style - -dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field -dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected -dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly - -dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case - -# Constants are PascalCase -dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants -dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style - -dotnet_naming_symbols.constants.applicable_kinds = field, local -dotnet_naming_symbols.constants.required_modifiers = const - -dotnet_naming_style.constant_style.capitalization = pascal_case - -# Static readonly fields are PascalCase -dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion -dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields -dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style - -dotnet_naming_symbols.static_fields.applicable_kinds = field -dotnet_naming_symbols.static_fields.required_modifiers = static, readonly - -dotnet_naming_style.static_field_style.capitalization = pascal_case - -# Instance fields are camelCase and start with _ -dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion -dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields -dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style - -dotnet_naming_symbols.instance_fields.applicable_kinds = field - -dotnet_naming_style.instance_field_style.capitalization = camel_case -dotnet_naming_style.instance_field_style.required_prefix = _ - -# Locals and parameters are camelCase -dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion -dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters -dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style - -dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local - -dotnet_naming_style.camel_case_style.capitalization = camel_case - -# Local functions are PascalCase -dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions -dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style - -dotnet_naming_symbols.local_functions.applicable_kinds = local_function - -dotnet_naming_style.local_function_style.capitalization = pascal_case - -# By default, name items with PascalCase -dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members -dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style - -dotnet_naming_symbols.all_members.applicable_kinds = * - -dotnet_naming_style.pascal_case_style.capitalization = pascal_case - -# Async methods should have "Async" suffix -dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods -dotnet_naming_rule.async_methods_end_in_async.style = end_in_async -dotnet_naming_rule.async_methods_end_in_async.severity = warning - -dotnet_naming_symbols.any_async_methods.applicable_kinds = method -dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * -dotnet_naming_symbols.any_async_methods.required_modifiers = async - -dotnet_naming_style.end_in_async.required_prefix = -dotnet_naming_style.end_in_async.required_suffix = Async -dotnet_naming_style.end_in_async.capitalization = pascal_case -dotnet_naming_style.end_in_async.word_separator = - -# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}' -dotnet_diagnostic.RS2008.severity = none - -# IDE0005: Remove unnecessary import -dotnet_diagnostic.IDE0005.severity = warning - -# IDE0007: Use `var` instead of explicit type -dotnet_diagnostic.IDE0007.severity = warning - -# IDE0035: Remove unreachable code -dotnet_diagnostic.IDE0035.severity = warning - -# IDE0036: Order modifiers -dotnet_diagnostic.IDE0036.severity = warning - -# IDE0043: Format string contains invalid placeholder -dotnet_diagnostic.IDE0043.severity = warning - -# IDE0044: Make field readonly -dotnet_diagnostic.IDE0044.severity = warning - -# CSharp code style settings: -[*.cs] -# Newline settings -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -# csharp_new_line_before_members_in_object_initializers = true TODO seems like Rider/ReSharper has the value inverted, uncomment when its fixed -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_between_query_expression_clauses = true - -# Indentation preferences -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = false -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left - -# Prefer "var" everywhere -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion - -# Prefer method-like constructs to have a block body -csharp_style_expression_bodied_methods = false:none -csharp_style_expression_bodied_constructors = false:none -csharp_style_expression_bodied_operators = false:none - -# Prefer property-like constructs to have an expression-body -csharp_style_expression_bodied_properties = true:none -csharp_style_expression_bodied_indexers = true:none -csharp_style_expression_bodied_accessors = true:none - -# Suggest more modern language features when available -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = do_not_ignore -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false - -# Blocks are allowed -csharp_prefer_braces = true:silent -csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = true diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100644 index d9e2ae9c..00000000 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 47e6660c..00000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 72cbe5db..00000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,36 +0,0 @@ -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 diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index c2cad660..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: 2 -updates: -- package-ecosystem: nuget - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 - -- package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: weekly - ignore: - - dependency-name: "*" - update-types: ["version-update:semver-minor", "version-update:semver-patch"] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index c0725e41..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,128 +0,0 @@ -name: .NET Core CI - -on: - push: - paths-ignore: - - '.github/*' - - '.github/*_TEMPLATE/**' - - '*.md' - pull_request: - paths-ignore: - - '.github/*' - - '.github/*_TEMPLATE/**' - - '*.md' - 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, macos-14, ubuntu-latest, windows-latest] - configuration: [Release, Debug] - env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - steps: - - uses: actions/checkout@v4 - - - name: Setup .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - - name: Build - run: dotnet publish DepotDownloader/DepotDownloader.csproj -c ${{ matrix.configuration }} -o artifacts - - - name: Upload artifact - uses: actions/upload-artifact@v4 - if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' - with: - name: DepotDownloader-framework - path: artifacts - if-no-files-found: error - - - name: Publish Windows-x64 - if: matrix.configuration == 'Release' && matrix.runs-on == 'windows-latest' - run: dotnet publish DepotDownloader/DepotDownloader.csproj --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 DepotDownloader/DepotDownloader.csproj --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 DepotDownloader/DepotDownloader.csproj --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 DepotDownloader/DepotDownloader.csproj --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 DepotDownloader/DepotDownloader.csproj --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 DepotDownloader/DepotDownloader.csproj --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-14' - run: dotnet publish DepotDownloader/DepotDownloader.csproj --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@v4 - 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@v4 - 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@v4 - 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@v4 - 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@v4 - 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@v4 - 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@v4 - if: matrix.configuration == 'Release' && matrix.runs-on == 'macos-14' - with: - name: DepotDownloader-macos-arm64 - path: selfcontained-osx-arm64 - if-no-files-found: error diff --git a/.github/workflows/sk2-ci.yml b/.github/workflows/sk2-ci.yml deleted file mode 100644 index 8827a46f..00000000 --- a/.github/workflows/sk2-ci.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: SteamKit2 Continuous Integration - -on: - schedule: - - cron: '0 1 * * SUN' - 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, macos-14, ubuntu-latest, windows-latest ] - configuration: [ Release, Debug ] - env: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - steps: - - uses: actions/checkout@v4 - - - name: Setup .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.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 DepotDownloader/DepotDownloader.csproj -c ${{ matrix.configuration }} -o artifacts - - - name: Upload artifact - uses: actions/upload-artifact@v4 - if: matrix.configuration == 'Release' - with: - name: DepotDownloader-${{ runner.os }} - path: artifacts - if-no-files-found: error diff --git a/DepotDownloader.sln b/DepotDownloader.sln deleted file mode 100644 index 631e75ec..00000000 --- a/DepotDownloader.sln +++ /dev/null @@ -1,22 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DepotDownloader", "DepotDownloader\DepotDownloader.csproj", "{39159C47-ACD3-449F-96CA-4F30C8ED147A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {39159C47-ACD3-449F-96CA-4F30C8ED147A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {39159C47-ACD3-449F-96CA-4F30C8ED147A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {39159C47-ACD3-449F-96CA-4F30C8ED147A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {39159C47-ACD3-449F-96CA-4F30C8ED147A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/DepotDownloader/ContentDownloader.cs b/DepotDownloader/ContentDownloader.cs index d9191610..cd14fc5c 100644 --- a/DepotDownloader/ContentDownloader.cs +++ b/DepotDownloader/ContentDownloader.cs @@ -81,11 +81,14 @@ namespace DepotDownloader static bool TestIsFileIncluded(string filename) { + if (!Config.UsingFileList) return true; filename = filename.Replace('\\', '/'); + filename = Path.GetFileName(filename); + if (Config.FilesToDownload.Contains(filename)) { return true; @@ -371,7 +374,7 @@ namespace DepotDownloader var stagingDir = Path.Combine(installDir, STAGING_DIR); var fileStagingPath = Path.Combine(stagingDir, fileName); - var fileFinalPath = Path.Combine(installDir, fileName); + var fileFinalPath = Path.Combine(installDir, Path.GetFileName(fileName)); Directory.CreateDirectory(Path.GetDirectoryName(fileFinalPath)); Directory.CreateDirectory(Path.GetDirectoryName(fileStagingPath)); @@ -379,7 +382,6 @@ namespace DepotDownloader using (var file = File.OpenWrite(fileStagingPath)) using (var client = HttpClientFactory.CreateHttpClient()) { - Console.WriteLine("Downloading {0}", fileName); var responseStream = await client.GetStreamAsync(url); await responseStream.CopyToAsync(file); } @@ -442,8 +444,6 @@ namespace DepotDownloader } else { - Console.WriteLine("Using app branch: '{0}'.", branch); - if (depots != null) { foreach (var depotSection in depots.Children) @@ -664,17 +664,12 @@ namespace DepotDownloader { await DownloadSteam3AsyncDepotFiles(cts, downloadCounter, depotFileData, allFileNamesAllDepots); } - - Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", - downloadCounter.TotalBytesCompressed, downloadCounter.TotalBytesUncompressed, depots.Count); } private static async Task ProcessDepotManifestAndFiles(CancellationTokenSource cts, DepotDownloadInfo depot) { var depotCounter = new DepotDownloadCounter(); - Console.WriteLine("Processing depot {0}", depot.DepotId); - ProtoManifest oldProtoManifest = null; ProtoManifest newProtoManifest = null; var configDir = Path.Combine(depot.InstallDir, CONFIG_DIR); @@ -707,9 +702,6 @@ namespace DepotDownloader if (expectedChecksum == null || !expectedChecksum.SequenceEqual(currentChecksum)) { - // We only have to show this warning if the old manifest ID was different - if (lastManifestId != depot.ManifestId) - Console.WriteLine("Manifest {0} on disk did not match the expected checksum.", lastManifestId); oldProtoManifest = null; } } @@ -718,7 +710,6 @@ namespace DepotDownloader if (lastManifestId == depot.ManifestId && oldProtoManifest != null) { newProtoManifest = oldProtoManifest; - Console.WriteLine("Already have manifest {0} for depot {1}.", depot.ManifestId, depot.DepotId); } else { @@ -740,7 +731,6 @@ namespace DepotDownloader if (newProtoManifest != null && (expectedChecksum == null || !expectedChecksum.SequenceEqual(currentChecksum))) { - Console.WriteLine("Manifest {0} on disk did not match the expected checksum.", depot.ManifestId); newProtoManifest = null; } } @@ -751,8 +741,6 @@ namespace DepotDownloader } else { - Console.Write("Downloading depot manifest..."); - DepotManifest depotManifest = null; ulong manifestRequestCode = 0; var manifestRequestCodeExpiration = DateTime.MinValue; @@ -789,11 +777,6 @@ namespace DepotDownloader } } - DebugLog.WriteLine("ContentDownloader", - "Downloading manifest {0} from {1} with {2}", - depot.ManifestId, - connection, - cdnPool.ProxyServer != null ? cdnPool.ProxyServer : "no proxy"); depotManifest = await cdnPool.CDNClient.DownloadManifestAsync( depot.DepotId, depot.ManifestId, @@ -850,15 +833,11 @@ namespace DepotDownloader newProtoManifest = new ProtoManifest(depotManifest, depot.ManifestId); newProtoManifest.SaveToFile(newManifestFileName, out var checksum); File.WriteAllBytes(newManifestFileName + ".sha", checksum); - - Console.WriteLine(" Done!"); } } newProtoManifest.Files.Sort((x, y) => string.Compare(x.FileName, y.FileName, StringComparison.Ordinal)); - Console.WriteLine("Manifest {0} ({1})", depot.ManifestId, newProtoManifest.CreationTime); - if (Config.DownloadManifestOnly) { DumpManifestToTextFile(depot, newProtoManifest); @@ -875,20 +854,8 @@ namespace DepotDownloader { allFileNames.Add(file.FileName); - var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName); - var fileStagingPath = Path.Combine(stagingDir, file.FileName); - - if (file.Flags.HasFlag(EDepotFileFlag.Directory)) - { - Directory.CreateDirectory(fileFinalPath); - Directory.CreateDirectory(fileStagingPath); - } - else + if (!file.Flags.HasFlag(EDepotFileFlag.Directory)) { - // Some manifests don't explicitly include all necessary directories - Directory.CreateDirectory(Path.GetDirectoryName(fileFinalPath)); - Directory.CreateDirectory(Path.GetDirectoryName(fileStagingPath)); - depotCounter.CompleteDownloadSize += file.TotalSize; } }); @@ -911,8 +878,6 @@ namespace DepotDownloader var depot = depotFilesData.depotDownloadInfo; var depotCounter = depotFilesData.depotCounter; - Console.WriteLine("Downloading depot {0}", depot.DepotId); - var files = depotFilesData.filteredFiles.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)).ToArray(); var networkChunkQueue = new ConcurrentQueue<(FileStreamData fileStreamData, ProtoManifest.FileData fileData, ProtoManifest.ChunkData chunk)>(); @@ -948,20 +913,17 @@ namespace DepotDownloader foreach (var existingFileName in previousFilteredFiles) { - var fileFinalPath = Path.Combine(depot.InstallDir, existingFileName); + var fileFinalPath = Path.Combine(depot.InstallDir, Path.GetFileName(existingFileName)); if (!File.Exists(fileFinalPath)) continue; File.Delete(fileFinalPath); - Console.WriteLine("Deleted {0}", fileFinalPath); } } DepotConfigStore.Instance.InstalledManifestIDs[depot.DepotId] = depot.ManifestId; DepotConfigStore.Save(); - - Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.DepotId, depotCounter.DepotBytesCompressed, depotCounter.DepotBytesUncompressed); } private static void DownloadSteam3AsyncDepotFile( @@ -982,8 +944,10 @@ namespace DepotDownloader oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName); } - var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName); - var fileStagingPath = Path.Combine(stagingDir, file.FileName); + var name = Path.GetFileName(file.FileName); + + var fileFinalPath = Path.Combine(depot.InstallDir, name); + var fileStagingPath = Path.Combine(stagingDir, name); // This may still exist if the previous run exited before cleanup if (File.Exists(fileStagingPath)) @@ -996,8 +960,6 @@ namespace DepotDownloader var fileDidExist = fi.Exists; if (!fileDidExist) { - Console.WriteLine("Pre-allocating {0}", fileFinalPath); - // create new file. need all chunks using var fs = File.Create(fileFinalPath); try @@ -1021,12 +983,6 @@ namespace DepotDownloader var hashMatches = oldManifestFile.FileHash.SequenceEqual(file.FileHash); if (Config.VerifyAll || !hashMatches) { - // we have a version of this file, but it doesn't fully match what we want - if (Config.VerifyAll) - { - Console.WriteLine("Validating {0}", fileFinalPath); - } - var matchingChunks = new List(); foreach (var chunk in file.Chunks) @@ -1116,7 +1072,6 @@ namespace DepotDownloader } } - Console.WriteLine("Validating {0}", fileFinalPath); neededChunks = Util.ValidateSteam3FileChecksums(fs, [.. file.Chunks.OrderBy(x => x.Offset)]); } @@ -1125,9 +1080,9 @@ namespace DepotDownloader lock (depotDownloadCounter) { depotDownloadCounter.SizeDownloaded += file.TotalSize; - Console.WriteLine("{0,6:#00.00}% {1}", (depotDownloadCounter.SizeDownloaded / (float)depotDownloadCounter.CompleteDownloadSize) * 100.0f, fileFinalPath); } + Console.WriteLine($"Downloaded {fileFinalPath}"); return; } @@ -1196,8 +1151,6 @@ namespace DepotDownloader try { connection = cdnPool.GetConnection(cts.Token); - - DebugLog.WriteLine("ContentDownloader", "Downloading chunk {0} from {1} with {2}", chunkID, connection, cdnPool.ProxyServer != null ? cdnPool.ProxyServer : "no proxy"); chunkData = await cdnPool.CDNClient.DownloadDepotChunkAsync( depot.DepotId, data, @@ -1249,7 +1202,7 @@ namespace DepotDownloader if (fileStreamData.fileStream == null) { - var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName); + var fileFinalPath = Path.Combine(depot.InstallDir, Path.GetFileName(file.FileName)); fileStreamData.fileStream = File.Open(fileFinalPath, FileMode.Open); } @@ -1282,12 +1235,6 @@ namespace DepotDownloader downloadCounter.TotalBytesCompressed += chunk.CompressedLength; downloadCounter.TotalBytesUncompressed += chunk.UncompressedLength; } - - if (remainingChunks == 0) - { - var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName); - Console.WriteLine("{0,6:#00.00}% {1}", (sizeDownloaded / (float)depotDownloadCounter.CompleteDownloadSize) * 100.0f, fileFinalPath); - } } static void DumpManifestToTextFile(DepotDownloadInfo depot, ProtoManifest manifest) diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj deleted file mode 100644 index 47cfd284..00000000 --- a/DepotDownloader/DepotDownloader.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - Exe - net8.0 - true - LatestMajor - 2.6.0 - Steam Downloading Utility - SteamRE Team - Copyright © SteamRE Team 2024 - ..\Icon\DepotDownloader.ico - true - - - - - - - - diff --git a/DepotDownloader/Program.cs b/DepotDownloader/Program.cs deleted file mode 100644 index 14f7f88d..00000000 --- a/DepotDownloader/Program.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using SteamKit2; - -namespace DepotDownloader -{ - class Program - { - static int Main(string[] args) - => MainAsync(args).GetAwaiter().GetResult(); - - internal static readonly char[] newLineCharacters = ['\n', '\r']; - - static async Task MainAsync(string[] args) - { - if (args.Length == 0) - { - PrintUsage(); - return 1; - } - - DebugLog.Enabled = false; - - AccountSettingsStore.LoadFromFile("account.config"); - - #region Common Options - - if (HasParameter(args, "-debug")) - { - DebugLog.Enabled = true; - DebugLog.AddListener((category, message) => - { - Console.WriteLine("[{0}] {1}", category, message); - }); - - var httpEventListener = new HttpDiagnosticEventListener(); - - DebugLog.WriteLine("DepotDownloader", "Version: {0}", Assembly.GetExecutingAssembly().GetName().Version); - DebugLog.WriteLine("DepotDownloader", "Runtime: {0}", RuntimeInformation.FrameworkDescription); - } - - var username = GetParameter(args, "-username") ?? GetParameter(args, "-user"); - var password = GetParameter(args, "-password") ?? GetParameter(args, "-pass"); - ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password"); - ContentDownloader.Config.UseQrCode = HasParameter(args, "-qr"); - - ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); - - var cellId = GetParameter(args, "-cellid", -1); - if (cellId == -1) - { - cellId = 0; - } - - ContentDownloader.Config.CellID = cellId; - - var fileList = GetParameter(args, "-filelist"); - - if (fileList != null) - { - const string RegexPrefix = "regex:"; - - try - { - var fileListData = await File.ReadAllTextAsync(fileList); - var files = fileListData.Split(newLineCharacters, StringSplitOptions.RemoveEmptyEntries); - - ContentDownloader.Config.UsingFileList = true; - ContentDownloader.Config.FilesToDownload = new HashSet(StringComparer.OrdinalIgnoreCase); - ContentDownloader.Config.FilesToDownloadRegex = []; - - foreach (var fileEntry in files) - { - if (fileEntry.StartsWith(RegexPrefix)) - { - var rgx = new Regex(fileEntry[RegexPrefix.Length..], RegexOptions.Compiled | RegexOptions.IgnoreCase); - ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); - } - else - { - ContentDownloader.Config.FilesToDownload.Add(fileEntry.Replace('\\', '/')); - } - } - - Console.WriteLine("Using filelist: '{0}'.", fileList); - } - catch (Exception ex) - { - Console.WriteLine("Warning: Unable to load filelist: {0}", ex); - } - } - - ContentDownloader.Config.InstallDirectory = GetParameter(args, "-dir"); - - ContentDownloader.Config.VerifyAll = HasParameter(args, "-verify-all") || HasParameter(args, "-verify_all") || HasParameter(args, "-validate"); - ContentDownloader.Config.MaxServers = GetParameter(args, "-max-servers", 20); - ContentDownloader.Config.MaxDownloads = GetParameter(args, "-max-downloads", 8); - ContentDownloader.Config.MaxServers = Math.Max(ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads); - ContentDownloader.Config.LoginID = HasParameter(args, "-loginid") ? GetParameter(args, "-loginid") : null; - - #endregion - - var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID); - if (appId == ContentDownloader.INVALID_APP_ID) - { - 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) - { - #region Pubfile Downloading - - if (InitializeSteam(username, password)) - { - try - { - await ContentDownloader.DownloadPubfileAsync(appId, pubFile).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } - } - else - { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; - } - - #endregion - } - else if (ugcId != ContentDownloader.INVALID_MANIFEST_ID) - { - #region UGC Downloading - - if (InitializeSteam(username, password)) - { - try - { - await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } - } - else - { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; - } - - #endregion - } - else - { - #region App downloading - - var branch = GetParameter(args, "-branch") ?? GetParameter(args, "-beta") ?? ContentDownloader.DEFAULT_BRANCH; - ContentDownloader.Config.BetaPassword = GetParameter(args, "-betapassword"); - - ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms"); - var os = GetParameter(args, "-os"); - - if (ContentDownloader.Config.DownloadAllPlatforms && !string.IsNullOrEmpty(os)) - { - Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); - return 1; - } - - var arch = GetParameter(args, "-osarch"); - - ContentDownloader.Config.DownloadAllLanguages = HasParameter(args, "-all-languages"); - var language = GetParameter(args, "-language"); - - if (ContentDownloader.Config.DownloadAllLanguages && !string.IsNullOrEmpty(language)) - { - Console.WriteLine("Error: Cannot specify -language when -all-languages is specified."); - return 1; - } - - var lv = HasParameter(args, "-lowviolence"); - - var depotManifestIds = new List<(uint, ulong)>(); - var isUGC = false; - - var depotIdList = GetParameterList(args, "-depot"); - var manifestIdList = GetParameterList(args, "-manifest"); - if (manifestIdList.Count > 0) - { - if (depotIdList.Count != manifestIdList.Count) - { - Console.WriteLine("Error: -manifest requires one id for every -depot specified"); - return 1; - } - - var zippedDepotManifest = depotIdList.Zip(manifestIdList, (depotId, manifestId) => (depotId, manifestId)); - depotManifestIds.AddRange(zippedDepotManifest); - } - else - { - depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID))); - } - - if (InitializeSteam(username, password)) - { - try - { - await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false); - } - catch (Exception ex) when ( - ex is ContentDownloaderException - || ex is OperationCanceledException) - { - Console.WriteLine(ex.Message); - return 1; - } - catch (Exception e) - { - Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message); - throw; - } - finally - { - ContentDownloader.ShutdownSteam3(); - } - } - else - { - Console.WriteLine("Error: InitializeSteam failed"); - return 1; - } - - #endregion - } - - return 0; - } - - static bool InitializeSteam(string username, string password) - { - if (!ContentDownloader.Config.UseQrCode) - { - if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginTokens.ContainsKey(username))) - { - do - { - 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."); - } - } - - return ContentDownloader.InitializeSteam3(username, password); - } - - static int IndexOfParam(string[] args, string param) - { - for (var x = 0; x < args.Length; ++x) - { - if (args[x].Equals(param, StringComparison.OrdinalIgnoreCase)) - return x; - } - - return -1; - } - - static bool HasParameter(string[] args, string param) - { - return IndexOfParam(args, param) > -1; - } - - static T GetParameter(string[] args, string param, T defaultValue = default) - { - var index = IndexOfParam(args, param); - - if (index == -1 || index == (args.Length - 1)) - return defaultValue; - - var strParam = args[index + 1]; - - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - return (T)converter.ConvertFromString(strParam); - } - - return default; - } - - static List GetParameterList(string[] args, string param) - { - var list = new List(); - var index = IndexOfParam(args, param); - - if (index == -1 || index == (args.Length - 1)) - return list; - - index++; - - while (index < args.Length) - { - var strParam = args[index]; - - if (strParam[0] == '-') break; - - var converter = TypeDescriptor.GetConverter(typeof(T)); - if (converter != null) - { - list.Add((T)converter.ConvertFromString(strParam)); - } - - index++; - } - - return list; - } - - static void PrintUsage() - { - Console.WriteLine(); - Console.WriteLine("Usage - downloading one or all depots for an app:"); - Console.WriteLine("\tdepotdownloader -app [-depot [-manifest ]]"); - Console.WriteLine("\t\t[-username [-password ]] [other options]"); - Console.WriteLine(); - Console.WriteLine("Usage - downloading a workshop item using pubfile id"); - Console.WriteLine("\tdepotdownloader -app -pubfile [-username [-password ]]"); - Console.WriteLine("Usage - downloading a workshop item using ugc id"); - Console.WriteLine("\tdepotdownloader -app -ugc [-username [-password ]]"); - Console.WriteLine(); - Console.WriteLine("Parameters:"); - Console.WriteLine("\t-app <#>\t\t\t\t- the AppID to download."); - Console.WriteLine("\t-depot <#>\t\t\t\t- the DepotID to download."); - Console.WriteLine("\t-manifest \t\t\t- manifest id of content to download (requires -depot, default: current for branch)."); - Console.WriteLine($"\t-beta \t\t\t- download from specified branch if available (default: {ContentDownloader.DEFAULT_BRANCH})."); - Console.WriteLine("\t-betapassword \t\t- branch password if applicable."); - Console.WriteLine("\t-all-platforms\t\t\t- downloads all platform-specific depots when -app is used."); - Console.WriteLine("\t-os \t\t\t\t- the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on)"); - Console.WriteLine("\t-osarch \t\t\t\t- the architecture for which to download the game (32 or 64, default: the host's architecture)"); - Console.WriteLine("\t-all-languages\t\t\t\t- download all language-specific depots when -app is used."); - Console.WriteLine("\t-language \t\t\t\t- the language for which to download the game (default: english)"); - Console.WriteLine("\t-lowviolence\t\t\t\t- download low violence depots when -app is used."); - Console.WriteLine(); - Console.WriteLine("\t-ugc <#>\t\t\t\t- the UGC ID to download."); - Console.WriteLine("\t-pubfile <#>\t\t\t- the PublishedFileId to download. (Will automatically resolve to UGC id)"); - Console.WriteLine(); - Console.WriteLine("\t-username \t\t- the username of the account to login to for restricted content."); - Console.WriteLine("\t-password \t\t- the password of the account to login to for restricted content."); - Console.WriteLine("\t-remember-password\t\t- if set, remember the password for subsequent logins of this user. (Use -username -remember-password as login credentials)"); - Console.WriteLine(); - Console.WriteLine("\t-dir \t\t- the directory in which to place downloaded files."); - Console.WriteLine("\t-filelist \t- a list of files to download (from the manifest). Prefix file path with 'regex:' if you want to match with regex."); - Console.WriteLine("\t-validate\t\t\t\t- Include checksum verification of files already downloaded"); - Console.WriteLine(); - Console.WriteLine("\t-manifest-only\t\t\t- downloads a human readable manifest for any depots that would be downloaded."); - Console.WriteLine("\t-cellid <#>\t\t\t\t- the overridden CellID of the content server to download from."); - Console.WriteLine("\t-max-servers <#>\t\t- maximum number of content servers to use. (default: 20)."); - Console.WriteLine("\t-max-downloads <#>\t\t- maximum number of chunks to download concurrently. (default: 8)."); - Console.WriteLine("\t-loginid <#>\t\t- a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently."); - } - } -} diff --git a/DepotDownloader/Steam3Session.cs b/DepotDownloader/Steam3Session.cs index d2ee88ab..d95f85c3 100644 --- a/DepotDownloader/Steam3Session.cs +++ b/DepotDownloader/Steam3Session.cs @@ -81,7 +81,6 @@ namespace DepotDownloader this.callbacks.Subscribe(LogOnCallback); this.callbacks.Subscribe(LicenseListCallback); - Console.Write("Connecting to Steam3..."); Connect(); } @@ -155,7 +154,6 @@ namespace DepotDownloader { var app = app_value.Value; - Console.WriteLine("Got AppInfo for {0}", app.ID); AppInfo[app.ID] = app; } @@ -250,7 +248,6 @@ namespace DepotDownloader Action cbMethod = depotKey => { completed = true; - Console.WriteLine("Got depot key for {0} result: {1}", depotKey.DepotID, depotKey.Result); if (depotKey.Result != EResult.OK) { @@ -275,10 +272,6 @@ namespace DepotDownloader var requestCode = await steamContent.GetManifestRequestCode(depotId, appId, manifestId, branch); - Console.WriteLine("Got manifest request code for {0} {1} result: {2}", - depotId, manifestId, - requestCode); - return requestCode; } @@ -431,7 +424,6 @@ namespace DepotDownloader private async void ConnectedCallback(SteamClient.ConnectedCallback connected) { - Console.WriteLine(" Done!"); bConnecting = false; bConnected = true; @@ -442,7 +434,6 @@ namespace DepotDownloader if (!authenticatedUser) { - Console.Write("Logging anonymously into Steam3..."); steamUser.LogOnAnonymous(); } else @@ -674,14 +665,11 @@ namespace DepotDownloader return; } - Console.WriteLine(" Done!"); - this.seq++; IsLoggedOn = true; if (ContentDownloader.Config.CellID == 0) { - Console.WriteLine("Using Steam3 suggested CellID: " + loggedOn.CellID); ContentDownloader.Config.CellID = (int)loggedOn.CellID; } } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..51a032f3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env +WORKDIR /app + +COPY . ./ + +RUN dotnet publish -c Release -o out + +FROM mcr.microsoft.com/dotnet/runtime:8.0 +WORKDIR /app + +COPY --from=build-env /app/out . + +ENTRYPOINT ["dotnet", "/app/SCPSL.DownloadFiles.dll"] \ No newline at end of file diff --git a/Icon/DepotDownloader.ico b/Icon/DepotDownloader.ico deleted file mode 100644 index 6b0c58ce4de5167df6a0167f8cfeaabd62eb7f7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46542 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 eb`dRyGxZqp0a+Btds7x`~rTw0ZpogWpO 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 Au>b%7 diff --git a/Icon/DepotDownloader.svg b/Icon/DepotDownloader.svg deleted file mode 100644 index ab1d371a..00000000 --- a/Icon/DepotDownloader.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d159169d..00000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/Program.cs b/Program.cs new file mode 100644 index 00000000..11827248 --- /dev/null +++ b/Program.cs @@ -0,0 +1,53 @@ +using McMaster.Extensions.CommandLineUtils; +using Microsoft.Extensions.Hosting; +using DepotDownloader; + +await Host.CreateDefaultBuilder() + .RunCommandLineApplicationAsync(args); + +[Command(Description = "Downloads SCP: SL assembly files.")] +public class AppCommand +{ + [Option(Description = "Branch.")] + public string Branch { get; set; } = ContentDownloader.DEFAULT_BRANCH; + + [Option(Description = "Files to download.")] + public string FilesToDownload { get; set; } = "Assembly-CSharp.dll"; + + public async Task OnExecute(IConsole console) + { + try + { + string refPath = ".\\References"; + + if (!Directory.Exists(refPath)) + Directory.CreateDirectory(refPath); + + AccountSettingsStore.LoadFromFile("account.config"); + + ContentDownloader.Config.InstallDirectory = refPath; + + ContentDownloader.Config.MaxServers = 20; + ContentDownloader.Config.MaxDownloads = 8; + + ContentDownloader.Config.UsingFileList = true; + ContentDownloader.Config.FilesToDownloadRegex = new(); + ContentDownloader.Config.FilesToDownload = FilesToDownload.Split(",").ToHashSet(); + + if (ContentDownloader.InitializeSteam3(null, null)) + { + Console.WriteLine("Start downloading files..."); + await ContentDownloader.DownloadAppAsync(996560, new List<(uint depotId, ulong manifestId)>(), Branch, "windows", null, null, false, false).ConfigureAwait(false); + } + + Console.WriteLine("Files downloaded!"); + return 0; + } + catch (Exception ex) + { + console.WriteLine(ex); + return 1; + } + } + +} \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 68fdaffd..00000000 --- a/README.md +++ /dev/null @@ -1,67 +0,0 @@ -DepotDownloader -=============== - -Steam depot downloader utilizing the SteamKit2 library. Supports .NET 8.0 - -### Downloading one or all depots for an app -``` -dotnet DepotDownloader.dll -app [-depot [-manifest ]] - [-username [-password ]] [other options] -``` - -For example: `dotnet DepotDownloader.dll -app 730 -depot 731 -manifest 7617088375292372759` - -### Downloading a workshop item using pubfile id -``` -dotnet DepotDownloader.dll -app -pubfile [-username [-password ]] -``` - -For example: `dotnet DepotDownloader.dll -app 730 -pubfile 1885082371` - -### Downloading a workshop item using ugc id -``` -dotnet DepotDownloader.dll -app -ugc [-username [-password ]] -``` - -For example: `dotnet DepotDownloader.dll -app 730 -ugc 770604181014286929` - -## Parameters - -Parameter | Description ---------- | ----------- --app \<#> | the AppID to download. --depot \<#> | the DepotID to download. --manifest \ | manifest id of content to download (requires -depot, default: current for branch). --ugc \<#> | the UGC ID to download. --beta \ | download from specified branch if available (default: Public). --betapassword \ | branch password if applicable. --all-platforms | downloads all platform-specific depots when -app is used. --os \ | the operating system for which to download the game (windows, macos or linux, default: OS the program is currently running on) --osarch \ | the architecture for which to download the game (32 or 64, default: the host's architecture) --all-languages | download all language-specific depots when -app is used. --language \ | the language for which to download the game (default: english) --lowviolence | download low violence depots when -app is used. --pubfile \<#> | the PublishedFileId to download. (Will automatically resolve to UGC id) --username \ | the username of the account to login to for restricted content. --password \ | the password of the account to login to for restricted content. --remember-password | if set, remember the password for subsequent logins of this user. (Use -username -remember-password as login credentials) --dir \ | the directory in which to place downloaded files. --filelist \ | a list of files to download (from the manifest). Prefix file path with `regex:` if you want to match with regex. --validate | Include checksum verification of files already downloaded --manifest-only | downloads a human readable manifest for any depots that would be downloaded. --cellid \<#> | the overridden CellID of the content server to download from. --max-servers \<#> | maximum number of content servers to use. (default: 20). --max-downloads \<#> | maximum number of chunks to download concurrently. (default: 8). --loginid \<#> | a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently. - - -## Frequently Asked Questions - -### Why am I prompted to enter a 2-factor code every time I run the app? -Your 2-factor code authenticates a Steam session. You need to "remember" your session with `-remember-password` which persists the login key for your Steam session. - -### Can I run DepotDownloader while an account is already connected to Steam? -Any connection to Steam will be closed if they share a LoginID. You can specify a different LoginID with `-loginid`. - -### Why doesn't my password containing special characters work? Do I have to specify the password on the command line? -If you pass the `-password` parameter with a password that contains special characters, you will need to escape the command appropriately for the shell you are using. You do not have to include the `-password` parameter on the command line as long as you include a `-username`. You will be prompted to enter your password interactively. diff --git a/SCPSL.DownloadFiles.csproj b/SCPSL.DownloadFiles.csproj new file mode 100644 index 00000000..2c67a9f9 --- /dev/null +++ b/SCPSL.DownloadFiles.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + latest + + + + + + + + + + + + diff --git a/SCPSL.DownloadFiles.sln b/SCPSL.DownloadFiles.sln new file mode 100644 index 00000000..73a72fdb --- /dev/null +++ b/SCPSL.DownloadFiles.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34607.119 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SCPSL.DownloadFiles", "SCPSL.DownloadFiles.csproj", "{4BFC0F11-647F-4096-A633-58C0592B81EE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4BFC0F11-647F-4096-A633-58C0592B81EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BFC0F11-647F-4096-A633-58C0592B81EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BFC0F11-647F-4096-A633-58C0592B81EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BFC0F11-647F-4096-A633-58C0592B81EE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0760B22E-EDAA-4317-9EE2-8EC540D8BBF2} + EndGlobalSection +EndGlobal diff --git a/action.yml b/action.yml new file mode 100644 index 00000000..49286966 --- /dev/null +++ b/action.yml @@ -0,0 +1,20 @@ +name: "SCPSL.DownloadFiles" +description: "Downloads SCP: SL assembly files." +branding: + icon: file + color: blue +inputs: + filestodownload: + description: "Files which will be downloaded." + required: true + branch: + description: "From which branch files will be downloaded." + required: false +runs: + using: "docker" + image: "Dockerfile" + args: + - "--filestodownload" + - ${{ inputs.filestodownload }} + - "--branch" + - ${{ inputs.branch }} \ No newline at end of file diff --git a/global.json b/global.json deleted file mode 100644 index c19a2e05..00000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "8.0.100", - "rollForward": "latestMinor" - } -}