Add roslyn-like .editorconfig (#236)

pull/232/head
js6pak 4 years ago committed by GitHub
parent 33483e594b
commit afab5e44b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,230 @@
; EditorConfig: http://EditorConfig.org # top-most EditorConfig file
root = true root = true
[*] [*]
indent_style = space indent_style = space
charset = utf-8
end_of_line = lf
insert_final_newline = true
# Code files
[*.{cs, csx, vb, vbx}]
indent_size = 4 indent_size = 4
# XML project files
[*.{csproj, vbproj, vcxproj, vcxproj.filters, proj, projitems, shproj}]
indent_size = 2
# XML config files
[*.{props, targets, ruleset, config, nuspec, resx, vsixmanifest, vsct}]
indent_size = 2
# Dotnet code style settings:
[*.{cs, vb}]
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:refactoring
dotnet_style_qualification_for_property = false:refactoring
dotnet_style_qualification_for_method = false:refactoring
dotnet_style_qualification_for_event = false:refactoring
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Non-private static fields are PascalCase
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
# Non-private readonly fields are PascalCase
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
# Constants are PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
dotnet_naming_symbols.constants.applicable_kinds = field, local
dotnet_naming_symbols.constants.required_modifiers = const
dotnet_naming_style.constant_style.capitalization = pascal_case
# Static readonly fields are PascalCase
dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static, readonly
dotnet_naming_style.static_field_style.capitalization = pascal_case
# Instance fields are camelCase and start with _
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
dotnet_naming_symbols.instance_fields.applicable_kinds = field
dotnet_naming_style.instance_field_style.capitalization = camel_case
dotnet_naming_style.instance_field_style.required_prefix = _
# Locals and parameters are camelCase
dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
dotnet_naming_style.camel_case_style.capitalization = camel_case
# Local functions are PascalCase
dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
dotnet_naming_style.local_function_style.capitalization = pascal_case
# By default, name items with PascalCase
dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.all_members.applicable_kinds = *
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Async methods should have "Async" suffix
dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
dotnet_naming_rule.async_methods_end_in_async.severity = warning
dotnet_naming_symbols.any_async_methods.applicable_kinds = method
dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
dotnet_naming_symbols.any_async_methods.required_modifiers = async
dotnet_naming_style.end_in_async.required_prefix =
dotnet_naming_style.end_in_async.required_suffix = Async
dotnet_naming_style.end_in_async.capitalization = pascal_case
dotnet_naming_style.end_in_async.word_separator =
# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}'
dotnet_diagnostic.RS2008.severity = none
# IDE0005: Remove unnecessary import
dotnet_diagnostic.IDE0005.severity = warning
# IDE0007: Use `var` instead of explicit type
dotnet_diagnostic.IDE0007.severity = warning
# IDE0035: Remove unreachable code
dotnet_diagnostic.IDE0035.severity = warning
# IDE0036: Order modifiers
dotnet_diagnostic.IDE0036.severity = warning
# IDE0043: Format string contains invalid placeholder
dotnet_diagnostic.IDE0043.severity = warning
# IDE0044: Make field readonly
dotnet_diagnostic.IDE0044.severity = warning
# CSharp code style settings:
[*.cs]
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
# csharp_new_line_before_members_in_object_initializers = true TODO seems like Rider/ReSharper has the value inverted, uncomment when its fixed
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = false:none
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_operators = false:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Blocks are allowed
csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true

@ -1,33 +1,31 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using ProtoBuf;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.IsolatedStorage; using System.IO.IsolatedStorage;
using System.Linq; using ProtoBuf;
using SteamKit2;
using SteamKit2.Discovery;
namespace DepotDownloader namespace DepotDownloader
{ {
[ProtoContract] [ProtoContract]
class AccountSettingsStore class AccountSettingsStore
{ {
[ProtoMember(1, IsRequired=false)] [ProtoMember(1, IsRequired = false)]
public Dictionary<string, byte[]> SentryData { get; private set; } public Dictionary<string, byte[]> SentryData { get; private set; }
[ProtoMember(2, IsRequired = false)] [ProtoMember(2, IsRequired = false)]
public System.Collections.Concurrent.ConcurrentDictionary<string, int> ContentServerPenalty { get; private set; } public ConcurrentDictionary<string, int> ContentServerPenalty { get; private set; }
[ProtoMember(3, IsRequired = false)] [ProtoMember(3, IsRequired = false)]
public Dictionary<string, string> LoginKeys { get; private set; } public Dictionary<string, string> LoginKeys { get; private set; }
string FileName = null; string FileName;
AccountSettingsStore() AccountSettingsStore()
{ {
SentryData = new Dictionary<string, byte[]>(); SentryData = new Dictionary<string, byte[]>();
ContentServerPenalty = new System.Collections.Concurrent.ConcurrentDictionary<string, int>(); ContentServerPenalty = new ConcurrentDictionary<string, int>();
LoginKeys = new Dictionary<string, string>(); LoginKeys = new Dictionary<string, string>();
} }
@ -36,7 +34,7 @@ namespace DepotDownloader
get { return Instance != null; } get { return Instance != null; }
} }
public static AccountSettingsStore Instance = null; public static AccountSettingsStore Instance;
static readonly IsolatedStorageFile IsolatedStorage = IsolatedStorageFile.GetUserStoreForAssembly(); static readonly IsolatedStorageFile IsolatedStorage = IsolatedStorageFile.GetUserStoreForAssembly();
public static void LoadFromFile(string filename) public static void LoadFromFile(string filename)
@ -49,9 +47,9 @@ namespace DepotDownloader
try try
{ {
using (var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read)) using (var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress)) using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
{ {
Instance = ProtoBuf.Serializer.Deserialize<AccountSettingsStore>(ds); Instance = Serializer.Deserialize<AccountSettingsStore>(ds);
} }
} }
catch (IOException ex) catch (IOException ex)
@ -76,9 +74,9 @@ namespace DepotDownloader
try try
{ {
using (var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write)) using (var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress)) using (var ds = new DeflateStream(fs, CompressionMode.Compress))
{ {
ProtoBuf.Serializer.Serialize<AccountSettingsStore>(ds, Instance); Serializer.Serialize(ds, Instance);
} }
} }
catch (IOException ex) catch (IOException ex)

@ -1,11 +1,11 @@
using SteamKit2; using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SteamKit2;
namespace DepotDownloader namespace DepotDownloader
{ {
@ -82,7 +82,7 @@ namespace DepotDownloader
private async Task ConnectionPoolMonitorAsync() private async Task ConnectionPoolMonitorAsync()
{ {
bool didPopulate = false; var didPopulate = false;
while (!shutdownToken.IsCancellationRequested) while (!shutdownToken.IsCancellationRequested)
{ {
@ -165,11 +165,9 @@ namespace DepotDownloader
var result = await authTokenCallbackPromise.Task; var result = await authTokenCallbackPromise.Task;
return result.Token; return result.Token;
} }
else
{
throw new Exception($"Failed to retrieve CDN token for server {server.Host} depot {depotId}"); throw new Exception($"Failed to retrieve CDN token for server {server.Host} depot {depotId}");
} }
}
public void ReturnConnection(CDNClient.Server server) public void ReturnConnection(CDNClient.Server server)
{ {

File diff suppressed because it is too large Load Diff

@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ProtoBuf;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using ProtoBuf;
namespace DepotDownloader namespace DepotDownloader
{ {
@ -12,7 +12,7 @@ namespace DepotDownloader
[ProtoMember(1)] [ProtoMember(1)]
public Dictionary<uint, ulong> InstalledManifestIDs { get; private set; } public Dictionary<uint, ulong> InstalledManifestIDs { get; private set; }
string FileName = null; string FileName;
DepotConfigStore() DepotConfigStore()
{ {
@ -24,7 +24,7 @@ namespace DepotDownloader
get { return Instance != null; } get { return Instance != null; }
} }
public static DepotConfigStore Instance = null; public static DepotConfigStore Instance;
public static void LoadFromFile(string filename) public static void LoadFromFile(string filename)
{ {
@ -33,9 +33,9 @@ namespace DepotDownloader
if (File.Exists(filename)) if (File.Exists(filename))
{ {
using (FileStream fs = File.Open(filename, FileMode.Open)) using (var fs = File.Open(filename, FileMode.Open))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress)) using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
Instance = ProtoBuf.Serializer.Deserialize<DepotConfigStore>(ds); Instance = Serializer.Deserialize<DepotConfigStore>(ds);
} }
else else
{ {
@ -50,9 +50,9 @@ namespace DepotDownloader
if (!Loaded) if (!Loaded)
throw new Exception("Saved config before loading"); throw new Exception("Saved config before loading");
using (FileStream fs = File.Open(Instance.FileName, FileMode.Create)) using (var fs = File.Open(Instance.FileName, FileMode.Create))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress)) using (var ds = new DeflateStream(fs, CompressionMode.Compress))
ProtoBuf.Serializer.Serialize<DepotConfigStore>(ds, Instance); Serializer.Serialize(ds, Instance);
} }
} }
} }

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<Version>2.4.3</Version> <Version>2.4.3</Version>
<Description>Steam Downloading Utility</Description> <Description>Steam Downloading Utility</Description>

