public static void LoadFromFile(string filename) { if (Loaded) throw new Exception("Config already loaded"); if (File.Exists(filename)) { using (FileStream fs = File.Open(filename, FileMode.Open)) using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress)) TheConfig = ProtoBuf.Serializer.Deserialize<ConfigStore>(ds); } else { TheConfig = new ConfigStore(); } TheConfig.FileName = filename; }
static async Task MainAsync(string[] args) { if (args.Length == 0) { PrintUsage(); return; } DebugLog.Enabled = false; ConfigStore.LoadFromFile(Path.Combine(Directory.GetCurrentDirectory(), "DepotDownloader.config")); bool bDumpManifest = HasParameter(args, "-manifest-only"); uint appId = GetParameter <uint>(args, "-app", ContentDownloader.INVALID_APP_ID); uint depotId = GetParameter <uint>(args, "-depot", ContentDownloader.INVALID_DEPOT_ID); ContentDownloader.Config.ManifestId = GetParameter <ulong>(args, "-manifest", ContentDownloader.INVALID_MANIFEST_ID); if (appId == ContentDownloader.INVALID_APP_ID) { Console.WriteLine("Error: -app not specified!"); return; } if (depotId == ContentDownloader.INVALID_DEPOT_ID && ContentDownloader.Config.ManifestId != ContentDownloader.INVALID_MANIFEST_ID) { Console.WriteLine("Error: -manifest requires -depot to be specified"); return; } ContentDownloader.Config.DownloadManifestOnly = bDumpManifest; int cellId = GetParameter <int>(args, "-cellid", -1); if (cellId == -1) { cellId = 0; } ContentDownloader.Config.CellID = cellId; ContentDownloader.Config.BetaPassword = GetParameter <string>(args, "-betapassword"); string fileList = GetParameter <string>(args, "-filelist"); string[] files = null; if (fileList != null) { try { string fileListData = File.ReadAllText(fileList); files = fileListData.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); ContentDownloader.Config.UsingFileList = true; ContentDownloader.Config.FilesToDownload = new List <string>(); ContentDownloader.Config.FilesToDownloadRegex = new List <Regex>(); foreach (var fileEntry in files) { try { Regex rgx = new Regex(fileEntry, RegexOptions.Compiled | RegexOptions.IgnoreCase); ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); } catch { ContentDownloader.Config.FilesToDownload.Add(fileEntry); continue; } } Console.WriteLine("Using filelist: '{0}'.", fileList); } catch (Exception ex) { Console.WriteLine("Warning: Unable to load filelist: {0}", ex.ToString()); } } string username = GetParameter <string>(args, "-username") ?? GetParameter <string>(args, "-user"); string password = GetParameter <string>(args, "-password") ?? GetParameter <string>(args, "-pass"); ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password"); ContentDownloader.Config.InstallDirectory = GetParameter <string>(args, "-dir"); ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms"); ContentDownloader.Config.VerifyAll = HasParameter(args, "-verify-all") || HasParameter(args, "-verify_all") || HasParameter(args, "-validate"); ContentDownloader.Config.MaxServers = GetParameter <int>(args, "-max-servers", 20); ContentDownloader.Config.MaxDownloads = GetParameter <int>(args, "-max-downloads", 4); string branch = GetParameter <string>(args, "-branch") ?? GetParameter <string>(args, "-beta") ?? "Public"; bool forceDepot = HasParameter(args, "-force-depot"); string os = GetParameter <string>(args, "-os", null); if (ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty(os)) { Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); return; } ContentDownloader.Config.MaxServers = Math.Max(ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads); if (username != null && password == null && (!ContentDownloader.Config.RememberPassword || !ConfigStore.TheConfig.LoginKeys.ContainsKey(username))) { Console.Write("Enter account password for \"{0}\": ", username); password = Util.ReadPassword(); Console.WriteLine(); } else if (username == null) { Console.WriteLine("No username given. Using anonymous account with dedicated server subscription."); } // capture the supplied password in case we need to re-use it after checking the login key ContentDownloader.Config.SuppliedPassword = password; if (ContentDownloader.InitializeSteam3(username, password)) { await ContentDownloader.DownloadAppAsync(appId, depotId, branch, os, forceDepot).ConfigureAwait(false); ContentDownloader.ShutdownSteam3(); } }
private static async Task DownloadSteam3Async(uint appId, List <DepotDownloadInfo> depots) { ulong TotalBytesCompressed = 0; ulong TotalBytesUncompressed = 0; foreach (var depot in depots) { ulong DepotBytesCompressed = 0; ulong DepotBytesUncompressed = 0; Console.WriteLine("Downloading depot {0} - {1}", depot.id, depot.contentName); CancellationTokenSource cts = new CancellationTokenSource(); cdnPool.ExhaustedToken = cts; 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; while (depotManifest == null) { CDNClient client = null; try { client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, CancellationToken.None).ConfigureAwait(false); depotManifest = await client.DownloadManifestAsync(depot.id, depot.manifestId).ConfigureAwait(false); cdnPool.ReturnConnection(client); } catch (WebException e) { cdnPool.ReturnBrokenConnection(client); if (e.Status == WebExceptionStatus.ProtocolError) { var response = e.Response as HttpWebResponse; if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) { Console.WriteLine("Encountered 401 for depot manifest {0} {1}. Aborting.", depot.id, depot.manifestId); break; } else { Console.WriteLine("Encountered error downloading depot manifest {0} {1}: {2}", depot.id, depot.manifestId, response.StatusCode); } } else { Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.id, depot.manifestId, e.Status); } } catch (Exception e) { cdnPool.ReturnBrokenConnection(client); Console.WriteLine("Encountered error downloading manifest for depot {0} {1}: {2}", depot.id, depot.manifestId, e.Message); } } if (depotManifest == null) { Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.manifestId, depot.id); return; } 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; } 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 semaphore = new SemaphoreSlim(Config.MaxDownloads); var files = filesAfterExclusions.Where(f => !f.Flags.HasFlag(EDepotFileFlag.Directory)).ToArray(); var tasks = new Task[files.Length]; for (var i = 0; i < files.Length; i++) { var file = files[i]; var task = Task.Run(async() => { cts.Token.ThrowIfCancellationRequested(); try { await semaphore.WaitAsync().ConfigureAwait(false); cts.Token.ThrowIfCancellationRequested(); 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) { fsOld.Seek(( long )match.OldChunk.Offset, SeekOrigin.Begin); byte[] tmp = new byte[match.OldChunk.UncompressedLength]; fsOld.Read(tmp, 0, tmp.Length); byte[] adler = Util.AdlerHash(tmp); if (!adler.SequenceEqual(match.OldChunk.Checksum)) { neededChunks.Add(match.NewChunk); } else { fs.Seek(( long )match.NewChunk.Offset, SeekOrigin.Begin); fs.Write(tmp, 0, tmp.Length); } } } 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.Dispose(); } return; } else { size_downloaded += (file.TotalSize - ( ulong )neededChunks.Select(x => ( long )x.UncompressedLength).Sum()); } } foreach (var chunk in neededChunks) { if (cts.IsCancellationRequested) { break; } string chunkID = Util.EncodeHexString(chunk.ChunkID); CDNClient.DepotChunk chunkData = null; while (!cts.IsCancellationRequested) { CDNClient client; try { client = await cdnPool.GetConnectionForDepotAsync(appId, depot.id, depot.depotKey, cts.Token).ConfigureAwait(false); } catch (OperationCanceledException) { break; } 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 = await client.DownloadDepotChunkAsync(depot.id, data).ConfigureAwait(false); cdnPool.ReturnConnection(client); break; } catch (WebException e) { cdnPool.ReturnBrokenConnection(client); if (e.Status == WebExceptionStatus.ProtocolError) { var response = e.Response as HttpWebResponse; if (response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden) { Console.WriteLine("Encountered 401 for chunk {0}. Aborting.", chunkID); cts.Cancel(); break; } else { Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, response.StatusCode); } } else { Console.WriteLine("Encountered error downloading chunk {0}: {1}", chunkID, e.Status); } } catch (Exception e) { cdnPool.ReturnBrokenConnection(client); Console.WriteLine("Encountered unexpected error downloading chunk {0}: {1}", chunkID, e.Message); } } if (chunkData == null) { Console.WriteLine("Failed to find any server with chunk {0} for depot {1}. Aborting.", chunkID, depot.id); cts.Cancel(); 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.Dispose(); Console.WriteLine("{0,6:#00.00}% {1}", (( float )size_downloaded / ( float )complete_download_size) * 100.0f, fileFinalPath); } finally { semaphore.Release(); } }); tasks[i] = task; } await Task.WhenAll(tasks).ConfigureAwait(false); ConfigStore.TheConfig.LastManifests[depot.id] = depot.manifestId; ConfigStore.Save(); Console.WriteLine("Depot {0} - Downloaded {1} bytes ({2} bytes uncompressed)", depot.id, DepotBytesCompressed, DepotBytesUncompressed); } Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", TotalBytesCompressed, TotalBytesUncompressed, depots.Count); }
static async Task MainAsync(string[] args) { if (args.Length == 0) { PrintUsage(); return; } DebugLog.Enabled = false; ConfigStore.LoadFromFile(Path.Combine(Directory.GetCurrentDirectory(), "DepotDownloader.config")); #region Common Options string username = GetParameter <string>(args, "-username") ?? GetParameter <string>(args, "-user"); string password = GetParameter <string>(args, "-password") ?? GetParameter <string>(args, "-pass"); ContentDownloader.Config.RememberPassword = HasParameter(args, "-remember-password"); ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); int cellId = GetParameter <int>(args, "-cellid", -1); if (cellId == -1) { cellId = 0; } ContentDownloader.Config.CellID = cellId; string fileList = GetParameter <string>(args, "-filelist"); string[] files = null; if (fileList != null) { try { string fileListData = File.ReadAllText(fileList); files = fileListData.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); ContentDownloader.Config.UsingFileList = true; ContentDownloader.Config.FilesToDownload = new List <string>(); ContentDownloader.Config.FilesToDownloadRegex = new List <Regex>(); foreach (var fileEntry in files) { try { Regex rgx = new Regex(fileEntry, RegexOptions.Compiled | RegexOptions.IgnoreCase); ContentDownloader.Config.FilesToDownloadRegex.Add(rgx); } catch { ContentDownloader.Config.FilesToDownload.Add(fileEntry); continue; } } Console.WriteLine("Using filelist: '{0}'.", fileList); } catch (Exception ex) { Console.WriteLine("Warning: Unable to load filelist: {0}", ex.ToString()); } } ContentDownloader.Config.InstallDirectory = GetParameter <string>(args, "-dir"); ContentDownloader.Config.VerifyAll = HasParameter(args, "-verify-all") || HasParameter(args, "-verify_all") || HasParameter(args, "-validate"); ContentDownloader.Config.MaxServers = GetParameter <int>(args, "-max-servers", 20); ContentDownloader.Config.MaxDownloads = GetParameter <int>(args, "-max-downloads", 4); ContentDownloader.Config.MaxServers = Math.Max(ContentDownloader.Config.MaxServers, ContentDownloader.Config.MaxDownloads); #endregion ulong pubFile = GetParameter <ulong>(args, "-pubfile", ContentDownloader.INVALID_MANIFEST_ID); if (pubFile != ContentDownloader.INVALID_MANIFEST_ID) { #region Pubfile Downloading if (InitializeSteam(username, password)) { await ContentDownloader.DownloadPubfileAsync(pubFile).ConfigureAwait(false); ContentDownloader.ShutdownSteam3(); } #endregion } else { #region App downloading string branch = GetParameter <string>(args, "-branch") ?? GetParameter <string>(args, "-beta") ?? ContentDownloader.DEFAULT_BRANCH; ContentDownloader.Config.BetaPassword = GetParameter <string>(args, "-betapassword"); ContentDownloader.Config.DownloadAllPlatforms = HasParameter(args, "-all-platforms"); string os = GetParameter <string>(args, "-os", null); if (ContentDownloader.Config.DownloadAllPlatforms && !String.IsNullOrEmpty(os)) { Console.WriteLine("Error: Cannot specify -os when -all-platforms is specified."); return; } uint appId = GetParameter <uint>(args, "-app", ContentDownloader.INVALID_APP_ID); if (appId == ContentDownloader.INVALID_APP_ID) { Console.WriteLine("Error: -app not specified!"); return; } uint depotId; bool isUGC = false; ulong manifestId = GetParameter <ulong>(args, "-ugc", ContentDownloader.INVALID_MANIFEST_ID); if (manifestId != ContentDownloader.INVALID_MANIFEST_ID) { depotId = appId; isUGC = true; } else { depotId = GetParameter <uint>(args, "-depot", ContentDownloader.INVALID_DEPOT_ID); manifestId = GetParameter <ulong>(args, "-manifest", ContentDownloader.INVALID_MANIFEST_ID); if (depotId == ContentDownloader.INVALID_DEPOT_ID && manifestId != ContentDownloader.INVALID_MANIFEST_ID) { Console.WriteLine("Error: -manifest requires -depot to be specified"); return; } } if (InitializeSteam(username, password)) { await ContentDownloader.DownloadAppAsync(appId, depotId, manifestId, branch, os, isUGC).ConfigureAwait(false); ContentDownloader.ShutdownSteam3(); } #endregion } }
static void Main(string[] args) { if (args.Length == 0) { PrintUsage(); return; } Program instance = new Program(); DebugLog.Enabled = false; ConfigStore.LoadFromFile(Path.Combine(Environment.CurrentDirectory, "DepotDownloader.config")); instance.ParseOrPromptUserAndPassword(args); ParseGeneralContentDownloaderParams(args); ParseFileList(args); if (HasParameter(args, "-steamvrwin")) { instance.GetSteamVRWin(); return; } if (HasParameter(args, "-steamvr")) { instance.GetSteamVR(); return; } ContentDownloader.Config.DownloadManifestOnly = HasParameter(args, "-manifest-only"); ContentDownloader.Config.BetaPassword = GetParameter <string>(args, "-betapassword"); var dir = GetParameter <string>(args, "-dir"); var dl = new Downloadable(); dl.AppId = GetParameter <uint>(args, "-app", dl.AppId); dl.DepotId = GetParameter <uint>(args, "-depot", dl.DepotId); dl.Branch = GetBranch(args); dl.ManifestId = GetParameter <ulong>(args, "-manifest", dl.ManifestId); if (dl.AppId == ContentDownloader.INVALID_APP_ID) { Console.WriteLine("Error: -app not specified!"); return; } if (dl.DepotId == ContentDownloader.INVALID_DEPOT_ID && dl.ManifestId != ContentDownloader.INVALID_MANIFEST_ID) { Console.WriteLine("Error: -manifest requires -depot to be specified"); return; } dl.ForceDepot = HasParameter(args, "-force-depot"); if (instance.Start()) { instance.Download(dir, dl); instance.Shutdown(); } }
private void LogOnCallback(SteamUser.LoggedOnCallback loggedOn) { bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied; bool is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor; bool isLoginKey = ContentDownloader.Config.RememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword; if (isSteamGuard || is2FA || isLoginKey) { bExpectingDisconnectRemote = true; Abort(false); if (!isLoginKey) { Console.WriteLine("This account is protected by Steam Guard."); } if (is2FA) { Console.Write("Please enter your 2 factor auth code from your authenticator app: "); logonDetails.TwoFactorCode = Console.ReadLine(); } else if (isLoginKey) { ConfigStore.TheConfig.LoginKeys.Remove(logonDetails.Username); ConfigStore.Save(); logonDetails.LoginKey = null; if (ContentDownloader.Config.SuppliedPassword != null) { Console.WriteLine("Login key was expired. Connecting with supplied password."); logonDetails.Password = ContentDownloader.Config.SuppliedPassword; } else { Console.WriteLine("Login key was expired. Please enter your password: "******"Please enter the authentication code sent to your email address: "); logonDetails.AuthCode = Console.ReadLine(); } Console.Write("Retrying Steam3 connection..."); Connect(); return; } else if (loggedOn.Result == EResult.ServiceUnavailable) { Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result); Abort(false); return; } else if (loggedOn.Result != EResult.OK) { Console.WriteLine("Unable to login to Steam3: {0}", loggedOn.Result); Abort(); return; } Console.WriteLine(" Done!"); this.seq++; credentials.LoggedOn = true; if (ContentDownloader.Config.CellID == 0) { Console.WriteLine("Using Steam3 suggested CellID: " + loggedOn.CellID); ContentDownloader.Config.CellID = ( int )loggedOn.CellID; } }
private static void DownloadSteam3(List <DepotDownloadInfo> depots) { ulong TotalBytesCompressed = 0; ulong TotalBytesUncompressed = 0; foreach (var depot in depots) { 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.manifestId); break; } catch (WebException) { } catch (SocketException) { } } if (depotManifest == null) { Console.WriteLine("\nUnable to download manifest {0} for depot {1}", depot.manifestId, depot.id); return; } 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; } 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(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); } Console.WriteLine("Total downloaded: {0} bytes ({1} bytes uncompressed) from {2} depots", TotalBytesCompressed, TotalBytesUncompressed, depots.Count); }
private void LogOnCallback(SteamUser.LoggedOnCallback loggedOn) { isLoggingIn = false; if (logonDetails != null) { bool isSteamGuard = loggedOn.Result == EResult.AccountLogonDenied; bool is2FA = loggedOn.Result == EResult.AccountLoginDeniedNeedTwoFactor; bool isLoginKey = logonDetails.ShouldRememberPassword && logonDetails.LoginKey != null && loggedOn.Result == EResult.InvalidPassword; if (isSteamGuard || is2FA || isLoginKey) { bExpectingDisconnectRemote = true; Abort(false); if (!isLoginKey) { DebugLog.WriteLine("Steam3Session", "This account is protected by Steam Guard."); } if (is2FA) { DebugLog.WriteLine("Steam3Session", "Please enter your 2 factor auth code from your authenticator app: "); on2faRequired?.Invoke(loggedOn.Result); //logonDetails.TwoFactorCode = Console.ReadLine(); } else if (isLoginKey) { ConfigStore.TheConfig.LoginKeys.Remove(logonDetails.Username); ConfigStore.Save(); logonDetails.LoginKey = null; DebugLog.WriteLine("Steam3Session", "Login key was expired. Please enter your password: "******"Steam3Session", "Please enter the authentication code sent to your email address: "); onAuthRequired?.Invoke(loggedOn.Result); //logonDetails.AuthCode = Console.ReadLine(); } //DebugLog.WriteLine("Steam3Session", "Retrying Steam3 connection..." ); //Connect(); onLogonFailed?.Invoke(loggedOn.Result); return; } else if (loggedOn.Result == EResult.ServiceUnavailable) { DebugLog.WriteLine("Steam3Session", "Unable to login to Steam3: " + loggedOn.Result); Abort(false); onLogonFailed?.Invoke(loggedOn.Result); return; } else if (loggedOn.Result != EResult.OK) { DebugLog.WriteLine("Steam3Session", "Unable to login to Steam3: " + loggedOn.Result); Abort(); onLogonFailed?.Invoke(loggedOn.Result); return; } } DebugLog.WriteLine("Steam3Session", " Done!"); DebugLog.WriteLine("Steam3Session", "Using Steam3 suggested CellID: " + loggedOn.CellID); ContentDownloader.Config.CellID = (int)loggedOn.CellID; onLoggedOn?.Invoke(); }