Convert to .NET Core.

Works on macOS, haven't tried Windows / .NET Framework yet.
pull/19/head
Netshroud 9 years ago
parent 35aa666ea5
commit c0b6d0ce3c

@ -25,7 +25,7 @@ namespace DepotDownloader
private BlockingCollection<CDNClient.Server> availableServerEndpoints;
private AutoResetEvent populatePoolEvent;
private Thread monitorThread;
private Task monitorTask;
public CDNClientPool(Steam3Session steamSession)
{
@ -37,13 +37,10 @@ namespace DepotDownloader
populatePoolEvent = new AutoResetEvent(true);
monitorThread = new Thread(ConnectionPoolMonitor);
monitorThread.Name = "CDNClient Pool Monitor";
monitorThread.IsBackground = true;
monitorThread.Start();
monitorTask = Task.Factory.StartNew(ConnectionPoolMonitorAsync).Unwrap();
}
private List<CDNClient.Server> FetchBootstrapServerList()
private async Task<IList<CDNClient.Server>> FetchBootstrapServerListAsync()
{
CDNClient bootstrap = new CDNClient(steamSession.steamClient);
@ -51,7 +48,7 @@ namespace DepotDownloader
{
try
{
var cdnServers = bootstrap.FetchServerList(cellId: (uint)ContentDownloader.Config.CellID);
var cdnServers = await bootstrap.FetchServerListAsync(cellId: (uint)ContentDownloader.Config.CellID).ConfigureAwait(false);
if (cdnServers != null)
{
return cdnServers;
@ -64,7 +61,7 @@ namespace DepotDownloader
}
}
private void ConnectionPoolMonitor()
private async Task ConnectionPoolMonitorAsync()
{
while(true)
{
@ -75,7 +72,7 @@ namespace DepotDownloader
steamSession.steamClient.IsConnected &&
steamSession.steamClient.GetServersOfType(EServerType.CS).Count > 0)
{
var servers = FetchBootstrapServerList();
var servers = await FetchBootstrapServerListAsync();
var weightedCdnServers = servers.Select(x =>
{
@ -101,7 +98,7 @@ namespace DepotDownloader
activeClientAuthed.TryRemove(client, out authData);
}
private CDNClient BuildConnection(uint appId, uint depotId, byte[] depotKey, CDNClient.Server serverSeed, CancellationToken token)
private async Task<CDNClient> BuildConnectionAsync(uint appId, uint depotId, byte[] depotKey, CDNClient.Server serverSeed, CancellationToken token)
{
CDNClient.Server server = null;
CDNClient client = null;
@ -147,8 +144,8 @@ namespace DepotDownloader
}
}
client.Connect(server);
client.AuthenticateDepot(depotId, depotKey, cdnAuthToken);
await client.ConnectAsync(server).ConfigureAwait(false);
await client.AuthenticateDepotAsync(depotId, depotKey, cdnAuthToken).ConfigureAwait(false);
}
catch (Exception ex)
{
@ -168,7 +165,7 @@ namespace DepotDownloader
return client;
}
private bool ReauthConnection(CDNClient client, CDNClient.Server server, uint appId, uint depotId, byte[] depotKey)
private async Task<bool> ReauthConnectionAsync(CDNClient client, CDNClient.Server server, uint appId, uint depotId, byte[] depotKey)
{
DebugLog.Assert(server.Type == "CDN" || steamSession.AppTickets[depotId] == null, "CDNClientPool", "Re-authing a CDN or anonymous connection");
@ -193,7 +190,7 @@ namespace DepotDownloader
}
}
client.AuthenticateDepot(depotId, depotKey, cdnAuthToken);
await client.AuthenticateDepotAsync(depotId, depotKey, cdnAuthToken).ConfigureAwait(false);
activeClientAuthed[client] = Tuple.Create(depotId, server);
return true;
}
@ -205,7 +202,7 @@ namespace DepotDownloader
return false;
}
public CDNClient GetConnectionForDepot(uint appId, uint depotId, byte[] depotKey, CancellationToken token)
public async Task<CDNClient> GetConnectionForDepotAsync(uint appId, uint depotId, byte[] depotKey, CancellationToken token)
{
CDNClient client = null;
@ -216,24 +213,24 @@ namespace DepotDownloader
// if we couldn't find a connection, make one now
if (client == null)
{
client = BuildConnection(appId, depotId, depotKey, null, token);
client = await BuildConnectionAsync(appId, depotId, depotKey, null, token).ConfigureAwait(false);
}
// if we couldn't find the authorization data or it's not authed to this depotid, re-initialize
if (!activeClientAuthed.TryGetValue(client, out authData) || authData.Item1 != depotId)
{
if (authData.Item2.Type == "CDN" && ReauthConnection(client, authData.Item2, appId, depotId, depotKey))
if (authData.Item2.Type == "CDN" && await ReauthConnectionAsync(client, authData.Item2, appId, depotId, depotKey).ConfigureAwait(false))
{
Console.WriteLine("Re-authed CDN connection to content server {0} from {1} to {2}", authData.Item2, authData.Item1, depotId);
}
else if (authData.Item2.Type == "CS" && steamSession.AppTickets[depotId] == null && ReauthConnection(client, authData.Item2, appId, depotId, depotKey))
else if (authData.Item2.Type == "CS" && steamSession.AppTickets[depotId] == null && await ReauthConnectionAsync(client, authData.Item2, appId, depotId, depotKey).ConfigureAwait(false))
{
Console.WriteLine("Re-authed anonymous connection to content server {0} from {1} to {2}", authData.Item2, authData.Item1, depotId);
}
else
{
ReleaseConnection(client);
client = BuildConnection(appId, depotId, depotKey, authData.Item2, token);
client = await BuildConnectionAsync(appId, depotId, depotKey, authData.Item2, token).ConfigureAwait(false);
}
}

@ -238,62 +238,62 @@ namespace DepotDownloader
if (branch != "Public" && node == KeyValue.Invalid)
{
var node_encrypted = manifests_encrypted[branch];
if (node_encrypted != KeyValue.Invalid)
{
string password = Config.BetaPassword;
if (password == null)
{
Console.Write("Please enter the password for branch {0}: ", branch);
Config.BetaPassword = password = Console.ReadLine();
}
var encrypted_v1 = node_encrypted["encrypted_gid"];
var encrypted_v2 = node_encrypted["encrypted_gid_2"];
if (encrypted_v1 != KeyValue.Invalid)
{
byte[] input = Util.DecodeHexString(encrypted_v1.Value);
byte[] manifest_bytes = CryptoHelper.VerifyAndDecryptPassword(input, password);
if (manifest_bytes == null)
{
Console.WriteLine("Password was invalid for branch {0}", branch);
return INVALID_MANIFEST_ID;
}
return BitConverter.ToUInt64(manifest_bytes, 0);
}
else if (encrypted_v2 != KeyValue.Invalid)
{
// Submit the password to Steam now to get encryption keys
steam3.CheckAppBetaPassword(appId, Config.BetaPassword);
if (!steam3.AppBetaPasswords.ContainsKey(branch))
{
Console.WriteLine("Password was invalid for branch {0}", branch);
return INVALID_MANIFEST_ID;
}
byte[] input = Util.DecodeHexString(encrypted_v2.Value);
byte[] manifest_bytes;
try
{
manifest_bytes = CryptoHelper.SymmetricDecryptECB(input, steam3.AppBetaPasswords[branch]);
}
catch (Exception e)
{
Console.WriteLine("Failed to decrypt branch {0}: {1}", branch, e.Message);
return INVALID_MANIFEST_ID;
}
return BitConverter.ToUInt64(manifest_bytes, 0);
}
else
{
Console.WriteLine("Unhandled depot encryption for depotId {0}", depotId);
return INVALID_MANIFEST_ID;
}
if (node_encrypted != KeyValue.Invalid)
{
string password = Config.BetaPassword;
if (password == null)
{
Console.Write("Please enter the password for branch {0}: ", branch);
Config.BetaPassword = password = Console.ReadLine();
}
var encrypted_v1 = node_encrypted["encrypted_gid"];
var encrypted_v2 = node_encrypted["encrypted_gid_2"];
if (encrypted_v1 != KeyValue.Invalid)
{
byte[] input = Util.DecodeHexString(encrypted_v1.Value);
byte[] manifest_bytes = CryptoHelper.VerifyAndDecryptPassword(input, password);
if (manifest_bytes == null)
{
Console.WriteLine("Password was invalid for branch {0}", branch);
return INVALID_MANIFEST_ID;
}
return BitConverter.ToUInt64(manifest_bytes, 0);
}
else if (encrypted_v2 != KeyValue.Invalid)
{
// Submit the password to Steam now to get encryption keys
steam3.CheckAppBetaPassword(appId, Config.BetaPassword);
if (!steam3.AppBetaPasswords.ContainsKey(branch))
{
Console.WriteLine("Password was invalid for branch {0}", branch);
return INVALID_MANIFEST_ID;
}
byte[] input = Util.DecodeHexString(encrypted_v2.Value);
byte[] manifest_bytes;
try
{
manifest_bytes = CryptoHelper.SymmetricDecryptECB(input, steam3.AppBetaPasswords[branch]);
}
catch (Exception e)
{
Console.WriteLine("Failed to decrypt branch {0}: {1}", branch, e.Message);
return INVALID_MANIFEST_ID;
}
return BitConverter.ToUInt64(manifest_bytes, 0);
}
else
{
Console.WriteLine("Unhandled depot encryption for depotId {0}", depotId);
return INVALID_MANIFEST_ID;
}
}
return INVALID_MANIFEST_ID;
@ -362,22 +362,22 @@ namespace DepotDownloader
steam3.Disconnect();
}
public static void DownloadApp(uint appId, uint depotId, string branch, bool forceDepot = false)
public static async Task DownloadAppAsync(uint appId, uint depotId, string branch, bool forceDepot = false)
{
if(steam3 != null)
steam3.RequestAppInfo(appId);
if (!AccountHasAccess(appId))
{
if (steam3.RequestFreeAppLicense(appId))
{
Console.WriteLine("Obtained FreeOnDemand license for app {0}", appId);
if (steam3.RequestFreeAppLicense(appId))
{
Console.WriteLine("Obtained FreeOnDemand license for app {0}", appId);
}
else
{
string contentName = GetAppOrDepotName(INVALID_DEPOT_ID, appId);
Console.WriteLine("App {0} ({1}) is not available from this account.", appId, contentName);
return;
else
{
string contentName = GetAppOrDepotName(INVALID_DEPOT_ID, appId);
Console.WriteLine("App {0} ({1}) is not available from this account.", appId, contentName);
return;
}
}
@ -451,7 +451,7 @@ namespace DepotDownloader
try
{
DownloadSteam3(appId, infos);
await DownloadSteam3Async(appId, infos).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@ -524,7 +524,7 @@ namespace DepotDownloader
public ProtoManifest.ChunkData NewChunk { get; private set; }
}
private static void DownloadSteam3( uint appId, List<DepotDownloadInfo> depots )
private static async Task DownloadSteam3Async( uint appId, List<DepotDownloadInfo> depots )
{
ulong TotalBytesCompressed = 0;
ulong TotalBytesUncompressed = 0;
@ -583,9 +583,9 @@ namespace DepotDownloader
{
CDNClient client = null;
try {
client = cdnPool.GetConnectionForDepot(appId, depot.id, depot.depotKey, CancellationToken.None);
client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, CancellationToken.None).ConfigureAwait(false);
depotManifest = client.DownloadManifest(depot.id, depot.manifestId);
depotManifest = await client.DownloadManifestAsync(depot.id, depot.manifestId).ConfigureAwait(false);
cdnPool.ReturnConnection(client);
}
@ -677,200 +677,210 @@ namespace DepotDownloader
}
});
var semaphore = new SemaphoreSlim(Config.MaxDownloads);
filesAfterExclusions.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory))
.AsParallel().WithCancellation(cts.Token).WithDegreeOfParallelism(Config.MaxDownloads)
.ForAll(file =>
.ForAll(async file =>
{
string fileFinalPath = Path.Combine(depot.installDir, file.FileName);
string fileStagingPath = Path.Combine(stagingDir, file.FileName);
// This may still exist if the previous run exited before cleanup
if (File.Exists(fileStagingPath))
try
{
File.Delete(fileStagingPath);
}
await semaphore.WaitAsync().ConfigureAwait(false);
FileStream fs = null;
List<ProtoManifest.ChunkData> neededChunks;
FileInfo fi = new FileInfo(fileFinalPath);
if (!fi.Exists)
{
// create new file. need all chunks
fs = File.Create(fileFinalPath);
fs.SetLength((long)file.TotalSize);
neededChunks = new List<ProtoManifest.ChunkData>(file.Chunks);
}
else
{
// open existing
ProtoManifest.FileData oldManifestFile = null;
if (oldProtoManifest != null)
string fileFinalPath = Path.Combine(depot.installDir, file.FileName);
string fileStagingPath = Path.Combine(stagingDir, file.FileName);
// This may still exist if the previous run exited before cleanup
if (File.Exists(fileStagingPath))
{
oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName);
File.Delete(fileStagingPath);
}
if (oldManifestFile != null)
FileStream fs = null;
List<ProtoManifest.ChunkData> neededChunks;
FileInfo fi = new FileInfo(fileFinalPath);
if (!fi.Exists)
{
neededChunks = new List<ProtoManifest.ChunkData>();
if (Config.VerifyAll || !oldManifestFile.FileHash.SequenceEqual(file.FileHash))
// create new file. need all chunks
fs = File.Create(fileFinalPath);
fs.SetLength((long)file.TotalSize);
neededChunks = new List<ProtoManifest.ChunkData>(file.Chunks);
}
else
{
// open existing
ProtoManifest.FileData oldManifestFile = null;
if (oldProtoManifest != null)
{
// we have a version of this file, but it doesn't fully match what we want
oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName);
}
var matchingChunks = new List<ChunkMatch>();
if (oldManifestFile != null)
{
neededChunks = new List<ProtoManifest.ChunkData>();
foreach (var chunk in file.Chunks)
if (Config.VerifyAll || !oldManifestFile.FileHash.SequenceEqual(file.FileHash))
{
var oldChunk = oldManifestFile.Chunks.FirstOrDefault(c => c.ChunkID.SequenceEqual(chunk.ChunkID));
if (oldChunk != null)
{
matchingChunks.Add(new ChunkMatch(oldChunk, chunk));
}
else
{
neededChunks.Add(chunk);
}
}
// we have a version of this file, but it doesn't fully match what we want
File.Move(fileFinalPath, fileStagingPath);
var matchingChunks = new List<ChunkMatch>();
fs = File.Open(fileFinalPath, FileMode.Create);
fs.SetLength((long)file.TotalSize);
using (var fsOld = File.Open(fileStagingPath, FileMode.Open))
{
foreach (var match in matchingChunks)
foreach (var chunk in file.Chunks)
{
fsOld.Seek((long)match.OldChunk.Offset, SeekOrigin.Begin);
byte[] tmp = new byte[match.OldChunk.UncompressedLength];
fsOld.Read(tmp, 0, tmp.Length);
byte[] adler = Util.AdlerHash(tmp);
if (!adler.SequenceEqual(match.OldChunk.Checksum))
var oldChunk = oldManifestFile.Chunks.FirstOrDefault(c => c.ChunkID.SequenceEqual(chunk.ChunkID));
if (oldChunk != null)
{
neededChunks.Add(match.NewChunk);
matchingChunks.Add(new ChunkMatch(oldChunk, chunk));
}
else
{
fs.Seek((long)match.NewChunk.Offset, SeekOrigin.Begin);
fs.Write(tmp, 0, tmp.Length);
neededChunks.Add(chunk);
}
}
}
File.Delete(fileStagingPath);
}
}
else
{
// No old manifest or file not in old manifest. We must validate.
File.Move(fileFinalPath, fileStagingPath);
fs = File.Open(fileFinalPath, FileMode.Open);
if ((ulong)fi.Length != file.TotalSize)
{
fs.SetLength((long)file.TotalSize);
}
fs = File.Open(fileFinalPath, FileMode.Create);
fs.SetLength((long)file.TotalSize);
neededChunks = Util.ValidateSteam3FileChecksums(fs, file.Chunks.OrderBy(x => x.Offset).ToArray());
}
if (neededChunks.Count() == 0)
{
size_downloaded += file.TotalSize;
Console.WriteLine("{0,6:#00.00}% {1}", ((float)size_downloaded / (float)complete_download_size) * 100.0f, fileFinalPath);
if (fs != null)
fs.Close();
return;
}
else
{
size_downloaded += (file.TotalSize - (ulong)neededChunks.Select(x => (long)x.UncompressedLength).Sum());
}
}
using (var fsOld = File.Open(fileStagingPath, FileMode.Open))
{
foreach (var match in matchingChunks)
{
fsOld.Seek((long)match.OldChunk.Offset, SeekOrigin.Begin);
byte[] tmp = new byte[match.OldChunk.UncompressedLength];
fsOld.Read(tmp, 0, tmp.Length);
byte[] adler = Util.AdlerHash(tmp);
if (!adler.SequenceEqual(match.OldChunk.Checksum))
{
neededChunks.Add(match.NewChunk);
}
else
{
fs.Seek((long)match.NewChunk.Offset, SeekOrigin.Begin);
fs.Write(tmp, 0, tmp.Length);
}
}
}
foreach (var chunk in neededChunks)
{
if (cts.IsCancellationRequested) break;
File.Delete(fileStagingPath);
}
}
else
{
// No old manifest or file not in old manifest. We must validate.
string chunkID = Util.EncodeHexString(chunk.ChunkID);
CDNClient.DepotChunk chunkData = null;
fs = File.Open(fileFinalPath, FileMode.Open);
if ((ulong)fi.Length != file.TotalSize)
{
fs.SetLength((long)file.TotalSize);
}
while (!cts.IsCancellationRequested)
{
CDNClient client;
try
neededChunks = Util.ValidateSteam3FileChecksums(fs, file.Chunks.OrderBy(x => x.Offset).ToArray());
}
if (neededChunks.Count() == 0)
{
client = cdnPool.GetConnectionForDepot(appId, depot.id, depot.depotKey, cts.Token);
size_downloaded += file.TotalSize;
Console.WriteLine("{0,6:#00.00}% {1}", ((float)size_downloaded / (float)complete_download_size) * 100.0f, fileFinalPath);
if (fs != null)
fs.Dispose();
return;
}
catch (OperationCanceledException)
else
{
break;
size_downloaded += (file.TotalSize - (ulong)neededChunks.Select(x => (long)x.UncompressedLength).Sum());
}
}
DepotManifest.ChunkData data = new DepotManifest.ChunkData();
data.ChunkID = chunk.ChunkID;
data.Checksum = chunk.Checksum;
data.Offset = chunk.Offset;
data.CompressedLength = chunk.CompressedLength;
data.UncompressedLength = chunk.UncompressedLength;
foreach (var chunk in neededChunks)
{
if (cts.IsCancellationRequested) break;
try
{
chunkData = client.DownloadDepotChunk(depot.id, data);
cdnPool.ReturnConnection(client);
break;
}
catch (WebException e)
string chunkID = Util.EncodeHexString(chunk.ChunkID);
CDNClient.DepotChunk chunkData = null;
while (!cts.IsCancellationRequested)
{
cdnPool.ReturnBrokenConnection(client);
CDNClient client;
try
{
client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, cts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}
if (e.Status == WebExceptionStatus.ProtocolError)
DepotManifest.ChunkData data = new DepotManifest.ChunkData();
data.ChunkID = chunk.ChunkID;
data.Checksum = chunk.Checksum;
data.Offset = chunk.Offset;
data.CompressedLength = chunk.CompressedLength;
data.UncompressedLength = chunk.UncompressedLength;
try
{
var response = e.Response as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
chunkData = await client.DownloadDepotChunkAsync(depot.id, data).ConfigureAwait(false);
cdnPool.ReturnConnection(client);
break;
}
catch (WebException e)
{
cdnPool.ReturnBrokenConnection(client);
if (e.Status == WebExceptionStatus.ProtocolError)
{
Console.WriteLine("Encountered 401 for chunk {0}. Aborting.", chunkID);
cts.Cancel();
break;
var response = e.Response as HttpWebResponse;
if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden)
{
Console.WriteLine("Encountered 401 for chunk {0}. Aborting.", chunkID);
cts.Cancel();
break;
}
else
{
Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, response.StatusCode);
}
}
else
{
Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, response.StatusCode);
Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, e.Status);
}
}
else
catch (Exception e)
{
Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, e.Status);
cdnPool.ReturnBrokenConnection(client);
Console.WriteLine("Encountered unexpected error downloading chunk {0}: {1}", chunkID, e.Message);
}
}
catch (Exception e)
if (chunkData == null)
{
cdnPool.ReturnBrokenConnection(client);
Console.WriteLine("Encountered unexpected error downloading chunk {0}: {1}", chunkID, e.Message);
Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.id);
return;
}
}
if (chunkData == null)
{
Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.id);
return;
}
TotalBytesCompressed += chunk.CompressedLength;
DepotBytesCompressed += chunk.CompressedLength;
TotalBytesUncompressed += chunk.UncompressedLength;
DepotBytesUncompressed += chunk.UncompressedLength;
TotalBytesCompressed += chunk.CompressedLength;
DepotBytesCompressed += chunk.CompressedLength;
TotalBytesUncompressed += chunk.UncompressedLength;
DepotBytesUncompressed += chunk.UncompressedLength;
fs.Seek((long)chunk.Offset, SeekOrigin.Begin);
fs.Write(chunkData.Data, 0, chunkData.Data.Length);
fs.Seek((long)chunk.Offset, SeekOrigin.Begin);
fs.Write(chunkData.Data, 0, chunkData.Data.Length);
size_downloaded += chunk.UncompressedLength;
}
size_downloaded += chunk.UncompressedLength;
}
fs.Close();
fs.Dispose();
Console.WriteLine("{0,6:#00.00}% {1}", ((float)size_downloaded / (float)complete_download_size) * 100.0f, fileFinalPath);
Console.WriteLine("{0,6:#00.00}% {1}", ((float)size_downloaded / (float)complete_download_size) * 100.0f, fileFinalPath);
}
finally
{
semaphore.Release();
}
});
ConfigStore.TheConfig.LastManifests[depot.id] = depot.manifestId;

