private static void SaveActiveVersion(VersionUIComponent component) { var previousState = component.State; component.State = VersionState.Saving; bool mainDownloaded = IsFullyDownloaded(component.version, Package.Main); bool gehennaDownloaded = IsFullyDownloaded(component.version, Package.Gehenna); bool prototypeDownloaded = IsFullyDownloaded(component.version, Package.Prototype); bool editorDownloaded = IsFullyDownloaded(component.version, Package.Editor); double processedFiles = 0; // There is an optimization here, where we could move instead of Copy & Delete -- but it's an edge case, so I'm not worried about it. var files = GetRelativeFiles(Settings.Default.activeVersionLocation); foreach (var(stem, relativePath) in files) { var package = DeterminePackage(stem); if ((package == Package.Main && !mainDownloaded) || (package == Package.Gehenna && !gehennaDownloaded) || (package == Package.Prototype && !prototypeDownloaded) || (package == Package.Editor && !editorDownloaded)) { var src = $"{Settings.Default.activeVersionLocation}{Path.DirectorySeparatorChar}{relativePath}"; var dst = $"{GetFolder(component.version, package)}{Path.DirectorySeparatorChar}{relativePath}"; Utils.FCopy(src, dst); } if ((package == Package.Gehenna && !Settings.Default.ownsGehenna) || (package == Package.Prototype && !Settings.Default.ownsPrototype) || (package == Package.Editor && !Settings.Default.wantsEditor)) { var src = new FileInfo($"{Settings.Default.activeVersionLocation}{Path.DirectorySeparatorChar}{relativePath}"); src.Delete(); } processedFiles++; component.SetProgress(processedFiles / files.Count); } component.State = previousState; }
public void LoadVersions() { // Ensure that it's safe to discard state if (ActionInProgress()) { Debug.Assert(false); return; } foreach (var uiComponent in uiComponents.Values) { uiComponent.Dispose(); } uiComponents.Clear(); int installedVersion = DepotManager.GetInstalledVersion(); this.Height = 50; foreach (int version in ManifestData.allVersions) { var uiComponent = new VersionUIComponent(version, this.Height - 50, this); bool mainDownloaded = DepotManager.IsFullyDownloaded(version, Package.Main); bool gehennaDownloaded = DepotManager.IsFullyDownloaded(version, Package.Gehenna); bool prototypeDownloaded = DepotManager.IsFullyDownloaded(version, Package.Prototype); bool editorDownloaded = DepotManager.IsFullyDownloaded(version, Package.Editor); if (mainDownloaded && (!Settings.Default.ownsGehenna || gehennaDownloaded) && (!Settings.Default.ownsPrototype || prototypeDownloaded) && (!Settings.Default.wantsEditor || editorDownloaded)) { // We have downloaded everything we should uiComponent.State = VersionState.Downloaded; } else if (mainDownloaded || (Settings.Default.ownsGehenna && gehennaDownloaded) || (Settings.Default.ownsPrototype && prototypeDownloaded) || (Settings.Default.wantsEditor && editorDownloaded)) { // We haven't downloaded everything, but we do have *something* uiComponent.State = VersionState.PartiallyDownloaded; } else { // We have nothing downloaded uiComponent.State = VersionState.NotDownloaded; } if (version == installedVersion) { if (uiComponent.State == VersionState.Downloaded && DepotManager.IsFullyCopied(installedVersion)) { // Only mark active if the data is fully copied. uiComponent.State = VersionState.Active; } DepotManager.SaveActiveVersionAsync(uiComponent); // Save any additional files that we find in the active version location } // Only add the version if it's downloaded, common, active in steam, or we're showing all versions if (uiComponent.State != VersionState.NotDownloaded || commonVersions.Contains(version) || version == installedVersion || Settings.Default.showAllVersions) { uiComponents[version] = uiComponent; this.Height += 20; } else { uiComponent.Dispose(); } } }
private static void DownloadDepots(VersionUIComponent component) { var previousState = component.State; component.State = VersionState.DownloadPending; // Pending until we acquire the lock lock (downloadLock) { int version = component.version; Logging.Log($"Downloading depots for {version}"); var drive = new DriveInfo(new DirectoryInfo(ManifestData.DepotLocation).Root.FullName); if (!drive.IsReady) { Logging.MessageBox("Drive unavailable", $"Steam install location is in drive {drive.Name}, which is unavailable."); return; } var neededManifests = new List <SteamManifest>(); if (!IsFullyDownloaded(version, Package.Main)) { neededManifests.AddRange(ManifestData.Get(version, Package.Main)); } if (Settings.Default.ownsGehenna && !IsFullyDownloaded(version, Package.Gehenna)) { neededManifests.AddRange(ManifestData.Get(version, Package.Gehenna)); } if (Settings.Default.ownsPrototype && !IsFullyDownloaded(version, Package.Prototype)) { neededManifests.AddRange(ManifestData.Get(version, Package.Prototype)); } if (Settings.Default.wantsEditor && !IsFullyDownloaded(version, Package.Editor)) { neededManifests.AddRange(ManifestData.Get(version, Package.Editor)); } if (neededManifests.Count == 0) { Logging.Log($"Attempted to download manifests for {version}, but no manfiests applied."); component.State = VersionState.Downloaded; return; } if (Settings.Default.steamHack == false) { // Note: There are intentional tab characters in this string -- that's because it's a verbatim string. Logging.MessageBox("Unable to download", @"Steam has broken the download_depots command, so this version can't be downloaded. To download versions, please change your beta participation in Steam, re-download the game, then re-launch the downpatcher. Version | Steam beta ------------|------------------------ 440323 | NONE 326589 | previousversion 252786 | legacy_winxp 244371 | speedrun-244371"); component.State = previousState; return; } double totalDownloadSize = 0; foreach (var manifest in neededManifests) { totalDownloadSize += manifest.size; } long freeSpace = drive.TotalFreeSpace; if (drive.TotalFreeSpace < totalDownloadSize) { Logging.MessageBox("Not enough space", $@"Steam install location is in drive {drive.Name} has {Math.Round(freeSpace / 1000000000.0, 1)} GB of free space but {Math.Round(totalDownloadSize / 1000000000.0, 1)} GB are required."); return; } component.State = VersionState.Downloading; { // Keep steam interaction close together, to avoid accidental user interference SteamCommand.OpenConsole(); Thread.Sleep(10); foreach (var manifest in neededManifests) { SteamCommand.DownloadDepot(manifest.appId, manifest.depotId, manifest.manifestId); } MainWindow.SetForeground(); } Thread.Sleep(5000); // Extra sleep to avoid a race condition where we check for depots before they're actually cleared. while (true) { long actualSize = 0; foreach (var manifest in neededManifests) { actualSize += Utils.GetFolderSize(manifest.location); } component.SetProgress(0.8 * actualSize / totalDownloadSize); // 80% - Downloading if (actualSize == totalDownloadSize) { break; } Thread.Sleep(1000); } component.State = VersionState.Saving; long copied = 0; // @Performance: Start copying while downloads are in progress? foreach (var manifest in neededManifests) { CopyAndOverwrite(manifest.location, GetFolder(version, manifest.package), delegate(long fileSize) { copied += fileSize; component.SetProgress(0.8 + 0.2 * (copied / totalDownloadSize)); // 20% - Copying }); } if (drive.TotalFreeSpace < 5 * totalDownloadSize) { Logging.Log("Low on disk space, clearing download directory"); Directory.Delete(ManifestData.DepotLocation, true); } component.State = VersionState.Downloaded; } }
public static void DownloadDepotsAsync(VersionUIComponent component) { Utils.RunAsync(delegate { DownloadDepots(component); }); }
public static void SaveActiveVersionAsync(VersionUIComponent component) { Utils.RunAsync(delegate { SaveActiveVersion(component); }); }
private static void SetActiveVersion(VersionUIComponent component, Action onSetActiveVersion) { string activeVersionLocation = Settings.Default.activeVersionLocation; component.State = VersionState.CopyPending; lock (versionLock) { if (component.version == Settings.Default.activeVersion) { component.State = VersionState.Active; return; } Logging.Log($"Changing active version from {Settings.Default.activeVersion} to {component.version}"); component.State = VersionState.Copying; onSetActiveVersion(); // Sets the previously active version to Downloaded // Reset active version for the duration of the copy, since any failure will leave us in a bad state. Settings.Default.activeVersion = 0; Settings.Default.Save(); // Clean target folder before copying try { Directory.Delete(activeVersionLocation, true); } catch (DirectoryNotFoundException) { Logging.Log("Caught DirectoryNotFoundException: Folder already deleted"); } catch (Exception ex) when(ex is UnauthorizedAccessException || ex is IOException) { Logging.MessageBox("Folder in use", $"Unable to clear {activeVersionLocation}, please ensure that nothing is using it."); component.State = VersionState.Downloaded; return; } Logging.Log("Deletion successful"); // Copy the x86 binaries to the x64 folder. They may be overwritten by the next copy operation if there are real x64 binaries. CopyAndOverwrite(GetFolder(component.version, Package.Main) + "/Bin", activeVersionLocation + "/Bin/x64"); List <Package> requiredPackages = new List <Package> { Package.Main }; if (Settings.Default.ownsGehenna) { requiredPackages.Add(Package.Gehenna); } if (Settings.Default.ownsPrototype) { requiredPackages.Add(Package.Prototype); } if (Settings.Default.wantsEditor) { requiredPackages.Add(Package.Editor); } double totalSize = 0; foreach (var package in requiredPackages) { totalSize += Utils.GetFolderSize(GetFolder(component.version, package)); } // @Performance Multithreading *may* save time here. I doubt it. long copied = 0; foreach (var package in requiredPackages) { CopyAndOverwrite(GetFolder(component.version, package), activeVersionLocation, delegate(long fileSize) { copied += fileSize; component.SetProgress(copied / totalSize); }); } Settings.Default.activeVersion = component.version; Settings.Default.Save(); component.State = VersionState.Active; } }
public static void SetActiveVersionAsync(VersionUIComponent component, Action onSetActiveVersion) { Utils.RunAsync(delegate { SetActiveVersion(component, onSetActiveVersion); }); }