mirror of
https://github.com/mmvanheusden/SteamDepotDownloaderGUI.git
synced 2026-02-04 05:31:19 +01:00
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "DepotDownloader"]
|
||||
path = DepotDownloader
|
||||
url = https://github.com/SteamRE/DepotDownloader
|
||||
7
depotdownloader/.editorconfig
Normal file
7
depotdownloader/.editorconfig
Normal file
@@ -0,0 +1,7 @@
|
||||
; EditorConfig: http://EditorConfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
37
depotdownloader/.github/workflows/build.yml
vendored
Normal file
37
depotdownloader/.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
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.os }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
configuration: [Release, Debug]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
- name: Build
|
||||
run: dotnet publish -c ${{ matrix.configuration }} -o artifacts
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: matrix.configuration == 'Release'
|
||||
with:
|
||||
name: DepotDownloader-${{ runner.os }}
|
||||
path: artifacts
|
||||
if-no-files-found: error
|
||||
120
depotdownloader/.gitignore
vendored
Normal file
120
depotdownloader/.gitignore
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# mstest test results
|
||||
TestResults
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
.vs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*
|
||||
|
||||
# NCrunch
|
||||
*.ncrunch*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish
|
||||
|
||||
# Publish Web Output
|
||||
*.Publish.xml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages
|
||||
*.nupkg
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
[Bb]in
|
||||
[Oo]bj
|
||||
sql
|
||||
TestResults
|
||||
[Tt]est[Rr]esult*
|
||||
*.Cache
|
||||
ClientBin
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*.dbmdl
|
||||
Generated_Code #added for RIA/Silverlight projects
|
||||
|
||||
# Backup & report files from converting an old project file to a newer
|
||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
|
||||
# third party libs
|
||||
boost/
|
||||
google/
|
||||
zlib/
|
||||
protobuf/
|
||||
cryptopp/
|
||||
|
||||
# misc
|
||||
Thumbs.db
|
||||
@@ -1,748 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v2.0/any",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v2.0": {},
|
||||
".NETCoreApp,Version=v2.0/any": {
|
||||
"DepotDownloader/1.0.0": {
|
||||
"dependencies": {
|
||||
"SteamKit2": "1.0.0",
|
||||
"protobuf-net": "2.4.4"
|
||||
},
|
||||
"runtime": {
|
||||
"DepotDownloader.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/2.1.0": {},
|
||||
"Microsoft.NETCore.Targets/1.1.0": {},
|
||||
"Microsoft.Win32.Registry/4.4.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"System.Security.AccessControl": "4.4.0",
|
||||
"System.Security.Principal.Windows": "4.5.0"
|
||||
}
|
||||
},
|
||||
"protobuf-net/2.4.4": {
|
||||
"dependencies": {
|
||||
"System.Reflection.Emit": "4.3.0",
|
||||
"System.Reflection.Emit.Lightweight": "4.3.0",
|
||||
"System.Reflection.TypeExtensions": "4.4.0",
|
||||
"System.ServiceModel.Primitives": "4.5.3",
|
||||
"System.Xml.XmlSerializer": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/protobuf-net.dll": {
|
||||
"assemblyVersion": "2.4.0.0",
|
||||
"fileVersion": "2.4.4.9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"runtime.any.System.Collections/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"runtime.any.System.Globalization/4.3.0": {},
|
||||
"runtime.any.System.IO/4.3.0": {},
|
||||
"runtime.any.System.Reflection/4.3.0": {},
|
||||
"runtime.any.System.Reflection.Extensions/4.3.0": {},
|
||||
"runtime.any.System.Reflection.Primitives/4.3.0": {},
|
||||
"runtime.any.System.Resources.ResourceManager/4.3.0": {},
|
||||
"runtime.any.System.Runtime/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Private.Uri": "4.3.0"
|
||||
}
|
||||
},
|
||||
"runtime.any.System.Runtime.Handles/4.3.0": {},
|
||||
"runtime.any.System.Runtime.InteropServices/4.3.0": {},
|
||||
"runtime.any.System.Text.Encoding/4.3.0": {},
|
||||
"runtime.any.System.Text.Encoding.Extensions/4.3.0": {},
|
||||
"runtime.any.System.Threading.Tasks/4.3.0": {},
|
||||
"System.Collections/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Collections": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.Debug/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Globalization/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Globalization": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.IO/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0",
|
||||
"runtime.any.System.IO": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.IO.FileSystem/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.IO.FileSystem.Primitives/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Linq/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Private.ServiceModel/4.5.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"System.Reflection.DispatchProxy": "4.5.0",
|
||||
"System.Security.Principal.Windows": "4.5.0"
|
||||
}
|
||||
},
|
||||
"System.Private.Uri/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Reflection": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.DispatchProxy/4.5.0": {
|
||||
"runtime": {
|
||||
"lib/netcoreapp2.0/System.Reflection.DispatchProxy.dll": {
|
||||
"assemblyVersion": "4.0.4.0",
|
||||
"fileVersion": "4.6.26515.6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Reflection.Emit/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.IO": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Emit.ILGeneration/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Emit.Lightweight/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Extensions/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Reflection.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Primitives/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Reflection.Primitives": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Reflection.TypeExtensions/4.4.0": {},
|
||||
"System.Resources.ResourceManager/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Resources.ResourceManager": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"runtime.any.System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Extensions/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Handles/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Runtime.Handles": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.InteropServices/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Handles": "4.3.0",
|
||||
"runtime.any.System.Runtime.InteropServices": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Security.AccessControl/4.4.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"System.Security.Principal.Windows": "4.5.0"
|
||||
}
|
||||
},
|
||||
"System.Security.Principal.Windows/4.5.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/System.Security.Principal.Windows.dll": {
|
||||
"assemblyVersion": "4.1.1.0",
|
||||
"fileVersion": "4.6.26515.6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.ServiceModel.Primitives/4.5.3": {
|
||||
"dependencies": {
|
||||
"System.Private.ServiceModel": "4.5.3"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/System.ServiceModel.Primitives.dll": {
|
||||
"assemblyVersion": "4.5.0.3",
|
||||
"fileVersion": "4.6.26720.1"
|
||||
},
|
||||
"lib/netstandard2.0/System.ServiceModel.dll": {
|
||||
"assemblyVersion": "4.0.0.0",
|
||||
"fileVersion": "4.6.26720.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Text.Encoding": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.Extensions/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"runtime.any.System.Text.Encoding.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.RegularExpressions/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Threading/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "2.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"runtime.any.System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.ReaderWriter/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.IO.FileSystem": "4.3.0",
|
||||
"System.IO.FileSystem.Primitives": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Runtime.InteropServices": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Text.Encoding.Extensions": "4.3.0",
|
||||
"System.Text.RegularExpressions": "4.3.0",
|
||||
"System.Threading.Tasks": "4.3.0",
|
||||
"System.Threading.Tasks.Extensions": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XmlDocument/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Diagnostics.Debug": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Xml.XmlSerializer/4.3.0": {
|
||||
"dependencies": {
|
||||
"System.Collections": "4.3.0",
|
||||
"System.Globalization": "4.3.0",
|
||||
"System.IO": "4.3.0",
|
||||
"System.Linq": "4.3.0",
|
||||
"System.Reflection": "4.3.0",
|
||||
"System.Reflection.Emit": "4.3.0",
|
||||
"System.Reflection.Emit.ILGeneration": "4.3.0",
|
||||
"System.Reflection.Extensions": "4.3.0",
|
||||
"System.Reflection.Primitives": "4.3.0",
|
||||
"System.Reflection.TypeExtensions": "4.4.0",
|
||||
"System.Resources.ResourceManager": "4.3.0",
|
||||
"System.Runtime": "4.3.0",
|
||||
"System.Runtime.Extensions": "4.3.0",
|
||||
"System.Text.RegularExpressions": "4.3.0",
|
||||
"System.Threading": "4.3.0",
|
||||
"System.Xml.ReaderWriter": "4.3.0",
|
||||
"System.Xml.XmlDocument": "4.3.0"
|
||||
}
|
||||
},
|
||||
"SteamKit2/1.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.Registry": "4.4.0",
|
||||
"protobuf-net": "2.4.4"
|
||||
},
|
||||
"runtime": {
|
||||
"SteamKit2.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"DepotDownloader/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/2.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==",
|
||||
"path": "microsoft.netcore.platforms/2.1.0",
|
||||
"hashPath": "microsoft.netcore.platforms.2.1.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.NETCore.Targets/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==",
|
||||
"path": "microsoft.netcore.targets/1.1.0",
|
||||
"hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Win32.Registry/4.4.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-LJh1PSoqdugrCv7t+k2B919GGdq4PyrntPVlMCMrcz59nsjOstYmtGWSCiFZ3UNpxv2dlsZdcLzJ847uyOlaEA==",
|
||||
"path": "microsoft.win32.registry/4.4.0",
|
||||
"hashPath": "microsoft.win32.registry.4.4.0.nupkg.sha512"
|
||||
},
|
||||
"protobuf-net/2.4.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-EYZyqfSlroeRVC2cvyONwjFE6ji50d+0gsGXFha+Ij/ZzIBLFd4HJmlytlabSyeqJFz1RCIDvhIoe9+AEyx3bg==",
|
||||
"path": "protobuf-net/2.4.4",
|
||||
"hashPath": "protobuf-net.2.4.4.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Collections/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-23g6rqftKmovn2cLeGsuHUYm0FD7pdutb0uQMJpZ3qTvq+zHkgmt6J65VtRry4WDGYlmkMa4xDACtaQ94alNag==",
|
||||
"path": "runtime.any.system.collections/4.3.0",
|
||||
"hashPath": "runtime.any.system.collections.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Globalization/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-sMDBnad4rp4t7GY442Jux0MCUuKL4otn5BK6Ni0ARTXTSpRNBzZ7hpMfKSvnVSED5kYJm96YOWsqV0JH0d2uuw==",
|
||||
"path": "runtime.any.system.globalization/4.3.0",
|
||||
"hashPath": "runtime.any.system.globalization.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.IO/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-SDZ5AD1DtyRoxYtEcqQ3HDlcrorMYXZeCt7ZhG9US9I5Vva+gpIWDGMkcwa5XiKL0ceQKRZIX2x0XEjLX7PDzQ==",
|
||||
"path": "runtime.any.system.io/4.3.0",
|
||||
"hashPath": "runtime.any.system.io.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Reflection/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-hLC3A3rI8jipR5d9k7+f0MgRCW6texsAp0MWkN/ci18FMtQ9KH7E2vDn/DH2LkxsszlpJpOn9qy6Z6/69rH6eQ==",
|
||||
"path": "runtime.any.system.reflection/4.3.0",
|
||||
"hashPath": "runtime.any.system.reflection.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Reflection.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-cPhT+Vqu52+cQQrDai/V91gubXUnDKNRvlBnH+hOgtGyHdC17aQIU64EaehwAQymd7kJA5rSrVRNfDYrbhnzyA==",
|
||||
"path": "runtime.any.system.reflection.extensions/4.3.0",
|
||||
"hashPath": "runtime.any.system.reflection.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Reflection.Primitives/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Nrm1p3armp6TTf2xuvaa+jGTTmncALWFq22CpmwRvhDf6dE9ZmH40EbOswD4GnFLrMRS0Ki6Kx5aUPmKK/hZBg==",
|
||||
"path": "runtime.any.system.reflection.primitives/4.3.0",
|
||||
"hashPath": "runtime.any.system.reflection.primitives.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Resources.ResourceManager/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Lxb89SMvf8w9p9+keBLyL6H6x/TEmc6QVsIIA0T36IuyOY3kNvIdyGddA2qt35cRamzxF8K5p0Opq4G4HjNbhQ==",
|
||||
"path": "runtime.any.system.resources.resourcemanager/4.3.0",
|
||||
"hashPath": "runtime.any.system.resources.resourcemanager.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Runtime/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-fRS7zJgaG9NkifaAxGGclDDoRn9HC7hXACl52Or06a/fxdzDajWb5wov3c6a+gVSlekRoexfjwQSK9sh5um5LQ==",
|
||||
"path": "runtime.any.system.runtime/4.3.0",
|
||||
"hashPath": "runtime.any.system.runtime.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Runtime.Handles/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-GG84X6vufoEzqx8PbeBKheE4srOhimv+yLtGb/JkR3Y2FmoqmueLNFU4Xx8Y67plFpltQSdK74x0qlEhIpv/CQ==",
|
||||
"path": "runtime.any.system.runtime.handles/4.3.0",
|
||||
"hashPath": "runtime.any.system.runtime.handles.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Runtime.InteropServices/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-lBoFeQfxe/4eqjPi46E0LU/YaCMdNkQ8B4MZu/mkzdIAZh8RQ1NYZSj0egrQKdgdvlPFtP4STtob40r4o2DBAw==",
|
||||
"path": "runtime.any.system.runtime.interopservices/4.3.0",
|
||||
"hashPath": "runtime.any.system.runtime.interopservices.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Text.Encoding/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==",
|
||||
"path": "runtime.any.system.text.encoding/4.3.0",
|
||||
"hashPath": "runtime.any.system.text.encoding.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Text.Encoding.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-NLrxmLsfRrOuVqPWG+2lrQZnE53MLVeo+w9c54EV+TUo4c8rILpsDXfY8pPiOy9kHpUHHP07ugKmtsU3vVW5Jg==",
|
||||
"path": "runtime.any.system.text.encoding.extensions/4.3.0",
|
||||
"hashPath": "runtime.any.system.text.encoding.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"runtime.any.System.Threading.Tasks/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-OhBAVBQG5kFj1S+hCEQ3TUHBAEtZ3fbEMgZMRNdN8A0Pj4x+5nTELEqL59DU0TjKVE6II3dqKw4Dklb3szT65w==",
|
||||
"path": "runtime.any.system.threading.tasks/4.3.0",
|
||||
"hashPath": "runtime.any.system.threading.tasks.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Collections/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==",
|
||||
"path": "system.collections/4.3.0",
|
||||
"hashPath": "system.collections.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Diagnostics.Debug/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==",
|
||||
"path": "system.diagnostics.debug/4.3.0",
|
||||
"hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Globalization/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==",
|
||||
"path": "system.globalization/4.3.0",
|
||||
"hashPath": "system.globalization.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.IO/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
|
||||
"path": "system.io/4.3.0",
|
||||
"hashPath": "system.io.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.IO.FileSystem/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==",
|
||||
"path": "system.io.filesystem/4.3.0",
|
||||
"hashPath": "system.io.filesystem.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.IO.FileSystem.Primitives/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==",
|
||||
"path": "system.io.filesystem.primitives/4.3.0",
|
||||
"hashPath": "system.io.filesystem.primitives.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Linq/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==",
|
||||
"path": "system.linq/4.3.0",
|
||||
"hashPath": "system.linq.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Private.ServiceModel/4.5.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ancrQgJagx+yC4SZbuE+eShiEAUIF0E1d21TRSoy1C/rTwafAVcBr/fKibkq5TQzyy9uNil2tx2/iaUxsy0S9g==",
|
||||
"path": "system.private.servicemodel/4.5.3",
|
||||
"hashPath": "system.private.servicemodel.4.5.3.nupkg.sha512"
|
||||
},
|
||||
"System.Private.Uri/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-I4SwANiUGho1esj4V4oSlPllXjzCZDE+5XXso2P03LW2vOda2Enzh8DWOxwN6hnrJyp314c7KuVu31QYhRzOGg==",
|
||||
"path": "system.private.uri/4.3.0",
|
||||
"hashPath": "system.private.uri.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
|
||||
"path": "system.reflection/4.3.0",
|
||||
"hashPath": "system.reflection.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.DispatchProxy/4.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-+UW1hq11TNSeb+16rIk8hRQ02o339NFyzMc4ma/FqmxBzM30l1c2IherBB4ld1MNcenS48fz8tbt50OW4rVULA==",
|
||||
"path": "system.reflection.dispatchproxy/4.5.0",
|
||||
"hashPath": "system.reflection.dispatchproxy.4.5.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.Emit/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==",
|
||||
"path": "system.reflection.emit/4.3.0",
|
||||
"hashPath": "system.reflection.emit.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.Emit.ILGeneration/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==",
|
||||
"path": "system.reflection.emit.ilgeneration/4.3.0",
|
||||
"hashPath": "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.Emit.Lightweight/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==",
|
||||
"path": "system.reflection.emit.lightweight/4.3.0",
|
||||
"hashPath": "system.reflection.emit.lightweight.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==",
|
||||
"path": "system.reflection.extensions/4.3.0",
|
||||
"hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.Primitives/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
|
||||
"path": "system.reflection.primitives/4.3.0",
|
||||
"hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Reflection.TypeExtensions/4.4.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-dkmh/ySlwnXJp/1qYP9uyKkCK1CXR/REFzl7abHcArxBcV91mY2CgrrzSRA5Z/X4MevJWwXsklGRdR3A7K9zbg==",
|
||||
"path": "system.reflection.typeextensions/4.4.0",
|
||||
"hashPath": "system.reflection.typeextensions.4.4.0.nupkg.sha512"
|
||||
},
|
||||
"System.Resources.ResourceManager/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==",
|
||||
"path": "system.resources.resourcemanager/4.3.0",
|
||||
"hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||
"path": "system.runtime/4.3.0",
|
||||
"hashPath": "system.runtime.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==",
|
||||
"path": "system.runtime.extensions/4.3.0",
|
||||
"hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.Handles/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==",
|
||||
"path": "system.runtime.handles/4.3.0",
|
||||
"hashPath": "system.runtime.handles.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.InteropServices/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==",
|
||||
"path": "system.runtime.interopservices/4.3.0",
|
||||
"hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.AccessControl/4.4.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-UPF+s4KctOCpk7dpBe3QtZ1AzFEHKdhOtiNDLxGnD/nxKsVbsow5OClTy8c7yOObw/Evbv95xRwZfYZ4wLV4fA==",
|
||||
"path": "system.security.accesscontrol/4.4.0",
|
||||
"hashPath": "system.security.accesscontrol.4.4.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.Principal.Windows/4.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-U77HfRXlZlOeIXd//Yoj6Jnk8AXlbeisf1oq1os+hxOGVnuG+lGSfGqTwTZBoORFF6j/0q7HXIl8cqwQ9aUGqQ==",
|
||||
"path": "system.security.principal.windows/4.5.0",
|
||||
"hashPath": "system.security.principal.windows.4.5.0.nupkg.sha512"
|
||||
},
|
||||
"System.ServiceModel.Primitives/4.5.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Wc9Hgg4Cmqi416zvEgq2sW1YYCGuhwWzspDclJWlFZqY6EGhFUPZU+kVpl5z9kAgrSOQP7/Uiik+PtSQtmq+5A==",
|
||||
"path": "system.servicemodel.primitives/4.5.3",
|
||||
"hashPath": "system.servicemodel.primitives.4.5.3.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
|
||||
"path": "system.text.encoding/4.3.0",
|
||||
"hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==",
|
||||
"path": "system.text.encoding.extensions/4.3.0",
|
||||
"hashPath": "system.text.encoding.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.RegularExpressions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==",
|
||||
"path": "system.text.regularexpressions/4.3.0",
|
||||
"hashPath": "system.text.regularexpressions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Threading/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==",
|
||||
"path": "system.threading/4.3.0",
|
||||
"hashPath": "system.threading.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Threading.Tasks/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
|
||||
"path": "system.threading.tasks/4.3.0",
|
||||
"hashPath": "system.threading.tasks.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Threading.Tasks.Extensions/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-npvJkVKl5rKXrtl1Kkm6OhOUaYGEiF9wFbppFRWSMoApKzt2PiPHT2Bb8a5sAWxprvdOAtvaARS9QYMznEUtug==",
|
||||
"path": "system.threading.tasks.extensions/4.3.0",
|
||||
"hashPath": "system.threading.tasks.extensions.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Xml.ReaderWriter/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==",
|
||||
"path": "system.xml.readerwriter/4.3.0",
|
||||
"hashPath": "system.xml.readerwriter.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Xml.XmlDocument/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==",
|
||||
"path": "system.xml.xmldocument/4.3.0",
|
||||
"hashPath": "system.xml.xmldocument.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Xml.XmlSerializer/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-MYoTCP7EZ98RrANESW05J5ZwskKDoN0AuZ06ZflnowE50LTpbR5yRg3tHckTVm5j/m47stuGgCrCHWePyHS70Q==",
|
||||
"path": "system.xml.xmlserializer/4.3.0",
|
||||
"hashPath": "system.xml.xmlserializer.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"SteamKit2/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "netcoreapp2.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"rollForwardOnNoCandidateFx": 2
|
||||
}
|
||||
}
|
||||
22
depotdownloader/DepotDownloader.sln
Normal file
22
depotdownloader/DepotDownloader.sln
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
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
|
||||
90
depotdownloader/DepotDownloader/AccountSettingsStore.cs
Normal file
90
depotdownloader/DepotDownloader/AccountSettingsStore.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ProtoBuf;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Linq;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Discovery;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract]
|
||||
class AccountSettingsStore
|
||||
{
|
||||
[ProtoMember(1, IsRequired=false)]
|
||||
public Dictionary<string, byte[]> SentryData { get; private set; }
|
||||
|
||||
[ProtoMember(2, IsRequired = false)]
|
||||
public System.Collections.Concurrent.ConcurrentDictionary<string, int> ContentServerPenalty { get; private set; }
|
||||
|
||||
[ProtoMember(3, IsRequired = false)]
|
||||
public Dictionary<string, string> LoginKeys { get; private set; }
|
||||
|
||||
string FileName = null;
|
||||
|
||||
AccountSettingsStore()
|
||||
{
|
||||
SentryData = new Dictionary<string, byte[]>();
|
||||
ContentServerPenalty = new System.Collections.Concurrent.ConcurrentDictionary<string, int>();
|
||||
LoginKeys = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
static bool Loaded
|
||||
{
|
||||
get { return Instance != null; }
|
||||
}
|
||||
|
||||
public static AccountSettingsStore Instance = null;
|
||||
static readonly IsolatedStorageFile IsolatedStorage = IsolatedStorageFile.GetUserStoreForAssembly();
|
||||
|
||||
public static void LoadFromFile(string filename)
|
||||
{
|
||||
if (Loaded)
|
||||
throw new Exception("Config already loaded");
|
||||
|
||||
if (IsolatedStorage.FileExists(filename))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
{
|
||||
Instance = ProtoBuf.Serializer.Deserialize<AccountSettingsStore>(ds);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("Failed to load account settings: {0}", ex.Message);
|
||||
Instance = new AccountSettingsStore();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = new AccountSettingsStore();
|
||||
}
|
||||
|
||||
Instance.FileName = filename;
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
if (!Loaded)
|
||||
throw new Exception("Saved config before loading");
|
||||
|
||||
try
|
||||
{
|
||||
using (var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
{
|
||||
ProtoBuf.Serializer.Serialize<AccountSettingsStore>(ds, Instance);
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
Console.WriteLine("Failed to save account settings: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
196
depotdownloader/DepotDownloader/CDNClientPool.cs
Normal file
196
depotdownloader/DepotDownloader/CDNClientPool.cs
Normal file
@@ -0,0 +1,196 @@
|
||||
using SteamKit2;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
/// <summary>
|
||||
/// CDNClientPool provides a pool of connections to CDN endpoints, requesting CDN tokens as needed
|
||||
/// </summary>
|
||||
class CDNClientPool
|
||||
{
|
||||
private const int ServerEndpointMinimumSize = 8;
|
||||
|
||||
private readonly Steam3Session steamSession;
|
||||
private readonly uint appId;
|
||||
public CDNClient CDNClient { get; }
|
||||
#if STEAMKIT_UNRELEASED
|
||||
public CDNClient.Server ProxyServer { get; private set; }
|
||||
#endif
|
||||
|
||||
private readonly ConcurrentStack<CDNClient.Server> activeConnectionPool;
|
||||
private readonly BlockingCollection<CDNClient.Server> availableServerEndpoints;
|
||||
|
||||
private readonly AutoResetEvent populatePoolEvent;
|
||||
private readonly Task monitorTask;
|
||||
private readonly CancellationTokenSource shutdownToken;
|
||||
public CancellationTokenSource ExhaustedToken { get; set; }
|
||||
|
||||
public CDNClientPool(Steam3Session steamSession, uint appId)
|
||||
{
|
||||
this.steamSession = steamSession;
|
||||
this.appId = appId;
|
||||
CDNClient = new CDNClient(steamSession.steamClient);
|
||||
|
||||
activeConnectionPool = new ConcurrentStack<CDNClient.Server>();
|
||||
availableServerEndpoints = new BlockingCollection<CDNClient.Server>();
|
||||
|
||||
populatePoolEvent = new AutoResetEvent(true);
|
||||
shutdownToken = new CancellationTokenSource();
|
||||
|
||||
monitorTask = Task.Factory.StartNew(ConnectionPoolMonitorAsync).Unwrap();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
shutdownToken.Cancel();
|
||||
monitorTask.Wait();
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyCollection<CDNClient.Server>> FetchBootstrapServerListAsync()
|
||||
{
|
||||
var backoffDelay = 0;
|
||||
|
||||
while (!shutdownToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cdnServers = await ContentServerDirectoryService.LoadAsync(this.steamSession.steamClient.Configuration, ContentDownloader.Config.CellID, shutdownToken.Token);
|
||||
if (cdnServers != null)
|
||||
{
|
||||
return cdnServers;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Failed to retrieve content server list: {0}", ex.Message);
|
||||
|
||||
if (ex is SteamKitWebRequestException e && e.StatusCode == (HttpStatusCode)429)
|
||||
{
|
||||
// If we're being throttled, add a delay to the next request
|
||||
backoffDelay = Math.Min(5, ++backoffDelay);
|
||||
await Task.Delay(TimeSpan.FromSeconds(backoffDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task ConnectionPoolMonitorAsync()
|
||||
{
|
||||
bool didPopulate = false;
|
||||
|
||||
while (!shutdownToken.IsCancellationRequested)
|
||||
{
|
||||
populatePoolEvent.WaitOne(TimeSpan.FromSeconds(1));
|
||||
|
||||
// We want the Steam session so we can take the CellID from the session and pass it through to the ContentServer Directory Service
|
||||
if (availableServerEndpoints.Count < ServerEndpointMinimumSize && steamSession.steamClient.IsConnected)
|
||||
{
|
||||
var servers = await FetchBootstrapServerListAsync().ConfigureAwait(false);
|
||||
|
||||
if (servers == null || servers.Count == 0)
|
||||
{
|
||||
ExhaustedToken?.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
#if STEAMKIT_UNRELEASED
|
||||
ProxyServer = servers.Where(x => x.UseAsProxy).FirstOrDefault();
|
||||
#endif
|
||||
|
||||
var weightedCdnServers = servers
|
||||
.Where(server =>
|
||||
{
|
||||
#if STEAMKIT_UNRELEASED
|
||||
var isEligibleForApp = server.AllowedAppIds == null || server.AllowedAppIds.Contains(appId);
|
||||
return isEligibleForApp && (server.Type == "SteamCache" || server.Type == "CDN");
|
||||
#else
|
||||
return server.Type == "SteamCache" || server.Type == "CDN";
|
||||
#endif
|
||||
})
|
||||
.Select(server =>
|
||||
{
|
||||
AccountSettingsStore.Instance.ContentServerPenalty.TryGetValue(server.Host, out var penalty);
|
||||
|
||||
return (server, penalty);
|
||||
})
|
||||
.OrderBy(pair => pair.penalty).ThenBy(pair => pair.server.WeightedLoad);
|
||||
|
||||
foreach (var (server, weight) in weightedCdnServers)
|
||||
{
|
||||
for (var i = 0; i < server.NumEntries; i++)
|
||||
{
|
||||
availableServerEndpoints.Add(server);
|
||||
}
|
||||
}
|
||||
|
||||
didPopulate = true;
|
||||
}
|
||||
else if (availableServerEndpoints.Count == 0 && !steamSession.steamClient.IsConnected && didPopulate)
|
||||
{
|
||||
ExhaustedToken?.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CDNClient.Server BuildConnection(CancellationToken token)
|
||||
{
|
||||
if (availableServerEndpoints.Count < ServerEndpointMinimumSize)
|
||||
{
|
||||
populatePoolEvent.Set();
|
||||
}
|
||||
|
||||
return availableServerEndpoints.Take(token);
|
||||
}
|
||||
|
||||
public CDNClient.Server GetConnection(CancellationToken token)
|
||||
{
|
||||
if (!activeConnectionPool.TryPop(out var connection))
|
||||
{
|
||||
connection = BuildConnection(token);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
public async Task<string> AuthenticateConnection(uint appId, uint depotId, CDNClient.Server server)
|
||||
{
|
||||
var host = steamSession.ResolveCDNTopLevelHost(server.Host);
|
||||
var cdnKey = $"{depotId:D}:{host}";
|
||||
|
||||
steamSession.RequestCDNAuthToken(appId, depotId, host, cdnKey);
|
||||
|
||||
if (steamSession.CDNAuthTokens.TryGetValue(cdnKey, out var authTokenCallbackPromise))
|
||||
{
|
||||
var result = await authTokenCallbackPromise.Task;
|
||||
return result.Token;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Failed to retrieve CDN token for server {server.Host} depot {depotId}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ReturnConnection(CDNClient.Server server)
|
||||
{
|
||||
if (server == null) return;
|
||||
|
||||
activeConnectionPool.Push(server);
|
||||
}
|
||||
|
||||
public void ReturnBrokenConnection(CDNClient.Server server)
|
||||
{
|
||||
if (server == null) return;
|
||||
|
||||
// Broken connections are not returned to the pool
|
||||
}
|
||||
}
|
||||
}
|
||||
1296
depotdownloader/DepotDownloader/ContentDownloader.cs
Normal file
1296
depotdownloader/DepotDownloader/ContentDownloader.cs
Normal file
File diff suppressed because it is too large
Load Diff
58
depotdownloader/DepotDownloader/DepotConfigStore.cs
Normal file
58
depotdownloader/DepotDownloader/DepotConfigStore.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ProtoBuf;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract]
|
||||
class DepotConfigStore
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public Dictionary<uint, ulong> InstalledManifestIDs { get; private set; }
|
||||
|
||||
string FileName = null;
|
||||
|
||||
DepotConfigStore()
|
||||
{
|
||||
InstalledManifestIDs = new Dictionary<uint, ulong>();
|
||||
}
|
||||
|
||||
static bool Loaded
|
||||
{
|
||||
get { return Instance != null; }
|
||||
}
|
||||
|
||||
public static DepotConfigStore Instance = null;
|
||||
|
||||
public static void LoadFromFile(string filename)
|
||||
{
|
||||
if (Loaded)
|
||||
throw new Exception("Config already loaded");
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
using (FileStream fs = File.Open(filename, FileMode.Open))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
Instance = ProtoBuf.Serializer.Deserialize<DepotConfigStore>(ds);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance = new DepotConfigStore();
|
||||
}
|
||||
|
||||
Instance.FileName = filename;
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
if (!Loaded)
|
||||
throw new Exception("Saved config before loading");
|
||||
|
||||
using (FileStream fs = File.Open(Instance.FileName, FileMode.Create))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
ProtoBuf.Serializer.Serialize<DepotConfigStore>(ds, Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
depotdownloader/DepotDownloader/DepotDownloader.csproj
Normal file
11
depotdownloader/DepotDownloader/DepotDownloader.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="protobuf-net" Version="3.0.73" />
|
||||
<PackageReference Include="SteamKit2" Version="2.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
33
depotdownloader/DepotDownloader/DownloadConfig.cs
Normal file
33
depotdownloader/DepotDownloader/DownloadConfig.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
class DownloadConfig
|
||||
{
|
||||
public int CellID { get; set; }
|
||||
public bool DownloadAllPlatforms { get; set; }
|
||||
public bool DownloadAllLanguages { get; set; }
|
||||
public bool DownloadManifestOnly { get; set; }
|
||||
public string InstallDirectory { get; set; }
|
||||
|
||||
public bool UsingFileList { get; set; }
|
||||
public List<string> FilesToDownload { get; set; }
|
||||
public List<Regex> FilesToDownloadRegex { get; set; }
|
||||
|
||||
public bool UsingExclusionList { get; set; }
|
||||
|
||||
public string BetaPassword { get; set; }
|
||||
|
||||
public bool VerifyAll { get; set; }
|
||||
|
||||
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
|
||||
public uint? LoginID {get; set; }
|
||||
}
|
||||
}
|
||||
416
depotdownloader/DepotDownloader/Program.cs
Normal file
416
depotdownloader/DepotDownloader/Program.cs
Normal file
@@ -0,0 +1,416 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using SteamKit2;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main( string[] args )
|
||||
=> MainAsync( args ).GetAwaiter().GetResult();
|
||||
|
||||
static async Task<int> 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 );
|
||||
});
|
||||
}
|
||||
|
||||
string username = GetParameter<string>( args, "-username" ) ?? GetParameter<string>( args, "-user" );
|
||||
string password = GetParameter<string>( args, "-password" ) ?? GetParameter<string>( args, "-pass" );
|
||||
ContentDownloader.Config.RememberPassword = HasParameter( args, "-remember-password" );
|
||||
|
||||
ContentDownloader.Config.DownloadManifestOnly = HasParameter( args, "-manifest-only" );
|
||||
|
||||
int cellId = GetParameter<int>( args, "-cellid", -1 );
|
||||
if ( cellId == -1 )
|
||||
{
|
||||
cellId = 0;
|
||||
}
|
||||
|
||||
ContentDownloader.Config.CellID = cellId;
|
||||
|
||||
string fileList = GetParameter<string>( args, "-filelist" );
|
||||
string[] files = null;
|
||||
|
||||
if ( fileList != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileListData = File.ReadAllText(fileList);
|
||||
files = fileListData.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries );
|
||||
|
||||
ContentDownloader.Config.UsingFileList = true;
|
||||
ContentDownloader.Config.FilesToDownload = new List<string>();
|
||||
ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>();
|
||||
|
||||
var isWindows = RuntimeInformation.IsOSPlatform( OSPlatform.Windows );
|
||||
foreach ( var fileEntry in files )
|
||||
{
|
||||
try
|
||||
{
|
||||
string fileEntryProcessed;
|
||||
if ( isWindows )
|
||||
{
|
||||
// On Windows, ensure that forward slashes can match either forward or backslashes in depot paths
|
||||
fileEntryProcessed = fileEntry.Replace( "/", "[\\\\|/]" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// On other systems, treat / normally
|
||||
fileEntryProcessed = fileEntry;
|
||||
}
|
||||
Regex rgx = new Regex( fileEntryProcessed, RegexOptions.Compiled | RegexOptions.IgnoreCase );
|
||||
ContentDownloader.Config.FilesToDownloadRegex.Add( rgx );
|
||||
}
|
||||
catch
|
||||
{
|
||||
// For anything that can't be processed as a Regex, allow both forward and backward slashes to match
|
||||
// on Windows
|
||||
if( isWindows )
|
||||
{
|
||||
ContentDownloader.Config.FilesToDownload.Add( fileEntry.Replace( "/", "\\" ) );
|
||||
}
|
||||
ContentDownloader.Config.FilesToDownload.Add( fileEntry );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine( "Using filelist: '{0}'.", fileList );
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine( "Warning: Unable to load filelist: {0}", ex.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
ContentDownloader.Config.InstallDirectory = GetParameter<string>( args, "-dir" );
|
||||
|
||||
ContentDownloader.Config.VerifyAll = HasParameter( args, "-verify-all" ) || HasParameter( args, "-verify_all" ) || HasParameter( args, "-validate" );
|
||||
ContentDownloader.Config.MaxServers = GetParameter<int>( args, "-max-servers", 20 );
|
||||
ContentDownloader.Config.MaxDownloads = GetParameter<int>( args, "-max-downloads", 8 );
|
||||
ContentDownloader.Config.MaxServers = Math.Max( ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads );
|
||||
ContentDownloader.Config.LoginID = HasParameter( args, "-loginid" ) ? (uint?)GetParameter<uint>( args, "-loginid" ) : null;
|
||||
|
||||
#endregion
|
||||
|
||||
uint appId = GetParameter<uint>( args, "-app", ContentDownloader.INVALID_APP_ID );
|
||||
if ( appId == ContentDownloader.INVALID_APP_ID )
|
||||
{
|
||||
Console.WriteLine( "Error: -app not specified!" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
ulong pubFile = GetParameter<ulong>( args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID );
|
||||
ulong ugcId = GetParameter<ulong>( 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
|
||||
|
||||
string branch = GetParameter<string>( args, "-branch" ) ?? GetParameter<string>( args, "-beta" ) ?? ContentDownloader.DEFAULT_BRANCH;
|
||||
ContentDownloader.Config.BetaPassword = GetParameter<string>( args, "-betapassword" );
|
||||
|
||||
ContentDownloader.Config.DownloadAllPlatforms = HasParameter( args, "-all-platforms" );
|
||||
string os = GetParameter<string>( args, "-os", null );
|
||||
|
||||
if ( ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty( os ) )
|
||||
{
|
||||
Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
string arch = GetParameter<string>( args, "-osarch", null );
|
||||
|
||||
ContentDownloader.Config.DownloadAllLanguages = HasParameter( args, "-all-languages" );
|
||||
string language = GetParameter<string>( args, "-language", null );
|
||||
|
||||
if ( ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty( language ) )
|
||||
{
|
||||
Console.WriteLine( "Error: Cannot specify -language when -all-languages is specified." );
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool lv = HasParameter( args, "-lowviolence" );
|
||||
|
||||
List<(uint, ulong)> depotManifestIds = new List<(uint, ulong)>();
|
||||
bool isUGC = false;
|
||||
|
||||
List<uint> depotIdList = GetParameterList<uint>( args, "-depot" );
|
||||
List<ulong> manifestIdList = GetParameterList<ulong>( args, "-manifest" );
|
||||
if ( manifestIdList.Count > 0 )
|
||||
{
|
||||
if ( depotIdList.Count != manifestIdList.Count )
|
||||
{
|
||||
Console.WriteLine( "Error: -manifest requires one id for every -depot specified" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
var zippedDepotManifest = depotIdList.Zip( manifestIdList, ( depotId, manifestId ) => ( depotId, manifestId ) );
|
||||
depotManifestIds.AddRange( zippedDepotManifest );
|
||||
}
|
||||
else
|
||||
{
|
||||
depotManifestIds.AddRange( depotIdList.Select( depotId => ( depotId, ContentDownloader.INVALID_MANIFEST_ID ) ) );
|
||||
}
|
||||
|
||||
if ( InitializeSteam( username, password ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
await ContentDownloader.DownloadAppAsync( appId, depotManifestIds, branch, os, arch, language, lv, isUGC ).ConfigureAwait( false );
|
||||
}
|
||||
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 ( username != null && password == null && ( !ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.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." );
|
||||
}
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
static int IndexOfParam( string[] args, string param )
|
||||
{
|
||||
for ( int 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<T>( string[] args, string param, T defaultValue = default( T ) )
|
||||
{
|
||||
int index = IndexOfParam( args, param );
|
||||
|
||||
if ( index == -1 || index == ( args.Length - 1 ) )
|
||||
return defaultValue;
|
||||
|
||||
string strParam = args[ index + 1 ];
|
||||
|
||||
var converter = TypeDescriptor.GetConverter( typeof( T ) );
|
||||
if ( converter != null )
|
||||
{
|
||||
return ( T )converter.ConvertFromString( strParam );
|
||||
}
|
||||
|
||||
return default( T );
|
||||
}
|
||||
|
||||
static List<T> GetParameterList<T>(string[] args, string param)
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
int index = IndexOfParam(args, param);
|
||||
|
||||
if (index == -1 || index == (args.Length - 1))
|
||||
return list;
|
||||
|
||||
index++;
|
||||
|
||||
while (index < args.Length)
|
||||
{
|
||||
string 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 <id> [-depot <id> [-manifest <id>]]" );
|
||||
Console.WriteLine( "\t\t[-username <username> [-password <password>]] [other options]" );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Usage - downloading a workshop item using pubfile id");
|
||||
Console.WriteLine( "\tdepotdownloader -app <id> -pubfile <id> [-username <username> [-password <password>]]" );
|
||||
Console.WriteLine("Usage - downloading a workshop item using ugc id");
|
||||
Console.WriteLine("\tdepotdownloader -app <id> -ugc <id> [-username <username> [-password <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 <id>\t\t\t- manifest id of content to download (requires -depot, default: current for branch)." );
|
||||
Console.WriteLine( "\t-beta <branchname>\t\t\t- download from specified branch if available (default: Public)." );
|
||||
Console.WriteLine( "\t-betapassword <pass>\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 <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 <arch>\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 <lang>\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 <user>\t\t- the username of the account to login to for restricted content.");
|
||||
Console.WriteLine( "\t-password <pass>\t\t- the password of the account to login to for restricted content." );
|
||||
Console.WriteLine( "\t-remember-password\t\t- if set, remember the password for subsequent logins of this user." );
|
||||
Console.WriteLine();
|
||||
Console.WriteLine( "\t-dir <installdir>\t\t- the directory in which to place downloaded files." );
|
||||
Console.WriteLine( "\t-filelist <file.txt>\t- a list of files to download (from the manifest). Can optionally use regex to download only certain files." );
|
||||
Console.WriteLine( "\t-validate\t\t\t\t- Include checksum verification of files already downloaded" );
|
||||
Console.WriteLine();
|
||||
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." );
|
||||
}
|
||||
}
|
||||
}
|
||||
34
depotdownloader/DepotDownloader/Properties/AssemblyInfo.cs
Normal file
34
depotdownloader/DepotDownloader/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle( "DepotDownloader" )]
|
||||
[assembly: AssemblyDescription("Steam Downloading Utility")]
|
||||
[assembly: AssemblyConfiguration( "" )]
|
||||
[assembly: AssemblyCompany("SteamRE Team")]
|
||||
[assembly: AssemblyProduct( "DepotDownloader" )]
|
||||
[assembly: AssemblyCopyright("Copyright © SteamRE Team 2017")]
|
||||
[assembly: AssemblyTrademark( "" )]
|
||||
[assembly: AssemblyCulture( "" )]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible( false )]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid( "df2ab32a-923c-46e3-a1b4-c901ee92ec94" )]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("2.3.0.0")]
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"profiles": {
|
||||
"DepotDownloader": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
163
depotdownloader/DepotDownloader/ProtoManifest.cs
Normal file
163
depotdownloader/DepotDownloader/ProtoManifest.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
using ProtoBuf;
|
||||
using SteamKit2;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
[ProtoContract()]
|
||||
class ProtoManifest
|
||||
{
|
||||
// Proto ctor
|
||||
private ProtoManifest()
|
||||
{
|
||||
Files = new List<FileData>();
|
||||
}
|
||||
|
||||
public ProtoManifest(DepotManifest sourceManifest, ulong id) : this()
|
||||
{
|
||||
sourceManifest.Files.ForEach(f => Files.Add(new FileData(f)));
|
||||
ID = id;
|
||||
CreationTime = sourceManifest.CreationTime;
|
||||
}
|
||||
|
||||
[ProtoContract()]
|
||||
public class FileData
|
||||
{
|
||||
// Proto ctor
|
||||
private FileData()
|
||||
{
|
||||
Chunks = new List<ChunkData>();
|
||||
}
|
||||
|
||||
public FileData(DepotManifest.FileData sourceData) : this()
|
||||
{
|
||||
FileName = sourceData.FileName;
|
||||
sourceData.Chunks.ForEach(c => Chunks.Add(new ChunkData(c)));
|
||||
Flags = sourceData.Flags;
|
||||
TotalSize = sourceData.TotalSize;
|
||||
FileHash = sourceData.FileHash;
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public string FileName { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunks that this file is composed of.
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public List<ChunkData> Chunks { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file flags
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public EDepotFileFlag Flags { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total size of this file.
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public ulong TotalSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash of this file.
|
||||
/// </summary>
|
||||
[ProtoMember(5)]
|
||||
public byte[] FileHash { get; private set; }
|
||||
}
|
||||
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class ChunkData
|
||||
{
|
||||
public ChunkData(DepotManifest.ChunkData sourceChunk)
|
||||
{
|
||||
ChunkID = sourceChunk.ChunkID;
|
||||
Checksum = sourceChunk.Checksum;
|
||||
Offset = sourceChunk.Offset;
|
||||
CompressedLength = sourceChunk.CompressedLength;
|
||||
UncompressedLength = sourceChunk.UncompressedLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SHA-1 hash chunk id.
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public byte[] ChunkID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the expected Adler32 checksum of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public byte[] Checksum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chunk offset.
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public ulong Offset { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compressed length of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public uint CompressedLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the decompressed length of this chunk.
|
||||
/// </summary>
|
||||
[ProtoMember(5)]
|
||||
public uint UncompressedLength { get; private set; }
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public List<FileData> Files { get; private set; }
|
||||
|
||||
[ProtoMember(2)]
|
||||
public ulong ID { get; private set; }
|
||||
|
||||
[ProtoMember(3)]
|
||||
public DateTime CreationTime { get; private set; }
|
||||
|
||||
public static ProtoManifest LoadFromFile(string filename, out byte[] checksum)
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
checksum = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
using (FileStream fs = File.Open(filename, FileMode.Open))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
|
||||
ds.CopyTo(ms);
|
||||
|
||||
checksum = Util.SHAHash(ms.ToArray());
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
return ProtoBuf.Serializer.Deserialize<ProtoManifest>(ms);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveToFile(string filename, out byte[] checksum)
|
||||
{
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
ProtoBuf.Serializer.Serialize<ProtoManifest>(ms, this);
|
||||
|
||||
checksum = Util.SHAHash(ms.ToArray());
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (FileStream fs = File.Open(filename, FileMode.Create))
|
||||
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
|
||||
ms.CopyTo(ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
771
depotdownloader/DepotDownloader/Steam3Session.cs
Normal file
771
depotdownloader/DepotDownloader/Steam3Session.cs
Normal file
@@ -0,0 +1,771 @@
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
|
||||
class Steam3Session
|
||||
{
|
||||
public class Credentials
|
||||
{
|
||||
public bool LoggedOn { get; set; }
|
||||
public ulong SessionToken { get; set; }
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get { return LoggedOn; }
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<SteamApps.LicenseListCallback.License> Licenses
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Dictionary<uint, byte[]> AppTickets { get; private set; }
|
||||
public Dictionary<uint, ulong> AppTokens { get; private set; }
|
||||
public Dictionary<uint, ulong> PackageTokens { get; private set; }
|
||||
public Dictionary<uint, byte[]> DepotKeys { get; private set; }
|
||||
public ConcurrentDictionary<string, TaskCompletionSource<SteamApps.CDNAuthTokenCallback>> CDNAuthTokens { get; private set; }
|
||||
public Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo> AppInfo { get; private set; }
|
||||
public Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo> PackageInfo { get; private set; }
|
||||
public Dictionary<string, byte[]> AppBetaPasswords { get; private set; }
|
||||
|
||||
public SteamClient steamClient;
|
||||
public SteamUser steamUser;
|
||||
SteamApps steamApps;
|
||||
SteamCloud steamCloud;
|
||||
SteamUnifiedMessages.UnifiedService<IPublishedFile> steamPublishedFile;
|
||||
|
||||
CallbackManager callbacks;
|
||||
|
||||
bool authenticatedUser;
|
||||
bool bConnected;
|
||||
bool bConnecting;
|
||||
bool bAborted;
|
||||
bool bExpectingDisconnectRemote;
|
||||
bool bDidDisconnect;
|
||||
bool bDidReceiveLoginKey;
|
||||
int connectionBackoff;
|
||||
int seq; // more hack fixes
|
||||
DateTime connectTime;
|
||||
|
||||
// input
|
||||
SteamUser.LogOnDetails logonDetails;
|
||||
|
||||
// output
|
||||
Credentials credentials;
|
||||
|
||||
static readonly TimeSpan STEAM3_TIMEOUT = TimeSpan.FromSeconds( 30 );
|
||||
|
||||
|
||||
public Steam3Session( SteamUser.LogOnDetails details )
|
||||
{
|
||||
this.logonDetails = details;
|
||||
|
||||
this.authenticatedUser = details.Username != null;
|
||||
this.credentials = new Credentials();
|
||||
this.bConnected = false;
|
||||
this.bConnecting = false;
|
||||
this.bAborted = false;
|
||||
this.bExpectingDisconnectRemote = false;
|
||||
this.bDidDisconnect = false;
|
||||
this.bDidReceiveLoginKey = false;
|
||||
this.seq = 0;
|
||||
|
||||
this.AppTickets = new Dictionary<uint, byte[]>();
|
||||
this.AppTokens = new Dictionary<uint, ulong>();
|
||||
this.PackageTokens = new Dictionary<uint, ulong>();
|
||||
this.DepotKeys = new Dictionary<uint, byte[]>();
|
||||
this.CDNAuthTokens = new ConcurrentDictionary<string, TaskCompletionSource<SteamApps.CDNAuthTokenCallback>>();
|
||||
this.AppInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
|
||||
this.PackageInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
|
||||
this.AppBetaPasswords = new Dictionary<string, byte[]>();
|
||||
|
||||
this.steamClient = new SteamClient();
|
||||
|
||||
this.steamUser = this.steamClient.GetHandler<SteamUser>();
|
||||
this.steamApps = this.steamClient.GetHandler<SteamApps>();
|
||||
this.steamCloud = this.steamClient.GetHandler<SteamCloud>();
|
||||
var steamUnifiedMessages = this.steamClient.GetHandler<SteamUnifiedMessages>();
|
||||
this.steamPublishedFile = steamUnifiedMessages.CreateService<IPublishedFile>();
|
||||
|
||||
this.callbacks = new CallbackManager( this.steamClient );
|
||||
|
||||
this.callbacks.Subscribe<SteamClient.ConnectedCallback>( ConnectedCallback );
|
||||
this.callbacks.Subscribe<SteamClient.DisconnectedCallback>( DisconnectedCallback );
|
||||
this.callbacks.Subscribe<SteamUser.LoggedOnCallback>( LogOnCallback );
|
||||
this.callbacks.Subscribe<SteamUser.SessionTokenCallback>( SessionTokenCallback );
|
||||
this.callbacks.Subscribe<SteamApps.LicenseListCallback>( LicenseListCallback );
|
||||
this.callbacks.Subscribe<SteamUser.UpdateMachineAuthCallback>( UpdateMachineAuthCallback );
|
||||
this.callbacks.Subscribe<SteamUser.LoginKeyCallback>( LoginKeyCallback );
|
||||
|
||||
Console.Write( "Connecting to Steam3..." );
|
||||
|
||||
if ( authenticatedUser )
|
||||
{
|
||||
FileInfo fi = new FileInfo( String.Format( "{0}.sentryFile", logonDetails.Username ) );
|
||||
if ( AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey( logonDetails.Username ) )
|
||||
{
|
||||
logonDetails.SentryFileHash = Util.SHAHash( AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] );
|
||||
}
|
||||
else if ( fi.Exists && fi.Length > 0 )
|
||||
{
|
||||
var sentryData = File.ReadAllBytes( fi.FullName );
|
||||
logonDetails.SentryFileHash = Util.SHAHash( sentryData );
|
||||
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = sentryData;
|
||||
AccountSettingsStore.Save();
|
||||
}
|
||||
}
|
||||
|
||||
Connect();
|
||||
}
|
||||
|
||||
public delegate bool WaitCondition();
|
||||
private object steamLock = new object();
|
||||
|
||||
public bool WaitUntilCallback( Action submitter, WaitCondition waiter )
|
||||
{
|
||||
while ( !bAborted && !waiter() )
|
||||
{
|
||||
lock (steamLock)
|
||||
{
|
||||
submitter();
|
||||
}
|
||||
|
||||
int seq = this.seq;
|
||||
do
|
||||
{
|
||||
lock (steamLock)
|
||||
{
|
||||
WaitForCallbacks();
|
||||
}
|
||||
}
|
||||
while ( !bAborted && this.seq == seq && !waiter() );
|
||||
}
|
||||
|
||||
return bAborted;
|
||||
}
|
||||
|
||||
public Credentials WaitForCredentials()
|
||||
{
|
||||
if ( credentials.IsValid || bAborted )
|
||||
return credentials;
|
||||
|
||||
WaitUntilCallback( () => { }, () => { return credentials.IsValid; } );
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
public void RequestAppInfo( uint appId, bool bForce = false )
|
||||
{
|
||||
if ( ( AppInfo.ContainsKey( appId ) && !bForce ) || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.PICSTokensCallback> cbMethodTokens = ( appTokens ) =>
|
||||
{
|
||||
completed = true;
|
||||
if ( appTokens.AppTokensDenied.Contains( appId ) )
|
||||
{
|
||||
Console.WriteLine( "Insufficient privileges to get access token for app {0}", appId );
|
||||
}
|
||||
|
||||
foreach ( var token_dict in appTokens.AppTokens )
|
||||
{
|
||||
this.AppTokens[ token_dict.Key ] = token_dict.Value;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetAccessTokens( new List<uint>() { appId }, new List<uint>() { } ), cbMethodTokens );
|
||||
}, () => { return completed; } );
|
||||
|
||||
completed = false;
|
||||
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( appInfo ) =>
|
||||
{
|
||||
completed = !appInfo.ResponsePending;
|
||||
|
||||
foreach ( var app_value in appInfo.Apps )
|
||||
{
|
||||
var app = app_value.Value;
|
||||
|
||||
Console.WriteLine( "Got AppInfo for {0}", app.ID );
|
||||
AppInfo[ app.ID ] = app;
|
||||
}
|
||||
|
||||
foreach ( var app in appInfo.UnknownApps )
|
||||
{
|
||||
AppInfo[ app ] = null;
|
||||
}
|
||||
};
|
||||
|
||||
SteamApps.PICSRequest request = new SteamApps.PICSRequest( appId );
|
||||
if ( AppTokens.ContainsKey( appId ) )
|
||||
{
|
||||
request.AccessToken = AppTokens[ appId ];
|
||||
request.Public = false;
|
||||
}
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>() { request }, new List<SteamApps.PICSRequest>() { } ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public void RequestPackageInfo( IEnumerable<uint> packageIds )
|
||||
{
|
||||
List<uint> packages = packageIds.ToList();
|
||||
packages.RemoveAll( pid => PackageInfo.ContainsKey( pid ) );
|
||||
|
||||
if ( packages.Count == 0 || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( packageInfo ) =>
|
||||
{
|
||||
completed = !packageInfo.ResponsePending;
|
||||
|
||||
foreach ( var package_value in packageInfo.Packages )
|
||||
{
|
||||
var package = package_value.Value;
|
||||
PackageInfo[ package.ID ] = package;
|
||||
}
|
||||
|
||||
foreach ( var package in packageInfo.UnknownPackages )
|
||||
{
|
||||
PackageInfo[package] = null;
|
||||
}
|
||||
};
|
||||
|
||||
var packageRequests = new List<SteamApps.PICSRequest>();
|
||||
|
||||
foreach ( var package in packages )
|
||||
{
|
||||
var request = new SteamApps.PICSRequest( package );
|
||||
|
||||
if ( PackageTokens.TryGetValue( package, out var token ) )
|
||||
{
|
||||
request.AccessToken = token;
|
||||
request.Public = false;
|
||||
}
|
||||
|
||||
packageRequests.Add( request );
|
||||
}
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>(), packageRequests ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public bool RequestFreeAppLicense( uint appId )
|
||||
{
|
||||
bool success = false;
|
||||
bool completed = false;
|
||||
Action<SteamApps.FreeLicenseCallback> cbMethod = ( resultInfo ) =>
|
||||
{
|
||||
completed = true;
|
||||
success = resultInfo.GrantedApps.Contains( appId );
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.RequestFreeLicense( appId ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void RequestAppTicket( uint appId )
|
||||
{
|
||||
if ( AppTickets.ContainsKey( appId ) || bAborted )
|
||||
return;
|
||||
|
||||
|
||||
if ( !authenticatedUser )
|
||||
{
|
||||
AppTickets[ appId ] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
bool completed = false;
|
||||
Action<SteamApps.AppOwnershipTicketCallback> cbMethod = ( appTicket ) =>
|
||||
{
|
||||
completed = true;
|
||||
|
||||
if ( appTicket.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to get appticket for {0}: {1}", appTicket.AppID, appTicket.Result );
|
||||
Abort();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Got appticket for {0}!", appTicket.AppID );
|
||||
AppTickets[ appTicket.AppID ] = appTicket.Ticket;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetAppOwnershipTicket( appId ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public void RequestDepotKey( uint depotId, uint appid = 0 )
|
||||
{
|
||||
if ( DepotKeys.ContainsKey( depotId ) || bAborted )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
|
||||
Action<SteamApps.DepotKeyCallback> cbMethod = ( depotKey ) =>
|
||||
{
|
||||
completed = true;
|
||||
Console.WriteLine( "Got depot key for {0} result: {1}", depotKey.DepotID, depotKey.Result );
|
||||
|
||||
if ( depotKey.Result != EResult.OK )
|
||||
{
|
||||
Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
DepotKeys[ depotKey.DepotID ] = depotKey.DepotKey;
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetDepotDecryptionKey( depotId, appid ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public string ResolveCDNTopLevelHost(string host)
|
||||
{
|
||||
// SteamPipe CDN shares tokens with all hosts
|
||||
if (host.EndsWith( ".steampipe.steamcontent.com" ) )
|
||||
{
|
||||
return "steampipe.steamcontent.com";
|
||||
}
|
||||
else if (host.EndsWith(".steamcontent.com"))
|
||||
{
|
||||
return "steamcontent.com";
|
||||
}
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
public void RequestCDNAuthToken( uint appid, uint depotid, string host, string cdnKey )
|
||||
{
|
||||
if ( CDNAuthTokens.ContainsKey( cdnKey ) || bAborted )
|
||||
return;
|
||||
|
||||
if ( !CDNAuthTokens.TryAdd( cdnKey, new TaskCompletionSource<SteamApps.CDNAuthTokenCallback>() ) )
|
||||
return;
|
||||
|
||||
bool completed = false;
|
||||
var timeoutDate = DateTime.Now.AddSeconds( 10 );
|
||||
Action<SteamApps.CDNAuthTokenCallback> cbMethod = ( cdnAuth ) =>
|
||||
{
|
||||
completed = true;
|
||||
Console.WriteLine( "Got CDN auth token for {0} result: {1} (expires {2})", host, cdnAuth.Result, cdnAuth.Expiration );
|
||||
|
||||
if ( cdnAuth.Result != EResult.OK )
|
||||
{
|
||||
Abort();
|
||||
return;
|
||||
}
|
||||
|
||||
CDNAuthTokens[cdnKey].TrySetResult( cdnAuth );
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.GetCDNAuthToken( appid, depotid, host ), cbMethod );
|
||||
}, () => { return completed || DateTime.Now >= timeoutDate; } );
|
||||
}
|
||||
|
||||
public void CheckAppBetaPassword( uint appid, string password )
|
||||
{
|
||||
bool completed = false;
|
||||
Action<SteamApps.CheckAppBetaPasswordCallback> cbMethod = ( appPassword ) =>
|
||||
{
|
||||
completed = true;
|
||||
|
||||
Console.WriteLine( "Retrieved {0} beta keys with result: {1}", appPassword.BetaPasswords.Count, appPassword.Result );
|
||||
|
||||
foreach ( var entry in appPassword.BetaPasswords )
|
||||
{
|
||||
AppBetaPasswords[ entry.Key ] = entry.Value;
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback( () =>
|
||||
{
|
||||
callbacks.Subscribe( steamApps.CheckAppBetaPassword( appid, password ), cbMethod );
|
||||
}, () => { return completed; } );
|
||||
}
|
||||
|
||||
public CPublishedFile_GetItemInfo_Response.WorkshopItemInfo GetPubfileItemInfo( uint appId, PublishedFileID pubFile )
|
||||
{
|
||||
var pubFileRequest = new CPublishedFile_GetItemInfo_Request() { app_id = appId };
|
||||
pubFileRequest.workshop_items.Add( new CPublishedFile_GetItemInfo_Request.WorkshopItem() { published_file_id = pubFile } );
|
||||
|
||||
bool completed = false;
|
||||
CPublishedFile_GetItemInfo_Response.WorkshopItemInfo details = null;
|
||||
|
||||
Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if ( callback.Result == EResult.OK )
|
||||
{
|
||||
var response = callback.GetDeserializedResponse<CPublishedFile_GetItemInfo_Response>();
|
||||
details = response.workshop_items.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception( $"EResult {(int)callback.Result} ({callback.Result}) while retrieving UGC id for pubfile {pubFile}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe( steamPublishedFile.SendMessage( api => api.GetItemInfo( pubFileRequest ) ), cbMethod );
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID pubFile)
|
||||
{
|
||||
var pubFileRequest = new CPublishedFile_GetDetails_Request() { appid = appId };
|
||||
pubFileRequest.publishedfileids.Add( pubFile );
|
||||
|
||||
bool completed = false;
|
||||
PublishedFileDetails details = null;
|
||||
|
||||
Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if (callback.Result == EResult.OK)
|
||||
{
|
||||
var response = callback.GetDeserializedResponse<CPublishedFile_GetDetails_Response>();
|
||||
details = response.publishedfiledetails.FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving file details for pubfile {pubFile}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe(steamPublishedFile.SendMessage(api => api.GetDetails(pubFileRequest)), cbMethod);
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
public SteamCloud.UGCDetailsCallback GetUGCDetails(UGCHandle ugcHandle)
|
||||
{
|
||||
bool completed = false;
|
||||
SteamCloud.UGCDetailsCallback details = null;
|
||||
|
||||
Action<SteamCloud.UGCDetailsCallback> cbMethod = callback =>
|
||||
{
|
||||
completed = true;
|
||||
if (callback.Result == EResult.OK)
|
||||
{
|
||||
details = callback;
|
||||
}
|
||||
else if (callback.Result == EResult.FileNotFound)
|
||||
{
|
||||
details = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"EResult {(int)callback.Result} ({callback.Result}) while retrieving UGC details for {ugcHandle}.");
|
||||
}
|
||||
};
|
||||
|
||||
WaitUntilCallback(() =>
|
||||
{
|
||||
callbacks.Subscribe(steamCloud.RequestUGCDetails(ugcHandle), cbMethod);
|
||||
}, () => { return completed; });
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
void Connect()
|
||||
{
|
||||
bAborted = false;
|
||||
bConnected = false;
|
||||
bConnecting = true;
|
||||
connectionBackoff = 0;
|
||||
bExpectingDisconnectRemote = false;
|
||||
bDidDisconnect = false;
|
||||
bDidReceiveLoginKey = false;
|
||||
this.connectTime = DateTime.Now;
|
||||
this.steamClient.Connect();
|
||||
}
|
||||
|
||||
private void Abort( bool sendLogOff = true )
|
||||
{
|
||||
Disconnect( sendLogOff );
|
||||
}
|
||||
public void Disconnect( bool sendLogOff = true )
|
||||
{
|
||||
if ( sendLogOff )
|
||||
{
|
||||
steamUser.LogOff();
|
||||
}
|
||||
|
||||
steamClient.Disconnect();
|
||||
bConnected = false;
|
||||
bConnecting = false;
|
||||
bAborted = true;
|
||||
|
||||
// flush callbacks until our disconnected event
|
||||
while ( !bDidDisconnect )
|
||||
{
|
||||
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void TryWaitForLoginKey()
|
||||
{
|
||||
if ( logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword ) return;
|
||||
|
||||
var totalWaitPeriod = DateTime.Now.AddSeconds( 3 );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
if ( now >= totalWaitPeriod ) break;
|
||||
|
||||
if ( bDidReceiveLoginKey ) break;
|
||||
|
||||
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void WaitForCallbacks()
|
||||
{
|
||||
callbacks.RunWaitCallbacks( TimeSpan.FromSeconds( 1 ) );
|
||||
|
||||
TimeSpan diff = DateTime.Now - connectTime;
|
||||
|
||||
if ( diff > STEAM3_TIMEOUT && !bConnected )
|
||||
{
|
||||
Console.WriteLine( "Timeout connecting to Steam3." );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConnectedCallback( SteamClient.ConnectedCallback connected )
|
||||
{
|
||||
Console.WriteLine( " Done!" );
|
||||
bConnecting = false;
|
||||
bConnected = true;
|
||||
if ( !authenticatedUser )
|
||||
{
|
||||
Console.Write( "Logging anonymously into Steam3..." );
|
||||
steamUser.LogOnAnonymous();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write( "Logging '{0}' into Steam3...", logonDetails.Username );
|
||||
steamUser.LogOn( logonDetails );
|
||||
}
|
||||
}
|
||||
|
||||
private void DisconnectedCallback( SteamClient.DisconnectedCallback disconnected )
|
||||
{
|
||||
bDidDisconnect = true;
|
||||
|
||||
if ( disconnected.UserInitiated || bExpectingDisconnectRemote )
|
||||
{
|
||||
Console.WriteLine( "Disconnected from Steam" );
|
||||
}
|
||||
else if ( connectionBackoff >= 10 )
|
||||
{
|
||||
Console.WriteLine( "Could not connect to Steam after 10 tries" );
|
||||
Abort( false );
|
||||
}
|
||||
else if ( !bAborted )
|
||||
{
|
||||
if ( bConnecting )
|
||||
{
|
||||
Console.WriteLine( "Connection to Steam failed. Trying again" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine( "Lost connection to Steam. Reconnecting" );
|
||||
}
|
||||
|
||||
Thread.Sleep( 1000 * ++connectionBackoff );
|
||||
steamClient.Connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void LogOnCallback( SteamUser.LoggedOnCallback loggedOn )
|
||||
{
|
||||
bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
|
||||
bool is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
|
||||
bool isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;
|
||||
|
||||
if ( isSteamGuard || is2FA || isLoginKey )
|
||||
{
|
||||
bExpectingDisconnectRemote = true;
|
||||
Abort( false );
|
||||
|
||||
if ( !isLoginKey )
|
||||
{
|
||||
Console.WriteLine( "This account is protected by Steam Guard." );
|
||||
}
|
||||
|
||||
if ( is2FA )
|
||||
{
|
||||
Console.Write( "Please enter your 2 factor auth code from your authenticator app: " );
|
||||
logonDetails.TwoFactorCode = Console.ReadLine();
|
||||
}
|
||||
else if ( isLoginKey )
|
||||
{
|
||||
AccountSettingsStore.Instance.LoginKeys.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();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write( "Please enter the authentication code sent to your email address: " );
|
||||
logonDetails.AuthCode = Console.ReadLine();
|
||||
}
|
||||
|
||||
Console.Write( "Retrying Steam3 connection..." );
|
||||
Connect();
|
||||
|
||||
return;
|
||||
}
|
||||
else if ( loggedOn.Result == EResult.ServiceUnavailable )
|
||||
{
|
||||
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result );
|
||||
Abort( false );
|
||||
|
||||
return;
|
||||
}
|
||||
else if ( loggedOn.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine( " Done!" );
|
||||
|
||||
this.seq++;
|
||||
credentials.LoggedOn = true;
|
||||
|
||||
if ( ContentDownloader.Config.CellID == 0 )
|
||||
{
|
||||
Console.WriteLine( "Using Steam3 suggested CellID: " + loggedOn.CellID );
|
||||
ContentDownloader.Config.CellID = ( int )loggedOn.CellID;
|
||||
}
|
||||
}
|
||||
|
||||
private void SessionTokenCallback( SteamUser.SessionTokenCallback sessionToken )
|
||||
{
|
||||
Console.WriteLine( "Got session token!" );
|
||||
credentials.SessionToken = sessionToken.SessionToken;
|
||||
}
|
||||
|
||||
private void LicenseListCallback( SteamApps.LicenseListCallback licenseList )
|
||||
{
|
||||
if ( licenseList.Result != EResult.OK )
|
||||
{
|
||||
Console.WriteLine( "Unable to get license list: {0} ", licenseList.Result );
|
||||
Abort();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine( "Got {0} licenses for account!", licenseList.LicenseList.Count );
|
||||
Licenses = licenseList.LicenseList;
|
||||
|
||||
foreach ( var license in licenseList.LicenseList )
|
||||
{
|
||||
if ( license.AccessToken > 0 )
|
||||
{
|
||||
PackageTokens.TryAdd( license.PackageID, license.AccessToken );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMachineAuthCallback( SteamUser.UpdateMachineAuthCallback machineAuth )
|
||||
{
|
||||
byte[] hash = Util.SHAHash( machineAuth.Data );
|
||||
Console.WriteLine( "Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash );
|
||||
|
||||
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = machineAuth.Data;
|
||||
AccountSettingsStore.Save();
|
||||
|
||||
var authResponse = new SteamUser.MachineAuthDetails
|
||||
{
|
||||
BytesWritten = machineAuth.BytesToWrite,
|
||||
FileName = machineAuth.FileName,
|
||||
FileSize = machineAuth.BytesToWrite,
|
||||
Offset = machineAuth.Offset,
|
||||
|
||||
SentryFileHash = hash, // should be the sha1 hash of the sentry file we just wrote
|
||||
|
||||
OneTimePassword = machineAuth.OneTimePassword, // not sure on this one yet, since we've had no examples of steam using OTPs
|
||||
|
||||
LastError = 0, // result from win32 GetLastError
|
||||
Result = EResult.OK, // if everything went okay, otherwise ~who knows~
|
||||
|
||||
JobID = machineAuth.JobID, // so we respond to the correct server job
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
170
depotdownloader/DepotDownloader/Util.cs
Normal file
170
depotdownloader/DepotDownloader/Util.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DepotDownloader
|
||||
{
|
||||
static class Util
|
||||
{
|
||||
public static string GetSteamOS()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return "windows";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return "macos";
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return "linux";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
public static string GetSteamArch()
|
||||
{
|
||||
return Environment.Is64BitOperatingSystem ? "64" : "32";
|
||||
}
|
||||
|
||||
public static string ReadPassword()
|
||||
{
|
||||
ConsoleKeyInfo keyInfo;
|
||||
StringBuilder password = new StringBuilder();
|
||||
|
||||
do
|
||||
{
|
||||
keyInfo = Console.ReadKey( true );
|
||||
|
||||
if ( keyInfo.Key == ConsoleKey.Backspace )
|
||||
{
|
||||
if ( password.Length > 0 )
|
||||
password.Remove( password.Length - 1, 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Printable ASCII characters only */
|
||||
char c = keyInfo.KeyChar;
|
||||
if ( c >= ' ' && c <= '~' )
|
||||
password.Append( c );
|
||||
} while ( keyInfo.Key != ConsoleKey.Enter );
|
||||
|
||||
return password.ToString();
|
||||
}
|
||||
|
||||
// Validate a file against Steam3 Chunk data
|
||||
public static List<ProtoManifest.ChunkData> ValidateSteam3FileChecksums(FileStream fs, ProtoManifest.ChunkData[] chunkdata)
|
||||
{
|
||||
var neededChunks = new List<ProtoManifest.ChunkData>();
|
||||
int read;
|
||||
|
||||
foreach (var data in chunkdata)
|
||||
{
|
||||
byte[] chunk = new byte[data.UncompressedLength];
|
||||
fs.Seek((long)data.Offset, SeekOrigin.Begin);
|
||||
read = fs.Read(chunk, 0, (int)data.UncompressedLength);
|
||||
|
||||
byte[] tempchunk;
|
||||
if (read < data.UncompressedLength)
|
||||
{
|
||||
tempchunk = new byte[read];
|
||||
Array.Copy(chunk, 0, tempchunk, 0, read);
|
||||
}
|
||||
else
|
||||
{
|
||||
tempchunk = chunk;
|
||||
}
|
||||
|
||||
byte[] adler = AdlerHash(tempchunk);
|
||||
if (!adler.SequenceEqual(data.Checksum))
|
||||
{
|
||||
neededChunks.Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
return neededChunks;
|
||||
}
|
||||
|
||||
public static byte[] AdlerHash(byte[] input)
|
||||
{
|
||||
uint a = 0, b = 0;
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
a = (a + input[i]) % 65521;
|
||||
b = (b + a) % 65521;
|
||||
}
|
||||
return BitConverter.GetBytes(a | (b << 16));
|
||||
}
|
||||
|
||||
public static byte[] SHAHash( byte[] input )
|
||||
{
|
||||
using (var sha = SHA1.Create())
|
||||
{
|
||||
var output = sha.ComputeHash( input );
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] DecodeHexString( string hex )
|
||||
{
|
||||
if ( hex == null )
|
||||
return null;
|
||||
|
||||
int chars = hex.Length;
|
||||
byte[] bytes = new byte[ chars / 2 ];
|
||||
|
||||
for ( int i = 0 ; i < chars ; i += 2 )
|
||||
bytes[ i / 2 ] = Convert.ToByte( hex.Substring( i, 2 ), 16 );
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static string EncodeHexString( byte[] input )
|
||||
{
|
||||
return input.Aggregate( new StringBuilder(),
|
||||
( sb, v ) => sb.Append( v.ToString( "x2" ) )
|
||||
).ToString();
|
||||
}
|
||||
|
||||
public static async Task InvokeAsync(IEnumerable<Func<Task>> taskFactories, int maxDegreeOfParallelism)
|
||||
{
|
||||
if (taskFactories == null) throw new ArgumentNullException(nameof(taskFactories));
|
||||
if (maxDegreeOfParallelism <= 0) throw new ArgumentException(nameof(maxDegreeOfParallelism));
|
||||
|
||||
Func<Task>[] queue = taskFactories.ToArray();
|
||||
|
||||
if (queue.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Task> tasksInFlight = new List<Task>(maxDegreeOfParallelism);
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
while (tasksInFlight.Count < maxDegreeOfParallelism && index < queue.Length)
|
||||
{
|
||||
Func<Task> taskFactory = queue[index++];
|
||||
|
||||
tasksInFlight.Add(taskFactory());
|
||||
}
|
||||
|
||||
Task completedTask = await Task.WhenAny(tasksInFlight).ConfigureAwait(false);
|
||||
|
||||
await completedTask.ConfigureAwait(false);
|
||||
|
||||
tasksInFlight.Remove(completedTask);
|
||||
}
|
||||
while (index < queue.Length || tasksInFlight.Count != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rollForwardOnNoCandidateFx": 2
|
||||
}
|
||||
@@ -50,6 +50,6 @@ Parameter | Description
|
||||
-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: 8).
|
||||
-max-downloads \<#> | maximum number of chunks to download concurrently. (default: 4).
|
||||
-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.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
dotnet DepotDownloader.dll "$@"
|
||||
@@ -1,2 +0,0 @@
|
||||
@echo off
|
||||
dotnet %~dp0DepotDownloader.dll %*
|
||||
Binary file not shown.
Reference in New Issue
Block a user