async Task <DepotChunk> DownloadDepotChunkCoreAsync(uint depotId, DepotManifest.ChunkData chunk, Server server, string cdnAuthToken, byte[] depotKey) { var chunkID = Utils.EncodeHexString(chunk.ChunkID); var chunkData = await DoRawCommandAsync(server, HttpMethod.Get, "depot", doAuth : true, args : string.Format("{0}/chunk/{1}", depotId, chunkID), authtoken : cdnAuthToken).ConfigureAwait(false); // assert that lengths match only if the chunk has a length assigned. if (chunk.CompressedLength != default(uint) && chunkData.Length != chunk.CompressedLength) { throw new InvalidDataException($"Length mismatch after downloading depot chunk! (was {chunkData.Length}, but should be {chunk.CompressedLength})"); } var depotChunk = new DepotChunk { ChunkInfo = chunk, Data = chunkData, }; if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
/// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> public DepotChunk DownloadDepotChunk(DepotManifest.ChunkData chunk) { if (chunk.ChunkID == null) { throw new ArgumentNullException("chunk.ChunkID"); } string chunkId = Utils.EncodeHexString(chunk.ChunkID); byte[] chunkData = DoRawCommand(connectedServer, "depot", doAuth: true, args: string.Format("{0}/chunk/{1}", depotId, chunkId)); if (chunk.CompressedLength != default(uint)) { // assert that lengths match only if the chunk has a length assigned. DebugLog.Assert(chunkData.Length == chunk.CompressedLength, "CDNClient", "Length mismatch after downloading depot chunk!"); } var depotChunk = new DepotChunk { ChunkInfo = chunk, Data = chunkData, }; if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
/// <summary> /// Downloads the specified depot chunk, does not processes the chunk and verifies the checksum if the depot decryption key has been provided yet. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="o:DownloadManifestAsync"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsStreamAsync(uint depotId, DepotManifest.ChunkData chunk) { depotCdnAuthKeys.TryGetValue(depotId, out var cdnToken); depotKeys.TryGetValue(depotId, out var depotKey); return(await DownloadDepotChunkAsStreamAsync(depotId, chunk, connectedServer, cdnToken, depotKey).ConfigureAwait(false)); }
DepotChunk DownloadDepotChunkCore(uint depotId, DepotManifest.ChunkData chunk, Server server, string cdnAuthToken, byte[] depotKey) { var chunkID = Utils.EncodeHexString(chunk.ChunkID); byte[] chunkData = DoRawCommand(server, "depot", doAuth: true, args: string.Format("{0}/chunk/{1}", depotId, chunkID), authtoken: cdnAuthToken); if (chunk.CompressedLength != default(uint)) { // assert that lengths match only if the chunk has a length assigned. DebugLog.Assert(chunkData.Length == chunk.CompressedLength, "CDNClient", "Length mismatch after downloading depot chunk!"); } var depotChunk = new DepotChunk { ChunkInfo = chunk, Data = chunkData, }; if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
/// <summary> /// Downloads the specified depot chunk as a stream, does not processes the chunk and verifies the checksum if the depot decryption key has been provided yet. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="o:DownloadManifestAsync"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="server">The content server to connect to.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsStreamAsync(uint depotId, DepotManifest.ChunkData chunk, Server server, string cdnAuthToken, byte[] depotKey) { if (server == null) { throw new ArgumentNullException(nameof(server)); } if (chunk == null) { throw new ArgumentNullException(nameof(chunk)); } if (chunk.ChunkID == null) { throw new ArgumentException("Chunk must have a ChunkID.", nameof(chunk)); } var chunkID = Utils.EncodeHexString(chunk.ChunkID); Stream stream = await DoRawCommandAsStreamAsync(server, HttpMethod.Get, "depot", doAuth : true, args : string.Format("{0}/chunk/{1}", depotId, chunkID), authtoken : cdnAuthToken).ConfigureAwait(false); long fullStreamLength = 0; //using ( stream ) //{ while (stream.ReadByte() > -1) { fullStreamLength++; } stream.Position = 0; //} // assert that lengths match only if the chunk has a length assigned. if (chunk.CompressedLength != default(uint) && fullStreamLength != chunk.CompressedLength) { throw new InvalidDataException($"Length mismatch after downloading depot chunk! (was {fullStreamLength}, but should be {chunk.CompressedLength})"); } var depotChunk = new DepotChunk { ChunkInfo = chunk, //Data = chunkData, DataStream = stream, }; //I'll need to do some extra work to process the stream with the depot key. Will come back to this later. if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
/// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="o:DownloadManifestAsync"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="host">CDN hostname.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <param name="proxyServer">Optional content server marked as UseAsProxy which transforms the request.</param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk, string host, string cdnAuthToken, byte[]?depotKey = null, Server?proxyServer = null) { var server = new Server { Protocol = Server.ConnectionProtocol.HTTP, Host = host, VHost = host, Port = 80 }; return(await DownloadDepotChunkAsync(depotId, chunk, server, cdnAuthToken, depotKey).ConfigureAwait(false)); }
/// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="o:DownloadManifestAsync"/>. /// </param> /// <param name="server">CDN server to download from.</param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> or <see ref="connectedServer"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk, Server server) { if (server is null) { throw new ArgumentNullException(nameof(server)); } depotCdnAuthKeys.TryGetValue(depotId, out var cdnToken); depotKeys.TryGetValue(depotId, out var depotKey); return(await DownloadDepotChunkAsync(depotId, chunk, server, cdnToken, depotKey).ConfigureAwait(false)); }
/// <summary> /// Initializes a new instance of the <see cref="DepotChunk"/> class. /// </summary> /// <param name="info">The manifest chunk information associated with this chunk.</param> /// <param name="data">The underlying data for this chunk.</param> public DepotChunk(DepotManifest.ChunkData info, byte[] data) { if (info is null) { throw new ArgumentNullException(nameof(info)); } if (data is null) { throw new ArgumentNullException(nameof(data)); } ChunkInfo = info; Data = data; }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="host">CDN hostname.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> public DepotChunk DownloadDepotChunk(uint depotId, DepotManifest.ChunkData chunk, string host, string cdnAuthToken, byte[] depotKey = null) #pragma warning restore 0419 { if (chunk.ChunkID == null) { throw new ArgumentNullException("chunk.ChunkID"); } var server = new Server { Host = host, Port = 80 }; return(DownloadDepotChunkCore(depotId, chunk, server, cdnAuthToken, depotKey)); }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> public DepotChunk DownloadDepotChunk(uint depotId, DepotManifest.ChunkData chunk) #pragma warning restore 0419 { if (chunk.ChunkID == null) { throw new ArgumentNullException("chunk.ChunkID"); } string cdnToken = null; depotCdnAuthKeys.TryGetValue(depotId, out cdnToken); byte[] depotKey = null; depotKeys.TryGetValue(depotId, out depotKey); return(DownloadDepotChunkCore(depotId, chunk, connectedServer, cdnToken, depotKey)); }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 // XML comment has cref attribute 'DownloadManifest' that could not be resolved #pragma warning disable 1574 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="host">CDN hostname.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk, string host, string cdnAuthToken, byte[] depotKey = null) #pragma warning restore 1574 #pragma warning restore 0419 { if (chunk.ChunkID == null) { throw new ArgumentNullException("chunk.ChunkID"); } var server = new Server { Host = host, Port = 80 }; return(await DownloadDepotChunkCoreAsync(depotId, chunk, server, cdnAuthToken, depotKey).ConfigureAwait(false)); }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 // XML comment has cref attribute 'DownloadManifest' that could not be resolved #pragma warning disable 1574 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk) #pragma warning restore 0419 #pragma warning restore 1574 { if (chunk.ChunkID == null) { throw new ArgumentNullException("chunk.ChunkID"); } string cdnToken = null; depotCdnAuthKeys.TryGetValue(depotId, out cdnToken); byte[] depotKey = null; depotKeys.TryGetValue(depotId, out depotKey); return(await DownloadDepotChunkCoreAsync(depotId, chunk, connectedServer, cdnToken, depotKey).ConfigureAwait(false)); }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 // XML comment has cref attribute 'DownloadManifest' that could not be resolved #pragma warning disable 1574 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk) #pragma warning restore 0419 #pragma warning restore 1574 { if (chunk == null) { throw new ArgumentNullException(nameof(chunk)); } if (chunk.ChunkID == null) { throw new ArgumentException("Chunk must have a ChunkID.", nameof(chunk)); } depotCdnAuthKeys.TryGetValue(depotId, out var cdnToken); depotKeys.TryGetValue(depotId, out var depotKey); return(await DownloadDepotChunkCoreAsync(depotId, chunk, connectedServer, cdnToken, depotKey).ConfigureAwait(false)); }
/// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> public DepotChunk DownloadDepotChunk(DepotManifest.ChunkData chunk) { string chunkId = Utils.EncodeHexString(chunk.ChunkID); byte[] chunkData = DoRawCommand(connectedServer, "depot", doAuth: true, args: string.Format("{0}/chunk/{1}", depotId, chunkId)); DebugLog.Assert(chunkData.Length == chunk.CompressedLength, "CDNClient", "Length mismatch after downloading depot chunk!"); var depotChunk = new DepotChunk { ChunkInfo = chunk, Data = chunkData, }; if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
/// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="o:DownloadManifestAsync"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="server">The content server to connect to.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <param name="proxyServer">Optional content server marked as UseAsProxy which transforms the request.</param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk, Server server, string?cdnAuthToken, byte[]?depotKey, Server?proxyServer = null) { if (server == null) { throw new ArgumentNullException(nameof(server)); } if (chunk == null) { throw new ArgumentNullException(nameof(chunk)); } if (chunk.ChunkID == null) { throw new ArgumentException("Chunk must have a ChunkID.", nameof(chunk)); } var chunkID = Utils.EncodeHexString(chunk.ChunkID); var chunkData = await DoRawCommandAsync(server, string.Format("depot/{0}/chunk/{1}", depotId, chunkID), cdnAuthToken, proxyServer).ConfigureAwait(false); // assert that lengths match only if the chunk has a length assigned. if (chunk.CompressedLength != default(uint) && chunkData.Length != chunk.CompressedLength) { throw new InvalidDataException($"Length mismatch after downloading depot chunk! (was {chunkData.Length}, but should be {chunk.CompressedLength})"); } var depotChunk = new DepotChunk(chunk, chunkData); if (depotKey != null) { // if we have the depot key, we can process the chunk immediately depotChunk.Process(depotKey); } return(depotChunk); }
// Ambiguous reference in cref attribute: 'CDNClient.DownloadManifest'. Assuming 'SteamKit2.CDNClient.DownloadManifest(uint, ulong)', // but could have also matched other overloads including 'SteamKit2.CDNClient.DownloadManifest(uint, ulong, string, string, byte[])'. #pragma warning disable 0419 // XML comment has cref attribute 'DownloadManifest' that could not be resolved #pragma warning disable 1574 /// <summary> /// Downloads the specified depot chunk, and optionally processes the chunk and verifies the checksum if the depot decryption key has been provided. /// </summary> /// <remarks> /// This function will also validate the length of the downloaded chunk with the value of <see cref="DepotManifest.ChunkData.CompressedLength"/>, /// if it has been assigned a value. /// </remarks> /// <param name="depotId">The id of the depot being accessed.</param> /// <param name="chunk"> /// A <see cref="DepotManifest.ChunkData"/> instance that represents the chunk to download. /// This value should come from a manifest downloaded with <see cref="CDNClient.DownloadManifest"/>. /// </param> /// <returns>A <see cref="DepotChunk"/> instance that contains the data for the given chunk.</returns> /// <param name="host">CDN hostname.</param> /// <param name="cdnAuthToken">CDN auth token for CDN content server endpoints.</param> /// <param name="depotKey"> /// The depot decryption key for the depot that will be downloaded. /// This is used for decrypting filenames (if needed) in depot manifests, and processing depot chunks. /// </param> /// <exception cref="System.ArgumentNullException">chunk's <see cref="DepotManifest.ChunkData.ChunkID"/> was null.</exception> /// <exception cref="System.IO.InvalidDataException">Thrown if the downloaded data does not match the expected length.</exception> /// <exception cref="HttpRequestException">An network error occurred when performing the request.</exception> /// <exception cref="SteamKitWebRequestException">A network error occurred when performing the request.</exception> public async Task <DepotChunk> DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunk, string host, string cdnAuthToken, byte[] depotKey = null) #pragma warning restore 1574 #pragma warning restore 0419 { if (chunk == null) { throw new ArgumentNullException(nameof(chunk)); } if (chunk.ChunkID == null) { throw new ArgumentException("Chunk must have a ChunkID.", nameof(chunk)); } var server = new Server { Protocol = Server.ConnectionProtocol.HTTP, Host = host, VHost = host, Port = 80 }; return(await DownloadDepotChunkCoreAsync(depotId, chunk, server, cdnAuthToken, depotKey).ConfigureAwait(false)); }
private static ulong DownloadSteam3(List<DepotDownloadInfo> depots) { ulong TotalBytesCompressed = 0; ulong TotalBytesUncompressed = 0; ulong result = 0; foreach (var depot in depots) { result ^= depot.manifestId; ulong DepotBytesCompressed = 0; ulong DepotBytesUncompressed = 0; Console.WriteLine("Downloading depot {0} - {1}", depot.id, depot.contentName); Console.Write("Finding content servers..."); List<CDNClient> cdnClients = null; Console.WriteLine(" Done!"); ProtoManifest oldProtoManifest = null; ProtoManifest newProtoManifest = null; string configDir = Path.Combine(depot.installDir, CONFIG_DIR); ulong lastManifestId = INVALID_MANIFEST_ID; ConfigStore.TheConfig.LastManifests.TryGetValue(depot.id, out lastManifestId); // In case we have an early exit, this will force equiv of verifyall next run. ConfigStore.TheConfig.LastManifests[depot.id] = INVALID_MANIFEST_ID; ConfigStore.Save(); if (lastManifestId != INVALID_MANIFEST_ID) { var oldManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", lastManifestId)); if (File.Exists(oldManifestFileName)) oldProtoManifest = ProtoManifest.LoadFromFile(oldManifestFileName); } if (lastManifestId == depot.manifestId && oldProtoManifest != null) { newProtoManifest = oldProtoManifest; Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id); } else { var newManifestFileName = Path.Combine(configDir, string.Format("{0}.bin", depot.manifestId)); if (newManifestFileName != null) { newProtoManifest = ProtoManifest.LoadFromFile(newManifestFileName); } if (newProtoManifest != null) { Console.WriteLine("Already have manifest {0} for depot {1}.", depot.manifestId, depot.id); } else { Console.Write("Downloading depot manifest..."); DepotManifest depotManifest = null; cdnClients = CollectCDNClientsForDepot(depot); foreach (var c in cdnClients) { try { depotManifest = c.DownloadManifest(depot.id, depot.manifestId); break; } catch (WebException) { } catch (SocketException) { } } if (depotManifest == null) { Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.manifestId, depot.id); return 0; } newProtoManifest = new ProtoManifest(depotManifest, depot.manifestId); newProtoManifest.SaveToFile(newManifestFileName); Console.WriteLine(" Done!"); } } newProtoManifest.Files.Sort((x, y) => { return x.FileName.CompareTo(y.FileName); }); if (Config.DownloadManifestOnly) { StringBuilder manifestBuilder = new StringBuilder(); string txtManifest = Path.Combine(depot.installDir, string.Format("manifest_{0}.txt", depot.id)); foreach (var file in newProtoManifest.Files) { if (file.Flags.HasFlag(EDepotFileFlag.Directory)) continue; manifestBuilder.Append(string.Format("{0}\n", file.FileName)); } File.WriteAllText(txtManifest, manifestBuilder.ToString()); continue; } #region post-manifest ulong complete_download_size = 0; ulong size_downloaded = 0; string stagingDir = Path.Combine(depot.installDir, STAGING_DIR); var filesAfterExclusions = newProtoManifest.Files.AsParallel().Where(f => TestIsFileIncluded(f.FileName)).ToList(); // Pre-process filesAfterExclusions.ForEach(file => { var fileFinalPath = Path.Combine(depot.installDir, file.FileName); var fileStagingPath = Path.Combine(stagingDir, file.FileName); if (file.Flags.HasFlag(EDepotFileFlag.Directory)) { Directory.CreateDirectory(fileFinalPath); Directory.CreateDirectory(fileStagingPath); } else { // Some manifests don't explicitly include all necessary directories Directory.CreateDirectory(Path.GetDirectoryName(fileFinalPath)); Directory.CreateDirectory(Path.GetDirectoryName(fileStagingPath)); complete_download_size += file.TotalSize; } }); var rand = new Random(); filesAfterExclusions.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)) .AsParallel().WithDegreeOfParallelism(Config.MaxDownloads) .ForAll(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)) { File.Delete(fileStagingPath); } 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) { oldManifestFile = oldProtoManifest.Files.SingleOrDefault(f => f.FileName == file.FileName); } if (oldManifestFile != null) { neededChunks = new List<ProtoManifest.ChunkData>(); if (Config.VerifyAll || !oldManifestFile.FileHash.SequenceEqual(file.FileHash)) { // we have a version of this file, but it doesn't fully match what we want var matchingChunks = new List<ChunkMatch>(); foreach (var chunk in file.Chunks) { var oldChunk = oldManifestFile.Chunks.FirstOrDefault(c => c.ChunkID.SequenceEqual(chunk.ChunkID)); if (oldChunk != null) { matchingChunks.Add(new ChunkMatch(oldChunk, chunk)); } else { neededChunks.Add(chunk); } } File.Move(fileFinalPath, fileStagingPath); fs = File.Open(fileFinalPath, FileMode.Create); fs.SetLength((long)file.TotalSize); using (var fsOld = File.Open(fileStagingPath, FileMode.Open)) { foreach (var match in matchingChunks) { fs.Seek((long)match.NewChunk.Offset, SeekOrigin.Begin); fsOld.Seek((long)match.OldChunk.Offset, SeekOrigin.Begin); byte[] tmp = new byte[match.OldChunk.UncompressedLength]; fsOld.Read(tmp, 0, tmp.Length); fs.Write(tmp, 0, tmp.Length); } } File.Delete(fileStagingPath); } } else { // No old manifest or file not in old manifest. We must validate. fs = File.Open(fileFinalPath, FileMode.Open); if ((ulong)fi.Length != file.TotalSize) { 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 => (int)x.UncompressedLength).Sum()); } } int cdnClientIndex = 0; if (neededChunks.Count > 0 && cdnClients == null) { // If we didn't need to connect to get manifests, connect now. cdnClients = CollectCDNClientsForDepot(depot); cdnClientIndex = rand.Next(0, cdnClients.Count); } foreach (var chunk in neededChunks) { string chunkID = Util.EncodeHexString(chunk.ChunkID); CDNClient.DepotChunk chunkData = null; int idx = cdnClientIndex; while (true) { 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 { chunkData = cdnClients[idx].DownloadDepotChunk(depot.id, data); break; } catch { if (++idx >= cdnClients.Count) idx = 0; if (idx == cdnClientIndex) break; } } if (chunkData == null) { Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot); return; } 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); size_downloaded += chunk.UncompressedLength; } fs.Close(); Console.WriteLine("{0,6:#00.00}% {1}", ((float)size_downloaded / (float)complete_download_size) * 100.0f, fileFinalPath); }); ConfigStore.TheConfig.LastManifests[depot.id] = depot.manifestId; ConfigStore.Save(); Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, DepotBytesCompressed, DepotBytesUncompressed); } #endregion Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", TotalBytesCompressed, TotalBytesUncompressed, depots.Count); return result; }