Represents a single chunk within a file.
Example #1
0
        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);
        }
Example #2
0
        /// <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);
        }
Example #3
0
        /// <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));
        }
Example #4
0
        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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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));
        }
Example #7
0
        /// <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));
        }
Example #8
0
            /// <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;
            }
Example #9
0
        // 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));
        }
Example #10
0
        // 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));
        }
Example #11
0
        // 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));
        }
Example #12
0
        // 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));
        }
Example #13
0
        // 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));
        }
Example #14
0
        /// <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);
        }
Example #15
0
        /// <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);
        }
Example #16
0
        // 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;
        }