pull/474/head
Pavel Djundik 2 years ago
parent 5cb0c17aa3
commit 7739fac67c

@ -29,8 +29,8 @@ namespace DepotDownloader
AccountSettingsStore()
{
ContentServerPenalty = new ConcurrentDictionary<string, int>();
LoginTokens = new Dictionary<string, string>();
GuardData = new Dictionary<string, string>();
LoginTokens = [];
GuardData = [];
}
static bool Loaded
@ -50,11 +50,9 @@ namespace DepotDownloader
{
try
{
using (var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read))
using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
{
Instance = Serializer.Deserialize<AccountSettingsStore>(ds);
}
using var fs = IsolatedStorage.OpenFile(filename, FileMode.Open, FileAccess.Read);
using var ds = new DeflateStream(fs, CompressionMode.Decompress);
Instance = Serializer.Deserialize<AccountSettingsStore>(ds);
}
catch (IOException ex)
{
@ -77,11 +75,9 @@ namespace DepotDownloader
try
{
using (var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write))
using (var ds = new DeflateStream(fs, CompressionMode.Compress))
{
Serializer.Serialize(ds, Instance);
}
using var fs = IsolatedStorage.OpenFile(Instance.FileName, FileMode.Create, FileAccess.Write);
using var ds = new DeflateStream(fs, CompressionMode.Compress);
Serializer.Serialize(ds, Instance);
}
catch (IOException ex)
{

@ -35,7 +35,7 @@ namespace DepotDownloader
CDNClient = new Client(steamSession.steamClient);
activeConnectionPool = new ConcurrentStack<Server>();
availableServerEndpoints = new BlockingCollection<Server>();
availableServerEndpoints = [];
populatePoolEvent = new AutoResetEvent(true);
shutdownToken = new CancellationTokenSource();

@ -11,9 +11,8 @@ using SteamKit2.CDN;
namespace DepotDownloader
{
class ContentDownloaderException : Exception
class ContentDownloaderException(string value) : Exception(value)
{
public ContentDownloaderException(String value) : base(value) { }
}
static class ContentDownloader
@ -23,7 +22,7 @@ namespace DepotDownloader
public const ulong INVALID_MANIFEST_ID = ulong.MaxValue;
public const string DEFAULT_BRANCH = "public";
public static DownloadConfig Config = new DownloadConfig();
public static DownloadConfig Config = new();
private static Steam3Session steam3;
private static Steam3Session.Credentials steam3Credentials;
@ -33,28 +32,16 @@ namespace DepotDownloader
private const string CONFIG_DIR = ".DepotDownloader";
private static readonly string STAGING_DIR = Path.Combine(CONFIG_DIR, "staging");
private sealed class DepotDownloadInfo
private sealed class DepotDownloadInfo(
uint depotid, uint appId, ulong manifestId, string branch,
string installDir, byte[] depotKey)
{
public uint id { get; private set; }
public uint appId { get; private set; }
public ulong manifestId { get; private set; }
public string branch { get; private set; }
public string installDir { get; private set; }
public byte[] depotKey { get; private set; }
public DepotDownloadInfo(
uint depotid, uint appId, ulong manifestId, string branch,
string installDir, byte[] depotKey)
{
this.id = depotid;
this.appId = appId;
this.manifestId = manifestId;
this.branch = branch;
this.installDir = installDir;
this.depotKey = depotKey;
}
public uint DepotId { get; } = depotid;
public uint AppId { get; } = appId;
public ulong ManifestId { get; } = manifestId;
public string Branch { get; } = branch;
public string InstallDir { get; } = installDir;
public byte[] DepotKey { get; } = depotKey;
}
static bool CreateDirectories(uint depotId, uint depotVersion, out string installDir)
@ -135,8 +122,7 @@ namespace DepotDownloader
foreach (var license in licenseQuery)
{
SteamApps.PICSProductInfoCallback.PICSProductInfo package;
if (steam3.PackageInfo.TryGetValue(license, out package) && package != null)
if (steam3.PackageInfo.TryGetValue(license, out var package) && package != null)
{
if (package.KeyValues["appids"].Children.Any(child => child.AsUnsignedInteger() == depotId))
return true;
@ -156,33 +142,20 @@ namespace DepotDownloader
return null;
}
SteamApps.PICSProductInfoCallback.PICSProductInfo app;
if (!steam3.AppInfo.TryGetValue(appId, out app) || app == null)
if (!steam3.AppInfo.TryGetValue(appId, out var app) || app == null)
{
return null;
}
var appinfo = app.KeyValues;
string section_key;
switch (section)
var section_key = section switch
{
case EAppInfoSection.Common:
section_key = "common";
break;
case EAppInfoSection.Extended:
section_key = "extended";
break;
case EAppInfoSection.Config:
section_key = "config";
break;
case EAppInfoSection.Depots:
section_key = "depots";
break;
default:
throw new NotImplementedException();
}
EAppInfoSection.Common => "common",
EAppInfoSection.Extended => "extended",
EAppInfoSection.Config => "config",
EAppInfoSection.Depots => "depots",
_ => throw new NotImplementedException(),
};
var section_kv = appinfo.Children.Where(c => c.Name == section_key).FirstOrDefault();
return section_kv;
}
@ -262,7 +235,7 @@ namespace DepotDownloader
// Submit the password to Steam now to get encryption keys
steam3.CheckAppBetaPassword(appId, Config.BetaPassword);
if (!steam3.AppBetaPasswords.ContainsKey(branch))
if (!steam3.AppBetaPasswords.TryGetValue(branch, out var appBetaPassword))
{
Console.WriteLine("Password was invalid for branch {0}", branch);
return INVALID_MANIFEST_ID;
@ -272,7 +245,7 @@ namespace DepotDownloader
byte[] manifest_bytes;
try
{
manifest_bytes = CryptoHelper.SymmetricDecryptECB(input, steam3.AppBetaPasswords[branch]);
manifest_bytes = CryptoHelper.SymmetricDecryptECB(input, appBetaPassword);
}
catch (Exception e)
{
@ -293,14 +266,14 @@ namespace DepotDownloader
if (node.Value == null)
return INVALID_MANIFEST_ID;
return UInt64.Parse(node.Value);
return ulong.Parse(node.Value);
}
static string GetAppName(uint appId)
{
var info = GetSteam3AppSection(appId, EAppInfoSection.Common);
if (info == null)
return String.Empty;
return string.Empty;
return info["name"].AsString();
}
@ -393,8 +366,7 @@ namespace DepotDownloader
private static async Task DownloadWebFile(uint appId, string fileName, string url)
{
string installDir;
if (!CreateDirectories(appId, 0, out installDir))
if (!CreateDirectories(appId, 0, out var installDir))
{
Console.WriteLine("Error: Unable to create install directories!");
return;
@ -437,8 +409,7 @@ namespace DepotDownloader
Directory.CreateDirectory(Path.Combine(configPath, CONFIG_DIR));
DepotConfigStore.LoadFromFile(Path.Combine(configPath, CONFIG_DIR, "depot.config"));
if (steam3 != null)
steam3.RequestAppInfo(appId);
steam3?.RequestAppInfo(appId);
if (!AccountHasAccess(appId))
{
@ -452,13 +423,13 @@ namespace DepotDownloader
else
{
var contentName = GetAppName(appId);
throw new ContentDownloaderException(String.Format("App {0} ({1}) is not available from this account.", appId, contentName));
throw new ContentDownloaderException(string.Format("App {0} ({1}) is not available from this account.", appId, contentName));
}
}
var hasSpecificDepots = depotManifestIds.Count > 0;
var depotIdsFound = new List<uint>();
var depotIdsExpected = depotManifestIds.Select(x => x.Item1).ToList();
var depotIdsExpected = depotManifestIds.Select(x => x.depotId).ToList();
var depots = GetSteam3AppSection(appId, EAppInfoSection.Depots);
if (isUgc)
@ -537,21 +508,21 @@ namespace DepotDownloader
if (depotManifestIds.Count == 0 && !hasSpecificDepots)
{
throw new ContentDownloaderException(String.Format("Couldn't find any depots to download for app {0}", appId));
throw new ContentDownloaderException(string.Format("Couldn't find any depots to download for app {0}", appId));
}
if (depotIdsFound.Count < depotIdsExpected.Count)
{
var remainingDepotIds = depotIdsExpected.Except(depotIdsFound);
throw new ContentDownloaderException(String.Format("Depot {0} not listed for app {1}", string.Join(", ", remainingDepotIds), appId));
throw new ContentDownloaderException(string.Format("Depot {0} not listed for app {1}", string.Join(", ", remainingDepotIds), appId));
}
}
var infos = new List<DepotDownloadInfo>();
foreach (var depotManifest in depotManifestIds)
foreach (var (depotId, manifestId) in depotManifestIds)
{
var info = GetDepotInfo(depotManifest.Item1, appId, depotManifest.Item2, branch);
var info = GetDepotInfo(depotId, appId, manifestId, branch);
if (info != null)
{
infos.Add(info);
@ -560,7 +531,7 @@ namespace DepotDownloader
try
{
await DownloadSteam3Async(appId, infos).ConfigureAwait(false);
await DownloadSteam3Async(infos).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@ -599,7 +570,7 @@ namespace DepotDownloader
}
steam3.RequestDepotKey(depotId, appId);
if (!steam3.DepotKeys.ContainsKey(depotId))
if (!steam3.DepotKeys.TryGetValue(depotId, out var depotKey))
{
Console.WriteLine("No valid depot key for {0}, unable to download.", depotId);
return null;
@ -607,28 +578,19 @@ namespace DepotDownloader
var uVersion = GetSteam3AppBuildNumber(appId, branch);
string installDir;
if (!CreateDirectories(depotId, uVersion, out installDir))
if (!CreateDirectories(depotId, uVersion, out var installDir))
{
Console.WriteLine("Error: Unable to create install directories!");
return null;
}
var depotKey = steam3.DepotKeys[depotId];
return new DepotDownloadInfo(depotId, appId, manifestId, branch, installDir, depotKey);
}
private class ChunkMatch
private class ChunkMatch(ProtoManifest.ChunkData oldChunk, ProtoManifest.ChunkData newChunk)
{
public ChunkMatch(ProtoManifest.ChunkData oldChunk, ProtoManifest.ChunkData newChunk)
{
OldChunk = oldChunk;
NewChunk = newChunk;
}
public ProtoManifest.ChunkData OldChunk { get; private set; }
public ProtoManifest.ChunkData NewChunk { get; private set; }
public ProtoManifest.ChunkData OldChunk { get; } = oldChunk;
public ProtoManifest.ChunkData NewChunk { get; } = newChunk;
}
private class DepotFilesData
@ -663,19 +625,19 @@ namespace DepotDownloader
public ulong DepotBytesUncompressed;
}
private static async Task DownloadSteam3Async(uint appId, List<DepotDownloadInfo> depots)
private static async Task DownloadSteam3Async(List<DepotDownloadInfo> depots)
{
var cts = new CancellationTokenSource();
cdnPool.ExhaustedToken = cts;
var downloadCounter = new GlobalDownloadCounter();
var depotsToDownload = new List<DepotFilesData>(depots.Count);
var allFileNamesAllDepots = new HashSet<String>();
var allFileNamesAllDepots = new HashSet<string>();
// First, fetch all the manifests for each depot (including previous manifests) and perform the initial setup
foreach (var depot in depots)
{
var depotFileData = await ProcessDepotManifestAndFiles(cts, appId, depot);
var depotFileData = await ProcessDepotManifestAndFiles(cts, depot);
if (depotFileData != null)
{
@ -690,7 +652,7 @@ namespace DepotDownloader
// This is in last-depot-wins order, from Steam or the list of depots supplied by the user
if (!string.IsNullOrWhiteSpace(Config.InstallDirectory) && depotsToDownload.Count > 0)
{
var claimedFileNames = new HashSet<String>();
var claimedFileNames = new HashSet<string>();
for (var i = depotsToDownload.Count - 1; i >= 0; i--)
{
@ -703,38 +665,37 @@ namespace DepotDownloader
foreach (var depotFileData in depotsToDownload)
{
await DownloadSteam3AsyncDepotFiles(cts, appId, downloadCounter, depotFileData, allFileNamesAllDepots);
await DownloadSteam3AsyncDepotFiles(cts, downloadCounter, depotFileData, allFileNamesAllDepots);
}
Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots",
downloadCounter.TotalBytesCompressed, downloadCounter.TotalBytesUncompressed, depots.Count);
}
private static async Task<DepotFilesData> ProcessDepotManifestAndFiles(CancellationTokenSource cts,
uint appId, DepotDownloadInfo depot)
private static async Task<DepotFilesData> ProcessDepotManifestAndFiles(CancellationTokenSource cts, DepotDownloadInfo depot)
{
var depotCounter = new DepotDownloadCounter();
Console.WriteLine("Processing depot {0}", depot.id);
Console.WriteLine("Processing depot {0}", depot.DepotId);
ProtoManifest oldProtoManifest = null;
ProtoManifest newProtoManifest = null;
var configDir = Path.Combine(depot.installDir, CONFIG_DIR);
var configDir = Path.Combine(depot.InstallDir, CONFIG_DIR);
var lastManifestId = INVALID_MANIFEST_ID;
DepotConfigStore.Instance.InstalledManifestIDs.TryGetValue(depot.id, out lastManifestId);
DepotConfigStore.Instance.InstalledManifestIDs.TryGetValue(depot.DepotId, out lastManifestId);
// In case we have an early exit, this will force equiv of verifyall next run.
DepotConfigStore.Instance.InstalledManifestIDs[depot.id] = INVALID_MANIFEST_ID;
DepotConfigStore.Instance.InstalledManifestIDs[depot.DepotId] = INVALID_MANIFEST_ID;
DepotConfigStore.Save();
if (lastManifestId != INVALID_MANIFEST_ID)
{
var oldManifestFileName = Path.Combine(configDir, string.Format("{0}_{1}.bin", depot.id, lastManifestId));
var oldManifestFileName = Path.Combine(configDir, string.Format("{0}_{1}.bin", depot.DepotId, lastManifestId));
if (File.Exists(oldManifestFileName))
{
byte[] expectedChecksum, currentChecksum;
byte[] expectedChecksum;
try
{
@ -745,29 +706,29 @@ namespace DepotDownloader
expectedChecksum = null;
}
oldProtoManifest = ProtoManifest.LoadFromFile(oldManifestFileName, out currentChecksum);
oldProtoManifest = ProtoManifest.LoadFromFile(oldManifestFileName, out var currentChecksum);
if (expectedChecksum == null || !expectedChecksum.SequenceEqual(currentChecksum))
{
// We only have to show this warning if the old manifest ID was different
if (lastManifestId != depot.manifestId)
if (lastManifestId != depot.ManifestId)
Console.WriteLine("Manifest {0} on disk did not match the expected checksum.", lastManifestId);
oldProtoManifest = null;
}
}
}
if (lastManifestId == depot.manifestId && oldProtoManifest != null)
if (lastManifestId == depot.ManifestId && oldProtoManifest != null)
{
newProtoManifest = oldProtoManifest;
Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id);
Console.WriteLine("Already have manifest {0} for depot {1}.", depot.ManifestId, depot.DepotId);
}
else
{
var newManifestFileName = Path.Combine(configDir, string.Format("{0}_{1}.bin", depot.id, depot.manifestId));
var newManifestFileName = Path.Combine(configDir, string.Format("{0}_{1}.bin", depot.DepotId, depot.ManifestId));
if (newManifestFileName != null)
{
byte[] expectedChecksum, currentChecksum;
byte[] expectedChecksum;
try
{
@ -778,18 +739,18 @@ namespace DepotDownloader
expectedChecksum = null;
}
newProtoManifest = ProtoManifest.LoadFromFile(newManifestFileName, out currentChecksum);
newProtoManifest = ProtoManifest.LoadFromFile(newManifestFileName, out var currentChecksum);
if (newProtoManifest != null && (expectedChecksum == null || !expectedChecksum.SequenceEqual(currentChecksum)))
{
Console.WriteLine("Manifest {0} on disk did not match the expected checksum.", depot.manifestId);
Console.WriteLine("Manifest {0} on disk did not match the expected checksum.", depot.ManifestId);
newProtoManifest = null;
}
}
if (newProtoManifest != null)
{
Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id);
Console.WriteLine("Already have manifest {0} for depot {1}.", depot.ManifestId, depot.DepotId);
}
else
{
@ -816,39 +777,39 @@ namespace DepotDownloader
if (manifestRequestCode == 0 || now >= manifestRequestCodeExpiration)
{
manifestRequestCode = await steam3.GetDepotManifestRequestCodeAsync(
depot.id,
depot.appId,
depot.manifestId,
depot.branch);
depot.DepotId,
depot.AppId,
depot.ManifestId,
depot.Branch);
// This code will hopefully be valid for one period following the issuing period
manifestRequestCodeExpiration = now.Add(TimeSpan.FromMinutes(5));
// If we could not get the manifest code, this is a fatal error
if (manifestRequestCode == 0)
{
Console.WriteLine("No manifest request code was returned for {0} {1}", depot.id, depot.manifestId);
Console.WriteLine("No manifest request code was returned for {0} {1}", depot.DepotId, depot.ManifestId);
cts.Cancel();
}
}
DebugLog.WriteLine("ContentDownloader",
"Downloading manifest {0} from {1} with {2}",
depot.manifestId,
depot.ManifestId,
connection,
cdnPool.ProxyServer != null ? cdnPool.ProxyServer : "no proxy");
depotManifest = await cdnPool.CDNClient.DownloadManifestAsync(
depot.id,
depot.manifestId,
depot.DepotId,
depot.ManifestId,
manifestRequestCode,
connection,
depot.depotKey,
depot.DepotKey,
cdnPool.ProxyServer).ConfigureAwait(false);
cdnPool.ReturnConnection(connection);
}
catch (TaskCanceledException)
{
Console.WriteLine("Connection timeout downloading depot manifest {0} {1}. Retrying.", depot.id, depot.manifestId);
Console.WriteLine("Connection timeout downloading depot manifest {0} {1}. Retrying.", depot.DepotId, depot.ManifestId);
}
catch (SteamKitWebRequestException e)
{
@ -856,17 +817,17 @@ namespace DepotDownloader
if (e.StatusCode == HttpStatusCode.Unauthorized || e.StatusCode == HttpStatusCode.Forbidden)
{
Console.WriteLine("Encountered 401 for depot manifest {0} {1}. Aborting.", depot.id, depot.manifestId);
Console.WriteLine("Encountered 401 for depot manifest {0} {1}. Aborting.", depot.DepotId, depot.ManifestId);
break;
}
if (e.StatusCode == HttpStatusCode.NotFound)
{
Console.WriteLine("Encountered 404 for depot manifest {0} {1}. Aborting.", depot.id, depot.manifestId);
Console.WriteLine("Encountered 404 for depot manifest {0} {1}. Aborting.", depot.DepotId, depot.ManifestId);
break;
}
Console.WriteLine("Encountered error downloading depot manifest {0} {1}: {2}", depot.id, depot.manifestId, e.StatusCode);
Console.WriteLine("Encountered error downloading depot manifest {0} {1}: {2}", depot.DepotId, depot.ManifestId, e.StatusCode);
}
catch (OperationCanceledException)
{
@ -875,23 +836,22 @@ namespace DepotDownloader
catch (Exception e)
{
cdnPool.ReturnBrokenConnection(connection);
Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.id, depot.manifestId, e.Message);
Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.DepotId, depot.ManifestId, e.Message);
}
} while (depotManifest == null);
if (depotManifest == null)
{
Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.manifestId, depot.id);
Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.ManifestId, depot.DepotId);
cts.Cancel();
}
// Throw the cancellation exception if requested so that this task is marked failed
cts.Token.ThrowIfCancellationRequested();
byte[] checksum;
newProtoManifest = new ProtoManifest(depotManifest, depot.manifestId);
newProtoManifest.SaveToFile(newManifestFileName, out checksum);
newProtoManifest = new ProtoManifest(depotManifest, depot.ManifestId);
newProtoManifest.SaveToFile(newManifestFileName, out var checksum);
File.WriteAllBytes(newManifestFileName + ".sha", checksum);
Console.WriteLine(" Done!");
@ -900,7 +860,7 @@ namespace DepotDownloader
newProtoManifest.Files.Sort((x, y) => string.Compare(x.FileName, y.FileName, StringComparison.Ordinal));
Console.WriteLine("Manifest {0} ({1})", depot.manifestId, newProtoManifest.CreationTime);
Console.WriteLine("Manifest {0} ({1})", depot.ManifestId, newProtoManifest.CreationTime);
if (Config.DownloadManifestOnly)
{
@ -908,7 +868,7 @@ namespace DepotDownloader
return null;
}
var stagingDir = Path.Combine(depot.installDir, STAGING_DIR);
var stagingDir = Path.Combine(depot.InstallDir, STAGING_DIR);
var filesAfterExclusions = newProtoManifest.Files.AsParallel().Where(f => TestIsFileIncluded(f.FileName)).ToList();
var allFileNames = new HashSet<string>(filesAfterExclusions.Count);
@ -918,7 +878,7 @@ namespace DepotDownloader
{
allFileNames.Add(file.FileName);
var fileFinalPath = Path.Combine(depot.installDir, file.FileName);
var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName);
var fileStagingPath = Path.Combine(stagingDir, file.FileName);
if (file.Flags.HasFlag(EDepotFileFlag.Directory))
@ -948,13 +908,13 @@ namespace DepotDownloader
};
}
private static async Task DownloadSteam3AsyncDepotFiles(CancellationTokenSource cts, uint appId,
GlobalDownloadCounter downloadCounter, DepotFilesData depotFilesData, HashSet<String> allFileNamesAllDepots)
private static async Task DownloadSteam3AsyncDepotFiles(CancellationTokenSource cts,
GlobalDownloadCounter downloadCounter, DepotFilesData depotFilesData, HashSet<string> allFileNamesAllDepots)
{
var depot = depotFilesData.depotDownloadInfo;
var depotCounter = depotFilesData.depotCounter;
Console.WriteLine("Downloading depot {0}", depot.id);
Console.WriteLine("Downloading depot {0}", depot.DepotId);
var files = depotFilesData.filteredFiles.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)).ToArray();
var networkChunkQueue = new ConcurrentQueue<(FileStreamData fileStreamData, ProtoManifest.FileData fileData, ProtoManifest.ChunkData chunk)>();
@ -967,7 +927,7 @@ namespace DepotDownloader
await Util.InvokeAsync(
networkChunkQueue.Select(q => new Func<Task>(async () =>
await Task.Run(() => DownloadSteam3AsyncDepotFileChunk(cts, appId, downloadCounter, depotFilesData,
await Task.Run(() => DownloadSteam3AsyncDepotFileChunk(cts, downloadCounter, depotFilesData,
q.fileData, q.fileStreamData, q.chunk)))),
maxDegreeOfParallelism: Config.MaxDownloads
);
@ -991,7 +951,7 @@ namespace DepotDownloader
foreach (var existingFileName in previousFilteredFiles)
{
var fileFinalPath = Path.Combine(depot.installDir, existingFileName);
var fileFinalPath = Path.Combine(depot.InstallDir, existingFileName);
if (!File.Exists(fileFinalPath))
continue;
@ -1001,10 +961,10 @@ namespace DepotDownloader
}
}
DepotConfigStore.Instance.InstalledManifestIDs[depot.id] = depot.manifestId;
DepotConfigStore.Instance.InstalledManifestIDs[depot.DepotId] = depot.ManifestId;
DepotConfigStore.Save();
Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, depotCounter.DepotBytesCompressed, depotCounter.DepotBytesUncompressed);
Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.DepotId, depotCounter.DepotBytesCompressed, depotCounter.DepotBytesUncompressed);
}
private static void DownloadSteam3AsyncDepotFile(
@ -1025,7 +985,7 @@ namespace DepotDownloader
oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName);
}
var fileFinalPath = Path.Combine(depot.installDir, file.FileName);
var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName);
var fileStagingPath = Path.Combine(stagingDir, file.FileName);
// This may still exist if the previous run exited before cleanup
@ -1049,7 +1009,7 @@ namespace DepotDownloader
}
catch (IOException ex)
{
throw new ContentDownloaderException(String.Format("Failed to allocate file {0}: {1}", fileFinalPath, ex.Message));
throw new ContentDownloaderException(string.Format("Failed to allocate file {0}: {1}", fileFinalPath, ex.Message));
}
neededChunks = new List<ProtoManifest.ChunkData>(file.Chunks);
@ -1059,7 +1019,7 @@ namespace DepotDownloader
// open existing
if (oldManifestFile != null)
{
neededChunks = new List<ProtoManifest.ChunkData>();
neededChunks = [];
var hashMatches = oldManifestFile.FileHash.SequenceEqual(file.FileHash);
if (Config.VerifyAll || !hashMatches)
@ -1123,7 +1083,7 @@ namespace DepotDownloader
}
catch (IOException ex)
{
throw new ContentDownloaderException(String.Format("Failed to resize file to expected size {0}: {1}", fileFinalPath, ex.Message));
throw new ContentDownloaderException(string.Format("Failed to resize file to expected size {0}: {1}", fileFinalPath, ex.Message));
}
foreach (var match in copyChunks)
@ -1155,12 +1115,12 @@ namespace DepotDownloader
}
catch (IOException ex)
{
throw new ContentDownloaderException(String.Format("Failed to allocate file {0}: {1}", fileFinalPath, ex.Message));
throw new ContentDownloaderException(string.Format("Failed to allocate file {0}: {1}", fileFinalPath, ex.Message));
}
}
Console.WriteLine("Validating {0}", fileFinalPath);
neededChunks = Util.ValidateSteam3FileChecksums(fs, file.Chunks.OrderBy(x => x.Offset).ToArray());
neededChunks = Util.ValidateSteam3FileChecksums(fs, [.. file.Chunks.OrderBy(x => x.Offset)]);
}
if (neededChunks.Count == 0)
@ -1205,7 +1165,7 @@ namespace DepotDownloader
}
private static async Task DownloadSteam3AsyncDepotFileChunk(
CancellationTokenSource cts, uint appId,
CancellationTokenSource cts,
GlobalDownloadCounter downloadCounter,
DepotFilesData depotFilesData,
ProtoManifest.FileData file,
@ -1219,12 +1179,14 @@ namespace DepotDownloader
var chunkID = Util.EncodeHexString(chunk.ChunkID);
var data = new DepotManifest.ChunkData();
data.ChunkID = chunk.ChunkID;
data.Checksum = chunk.Checksum;
data.Offset = chunk.Offset;
data.CompressedLength = chunk.CompressedLength;
data.UncompressedLength = chunk.UncompressedLength;
var data = new DepotManifest.ChunkData
{
ChunkID = chunk.ChunkID,
Checksum = chunk.Checksum,
Offset = chunk.Offset,
CompressedLength = chunk.CompressedLength,
UncompressedLength = chunk.UncompressedLength
};
DepotChunk chunkData = null;
@ -1240,10 +1202,10 @@ namespace DepotDownloader
DebugLog.WriteLine("ContentDownloader", "Downloading chunk {0} from {1} with {2}", chunkID, connection, cdnPool.ProxyServer != null ? cdnPool.ProxyServer : "no proxy");
chunkData = await cdnPool.CDNClient.DownloadDepotChunkAsync(
depot.id,
depot.DepotId,
data,
connection,
depot.depotKey,
depot.DepotKey,
cdnPool.ProxyServer).ConfigureAwait(false);
cdnPool.ReturnConnection(connection);
@ -1277,7 +1239,7 @@ namespace DepotDownloader
if (chunkData == null)
{
Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.id);
Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.DepotId);
cts.Cancel();
}
@ -1290,12 +1252,12 @@ namespace DepotDownloader
if (fileStreamData.fileStream == null)
{
var fileFinalPath = Path.Combine(depot.installDir, file.FileName);
var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName);
fileStreamData.fileStream = File.Open(fileFinalPath, FileMode.Open);
}
fileStreamData.fileStream.Seek((long)chunkData.ChunkInfo.Offset, SeekOrigin.Begin);
await fileStreamData.fileStream.WriteAsync(chunkData.Data, 0, chunkData.Data.Length);
await fileStreamData.fileStream.WriteAsync(chunkData.Data.AsMemory(0, chunkData.Data.Length), cts.Token);
}
finally
{
@ -1326,51 +1288,49 @@ namespace DepotDownloader
if (remainingChunks == 0)
{
var fileFinalPath = Path.Combine(depot.installDir, file.FileName);
var fileFinalPath = Path.Combine(depot.InstallDir, file.FileName);
Console.WriteLine("{0,6:#00.00}% {1}", (sizeDownloaded / (float)depotDownloadCounter.CompleteDownloadSize) * 100.0f, fileFinalPath);
}
}
static void DumpManifestToTextFile(DepotDownloadInfo depot, ProtoManifest manifest)
{
var txtManifest = Path.Combine(depot.installDir, $"manifest_{depot.id}_{depot.manifestId}.txt");
var txtManifest = Path.Combine(depot.InstallDir, $"manifest_{depot.DepotId}_{depot.ManifestId}.txt");
using var sw = new StreamWriter(txtManifest);
using (var sw = new StreamWriter(txtManifest))
{
sw.WriteLine($"Content Manifest for Depot {depot.id}");
sw.WriteLine();
sw.WriteLine($"Manifest ID / date : {depot.manifestId} / {manifest.CreationTime}");
sw.WriteLine($"Content Manifest for Depot {depot.DepotId}");
sw.WriteLine();
sw.WriteLine($"Manifest ID / date : {depot.ManifestId} / {manifest.CreationTime}");
int numFiles = 0, numChunks = 0;
ulong uncompressedSize = 0, compressedSize = 0;
int numFiles = 0, numChunks = 0;
ulong uncompressedSize = 0, compressedSize = 0;
foreach (var file in manifest.Files)
{
if (file.Flags.HasFlag(EDepotFileFlag.Directory))
continue;
foreach (var file in manifest.Files)
{
if (file.Flags.HasFlag(EDepotFileFlag.Directory))
continue;
numFiles++;
numChunks += file.Chunks.Count;
numFiles++;
numChunks += file.Chunks.Count;
foreach (var chunk in file.Chunks)
{
uncompressedSize += chunk.UncompressedLength;
compressedSize += chunk.CompressedLength;
}
foreach (var chunk in file.Chunks)
{
uncompressedSize += chunk.UncompressedLength;
compressedSize += chunk.CompressedLength;
}
}
sw.WriteLine($"Total number of files : {numFiles}");
sw.WriteLine($"Total number of chunks : {numChunks}");
sw.WriteLine($"Total bytes on disk : {uncompressedSize}");
sw.WriteLine($"Total bytes compressed : {compressedSize}");
sw.WriteLine();
sw.WriteLine(" Size Chunks File SHA Flags Name");
sw.WriteLine($"Total number of files : {numFiles}");
sw.WriteLine($"Total number of chunks : {numChunks}");
sw.WriteLine($"Total bytes on disk : {uncompressedSize}");
sw.WriteLine($"Total bytes compressed : {compressedSize}");
sw.WriteLine();
sw.WriteLine(" Size Chunks File SHA Flags Name");
foreach (var file in manifest.Files)
{
var sha1Hash = BitConverter.ToString(file.FileHash).Replace("-", "");
sw.WriteLine($"{file.TotalSize,14} {file.Chunks.Count,6} {sha1Hash} {file.Flags,5:D} {file.FileName}");
}
foreach (var file in manifest.Files)
{
var sha1Hash = BitConverter.ToString(file.FileHash).Replace("-", "");
sw.WriteLine($"{file.TotalSize,14} {file.Chunks.Count,6} {sha1Hash} {file.Flags,5:D} {file.FileName}");
}
}
}

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@ -16,7 +16,7 @@ namespace DepotDownloader
DepotConfigStore()
{
InstalledManifestIDs = new Dictionary<uint, ulong>();
InstalledManifestIDs = [];
}
static bool Loaded
@ -33,9 +33,9 @@ namespace DepotDownloader
if (File.Exists(filename))
{
using (var fs = File.Open(filename, FileMode.Open))
using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
Instance = Serializer.Deserialize<DepotConfigStore>(ds);
using var fs = File.Open(filename, FileMode.Open);
using var ds = new DeflateStream(fs, CompressionMode.Decompress);
Instance = Serializer.Deserialize<DepotConfigStore>(ds);
}
else
{
@ -50,9 +50,9 @@ namespace DepotDownloader
if (!Loaded)
throw new Exception("Saved config before loading");
using (var fs = File.Open(Instance.FileName, FileMode.Create))
using (var ds = new DeflateStream(fs, CompressionMode.Compress))
Serializer.Serialize(ds, Instance);
using var fs = File.Open(Instance.FileName, FileMode.Create);
using var ds = new DeflateStream(fs, CompressionMode.Compress);
Serializer.Serialize(ds, Instance);
}
}
}

