@ -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 , ste am3. A ppBetaPasswords[ 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 S tring. Empty ;
return s tring. 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 ( S tring. Format ( "App {0} ({1}) is not available from this account." , appId , contentName ) ) ;
throw new ContentDownloaderException ( s tring. 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 ( S tring. Format ( "Couldn't find any depots to download for app {0}" , appId ) ) ;
throw new ContentDownloaderException ( s tring. 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 ( S tring. Format ( "Depot {0} not listed for app {1}" , string . Join ( ", " , remainingDepotIds ) , appId ) ) ;
throw new ContentDownloaderException ( s tring. 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 ( depot Manifest. Item1 , appId , depotManifest . Item2 , branch ) ;
var info = GetDepotInfo ( depot Id, 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 < S tring> ( ) ;
var allFileNamesAllDepots = new HashSet < s tring> ( ) ;
// 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 < S tring> ( ) ;
var claimedFileNames = new HashSet < s tring> ( ) ;
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 . i d) ;
Console . WriteLine ( "Processing depot {0}" , depot . DepotI d) ;
ProtoManifest oldProtoManifest = null ;
ProtoManifest newProtoManifest = null ;
var configDir = Path . Combine ( depot . i nstallDir, CONFIG_DIR ) ;
var configDir = Path . Combine ( depot . I nstallDir, CONFIG_DIR ) ;
var lastManifestId = INVALID_MANIFEST_ID ;
DepotConfigStore . Instance . InstalledManifestIDs . TryGetValue ( depot . i d, out lastManifestId ) ;
DepotConfigStore . Instance . InstalledManifestIDs . TryGetValue ( depot . DepotI d, out lastManifestId ) ;
// In case we have an early exit, this will force equiv of verifyall next run.
DepotConfigStore . Instance . InstalledManifestIDs [ depot . i d] = INVALID_MANIFEST_ID ;
DepotConfigStore . Instance . InstalledManifestIDs [ depot . DepotI d] = INVALID_MANIFEST_ID ;
DepotConfigStore . Save ( ) ;
if ( lastManifestId ! = INVALID_MANIFEST_ID )
{
var oldManifestFileName = Path . Combine ( configDir , string . Format ( "{0}_{1}.bin" , depot . i d, lastManifestId ) ) ;
var oldManifestFileName = Path . Combine ( configDir , string . Format ( "{0}_{1}.bin" , depot . DepotI d, 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 . m anifestId)
if ( lastManifestId ! = depot . M anifestId)
Console . WriteLine ( "Manifest {0} on disk did not match the expected checksum." , lastManifestId ) ;
oldProtoManifest = null ;
}
}
}
if ( lastManifestId = = depot . m anifestId & & oldProtoManifest ! = null )
if ( lastManifestId = = depot . M anifestId & & oldProtoManifest ! = null )
{
newProtoManifest = oldProtoManifest ;
Console . WriteLine ( "Already have manifest {0} for depot {1}." , depot . manifestId, depot . i d) ;
Console . WriteLine ( "Already have manifest {0} for depot {1}." , depot . ManifestId, depot . DepotI d) ;
}
else
{
var newManifestFileName = Path . Combine ( configDir , string . Format ( "{0}_{1}.bin" , depot . id, depot . m anifestId) ) ;
var newManifestFileName = Path . Combine ( configDir , string . Format ( "{0}_{1}.bin" , depot . DepotId, depot . M anifestId) ) ;
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 . m anifestId) ;
Console . WriteLine ( "Manifest {0} on disk did not match the expected checksum." , depot . M anifestId) ;
newProtoManifest = null ;
}
}
if ( newProtoManifest ! = null )
{
Console . WriteLine ( "Already have manifest {0} for depot {1}." , depot . manifestId, depot . i d) ;
Console . WriteLine ( "Already have manifest {0} for depot {1}." , depot . ManifestId, depot . DepotI d) ;
}
else
{
@ -816,39 +777,39 @@ namespace DepotDownloader
if ( manifestRequestCode = = 0 | | now > = manifestRequestCodeExpiration )
{
manifestRequestCode = await steam3 . GetDepotManifestRequestCodeAsync (
depot . i d,
depot . a ppId,
depot . m anifestId,
depot . b ranch) ;
depot . DepotI d,
depot . A ppId,
depot . M anifestId,
depot . B ranch) ;
// 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 . m anifestId) ;
Console . WriteLine ( "No manifest request code was returned for {0} {1}" , depot . DepotId, depot . M anifestId) ;
cts . Cancel ( ) ;
}
}
DebugLog . WriteLine ( "ContentDownloader" ,
"Downloading manifest {0} from {1} with {2}" ,
depot . m anifestId,
depot . M anifestId,
connection ,
cdnPool . ProxyServer ! = null ? cdnPool . ProxyServer : "no proxy" ) ;
depotManifest = await cdnPool . CDNClient . DownloadManifestAsync (
depot . i d,
depot . m anifestId,
depot . DepotI d,
depot . M anifestId,
manifestRequestCode ,
connection ,
depot . d epotKey,
depot . D epotKey,
cdnPool . ProxyServer ) . ConfigureAwait ( false ) ;
cdnPool . ReturnConnection ( connection ) ;
}
catch ( TaskCanceledException )
{
Console . WriteLine ( "Connection timeout downloading depot manifest {0} {1}. Retrying." , depot . id, depot . m anifestId) ;
Console . WriteLine ( "Connection timeout downloading depot manifest {0} {1}. Retrying." , depot . DepotId, depot . M anifestId) ;
}
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 . m anifestId) ;
Console . WriteLine ( "Encountered 401 for depot manifest {0} {1}. Aborting." , depot . DepotId, depot . M anifestId) ;
break ;
}
if ( e . StatusCode = = HttpStatusCode . NotFound )
{
Console . WriteLine ( "Encountered 404 for depot manifest {0} {1}. Aborting." , depot . id, depot . m anifestId) ;
Console . WriteLine ( "Encountered 404 for depot manifest {0} {1}. Aborting." , depot . DepotId, depot . M anifestId) ;
break ;
}
Console . WriteLine ( "Encountered error downloading depot manifest {0} {1}: {2}" , depot . id, depot . m anifestId, e . StatusCode ) ;
Console . WriteLine ( "Encountered error downloading depot manifest {0} {1}: {2}" , depot . DepotId, depot . M anifestId, 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 . m anifestId, e . Message ) ;
Console . WriteLine ( "Encountered error downloading manifest for depot {0} {1}: {2}" , depot . DepotId, depot . M anifestId, e . Message ) ;
}
} while ( depotManifest = = null ) ;
if ( depotManifest = = null )
{
Console . WriteLine ( "\nUnable to download manifest {0} for depot {1}" , depot . manifestId, depot . i d) ;
Console . WriteLine ( "\nUnable to download manifest {0} for depot {1}" , depot . ManifestId, depot . DepotI d) ;
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 . m anifestId) ;
newProtoManifest . SaveToFile ( newManifestFileName , out checksum ) ;
newProtoManifest = new ProtoManifest ( depotManifest , depot . M anifestId) ;
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 . m anifestId, newProtoManifest . CreationTime ) ;
Console . WriteLine ( "Manifest {0} ({1})" , depot . M anifestId, newProtoManifest . CreationTime ) ;
if ( Config . DownloadManifestOnly )
{
@ -908,7 +868,7 @@ namespace DepotDownloader
return null ;
}
var stagingDir = Path . Combine ( depot . i nstallDir, STAGING_DIR ) ;
var stagingDir = Path . Combine ( depot . I nstallDir, 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 . i nstallDir, file . FileName ) ;
var fileFinalPath = Path . Combine ( depot . I nstallDir, 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 < S tring> allFileNamesAllDepots )
private static async Task DownloadSteam3AsyncDepotFiles ( CancellationTokenSource cts ,
GlobalDownloadCounter downloadCounter , DepotFilesData depotFilesData , HashSet < s tring> allFileNamesAllDepots )
{
var depot = depotFilesData . depotDownloadInfo ;
var depotCounter = depotFilesData . depotCounter ;
Console . WriteLine ( "Downloading depot {0}" , depot . i d) ;
Console . WriteLine ( "Downloading depot {0}" , depot . DepotI d) ;
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 . i nstallDir, existingFileName ) ;
var fileFinalPath = Path . Combine ( depot . I nstallDir, existingFileName ) ;
if ( ! File . Exists ( fileFinalPath ) )
continue ;
@ -1001,10 +961,10 @@ namespace DepotDownloader
}
}
DepotConfigStore . Instance . InstalledManifestIDs [ depot . id] = depot . m anifestId;
DepotConfigStore . Instance . InstalledManifestIDs [ depot . DepotId] = depot . M anifestId;
DepotConfigStore . Save ( ) ;
Console . WriteLine ( "Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)" , depot . i d, depotCounter . DepotBytesCompressed , depotCounter . DepotBytesUncompressed ) ;
Console . WriteLine ( "Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)" , depot . DepotI d, 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 . i nstallDir, file . FileName ) ;
var fileFinalPath = Path . Combine ( depot . I nstallDir, 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 ( S tring. Format ( "Failed to allocate file {0}: {1}" , fileFinalPath , ex . Message ) ) ;
throw new ContentDownloaderException ( s tring. 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 ( S tring. Format ( "Failed to resize file to expected size {0}: {1}" , fileFinalPath , ex . Message ) ) ;
throw new ContentDownloaderException ( s tring. 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 ( S tring. Format ( "Failed to allocate file {0}: {1}" , fileFinalPath , ex . Message ) ) ;
throw new ContentDownloaderException ( s tring. 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 . i d,
depot . DepotI d,
data ,
connection ,
depot . d epotKey,
depot . D epotKey,
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 . i d) ;
Console . WriteLine ( "Failed to find any server with chunk {0} for depot {1}. Aborting." , chunkID , depot . DepotI d) ;
cts . Cancel ( ) ;
}
@ -1290,12 +1252,12 @@ namespace DepotDownloader
if ( fileStreamData . fileStream = = null )
{
var fileFinalPath = Path . Combine ( depot . i nstallDir, file . FileName ) ;
var fileFinalPath = Path . Combine ( depot . I nstallDir, 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 . i nstallDir, file . FileName ) ;
var fileFinalPath = Path . Combine ( depot . I nstallDir, 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}" ) ;
}
}
}