@ -26,6 +26,6 @@ namespace DepotDownloader
public bool RememberPassword { get; set; } public bool RememberPassword { get; set; }
// A Steam LoginID to allow multiple concurrent connections // A Steam LoginID to allow multiple concurrent connections
public uint? LoginID {get; set; } public uint? LoginID { get; set; }
} }
} }

@ -1,11 +1,7 @@
using System; using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -33,7 +29,7 @@ namespace DepotDownloader
// By default, we create dual-mode sockets: // By default, we create dual-mode sockets:
// Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp); // Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true; socket.NoDelay = true;
try try

@ -26,7 +26,7 @@ namespace DepotDownloader
protected override void OnEventWritten(EventWrittenEventArgs eventData) protected override void OnEventWritten(EventWrittenEventArgs eventData)
{ {
var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff} {eventData.EventSource.Name}.{eventData.EventName}("); var sb = new StringBuilder().Append($"{eventData.TimeStamp:HH:mm:ss.fffffff} {eventData.EventSource.Name}.{eventData.EventName}(");
for (int i = 0; i < eventData.Payload?.Count; i++) for (var i = 0; i < eventData.Payload?.Count; i++)
{ {
sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]); sb.Append(eventData.PayloadNames?[i]).Append(": ").Append(eventData.Payload[i]);
if (i < eventData.Payload?.Count - 1) if (i < eventData.Payload?.Count - 1)

@ -1,22 +1,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using SteamKit2;
using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using SteamKit2;
namespace DepotDownloader namespace DepotDownloader
{ {
class Program class Program
{ {
static int Main( string[] args ) static int Main(string[] args)
=> MainAsync( args ).GetAwaiter().GetResult(); => MainAsync(args).GetAwaiter().GetResult();
static async Task<int> MainAsync( string[] args ) static async Task<int> MainAsync(string[] args)
{ {
if ( args.Length == 0 ) if (args.Length == 0)
{ {
PrintUsage(); PrintUsage();
return 1; return 1;
@ -24,108 +24,108 @@ namespace DepotDownloader
DebugLog.Enabled = false; DebugLog.Enabled = false;
AccountSettingsStore.LoadFromFile( "account.config" ); AccountSettingsStore.LoadFromFile("account.config");
#region Common Options #region Common Options
if ( HasParameter( args, "-debug" ) ) if (HasParameter(args, "-debug"))
{ {
DebugLog.Enabled = true; DebugLog.Enabled = true;
DebugLog.AddListener( ( category, message ) => DebugLog.AddListener((category, message) =>
{ {
Console.WriteLine( "[{0}] {1}", category, message ); Console.WriteLine("[{0}] {1}", category, message);
}); });
var httpEventListener = new HttpDiagnosticEventListener(); var httpEventListener = new HttpDiagnosticEventListener();
} }
string username = GetParameter<string>( args, "-username" ) ?? GetParameter<string>( args, "-user" ); var username = GetParameter<string>(args, "-username") ?? GetParameter<string>(args, "-user");
string password = GetParameter<string>( args, "-password" ) ?? GetParameter<string>( args, "-pass" ); var password = GetParameter<string>(args, "-password") ?? GetParameter<string>(args, "-pass");
ContentDownloader.Config.RememberPassword = HasParameter( args, "-remember-password" ); ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password");
ContentDownloader.Config.DownloadManifestOnly = HasParameter( args, "-manifest-only" ); ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only");
int cellId = GetParameter<int>( args, "-cellid", -1 ); var cellId = GetParameter(args, "-cellid", -1);
if ( cellId == -1 ) if (cellId == -1)
{ {
cellId = 0; cellId = 0;
} }
ContentDownloader.Config.CellID = cellId; ContentDownloader.Config.CellID = cellId;
string fileList = GetParameter<string>( args, "-filelist" ); var fileList = GetParameter<string>(args, "-filelist");
if ( fileList != null ) if (fileList != null)
{ {
try try
{ {
string fileListData = await File.ReadAllTextAsync( fileList ); var fileListData = await File.ReadAllTextAsync(fileList);
var files = fileListData.Split( new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries ); var files = fileListData.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
ContentDownloader.Config.UsingFileList = true; ContentDownloader.Config.UsingFileList = true;
ContentDownloader.Config.FilesToDownload = new HashSet<string>( StringComparer.OrdinalIgnoreCase ); ContentDownloader.Config.FilesToDownload = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>(); ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>();
foreach ( var fileEntry in files ) foreach (var fileEntry in files)
{ {
if ( fileEntry.StartsWith( "regex:" ) ) if (fileEntry.StartsWith("regex:"))
{ {
Regex rgx = new Regex( fileEntry.Substring( 6 ), RegexOptions.Compiled | RegexOptions.IgnoreCase ); var rgx = new Regex(fileEntry.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase);
ContentDownloader.Config.FilesToDownloadRegex.Add( rgx ); ContentDownloader.Config.FilesToDownloadRegex.Add(rgx);
} }
else else
{ {
ContentDownloader.Config.FilesToDownload.Add( fileEntry.Replace( '\\', '/' ) ); ContentDownloader.Config.FilesToDownload.Add(fileEntry.Replace('\\', '/'));
} }
} }
Console.WriteLine( "Using filelist: '{0}'.", fileList ); Console.WriteLine("Using filelist: '{0}'.", fileList);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine( "Warning: Unable to load filelist: {0}", ex.ToString() ); Console.WriteLine("Warning: Unable to load filelist: {0}", ex);
} }
} }
ContentDownloader.Config.InstallDirectory = GetParameter<string>( args, "-dir" ); ContentDownloader.Config.InstallDirectory = GetParameter<string>(args, "-dir");
ContentDownloader.Config.VerifyAll = HasParameter( args, "-verify-all" ) || HasParameter( args, "-verify_all" ) || HasParameter( args, "-validate" ); 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.MaxServers = GetParameter(args, "-max-servers", 20);
ContentDownloader.Config.MaxDownloads = GetParameter<int>( args, "-max-downloads", 8 ); ContentDownloader.Config.MaxDownloads = GetParameter(args, "-max-downloads", 8);
ContentDownloader.Config.MaxServers = Math.Max( ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads ); ContentDownloader.Config.MaxServers = Math.Max(ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads);
ContentDownloader.Config.LoginID = HasParameter( args, "-loginid" ) ? (uint?)GetParameter<uint>( args, "-loginid" ) : null; ContentDownloader.Config.LoginID = HasParameter(args, "-loginid") ? GetParameter<uint>(args, "-loginid") : null;
#endregion #endregion
uint appId = GetParameter<uint>( args, "-app", ContentDownloader.INVALID_APP_ID ); var appId = GetParameter(args, "-app", ContentDownloader.INVALID_APP_ID);
if ( appId == ContentDownloader.INVALID_APP_ID ) if (appId == ContentDownloader.INVALID_APP_ID)
{ {
Console.WriteLine( "Error: -app not specified!" ); Console.WriteLine("Error: -app not specified!");
return 1; return 1;
} }
ulong pubFile = GetParameter<ulong>( args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID ); var pubFile = GetParameter(args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID);
ulong ugcId = GetParameter<ulong>( args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID ); var ugcId = GetParameter(args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID);
if ( pubFile != ContentDownloader.INVALID_MANIFEST_ID ) if (pubFile != ContentDownloader.INVALID_MANIFEST_ID)
{ {
#region Pubfile Downloading #region Pubfile Downloading
if ( InitializeSteam( username, password ) ) if (InitializeSteam(username, password))
{ {
try try
{ {
await ContentDownloader.DownloadPubfileAsync( appId, pubFile ).ConfigureAwait( false ); await ContentDownloader.DownloadPubfileAsync(appId, pubFile).ConfigureAwait(false);
} }
catch ( Exception ex ) when ( catch (Exception ex) when (
ex is ContentDownloaderException ex is ContentDownloaderException
|| ex is OperationCanceledException ) || ex is OperationCanceledException)
{ {
Console.WriteLine( ex.Message ); Console.WriteLine(ex.Message);
return 1; return 1;
} }
catch ( Exception e ) catch (Exception e)
{ {
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message ); Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message);
throw; throw;
} }
finally finally
@ -135,32 +135,32 @@ namespace DepotDownloader
} }
else else
{ {
Console.WriteLine( "Error: InitializeSteam failed" ); Console.WriteLine("Error: InitializeSteam failed");
return 1; return 1;
} }
#endregion #endregion
} }
else if ( ugcId != ContentDownloader.INVALID_MANIFEST_ID ) else if (ugcId != ContentDownloader.INVALID_MANIFEST_ID)
{ {
#region UGC Downloading #region UGC Downloading
if ( InitializeSteam( username, password ) ) if (InitializeSteam(username, password))
{ {
try try
{ {
await ContentDownloader.DownloadUGCAsync( appId, ugcId ).ConfigureAwait( false ); await ContentDownloader.DownloadUGCAsync(appId, ugcId).ConfigureAwait(false);
} }
catch ( Exception ex ) when ( catch (Exception ex) when (
ex is ContentDownloaderException ex is ContentDownloaderException
|| ex is OperationCanceledException ) || ex is OperationCanceledException)
{ {
Console.WriteLine( ex.Message ); Console.WriteLine(ex.Message);
return 1; return 1;
} }
catch ( Exception e ) catch (Exception e)
{ {
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message ); Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message);
throw; throw;
} }
finally finally
@ -170,7 +170,7 @@ namespace DepotDownloader
} }
else else
{ {
Console.WriteLine( "Error: InitializeSteam failed" ); Console.WriteLine("Error: InitializeSteam failed");
return 1; return 1;
} }
@ -180,68 +180,68 @@ namespace DepotDownloader
{ {
#region App downloading #region App downloading
string branch = GetParameter<string>( args, "-branch" ) ?? GetParameter<string>( args, "-beta" ) ?? ContentDownloader.DEFAULT_BRANCH; var branch = GetParameter<string>(args, "-branch") ?? GetParameter<string>(args, "-beta") ?? ContentDownloader.DEFAULT_BRANCH;
ContentDownloader.Config.BetaPassword = GetParameter<string>( args, "-betapassword" ); ContentDownloader.Config.BetaPassword = GetParameter<string>(args, "-betapassword");
ContentDownloader.Config.DownloadAllPlatforms = HasParameter( args, "-all-platforms" ); ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms");
string os = GetParameter<string>( args, "-os", null ); var os = GetParameter<string>(args, "-os");
if ( ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty( os ) ) if (ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty(os))
{ {
Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified.");
return 1; return 1;
} }
string arch = GetParameter<string>( args, "-osarch", null ); var arch = GetParameter<string>(args, "-osarch");
ContentDownloader.Config.DownloadAllLanguages = HasParameter( args, "-all-languages" ); ContentDownloader.Config.DownloadAllLanguages = HasParameter(args, "-all-languages");
string language = GetParameter<string>( args, "-language", null ); var language = GetParameter<string>(args, "-language");
if ( ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty( language ) ) if (ContentDownloader.Config.DownloadAllLanguages && !String.IsNullOrEmpty(language))
{ {
Console.WriteLine( "Error: Cannot specify -language when -all-languages is specified." ); Console.WriteLine("Error: Cannot specify -language when -all-languages is specified.");
return 1; return 1;
} }
bool lv = HasParameter( args, "-lowviolence" ); var lv = HasParameter(args, "-lowviolence");
List<(uint, ulong)> depotManifestIds = new List<(uint, ulong)>(); var depotManifestIds = new List<(uint, ulong)>();
bool isUGC = false; var isUGC = false;
List<uint> depotIdList = GetParameterList<uint>( args, "-depot" ); var depotIdList = GetParameterList<uint>(args, "-depot");
List<ulong> manifestIdList = GetParameterList<ulong>( args, "-manifest" ); var manifestIdList = GetParameterList<ulong>(args, "-manifest");
if ( manifestIdList.Count > 0 ) if (manifestIdList.Count > 0)
{ {
if ( depotIdList.Count != manifestIdList.Count ) if (depotIdList.Count != manifestIdList.Count)
{ {
Console.WriteLine( "Error: -manifest requires one id for every -depot specified" ); Console.WriteLine("Error: -manifest requires one id for every -depot specified");
return 1; return 1;
} }
var zippedDepotManifest = depotIdList.Zip( manifestIdList, ( depotId, manifestId ) => ( depotId, manifestId ) ); var zippedDepotManifest = depotIdList.Zip(manifestIdList, (depotId, manifestId) => (depotId, manifestId));
depotManifestIds.AddRange( zippedDepotManifest ); depotManifestIds.AddRange(zippedDepotManifest);
} }
else else
{ {
depotManifestIds.AddRange( depotIdList.Select( depotId => ( depotId, ContentDownloader.INVALID_MANIFEST_ID ) ) ); depotManifestIds.AddRange(depotIdList.Select(depotId => (depotId, ContentDownloader.INVALID_MANIFEST_ID)));
} }
if ( InitializeSteam( username, password ) ) if (InitializeSteam(username, password))
{ {
try try
{ {
await ContentDownloader.DownloadAppAsync( appId, depotManifestIds, branch, os, arch, language, lv, isUGC ).ConfigureAwait( false ); await ContentDownloader.DownloadAppAsync(appId, depotManifestIds, branch, os, arch, language, lv, isUGC).ConfigureAwait(false);
} }
catch ( Exception ex ) when ( catch (Exception ex) when (
ex is ContentDownloaderException ex is ContentDownloaderException
|| ex is OperationCanceledException ) || ex is OperationCanceledException)
{ {
Console.WriteLine( ex.Message ); Console.WriteLine(ex.Message);
return 1; return 1;
} }
catch ( Exception e ) catch (Exception e)
{ {
Console.WriteLine( "Download failed to due to an unhandled exception: {0}", e.Message ); Console.WriteLine("Download failed to due to an unhandled exception: {0}", e.Message);
throw; throw;
} }
finally finally
@ -251,7 +251,7 @@ namespace DepotDownloader
} }
else else
{ {
Console.WriteLine( "Error: InitializeSteam failed" ); Console.WriteLine("Error: InitializeSteam failed");
return 1; return 1;
} }
@ -261,14 +261,14 @@ namespace DepotDownloader
return 0; return 0;
} }
static bool InitializeSteam( string username, string password ) static bool InitializeSteam(string username, string password)
{ {
if ( username != null && password == null && ( !ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey( username ) ) ) if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !AccountSettingsStore.Instance.LoginKeys.ContainsKey(username)))
{ {
do do
{ {
Console.Write( "Enter account password for \"{0}\": ", username ); Console.Write("Enter account password for \"{0}\": ", username);
if ( Console.IsInputRedirected ) if (Console.IsInputRedirected)
{ {
password = Console.ReadLine(); password = Console.ReadLine();
} }
@ -277,56 +277,59 @@ namespace DepotDownloader
// Avoid console echoing of password // Avoid console echoing of password
password = Util.ReadPassword(); password = Util.ReadPassword();
} }
Console.WriteLine(); Console.WriteLine();
} while ( String.Empty == password ); } while (String.Empty == password);
} }
else if ( username == null ) else if (username == null)
{ {
Console.WriteLine( "No username given. Using anonymous account with dedicated server subscription." ); 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 // capture the supplied password in case we need to re-use it after checking the login key
ContentDownloader.Config.SuppliedPassword = password; ContentDownloader.Config.SuppliedPassword = password;
return ContentDownloader.InitializeSteam3( username, password ); return ContentDownloader.InitializeSteam3(username, password);
} }
static int IndexOfParam( string[] args, string param ) static int IndexOfParam(string[] args, string param)
{ {
for ( int x = 0; x < args.Length; ++x ) for (var x = 0; x < args.Length; ++x)
{ {
if ( args[ x ].Equals( param, StringComparison.OrdinalIgnoreCase ) ) if (args[x].Equals(param, StringComparison.OrdinalIgnoreCase))
return x; return x;
} }
return -1; return -1;
} }
static bool HasParameter( string[] args, string param )
static bool HasParameter(string[] args, string param)
{ {
return IndexOfParam( args, param ) > -1; return IndexOfParam(args, param) > -1;
} }
static T GetParameter<T>( string[] args, string param, T defaultValue = default( T ) ) static T GetParameter<T>(string[] args, string param, T defaultValue = default(T))
{ {
int index = IndexOfParam( args, param ); var index = IndexOfParam(args, param);
if ( index == -1 || index == ( args.Length - 1 ) ) if (index == -1 || index == (args.Length - 1))
return defaultValue; return defaultValue;
string strParam = args[ index + 1 ]; var strParam = args[index + 1];
var converter = TypeDescriptor.GetConverter( typeof( T ) ); var converter = TypeDescriptor.GetConverter(typeof(T));
if ( converter != null ) if (converter != null)
{ {
return ( T )converter.ConvertFromString( strParam ); return (T)converter.ConvertFromString(strParam);
} }
return default( T ); return default(T);
} }
static List<T> GetParameterList<T>(string[] args, string param) static List<T> GetParameterList<T>(string[] args, string param)
{ {
List<T> list = new List<T>(); var list = new List<T>();
int index = IndexOfParam(args, param); var index = IndexOfParam(args, param);
if (index == -1 || index == (args.Length - 1)) if (index == -1 || index == (args.Length - 1))
return list; return list;
@ -335,7 +338,7 @@ namespace DepotDownloader
while (index < args.Length) while (index < args.Length)
{ {
string strParam = args[index]; var strParam = args[index];
if (strParam[0] == '-') break; if (strParam[0] == '-') break;
@ -354,44 +357,44 @@ namespace DepotDownloader
static void PrintUsage() static void PrintUsage()
{ {
Console.WriteLine(); Console.WriteLine();
Console.WriteLine( "Usage - downloading one or all depots for an app:" ); Console.WriteLine("Usage - downloading one or all depots for an app:");
Console.WriteLine( "\tdepotdownloader -app <id> [-depot <id> [-manifest <id>]]" ); Console.WriteLine("\tdepotdownloader -app <id> [-depot <id> [-manifest <id>]]");
Console.WriteLine( "\t\t[-username <username> [-password <password>]] [other options]" ); Console.WriteLine("\t\t[-username <username> [-password <password>]] [other options]");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Usage - downloading a workshop item using pubfile id"); Console.WriteLine("Usage - downloading a workshop item using pubfile id");
Console.WriteLine( "\tdepotdownloader -app <id> -pubfile <id> [-username <username> [-password <password>]]" ); Console.WriteLine("\tdepotdownloader -app <id> -pubfile <id> [-username <username> [-password <password>]]");
Console.WriteLine("Usage - downloading a workshop item using ugc id"); Console.WriteLine("Usage - downloading a workshop item using ugc id");
Console.WriteLine("\tdepotdownloader -app <id> -ugc <id> [-username <username> [-password <password>]]"); Console.WriteLine("\tdepotdownloader -app <id> -ugc <id> [-username <username> [-password <password>]]");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine( "Parameters:" ); Console.WriteLine("Parameters:");
Console.WriteLine( "\t-app <#>\t\t\t\t- the AppID to download." ); 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-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-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-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-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-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-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-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-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-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("\t-lowviolence\t\t\t\t- download low violence depots when -app is used.");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine( "\t-ugc <#>\t\t\t\t- the UGC ID to download." ); 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("\t-pubfile <#>\t\t\t- the PublishedFileId to download. (Will automatically resolve to UGC id)");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine( "\t-username <user>\t\t- the username of the account to login to for restricted content."); 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-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("\t-remember-password\t\t- if set, remember the password for subsequent logins of this user.");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine( "\t-dir <installdir>\t\t- the directory in which to place downloaded files." ); 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). Prefix file path with 'regex:' if you want to match with regex." ); Console.WriteLine("\t-filelist <file.txt>\t- a list of files to download (from the manifest). Prefix file path with 'regex:' if you want to match with regex.");
Console.WriteLine( "\t-validate\t\t\t\t- Include checksum verification of files already downloaded" ); Console.WriteLine("\t-validate\t\t\t\t- Include checksum verification of files already downloaded");
Console.WriteLine(); 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-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-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-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-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." ); Console.WriteLine("\t-loginid <#>\t\t- a unique 32-bit integer Steam LogonID in decimal, required if running multiple instances of DepotDownloader concurrently.");
} }
} }
} }

@ -2,13 +2,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using ProtoBuf; using ProtoBuf;
using SteamKit2; using SteamKit2;
namespace DepotDownloader namespace DepotDownloader
{ {
[ProtoContract()] [ProtoContract]
class ProtoManifest class ProtoManifest
{ {
// Proto ctor // Proto ctor
@ -24,7 +23,7 @@ namespace DepotDownloader
CreationTime = sourceManifest.CreationTime; CreationTime = sourceManifest.CreationTime;
} }
[ProtoContract()] [ProtoContract]
public class FileData public class FileData
{ {
// Proto ctor // Proto ctor
@ -130,32 +129,31 @@ namespace DepotDownloader
return null; return null;
} }
using (MemoryStream ms = new MemoryStream()) using (var ms = new MemoryStream())
{ {
using (FileStream fs = File.Open(filename, FileMode.Open)) using (var fs = File.Open(filename, FileMode.Open))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress)) using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
ds.CopyTo(ms); ds.CopyTo(ms);
checksum = Util.SHAHash(ms.ToArray()); checksum = Util.SHAHash(ms.ToArray());
ms.Seek(0, SeekOrigin.Begin); ms.Seek(0, SeekOrigin.Begin);
return ProtoBuf.Serializer.Deserialize<ProtoManifest>(ms); return Serializer.Deserialize<ProtoManifest>(ms);
} }
} }
public void SaveToFile(string filename, out byte[] checksum) public void SaveToFile(string filename, out byte[] checksum)
{ {
using (var ms = new MemoryStream())
using (MemoryStream ms = new MemoryStream())
{ {
ProtoBuf.Serializer.Serialize<ProtoManifest>(ms, this); Serializer.Serialize(ms, this);
checksum = Util.SHAHash(ms.ToArray()); checksum = Util.SHAHash(ms.ToArray());
ms.Seek(0, SeekOrigin.Begin); ms.Seek(0, SeekOrigin.Begin);
using (FileStream fs = File.Open(filename, FileMode.Create)) using (var fs = File.Open(filename, FileMode.Create))
using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress)) using (var ds = new DeflateStream(fs, CompressionMode.Compress))
ms.CopyTo(ds); ms.CopyTo(ds);
} }
} }

@ -1,6 +1,4 @@
using SteamKit2; using System;
using SteamKit2.Internal;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -8,10 +6,11 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using SteamKit2;
using SteamKit2.Internal;
namespace DepotDownloader namespace DepotDownloader
{ {
class Steam3Session class Steam3Session
{ {
public class Credentials public class Credentials
@ -41,13 +40,13 @@ namespace DepotDownloader
public SteamClient steamClient; public SteamClient steamClient;
public SteamUser steamUser; public SteamUser steamUser;
SteamApps steamApps; readonly SteamApps steamApps;
SteamCloud steamCloud; readonly SteamCloud steamCloud;
SteamUnifiedMessages.UnifiedService<IPublishedFile> steamPublishedFile; readonly SteamUnifiedMessages.UnifiedService<IPublishedFile> steamPublishedFile;
CallbackManager callbacks; readonly CallbackManager callbacks;
bool authenticatedUser; readonly bool authenticatedUser;
bool bConnected; bool bConnected;
bool bConnecting; bool bConnecting;
bool bAborted; bool bAborted;
@ -60,15 +59,15 @@ namespace DepotDownloader
DateTime connectTime; DateTime connectTime;
// input // input
SteamUser.LogOnDetails logonDetails; readonly SteamUser.LogOnDetails logonDetails;
// output // output
Credentials credentials; readonly Credentials credentials;
static readonly TimeSpan STEAM3_TIMEOUT = TimeSpan.FromSeconds( 30 ); static readonly TimeSpan STEAM3_TIMEOUT = TimeSpan.FromSeconds(30);
public Steam3Session( SteamUser.LogOnDetails details ) public Steam3Session(SteamUser.LogOnDetails details)
{ {
this.logonDetails = details; this.logonDetails = details;
@ -103,30 +102,30 @@ namespace DepotDownloader
var steamUnifiedMessages = this.steamClient.GetHandler<SteamUnifiedMessages>(); var steamUnifiedMessages = this.steamClient.GetHandler<SteamUnifiedMessages>();
this.steamPublishedFile = steamUnifiedMessages.CreateService<IPublishedFile>(); this.steamPublishedFile = steamUnifiedMessages.CreateService<IPublishedFile>();
this.callbacks = new CallbackManager( this.steamClient ); this.callbacks = new CallbackManager(this.steamClient);
this.callbacks.Subscribe<SteamClient.ConnectedCallback>( ConnectedCallback ); this.callbacks.Subscribe<SteamClient.ConnectedCallback>(ConnectedCallback);
this.callbacks.Subscribe<SteamClient.DisconnectedCallback>( DisconnectedCallback ); this.callbacks.Subscribe<SteamClient.DisconnectedCallback>(DisconnectedCallback);
this.callbacks.Subscribe<SteamUser.LoggedOnCallback>( LogOnCallback ); this.callbacks.Subscribe<SteamUser.LoggedOnCallback>(LogOnCallback);
this.callbacks.Subscribe<SteamUser.SessionTokenCallback>( SessionTokenCallback ); this.callbacks.Subscribe<SteamUser.SessionTokenCallback>(SessionTokenCallback);
this.callbacks.Subscribe<SteamApps.LicenseListCallback>( LicenseListCallback ); this.callbacks.Subscribe<SteamApps.LicenseListCallback>(LicenseListCallback);
this.callbacks.Subscribe<SteamUser.UpdateMachineAuthCallback>( UpdateMachineAuthCallback ); this.callbacks.Subscribe<SteamUser.UpdateMachineAuthCallback>(UpdateMachineAuthCallback);
this.callbacks.Subscribe<SteamUser.LoginKeyCallback>( LoginKeyCallback ); this.callbacks.Subscribe<SteamUser.LoginKeyCallback>(LoginKeyCallback);
Console.Write( "Connecting to Steam3..." ); Console.Write("Connecting to Steam3...");
if ( authenticatedUser ) if (authenticatedUser)
{ {
FileInfo fi = new FileInfo( String.Format( "{0}.sentryFile", logonDetails.Username ) ); var fi = new FileInfo(String.Format("{0}.sentryFile", logonDetails.Username));
if ( AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey( logonDetails.Username ) ) if (AccountSettingsStore.Instance.SentryData != null && AccountSettingsStore.Instance.SentryData.ContainsKey(logonDetails.Username))
{ {
logonDetails.SentryFileHash = Util.SHAHash( AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] ); logonDetails.SentryFileHash = Util.SHAHash(AccountSettingsStore.Instance.SentryData[logonDetails.Username]);
} }
else if ( fi.Exists && fi.Length > 0 ) else if (fi.Exists && fi.Length > 0)
{ {
var sentryData = File.ReadAllBytes( fi.FullName ); var sentryData = File.ReadAllBytes(fi.FullName);
logonDetails.SentryFileHash = Util.SHAHash( sentryData ); logonDetails.SentryFileHash = Util.SHAHash(sentryData);
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = sentryData; AccountSettingsStore.Instance.SentryData[logonDetails.Username] = sentryData;
AccountSettingsStore.Save(); AccountSettingsStore.Save();
} }
} }
@ -135,26 +134,26 @@ namespace DepotDownloader
} }
public delegate bool WaitCondition(); public delegate bool WaitCondition();
private object steamLock = new object();
public bool WaitUntilCallback( Action submitter, WaitCondition waiter ) private readonly object steamLock = new object();
public bool WaitUntilCallback(Action submitter, WaitCondition waiter)
{ {
while ( !bAborted && !waiter() ) while (!bAborted && !waiter())
{ {
lock (steamLock) lock (steamLock)
{ {
submitter(); submitter();
} }
int seq = this.seq; var seq = this.seq;
do do
{ {
lock (steamLock) lock (steamLock)
{ {
WaitForCallbacks(); WaitForCallbacks();
} }
} } while (!bAborted && this.seq == seq && !waiter());
while ( !bAborted && this.seq == seq && !waiter() );
} }
return bAborted; return bAborted;
@ -162,91 +161,91 @@ namespace DepotDownloader
public Credentials WaitForCredentials() public Credentials WaitForCredentials()
{ {
if ( credentials.IsValid || bAborted ) if (credentials.IsValid || bAborted)
return credentials; return credentials;
WaitUntilCallback( () => { }, () => { return credentials.IsValid; } ); WaitUntilCallback(() => { }, () => { return credentials.IsValid; });
return credentials; return credentials;
} }
public void RequestAppInfo( uint appId, bool bForce = false ) public void RequestAppInfo(uint appId, bool bForce = false)
{ {
if ( ( AppInfo.ContainsKey( appId ) && !bForce ) || bAborted ) if ((AppInfo.ContainsKey(appId) && !bForce) || bAborted)
return; return;
bool completed = false; var completed = false;
Action<SteamApps.PICSTokensCallback> cbMethodTokens = ( appTokens ) => Action<SteamApps.PICSTokensCallback> cbMethodTokens = appTokens =>
{ {
completed = true; completed = true;
if ( appTokens.AppTokensDenied.Contains( appId ) ) if (appTokens.AppTokensDenied.Contains(appId))
{ {
Console.WriteLine( "Insufficient privileges to get access token for app {0}", appId ); Console.WriteLine("Insufficient privileges to get access token for app {0}", appId);
} }
foreach ( var token_dict in appTokens.AppTokens ) foreach (var token_dict in appTokens.AppTokens)
{ {
this.AppTokens[ token_dict.Key ] = token_dict.Value; this.AppTokens[token_dict.Key] = token_dict.Value;
} }
}; };
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.PICSGetAccessTokens( new List<uint>() { appId }, new List<uint>() { } ), cbMethodTokens ); callbacks.Subscribe(steamApps.PICSGetAccessTokens(new List<uint> { appId }, new List<uint>()), cbMethodTokens);
}, () => { return completed; } ); }, () => { return completed; });
completed = false; completed = false;
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( appInfo ) => Action<SteamApps.PICSProductInfoCallback> cbMethod = appInfo =>
{ {
completed = !appInfo.ResponsePending; completed = !appInfo.ResponsePending;
foreach ( var app_value in appInfo.Apps ) foreach (var app_value in appInfo.Apps)
{ {
var app = app_value.Value; var app = app_value.Value;
Console.WriteLine( "Got AppInfo for {0}", app.ID ); Console.WriteLine("Got AppInfo for {0}", app.ID);
AppInfo[ app.ID ] = app; AppInfo[app.ID] = app;
} }
foreach ( var app in appInfo.UnknownApps ) foreach (var app in appInfo.UnknownApps)
{ {
AppInfo[ app ] = null; AppInfo[app] = null;
} }
}; };
SteamApps.PICSRequest request = new SteamApps.PICSRequest( appId ); var request = new SteamApps.PICSRequest(appId);
if ( AppTokens.ContainsKey( appId ) ) if (AppTokens.ContainsKey(appId))
{ {
request.AccessToken = AppTokens[ appId ]; request.AccessToken = AppTokens[appId];
request.Public = false; request.Public = false;
} }
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>() { request }, new List<SteamApps.PICSRequest>() { } ), cbMethod ); callbacks.Subscribe(steamApps.PICSGetProductInfo(new List<SteamApps.PICSRequest> { request }, new List<SteamApps.PICSRequest>()), cbMethod);
}, () => { return completed; } ); }, () => { return completed; });
} }
public void RequestPackageInfo( IEnumerable<uint> packageIds ) public void RequestPackageInfo(IEnumerable<uint> packageIds)
{ {
List<uint> packages = packageIds.ToList(); var packages = packageIds.ToList();
packages.RemoveAll( pid => PackageInfo.ContainsKey( pid ) ); packages.RemoveAll(pid => PackageInfo.ContainsKey(pid));
if ( packages.Count == 0 || bAborted ) if (packages.Count == 0 || bAborted)
return; return;
bool completed = false; var completed = false;
Action<SteamApps.PICSProductInfoCallback> cbMethod = ( packageInfo ) => Action<SteamApps.PICSProductInfoCallback> cbMethod = packageInfo =>
{ {
completed = !packageInfo.ResponsePending; completed = !packageInfo.ResponsePending;
foreach ( var package_value in packageInfo.Packages ) foreach (var package_value in packageInfo.Packages)
{ {
var package = package_value.Value; var package = package_value.Value;
PackageInfo[ package.ID ] = package; PackageInfo[package.ID] = package;
} }
foreach ( var package in packageInfo.UnknownPackages ) foreach (var package in packageInfo.UnknownPackages)
{ {
PackageInfo[package] = null; PackageInfo[package] = null;
} }
@ -254,78 +253,79 @@ namespace DepotDownloader
var packageRequests = new List<SteamApps.PICSRequest>(); var packageRequests = new List<SteamApps.PICSRequest>();
foreach ( var package in packages ) foreach (var package in packages)
{ {
var request = new SteamApps.PICSRequest( package ); var request = new SteamApps.PICSRequest(package);
if ( PackageTokens.TryGetValue( package, out var token ) ) if (PackageTokens.TryGetValue(package, out var token))
{ {
request.AccessToken = token; request.AccessToken = token;
request.Public = false; request.Public = false;
} }
packageRequests.Add( request ); packageRequests.Add(request);
} }
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.PICSGetProductInfo( new List<SteamApps.PICSRequest>(), packageRequests ), cbMethod ); callbacks.Subscribe(steamApps.PICSGetProductInfo(new List<SteamApps.PICSRequest>(), packageRequests), cbMethod);
}, () => { return completed; } ); }, () => { return completed; });
} }
public bool RequestFreeAppLicense( uint appId ) public bool RequestFreeAppLicense(uint appId)
{ {
bool success = false; var success = false;
bool completed = false; var completed = false;
Action<SteamApps.FreeLicenseCallback> cbMethod = ( resultInfo ) => Action<SteamApps.FreeLicenseCallback> cbMethod = resultInfo =>
{ {
completed = true; completed = true;
success = resultInfo.GrantedApps.Contains( appId ); success = resultInfo.GrantedApps.Contains(appId);
}; };
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.RequestFreeLicense( appId ), cbMethod ); callbacks.Subscribe(steamApps.RequestFreeLicense(appId), cbMethod);
}, () => { return completed; } ); }, () => { return completed; });
return success; return success;
} }
public void RequestDepotKey( uint depotId, uint appid = 0 ) public void RequestDepotKey(uint depotId, uint appid = 0)
{ {
if ( DepotKeys.ContainsKey( depotId ) || bAborted ) if (DepotKeys.ContainsKey(depotId) || bAborted)
return; return;
bool completed = false; var completed = false;
Action<SteamApps.DepotKeyCallback> cbMethod = ( depotKey ) => Action<SteamApps.DepotKeyCallback> cbMethod = depotKey =>
{ {
completed = true; completed = true;
Console.WriteLine( "Got depot key for {0} result: {1}", depotKey.DepotID, depotKey.Result ); Console.WriteLine("Got depot key for {0} result: {1}", depotKey.DepotID, depotKey.Result);
if ( depotKey.Result != EResult.OK ) if (depotKey.Result != EResult.OK)
{ {
Abort(); Abort();
return; return;
} }
DepotKeys[ depotKey.DepotID ] = depotKey.DepotKey; DepotKeys[depotKey.DepotID] = depotKey.DepotKey;
}; };
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.GetDepotDecryptionKey( depotId, appid ), cbMethod ); callbacks.Subscribe(steamApps.GetDepotDecryptionKey(depotId, appid), cbMethod);
}, () => { return completed; } ); }, () => { return completed; });
} }
public string ResolveCDNTopLevelHost(string host) public string ResolveCDNTopLevelHost(string host)
{ {
// SteamPipe CDN shares tokens with all hosts // SteamPipe CDN shares tokens with all hosts
if (host.EndsWith( ".steampipe.steamcontent.com" ) ) if (host.EndsWith(".steampipe.steamcontent.com"))
{ {
return "steampipe.steamcontent.com"; return "steampipe.steamcontent.com";
} }
else if (host.EndsWith(".steamcontent.com"))
if (host.EndsWith(".steamcontent.com"))
{ {
return "steamcontent.com"; return "steamcontent.com";
} }
@ -333,63 +333,63 @@ namespace DepotDownloader
return host; return host;
} }
public void RequestCDNAuthToken( uint appid, uint depotid, string host, string cdnKey ) public void RequestCDNAuthToken(uint appid, uint depotid, string host, string cdnKey)
{ {
if ( CDNAuthTokens.ContainsKey( cdnKey ) || bAborted ) if (CDNAuthTokens.ContainsKey(cdnKey) || bAborted)
return; return;
if ( !CDNAuthTokens.TryAdd( cdnKey, new TaskCompletionSource<SteamApps.CDNAuthTokenCallback>() ) ) if (!CDNAuthTokens.TryAdd(cdnKey, new TaskCompletionSource<SteamApps.CDNAuthTokenCallback>()))
return; return;
bool completed = false; var completed = false;
var timeoutDate = DateTime.Now.AddSeconds( 10 ); var timeoutDate = DateTime.Now.AddSeconds(10);
Action<SteamApps.CDNAuthTokenCallback> cbMethod = ( cdnAuth ) => Action<SteamApps.CDNAuthTokenCallback> cbMethod = cdnAuth =>
{ {
completed = true; completed = true;
Console.WriteLine( "Got CDN auth token for {0} result: {1} (expires {2})", host, cdnAuth.Result, cdnAuth.Expiration ); Console.WriteLine("Got CDN auth token for {0} result: {1} (expires {2})", host, cdnAuth.Result, cdnAuth.Expiration);
if ( cdnAuth.Result != EResult.OK ) if (cdnAuth.Result != EResult.OK)
{ {
Abort(); Abort();
return; return;
} }
CDNAuthTokens[cdnKey].TrySetResult( cdnAuth ); CDNAuthTokens[cdnKey].TrySetResult(cdnAuth);
}; };
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.GetCDNAuthToken( appid, depotid, host ), cbMethod ); callbacks.Subscribe(steamApps.GetCDNAuthToken(appid, depotid, host), cbMethod);
}, () => { return completed || DateTime.Now >= timeoutDate; } ); }, () => { return completed || DateTime.Now >= timeoutDate; });
} }
public void CheckAppBetaPassword( uint appid, string password ) public void CheckAppBetaPassword(uint appid, string password)
{ {
bool completed = false; var completed = false;
Action<SteamApps.CheckAppBetaPasswordCallback> cbMethod = ( appPassword ) => Action<SteamApps.CheckAppBetaPasswordCallback> cbMethod = appPassword =>
{ {
completed = true; completed = true;
Console.WriteLine( "Retrieved {0} beta keys with result: {1}", appPassword.BetaPasswords.Count, appPassword.Result ); Console.WriteLine("Retrieved {0} beta keys with result: {1}", appPassword.BetaPasswords.Count, appPassword.Result);
foreach ( var entry in appPassword.BetaPasswords ) foreach (var entry in appPassword.BetaPasswords)
{ {
AppBetaPasswords[ entry.Key ] = entry.Value; AppBetaPasswords[entry.Key] = entry.Value;
} }
}; };
WaitUntilCallback( () => WaitUntilCallback(() =>
{ {
callbacks.Subscribe( steamApps.CheckAppBetaPassword( appid, password ), cbMethod ); callbacks.Subscribe(steamApps.CheckAppBetaPassword(appid, password), cbMethod);
}, () => { return completed; } ); }, () => { return completed; });
} }
public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID pubFile) public PublishedFileDetails GetPublishedFileDetails(uint appId, PublishedFileID pubFile)
{ {
var pubFileRequest = new CPublishedFile_GetDetails_Request() { appid = appId }; var pubFileRequest = new CPublishedFile_GetDetails_Request { appid = appId };
pubFileRequest.publishedfileids.Add( pubFile ); pubFileRequest.publishedfileids.Add(pubFile);
bool completed = false; var completed = false;
PublishedFileDetails details = null; PublishedFileDetails details = null;
Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback => Action<SteamUnifiedMessages.ServiceMethodResponse> cbMethod = callback =>
@ -417,7 +417,7 @@ namespace DepotDownloader
public SteamCloud.UGCDetailsCallback GetUGCDetails(UGCHandle ugcHandle) public SteamCloud.UGCDetailsCallback GetUGCDetails(UGCHandle ugcHandle)
{ {
bool completed = false; var completed = false;
SteamCloud.UGCDetailsCallback details = null; SteamCloud.UGCDetailsCallback details = null;
Action<SteamCloud.UGCDetailsCallback> cbMethod = callback => Action<SteamCloud.UGCDetailsCallback> cbMethod = callback =>
@ -466,13 +466,14 @@ namespace DepotDownloader
this.steamClient.Connect(); this.steamClient.Connect();
} }
private void Abort( bool sendLogOff = true ) private void Abort(bool sendLogOff = true)
{ {
Disconnect( sendLogOff ); Disconnect(sendLogOff);
} }
public void Disconnect( bool sendLogOff = true )
public void Disconnect(bool sendLogOff = true)
{ {
if ( sendLogOff ) if (sendLogOff)
{ {
steamUser.LogOff(); steamUser.LogOff();
} }
@ -484,9 +485,9 @@ namespace DepotDownloader
steamClient.Disconnect(); steamClient.Disconnect();
// flush callbacks until our disconnected event // flush callbacks until our disconnected event
while ( !bDidDisconnect ) while (!bDidDisconnect)
{ {
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) ); callbacks.RunWaitAllCallbacks(TimeSpan.FromMilliseconds(100));
} }
} }
@ -498,82 +499,80 @@ namespace DepotDownloader
public void TryWaitForLoginKey() public void TryWaitForLoginKey()
{ {
if ( logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword ) return; if (logonDetails.Username == null || !credentials.LoggedOn || !ContentDownloader.Config.RememberPassword) return;
var totalWaitPeriod = DateTime.Now.AddSeconds( 3 ); var totalWaitPeriod = DateTime.Now.AddSeconds(3);
while ( true ) while (true)
{ {
DateTime now = DateTime.Now; var now = DateTime.Now;
if ( now >= totalWaitPeriod ) break; if (now >= totalWaitPeriod) break;
if ( bDidReceiveLoginKey ) break; if (bDidReceiveLoginKey) break;
callbacks.RunWaitAllCallbacks( TimeSpan.FromMilliseconds( 100 ) ); callbacks.RunWaitAllCallbacks(TimeSpan.FromMilliseconds(100));
} }
} }
private void WaitForCallbacks() private void WaitForCallbacks()
{ {
callbacks.RunWaitCallbacks( TimeSpan.FromSeconds( 1 ) ); callbacks.RunWaitCallbacks(TimeSpan.FromSeconds(1));
TimeSpan diff = DateTime.Now - connectTime; var diff = DateTime.Now - connectTime;
if ( diff > STEAM3_TIMEOUT && !bConnected ) if (diff > STEAM3_TIMEOUT && !bConnected)
{ {
Console.WriteLine( "Timeout connecting to Steam3." ); Console.WriteLine("Timeout connecting to Steam3.");
Abort(); Abort();
return;
} }
} }
private void ConnectedCallback( SteamClient.ConnectedCallback connected ) private void ConnectedCallback(SteamClient.ConnectedCallback connected)
{ {
Console.WriteLine( " Done!" ); Console.WriteLine(" Done!");
bConnecting = false; bConnecting = false;
bConnected = true; bConnected = true;
if ( !authenticatedUser ) if (!authenticatedUser)
{ {
Console.Write( "Logging anonymously into Steam3..." ); Console.Write("Logging anonymously into Steam3...");
steamUser.LogOnAnonymous(); steamUser.LogOnAnonymous();
} }
else else
{ {
Console.Write( "Logging '{0}' into Steam3...", logonDetails.Username ); Console.Write("Logging '{0}' into Steam3...", logonDetails.Username);
steamUser.LogOn( logonDetails ); steamUser.LogOn(logonDetails);
} }
} }
private void DisconnectedCallback( SteamClient.DisconnectedCallback disconnected ) private void DisconnectedCallback(SteamClient.DisconnectedCallback disconnected)
{ {
bDidDisconnect = true; bDidDisconnect = true;
// When recovering the connection, we want to reconnect even if the remote disconnects us // When recovering the connection, we want to reconnect even if the remote disconnects us
if ( !bIsConnectionRecovery && ( disconnected.UserInitiated || bExpectingDisconnectRemote ) ) if (!bIsConnectionRecovery && (disconnected.UserInitiated || bExpectingDisconnectRemote))
{ {
Console.WriteLine( "Disconnected from Steam" ); Console.WriteLine("Disconnected from Steam");
// Any operations outstanding need to be aborted // Any operations outstanding need to be aborted
bAborted = true; bAborted = true;
} }
else if ( connectionBackoff >= 10 ) else if (connectionBackoff >= 10)
{ {
Console.WriteLine( "Could not connect to Steam after 10 tries" ); Console.WriteLine("Could not connect to Steam after 10 tries");
Abort( false ); Abort(false);
} }
else if ( !bAborted ) else if (!bAborted)
{ {
if ( bConnecting ) if (bConnecting)
{ {
Console.WriteLine( "Connection to Steam failed. Trying again" ); Console.WriteLine("Connection to Steam failed. Trying again");
} }
else else
{ {
Console.WriteLine( "Lost connection to Steam. Reconnecting" ); Console.WriteLine("Lost connection to Steam. Reconnecting");
} }
Thread.Sleep( 1000 * ++connectionBackoff ); Thread.Sleep(1000 * ++connectionBackoff);
// Any connection related flags need to be reset here to match the state after Connect // Any connection related flags need to be reset here to match the state after Connect
ResetConnectionFlags(); ResetConnectionFlags();
@ -581,125 +580,128 @@ namespace DepotDownloader
} }
} }
private void LogOnCallback( SteamUser.LoggedOnCallback loggedOn ) private void LogOnCallback(SteamUser.LoggedOnCallback loggedOn)
{ {
bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied; var isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied;
bool is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor; var is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor;
bool isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword; var isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword;
if ( isSteamGuard || is2FA || isLoginKey ) if (isSteamGuard || is2FA || isLoginKey)
{ {
bExpectingDisconnectRemote = true; bExpectingDisconnectRemote = true;
Abort( false ); Abort(false);
if ( !isLoginKey ) if (!isLoginKey)
{ {
Console.WriteLine( "This account is protected by Steam Guard." ); Console.WriteLine("This account is protected by Steam Guard.");
} }
if ( is2FA ) if (is2FA)
{ {
Console.Write( "Please enter your 2 factor auth code from your authenticator app: " ); Console.Write("Please enter your 2 factor auth code from your authenticator app: ");
logonDetails.TwoFactorCode = Console.ReadLine(); logonDetails.TwoFactorCode = Console.ReadLine();
} }
else if ( isLoginKey ) else if (isLoginKey)
{ {
AccountSettingsStore.Instance.LoginKeys.Remove( logonDetails.Username ); AccountSettingsStore.Instance.LoginKeys.Remove(logonDetails.Username);
AccountSettingsStore.Save(); AccountSettingsStore.Save();
logonDetails.LoginKey = null; logonDetails.LoginKey = null;
if ( ContentDownloader.Config.SuppliedPassword != null ) if (ContentDownloader.Config.SuppliedPassword != null)
{ {
Console.WriteLine( "Login key was expired. Connecting with supplied password." ); Console.WriteLine("Login key was expired. Connecting with supplied password.");
logonDetails.Password = ContentDownloader.Config.SuppliedPassword; logonDetails.Password = ContentDownloader.Config.SuppliedPassword;
} }
else else
{ {
Console.Write( "Login key was expired. Please enter your password: " ); Console.Write("Login key was expired. Please enter your password: ");
logonDetails.Password = Util.ReadPassword(); logonDetails.Password = Util.ReadPassword();
} }
} }
else else
{ {
Console.Write( "Please enter the authentication code sent to your email address: " ); Console.Write("Please enter the authentication code sent to your email address: ");
logonDetails.AuthCode = Console.ReadLine(); logonDetails.AuthCode = Console.ReadLine();
} }
Console.Write( "Retrying Steam3 connection..." ); Console.Write("Retrying Steam3 connection...");
Connect(); Connect();
return; return;
} }
else if ( loggedOn.Result == EResult.TryAnotherCM )
if (loggedOn.Result == EResult.TryAnotherCM)
{ {
Console.Write( "Retrying Steam3 connection (TryAnotherCM)..." ); Console.Write("Retrying Steam3 connection (TryAnotherCM)...");
Reconnect(); Reconnect();
return; return;
} }
else if ( loggedOn.Result == EResult.ServiceUnavailable )
if (loggedOn.Result == EResult.ServiceUnavailable)
{ {
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result ); Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result);
Abort( false ); Abort(false);
return; return;
} }
else if ( loggedOn.Result != EResult.OK )
if (loggedOn.Result != EResult.OK)
{ {
Console.WriteLine( "Unable to login to Steam3: {0}", loggedOn.Result ); Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result);
Abort(); Abort();
return; return;
} }
Console.WriteLine( " Done!" ); Console.WriteLine(" Done!");
this.seq++; this.seq++;
credentials.LoggedOn = true; credentials.LoggedOn = true;
if ( ContentDownloader.Config.CellID == 0 ) if (ContentDownloader.Config.CellID == 0)
{ {
Console.WriteLine( "Using Steam3 suggested CellID: " + loggedOn.CellID ); Console.WriteLine("Using Steam3 suggested CellID: " + loggedOn.CellID);
ContentDownloader.Config.CellID = ( int )loggedOn.CellID; ContentDownloader.Config.CellID = (int)loggedOn.CellID;
} }
} }
private void SessionTokenCallback( SteamUser.SessionTokenCallback sessionToken ) private void SessionTokenCallback(SteamUser.SessionTokenCallback sessionToken)
{ {
Console.WriteLine( "Got session token!" ); Console.WriteLine("Got session token!");
credentials.SessionToken = sessionToken.SessionToken; credentials.SessionToken = sessionToken.SessionToken;
} }
private void LicenseListCallback( SteamApps.LicenseListCallback licenseList ) private void LicenseListCallback(SteamApps.LicenseListCallback licenseList)
{ {
if ( licenseList.Result != EResult.OK ) if (licenseList.Result != EResult.OK)
{ {
Console.WriteLine( "Unable to get license list: {0} ", licenseList.Result ); Console.WriteLine("Unable to get license list: {0} ", licenseList.Result);
Abort(); Abort();
return; return;
} }
Console.WriteLine( "Got {0} licenses for account!", licenseList.LicenseList.Count ); Console.WriteLine("Got {0} licenses for account!", licenseList.LicenseList.Count);
Licenses = licenseList.LicenseList; Licenses = licenseList.LicenseList;
foreach ( var license in licenseList.LicenseList ) foreach (var license in licenseList.LicenseList)
{ {
if ( license.AccessToken > 0 ) if (license.AccessToken > 0)
{ {
PackageTokens.TryAdd( license.PackageID, license.AccessToken ); PackageTokens.TryAdd(license.PackageID, license.AccessToken);
} }
} }
} }
private void UpdateMachineAuthCallback( SteamUser.UpdateMachineAuthCallback machineAuth ) private void UpdateMachineAuthCallback(SteamUser.UpdateMachineAuthCallback machineAuth)
{ {
byte[] hash = Util.SHAHash( machineAuth.Data ); var hash = Util.SHAHash(machineAuth.Data);
Console.WriteLine( "Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash ); Console.WriteLine("Got Machine Auth: {0} {1} {2} {3}", machineAuth.FileName, machineAuth.Offset, machineAuth.BytesToWrite, machineAuth.Data.Length, hash);
AccountSettingsStore.Instance.SentryData[ logonDetails.Username ] = machineAuth.Data; AccountSettingsStore.Instance.SentryData[logonDetails.Username] = machineAuth.Data;
AccountSettingsStore.Save(); AccountSettingsStore.Save();
var authResponse = new SteamUser.MachineAuthDetails var authResponse = new SteamUser.MachineAuthDetails
@ -720,21 +722,19 @@ namespace DepotDownloader
}; };
// send off our response // send off our response
steamUser.SendMachineAuthResponse( authResponse ); steamUser.SendMachineAuthResponse(authResponse);
} }
private void LoginKeyCallback( SteamUser.LoginKeyCallback loginKey ) private void LoginKeyCallback(SteamUser.LoginKeyCallback loginKey)
{ {
Console.WriteLine( "Accepted new login key for account {0}", logonDetails.Username ); Console.WriteLine("Accepted new login key for account {0}", logonDetails.Username);
AccountSettingsStore.Instance.LoginKeys[ logonDetails.Username ] = loginKey.LoginKey; AccountSettingsStore.Instance.LoginKeys[logonDetails.Username] = loginKey.LoginKey;
AccountSettingsStore.Save(); AccountSettingsStore.Save();
steamUser.AcceptNewLoginKey( loginKey ); steamUser.AcceptNewLoginKey(loginKey);
bDidReceiveLoginKey = true; bDidReceiveLoginKey = true;
} }
} }
} }