@ -1,124 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{39159C47-ACD3-449F-96CA-4F30C8ED147A}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DepotDownloader</RootNamespace>
<AssemblyName>DepotDownloader</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<UpgradeBackupLocation />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<PlatformTarget>AnyCPU</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="protobuf-net, Version=2.0.0.668, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
<SpecificVersion>True</SpecificVersion>
<HintPath>..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="SteamKit2, Version=1.8.1.0, Culture=neutral, PublicKeyToken=ed3ce47ed5aad940, processorArchitecture=MSIL">
<HintPath>..\packages\SteamKit2.1.8.1\lib\net45\SteamKit2.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CDNClientPool.cs" />
<Compile Include="ContentDownloader.cs" />
<Compile Include="ConfigStore.cs" />
<Compile Include="DownloadConfig.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProtoManifest.cs" />
<Compile Include="Steam3Session.cs" />
<Compile Include="Util.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp1.1;net46</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\SteamKit\SteamKit2\SteamKit2\SteamKit2.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="2.1.0" />
<PackageReference Include="system.componentmodel.typeconverter" Version="4.3.0" />
</ItemGroup>
</Project>

@ -4,12 +4,16 @@ using System.IO;
using System.Text.RegularExpressions;
using SteamKit2;
using System.ComponentModel;
using System.Threading.Tasks;
namespace DepotDownloader
{
class Program
{
static void Main( string[] args )
=> MainAsync(args).GetAwaiter().GetResult();
static async Task MainAsync( string[] args )
{
if ( args.Length == 0 )
{
@ -19,7 +23,7 @@ namespace DepotDownloader
DebugLog.Enabled = false;
ConfigStore.LoadFromFile(Path.Combine(Environment.CurrentDirectory, "DepotDownloader.config"));
ConfigStore.LoadFromFile(Path.Combine(Directory.GetCurrentDirectory(), "DepotDownloader.config"));
bool bDumpManifest = HasParameter( args, "-manifest-only" );
uint appId = GetParameter<uint>( args, "-app", ContentDownloader.INVALID_APP_ID );
@ -110,7 +114,7 @@ namespace DepotDownloader
if (ContentDownloader.InitializeSteam3(username, password))
{
ContentDownloader.DownloadApp(appId, depotId, branch, forceDepot);
await ContentDownloader.DownloadAppAsync(appId, depotId, branch, forceDepot).ConfigureAwait(false);
ContentDownloader.ShutdownSteam3();
}
}

@ -31,4 +31,4 @@ using System.Runtime.InteropServices;
// 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.2.1.*")]
[assembly: AssemblyVersion("2.2.2.0")]

@ -11,60 +11,19 @@ namespace DepotDownloader
{
static class Util
{
[DllImport( "libc" )]
static extern int uname( IntPtr buf );
static int _isMacOSX = -1;
// Environment.OSVersion.Platform returns PlatformID.Unix under Mono on OS X
// Code adapted from Mono: mcs/class/Managed.Windows.Forms/System.Windows.Forms/XplatUI.cs
private static bool IsMacOSX()
public static string GetSteamOS()
{
if ( _isMacOSX != -1 )
return _isMacOSX == 1;
IntPtr buf = IntPtr.Zero;
try
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// The size of the utsname struct varies from system to system, but this _seems_ more than enough
buf = Marshal.AllocHGlobal( 4096 );
if ( uname( buf ) == 0 )
{
string sys = Marshal.PtrToStringAnsi( buf );
if ( sys == "Darwin" )
{
_isMacOSX = 1;
return true;
}
}
return "windows";
}
catch
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// Do nothing?
return "macos";
}
finally
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
if ( buf != IntPtr.Zero )
Marshal.FreeHGlobal( buf );
}
_isMacOSX = 0;
return false;
}
public static string GetSteamOS()
{
switch (Environment.OSVersion.Platform)
{
case PlatformID.Win32NT:
case PlatformID.Win32Windows:
return "windows";
case PlatformID.MacOSX:
return "macos";
case PlatformID.Unix:
return IsMacOSX() ? "macos" : "linux";
return "linux";
}
return "unknown";
@ -141,13 +100,12 @@ namespace DepotDownloader
public static byte[] SHAHash( byte[] input )
{
SHA1Managed sha = new SHA1Managed();
byte[] output = sha.ComputeHash( input );
sha.Clear();
using (var sha = SHA1.Create())
{
var output = sha.ComputeHash( input );
return output;
return output;
}
}
public static byte[] DecodeHexString( string hex )

Loading…
Cancel
Save