@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
@ -29,8 +29,10 @@ namespace DepotDownloader
// By default, we create dual-mode sockets:
// Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.NoDelay = true;
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
NoDelay = true
};
try
{

@ -16,6 +16,8 @@ namespace DepotDownloader
static int Main(string[] args)
=> MainAsync(args).GetAwaiter().GetResult();
internal static readonly char[] newLineCharacters = ['\n', '\r'];
static async Task<int> MainAsync(string[] args)
{
if (args.Length == 0)
@ -63,20 +65,22 @@ namespace DepotDownloader
if (fileList != null)
{
const string RegexPrefix = "regex:";
try
{
var fileListData = await File.ReadAllTextAsync(fileList);
var files = fileListData.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var files = fileListData.Split(newLineCharacters, StringSplitOptions.RemoveEmptyEntries);
ContentDownloader.Config.UsingFileList = true;
ContentDownloader.Config.FilesToDownload = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
ContentDownloader.Config.FilesToDownloadRegex = new List<Regex>();
ContentDownloader.Config.FilesToDownloadRegex = [];
foreach (var fileEntry in files)
{
if (fileEntry.StartsWith("regex:"))
if (fileEntry.StartsWith(RegexPrefix))
{
var rgx = new Regex(fileEntry.Substring(6), RegexOptions.Compiled | RegexOptions.IgnoreCase);
var rgx = new Regex(fileEntry[RegexPrefix.Length..], RegexOptions.Compiled | RegexOptions.IgnoreCase);
ContentDownloader.Config.FilesToDownloadRegex.Add(rgx);
}
else
@ -192,7 +196,7 @@ namespace DepotDownloader
ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms");
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.");
return 1;
@ -203,7 +207,7 @@ namespace DepotDownloader
ContentDownloader.Config.DownloadAllLanguages = HasParameter(args, "-all-languages");
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.");
return 1;
@ -314,7 +318,7 @@ namespace DepotDownloader
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)
{
var index = IndexOfParam(args, param);
@ -329,7 +333,7 @@ namespace DepotDownloader
return (T)converter.ConvertFromString(strParam);
}
return default(T);
return default;
}
static List<T> GetParameterList<T>(string[] args, string param)

@ -14,7 +14,7 @@ namespace DepotDownloader
// Proto ctor
private ProtoManifest()
{
Files = new List<FileData>();
Files = [];
}
public ProtoManifest(DepotManifest sourceManifest, ulong id) : this()
@ -30,7 +30,7 @@ namespace DepotDownloader
// Proto ctor
private FileData()
{
Chunks = new List<ChunkData>();
Chunks = [];
}
public FileData(DepotManifest.FileData sourceData) : this()
@ -130,33 +130,29 @@ namespace DepotDownloader
return null;
}
using (var ms = new MemoryStream())
{
using (var fs = File.Open(filename, FileMode.Open))
using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
ds.CopyTo(ms);
using var ms = new MemoryStream();
using (var fs = File.Open(filename, FileMode.Open))
using (var ds = new DeflateStream(fs, CompressionMode.Decompress))
ds.CopyTo(ms);
checksum = SHA1.HashData(ms.ToArray());
checksum = SHA1.HashData(ms.ToArray());
ms.Seek(0, SeekOrigin.Begin);
return Serializer.Deserialize<ProtoManifest>(ms);
}
ms.Seek(0, SeekOrigin.Begin);
return Serializer.Deserialize<ProtoManifest>(ms);
}
public void SaveToFile(string filename, out byte[] checksum)
{
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, this);
using var ms = new MemoryStream();
Serializer.Serialize(ms, this);
checksum = SHA1.HashData(ms.ToArray());
checksum = SHA1.HashData(ms.ToArray());
ms.Seek(0, SeekOrigin.Begin);
ms.Seek(0, SeekOrigin.Begin);
using (var fs = File.Open(filename, FileMode.Create))
using (var ds = new DeflateStream(fs, CompressionMode.Compress))
ms.CopyTo(ds);
}
using var fs = File.Open(filename, FileMode.Create);
using var ds = new DeflateStream(fs, CompressionMode.Compress);
ms.CopyTo(ds);
}
}
}

@ -82,13 +82,13 @@ namespace DepotDownloader
this.bDidDisconnect = false;
this.seq = 0;
this.AppTokens = new Dictionary<uint, ulong>();
this.PackageTokens = new Dictionary<uint, ulong>();
this.DepotKeys = new Dictionary<uint, byte[]>();
this.AppTokens = [];
this.PackageTokens = [];
this.DepotKeys = [];
this.CDNAuthTokens = new ConcurrentDictionary<string, TaskCompletionSource<SteamApps.CDNAuthTokenCallback>>();
this.AppInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
this.PackageInfo = new Dictionary<uint, SteamApps.PICSProductInfoCallback.PICSProductInfo>();
this.AppBetaPasswords = new Dictionary<string, byte[]>();
this.AppInfo = [];
this.PackageInfo = [];
this.AppBetaPasswords = [];
var clientConfiguration = SteamConfiguration.Create(config =>
config
@ -118,7 +118,7 @@ namespace DepotDownloader
public delegate bool WaitCondition();
private readonly object steamLock = new object();
private readonly object steamLock = new();
public bool WaitUntilCallback(Action submitter, WaitCondition waiter)
{

Loading…
Cancel
Save