@ -17,11 +17,13 @@ namespace DepotDownloader
{ {
return "windows"; return "windows";
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{ {
return "macos"; return "macos";
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{ {
return "linux"; return "linux";
} }
@ -37,30 +39,31 @@ namespace DepotDownloader
public static string ReadPassword() public static string ReadPassword()
{ {
ConsoleKeyInfo keyInfo; ConsoleKeyInfo keyInfo;
StringBuilder password = new StringBuilder(); var password = new StringBuilder();
do do
{ {
keyInfo = Console.ReadKey( true ); keyInfo = Console.ReadKey(true);
if ( keyInfo.Key == ConsoleKey.Backspace ) if (keyInfo.Key == ConsoleKey.Backspace)
{ {
if ( password.Length > 0 ) if (password.Length > 0)
{ {
password.Remove( password.Length - 1, 1 ); password.Remove(password.Length - 1, 1);
Console.Write( "\x1B[1D\x1B[1P" ); Console.Write("\x1B[1D\x1B[1P");
} }
continue; continue;
} }
/* Printable ASCII characters only */ /* Printable ASCII characters only */
char c = keyInfo.KeyChar; var c = keyInfo.KeyChar;
if ( c >= ' ' && c <= '~' ) if (c >= ' ' && c <= '~')
{ {
password.Append( c ); password.Append(c);
Console.Write( '*' ); Console.Write('*');
} }
} while ( keyInfo.Key != ConsoleKey.Enter ); } while (keyInfo.Key != ConsoleKey.Enter);
return password.ToString(); return password.ToString();
} }
@ -73,7 +76,7 @@ namespace DepotDownloader
foreach (var data in chunkdata) foreach (var data in chunkdata)
{ {
byte[] chunk = new byte[data.UncompressedLength]; var chunk = new byte[data.UncompressedLength];
fs.Seek((long)data.Offset, SeekOrigin.Begin); fs.Seek((long)data.Offset, SeekOrigin.Begin);
read = fs.Read(chunk, 0, (int)data.UncompressedLength); read = fs.Read(chunk, 0, (int)data.UncompressedLength);
@ -88,7 +91,7 @@ namespace DepotDownloader
tempchunk = chunk; tempchunk = chunk;
} }
byte[] adler = AdlerHash(tempchunk); var adler = AdlerHash(tempchunk);
if (!adler.SequenceEqual(data.Checksum)) if (!adler.SequenceEqual(data.Checksum))
{ {
neededChunks.Add(data); neededChunks.Add(data);
@ -101,42 +104,43 @@ namespace DepotDownloader
public static byte[] AdlerHash(byte[] input) public static byte[] AdlerHash(byte[] input)
{ {
uint a = 0, b = 0; uint a = 0, b = 0;
for (int i = 0; i < input.Length; i++) for (var i = 0; i < input.Length; i++)
{ {
a = (a + input[i]) % 65521; a = (a + input[i]) % 65521;
b = (b + a) % 65521; b = (b + a) % 65521;
} }
return BitConverter.GetBytes(a | (b << 16)); return BitConverter.GetBytes(a | (b << 16));
} }
public static byte[] SHAHash( byte[] input ) public static byte[] SHAHash(byte[] input)
{ {
using (var sha = SHA1.Create()) using (var sha = SHA1.Create())
{ {
var output = sha.ComputeHash( input ); var output = sha.ComputeHash(input);
return output; return output;
} }
} }
public static byte[] DecodeHexString( string hex ) public static byte[] DecodeHexString(string hex)
{ {
if ( hex == null ) if (hex == null)
return null; return null;
int chars = hex.Length; var chars = hex.Length;
byte[] bytes = new byte[ chars / 2 ]; var bytes = new byte[chars / 2];
for ( int i = 0 ; i < chars ; i += 2 ) for (var i = 0; i < chars; i += 2)
bytes[ i / 2 ] = Convert.ToByte( hex.Substring( i, 2 ), 16 ); bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes; return bytes;
} }
public static string EncodeHexString( byte[] input ) public static string EncodeHexString(byte[] input)
{ {
return input.Aggregate( new StringBuilder(), return input.Aggregate(new StringBuilder(),
( sb, v ) => sb.Append( v.ToString( "x2" ) ) (sb, v) => sb.Append(v.ToString("x2"))
).ToString(); ).ToString();
} }
@ -145,32 +149,31 @@ namespace DepotDownloader
if (taskFactories == null) throw new ArgumentNullException(nameof(taskFactories)); if (taskFactories == null) throw new ArgumentNullException(nameof(taskFactories));
if (maxDegreeOfParallelism <= 0) throw new ArgumentException(nameof(maxDegreeOfParallelism)); if (maxDegreeOfParallelism <= 0) throw new ArgumentException(nameof(maxDegreeOfParallelism));
Func<Task>[] queue = taskFactories.ToArray(); var queue = taskFactories.ToArray();
if (queue.Length == 0) if (queue.Length == 0)
{ {
return; return;
} }
List<Task> tasksInFlight = new List<Task>(maxDegreeOfParallelism); var tasksInFlight = new List<Task>(maxDegreeOfParallelism);
int index = 0; var index = 0;
do do
{ {
while (tasksInFlight.Count < maxDegreeOfParallelism && index < queue.Length) while (tasksInFlight.Count < maxDegreeOfParallelism && index < queue.Length)
{ {
Func<Task> taskFactory = queue[index++]; var taskFactory = queue[index++];
tasksInFlight.Add(taskFactory()); tasksInFlight.Add(taskFactory());
} }
Task completedTask = await Task.WhenAny(tasksInFlight).ConfigureAwait(false); var completedTask = await Task.WhenAny(tasksInFlight).ConfigureAwait(false);
await completedTask.ConfigureAwait(false); await completedTask.ConfigureAwait(false);
tasksInFlight.Remove(completedTask); tasksInFlight.Remove(completedTask);
} } while (index < queue.Length || tasksInFlight.Count != 0);
while (index < queue.Length || tasksInFlight.Count != 0);
} }
} }
} }

Loading…
Cancel
Save