Merge pull request #81 from pizzaboxer/architecture-rework

Revamp bootstrapper/dialog handling
This commit is contained in:
pizzaboxer 2023-02-11 21:40:04 +00:00 committed by GitHub
commit d07f99afcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 219 additions and 236 deletions

View File

@ -5,12 +5,14 @@ using System.IO;
using System.Net.Http; using System.Net.Http;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Microsoft.Win32; using Microsoft.Win32;
using Bloxstrap.Models; using Bloxstrap.Dialogs;
using Bloxstrap.Enums; using Bloxstrap.Enums;
using Bloxstrap.Helpers; using Bloxstrap.Helpers;
using Bloxstrap.Models;
using Bloxstrap.Views; using Bloxstrap.Views;
namespace Bloxstrap namespace Bloxstrap
@ -55,6 +57,7 @@ namespace Bloxstrap
{ {
Settings.Save(); Settings.Save();
State.Save(); State.Save();
Debug.WriteLine($"[App] Terminating with exit code {code}");
Environment.Exit(code); Environment.Exit(code);
} }
@ -167,7 +170,34 @@ namespace Bloxstrap
ShouldSaveConfigs = true; ShouldSaveConfigs = true;
DeployManager.Channel = Settings.Prop.Channel; DeployManager.Channel = Settings.Prop.Channel;
Settings.Prop.BootstrapperStyle.Show(new Bootstrapper(commandLine));
// start bootstrapper and show the bootstrapper modal if we're not running silently
Bootstrapper bootstrapper = new Bootstrapper(commandLine);
IBootstrapperDialog? dialog = null;
if (!IsQuiet)
{
dialog = Settings.Prop.BootstrapperStyle.GetNew();
bootstrapper.Dialog = dialog;
dialog.Bootstrapper = bootstrapper;
}
Task bootstrapperTask = Task.Run(() => bootstrapper.Run()).ContinueWith(t =>
{
// TODO: add error logging
if (t.Exception is null)
return;
#if DEBUG
throw t.Exception;
#else
dialog?.ShowError(t.Exception.ToString());
#endif
});
dialog?.ShowBootstrapper();
bootstrapperTask.Wait();
} }
Terminate(); Terminate();

View File

@ -28,7 +28,6 @@ namespace Bloxstrap
public const int ERROR_SUCCESS = 0; public const int ERROR_SUCCESS = 0;
public const int ERROR_INSTALL_USEREXIT = 1602; public const int ERROR_INSTALL_USEREXIT = 1602;
public const int ERROR_INSTALL_FAILURE = 1603; public const int ERROR_INSTALL_FAILURE = 1603;
public const int ERROR_PRODUCT_UNINSTALLED = 1614;
// in case a new package is added, you can find the corresponding directory // in case a new package is added, you can find the corresponding directory
// by opening the stock bootstrapper in a hex editor // by opening the stock bootstrapper in a hex editor
@ -58,38 +57,46 @@ namespace Bloxstrap
{ "extracontent-places.zip", @"ExtraContent\places\" }, { "extracontent-places.zip", @"ExtraContent\places\" },
}; };
private static readonly string AppSettings = private const string AppSettings =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<Settings>\n" + "<Settings>\n" +
" <ContentFolder>content</ContentFolder>\n" + " <ContentFolder>content</ContentFolder>\n" +
" <BaseUrl>http://www.roblox.com</BaseUrl>\n" + " <BaseUrl>http://www.roblox.com</BaseUrl>\n" +
"</Settings>\n"; "</Settings>\n";
private string? LaunchCommandLine; private readonly CancellationTokenSource _cancelTokenSource = new();
private string VersionGuid = null!; private static bool FreshInstall => String.IsNullOrEmpty(App.State.Prop.VersionGuid);
private PackageManifest VersionPackageManifest = null!;
private string VersionFolder = null!;
private readonly bool FreshInstall; private string? _launchCommandLine;
private double ProgressIncrement; private string _versionGuid = null!;
private long TotalBytes = 0; private PackageManifest _versionPackageManifest = null!;
private long TotalDownloadedBytes = 0; private string _versionFolder = null!;
private int PackagesExtracted = 0;
private bool CancelFired = false;
public IBootstrapperDialog Dialog = null!; private bool _isInstalling = false;
private double _progressIncrement;
private long _totalDownloadedBytes = 0;
private int _packagesExtracted = 0;
private bool _cancelFired = false;
public IBootstrapperDialog? Dialog = null;
#endregion #endregion
#region Core #region Core
public Bootstrapper(string? launchCommandLine = null) public Bootstrapper(string? launchCommandLine = null)
{ {
LaunchCommandLine = launchCommandLine; _launchCommandLine = launchCommandLine;
FreshInstall = String.IsNullOrEmpty(App.State.Prop.VersionGuid); }
private void SetStatus(string message)
{
Debug.WriteLine($"[Bootstrapper] {message}");
if (Dialog is not null)
Dialog.Message = message;
} }
// this is called from BootstrapperStyleForm.SetupDialog()
public async Task Run() public async Task Run()
{ {
if (App.IsUninstall) if (App.IsUninstall)
@ -107,14 +114,11 @@ namespace Bloxstrap
// if bloxstrap is installing for the first time but is running, prompt to close roblox // if bloxstrap is installing for the first time but is running, prompt to close roblox
// if roblox needs updating but is running, ignore update for now // if roblox needs updating but is running, ignore update for now
if (!Directory.Exists(VersionFolder) && CheckIfRunning(true) || App.State.Prop.VersionGuid != VersionGuid && !CheckIfRunning(false)) if (!Directory.Exists(_versionFolder) && CheckIfRunning(true) || App.State.Prop.VersionGuid != _versionGuid && !CheckIfRunning(false))
await InstallLatestVersion(); await InstallLatestVersion();
if (App.IsFirstRun) if (App.IsFirstRun)
{
//App.Settings.ShouldSave = App.State.ShouldSave = true;
App.ShouldSaveConfigs = true; App.ShouldSaveConfigs = true;
}
await ApplyModifications(); await ApplyModifications();
@ -129,7 +133,7 @@ namespace Bloxstrap
App.State.Save(); App.State.Save();
if (App.IsFirstRun && App.IsNoLaunch) if (App.IsFirstRun && App.IsNoLaunch)
Dialog.ShowSuccess($"{App.ProjectName} has successfully installed"); Dialog?.ShowSuccess($"{App.ProjectName} has successfully installed");
else if (!App.IsNoLaunch) else if (!App.IsNoLaunch)
await StartRoblox(); await StartRoblox();
} }
@ -140,10 +144,10 @@ namespace Bloxstrap
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest"); var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{App.ProjectRepository}/releases/latest");
if (releaseInfo is null || releaseInfo.Name is null || releaseInfo.Assets is null || currentVersion == releaseInfo.Name) if (releaseInfo?.Assets is null || currentVersion == releaseInfo.Name)
return; return;
Dialog.Message = $"Getting the latest {App.ProjectName}..."; SetStatus($"Getting the latest {App.ProjectName}...");
// 64-bit is always the first option // 64-bit is always the first option
GithubReleaseAsset asset = releaseInfo.Assets[Environment.Is64BitOperatingSystem ? 0 : 1]; GithubReleaseAsset asset = releaseInfo.Assets[Environment.Is64BitOperatingSystem ? 0 : 1];
@ -180,12 +184,12 @@ namespace Bloxstrap
private async Task CheckLatestVersion() private async Task CheckLatestVersion()
{ {
Dialog.Message = "Connecting to Roblox..."; SetStatus("Connecting to Roblox...");
ClientVersion clientVersion = await DeployManager.GetLastDeploy(App.Settings.Prop.Channel); ClientVersion clientVersion = await DeployManager.GetLastDeploy(App.Settings.Prop.Channel);
VersionGuid = clientVersion.VersionGuid; _versionGuid = clientVersion.VersionGuid;
VersionFolder = Path.Combine(Directories.Versions, VersionGuid); _versionFolder = Path.Combine(Directories.Versions, _versionGuid);
VersionPackageManifest = await PackageManifest.Get(VersionGuid); _versionPackageManifest = await PackageManifest.Get(_versionGuid);
} }
private bool CheckIfRunning(bool shutdown) private bool CheckIfRunning(bool shutdown)
@ -197,7 +201,7 @@ namespace Bloxstrap
if (shutdown) if (shutdown)
{ {
Dialog.PromptShutdown(); Dialog?.PromptShutdown();
try try
{ {
@ -219,24 +223,24 @@ namespace Bloxstrap
{ {
string startEventName = App.ProjectName.Replace(" ", "") + "StartEvent"; string startEventName = App.ProjectName.Replace(" ", "") + "StartEvent";
Dialog.Message = "Starting Roblox..."; SetStatus("Starting Roblox...");
if (LaunchCommandLine == "--app" && App.Settings.Prop.UseDisableAppPatch) if (_launchCommandLine == "--app" && App.Settings.Prop.UseDisableAppPatch)
{ {
Utilities.OpenWebsite("https://www.roblox.com/games"); Utilities.OpenWebsite("https://www.roblox.com/games");
return; return;
} }
// launch time isn't really required for all launches, but it's usually just safest to do this // launch time isn't really required for all launches, but it's usually just safest to do this
LaunchCommandLine += " --launchtime=" + DateTimeOffset.Now.ToUnixTimeMilliseconds(); _launchCommandLine += " --launchtime=" + DateTimeOffset.Now.ToUnixTimeMilliseconds();
if (App.Settings.Prop.Channel.ToLower() != DeployManager.DefaultChannel.ToLower()) if (App.Settings.Prop.Channel.ToLower() != DeployManager.DefaultChannel.ToLower())
LaunchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLower(); _launchCommandLine += " -channel " + App.Settings.Prop.Channel.ToLower();
LaunchCommandLine += " -startEvent " + startEventName; _launchCommandLine += " -startEvent " + startEventName;
bool shouldWait = false; bool shouldWait = false;
Process gameClient = Process.Start(Path.Combine(VersionFolder, "RobloxPlayerBeta.exe"), LaunchCommandLine); Process gameClient = Process.Start(Path.Combine(_versionFolder, "RobloxPlayerBeta.exe"), _launchCommandLine);
Process? rbxFpsUnlocker = null; Process? rbxFpsUnlocker = null;
DiscordRichPresence? richPresence = null; DiscordRichPresence? richPresence = null;
Mutex? singletonMutex = null; Mutex? singletonMutex = null;
@ -286,7 +290,7 @@ namespace Bloxstrap
return; return;
// keep bloxstrap open in the background // keep bloxstrap open in the background
Dialog.HideBootstrapper(); Dialog?.CloseBootstrapper();
await gameClient.WaitForExitAsync(); await gameClient.WaitForExitAsync();
richPresence?.Dispose(); richPresence?.Dispose();
@ -295,24 +299,30 @@ namespace Bloxstrap
rbxFpsUnlocker.Kill(); rbxFpsUnlocker.Kill();
} }
public void CancelButtonClicked() public void CancelInstall()
{ {
if (!Dialog.CancelEnabled) if (!_isInstalling)
{ {
App.Terminate(ERROR_INSTALL_USEREXIT); App.Terminate(ERROR_INSTALL_USEREXIT);
return; return;
} }
CancelFired = true; _cancelTokenSource.Cancel();
_cancelFired = true;
try try
{ {
// clean up install
if (App.IsFirstRun) if (App.IsFirstRun)
Directory.Delete(Directories.Base, true); Directory.Delete(Directories.Base, true);
else if (Directory.Exists(VersionFolder)) else if (Directory.Exists(_versionFolder))
Directory.Delete(VersionFolder, true); Directory.Delete(_versionFolder, true);
}
catch (Exception e)
{
Debug.WriteLine("[Bootstrapper} Could not fully clean up installation!");
Debug.WriteLine(e);
} }
catch (Exception) { }
App.Terminate(ERROR_INSTALL_USEREXIT); App.Terminate(ERROR_INSTALL_USEREXIT);
} }
@ -332,7 +342,10 @@ namespace Bloxstrap
if (Directory.Exists(oldInstallLocation)) if (Directory.Exists(oldInstallLocation))
Directory.Delete(oldInstallLocation, true); Directory.Delete(oldInstallLocation, true);
} }
catch (Exception) { } catch (Exception)
{
// ignored
}
applicationKey.DeleteValue("OldInstallLocation"); applicationKey.DeleteValue("OldInstallLocation");
} }
@ -415,7 +428,7 @@ namespace Bloxstrap
{ {
CheckIfRunning(true); CheckIfRunning(true);
Dialog.Message = $"Uninstalling {App.ProjectName}..."; SetStatus($"Uninstalling {App.ProjectName}...");
//App.Settings.ShouldSave = false; //App.Settings.ShouldSave = false;
App.ShouldSaveConfigs = false; App.ShouldSaveConfigs = false;
@ -460,31 +473,29 @@ namespace Bloxstrap
Debug.WriteLine($"Could not fully uninstall! ({e})"); Debug.WriteLine($"Could not fully uninstall! ({e})");
} }
Dialog.ShowSuccess($"{App.ProjectName} has succesfully uninstalled"); Dialog?.ShowSuccess($"{App.ProjectName} has succesfully uninstalled");
App.Terminate();
} }
#endregion #endregion
#region Roblox Install #region Roblox Install
private void UpdateProgressbar() private void UpdateProgressbar()
{ {
int newProgress = (int)Math.Floor(ProgressIncrement * TotalDownloadedBytes); int newProgress = (int)Math.Floor(_progressIncrement * _totalDownloadedBytes);
// bugcheck: if we're restoring a file from a package, it'll incorrectly increment the progress beyond 100 // bugcheck: if we're restoring a file from a package, it'll incorrectly increment the progress beyond 100
// too lazy to fix properly so lol // too lazy to fix properly so lol
if (newProgress > 100) if (newProgress > 100)
return; return;
Dialog.ProgressValue = newProgress; if (Dialog is not null)
Dialog.ProgressValue = newProgress;
} }
private async Task InstallLatestVersion() private async Task InstallLatestVersion()
{ {
if (FreshInstall) _isInstalling = true;
Dialog.Message = "Installing Roblox...";
else SetStatus(FreshInstall ? "Installing Roblox..." : "Upgrading Roblox...");
Dialog.Message = "Upgrading Roblox...";
// check if we have at least 300 megabytes of free disk space // check if we have at least 300 megabytes of free disk space
if (Utilities.GetFreeDiskSpace(Directories.Base) < 1024*1024*300) if (Utilities.GetFreeDiskSpace(Directories.Base) < 1024*1024*300)
@ -496,44 +507,54 @@ namespace Bloxstrap
Directory.CreateDirectory(Directories.Base); Directory.CreateDirectory(Directories.Base);
Dialog.CancelEnabled = true; if (Dialog is not null)
Dialog.ProgressStyle = ProgressBarStyle.Continuous; {
Dialog.CancelEnabled = true;
Dialog.ProgressStyle = ProgressBarStyle.Continuous;
}
// compute total bytes to download // compute total bytes to download
_progressIncrement = (double)100 / _versionPackageManifest.Sum(package => package.PackedSize);
foreach (Package package in VersionPackageManifest)
TotalBytes += package.PackedSize;
ProgressIncrement = (double)1 / TotalBytes * 100;
Directory.CreateDirectory(Directories.Downloads); Directory.CreateDirectory(Directories.Downloads);
Directory.CreateDirectory(Directories.Versions); Directory.CreateDirectory(Directories.Versions);
foreach (Package package in VersionPackageManifest) foreach (Package package in _versionPackageManifest)
{ {
if (_cancelFired)
return;
// download all the packages synchronously // download all the packages synchronously
await DownloadPackage(package); await DownloadPackage(package);
// extract the package immediately after download // extract the package immediately after download asynchronously
ExtractPackage(package); ExtractPackage(package);
} }
if (_cancelFired)
return;
// allow progress bar to 100% before continuing (purely ux reasons lol) // allow progress bar to 100% before continuing (purely ux reasons lol)
await Task.Delay(1000); await Task.Delay(1000);
Dialog.ProgressStyle = ProgressBarStyle.Marquee; if (Dialog is not null)
{
Dialog.Message = "Configuring Roblox..."; Dialog.ProgressStyle = ProgressBarStyle.Marquee;
SetStatus("Configuring Roblox...");
}
// wait for all packages to finish extracting // wait for all packages to finish extracting
while (PackagesExtracted < VersionPackageManifest.Count) while (_packagesExtracted < _versionPackageManifest.Count)
{ {
await Task.Delay(100); await Task.Delay(100);
} }
string appSettingsLocation = Path.Combine(VersionFolder, "AppSettings.xml"); string appSettingsLocation = Path.Combine(_versionFolder, "AppSettings.xml");
await File.WriteAllTextAsync(appSettingsLocation, AppSettings); await File.WriteAllTextAsync(appSettingsLocation, AppSettings);
if (_cancelFired)
return;
if (!FreshInstall) if (!FreshInstall)
{ {
ReShade.SynchronizeConfigFile(); ReShade.SynchronizeConfigFile();
@ -541,27 +562,30 @@ namespace Bloxstrap
// let's take this opportunity to delete any packages we don't need anymore // let's take this opportunity to delete any packages we don't need anymore
foreach (string filename in Directory.GetFiles(Directories.Downloads)) foreach (string filename in Directory.GetFiles(Directories.Downloads))
{ {
if (!VersionPackageManifest.Exists(package => filename.Contains(package.Signature))) if (!_versionPackageManifest.Exists(package => filename.Contains(package.Signature)))
File.Delete(filename); File.Delete(filename);
} }
string oldVersionFolder = Path.Combine(Directories.Versions, App.State.Prop.VersionGuid); string oldVersionFolder = Path.Combine(Directories.Versions, App.State.Prop.VersionGuid);
if (VersionGuid != App.State.Prop.VersionGuid && Directory.Exists(oldVersionFolder)) if (_versionGuid != App.State.Prop.VersionGuid && Directory.Exists(oldVersionFolder))
{ {
// and also to delete our old version folder // and also to delete our old version folder
Directory.Delete(oldVersionFolder, true); Directory.Delete(oldVersionFolder, true);
} }
} }
Dialog.CancelEnabled = false; if (Dialog is not null)
Dialog.CancelEnabled = false;
App.State.Prop.VersionGuid = VersionGuid; App.State.Prop.VersionGuid = _versionGuid;
_isInstalling = false;
} }
private async Task ApplyModifications() private async Task ApplyModifications()
{ {
Dialog.Message = "Applying Roblox modifications..."; SetStatus("Applying Roblox modifications...");
string modFolder = Path.Combine(Directories.Modifications); string modFolder = Path.Combine(Directories.Modifications);
@ -599,7 +623,7 @@ namespace Bloxstrap
foreach (string file in modFolderFiles) foreach (string file in modFolderFiles)
{ {
string fileModFolder = Path.Combine(modFolder, file); string fileModFolder = Path.Combine(modFolder, file);
string fileVersionFolder = Path.Combine(VersionFolder, file); string fileVersionFolder = Path.Combine(_versionFolder, file);
if (File.Exists(fileVersionFolder)) if (File.Exists(fileVersionFolder))
{ {
@ -635,7 +659,7 @@ namespace Bloxstrap
catch (InvalidOperationException) catch (InvalidOperationException)
{ {
// package doesn't exist, likely mistakenly placed file // package doesn't exist, likely mistakenly placed file
string versionFileLocation = Path.Combine(VersionFolder, fileLocation); string versionFileLocation = Path.Combine(_versionFolder, fileLocation);
File.Delete(versionFileLocation); File.Delete(versionFileLocation);
@ -678,7 +702,10 @@ namespace Bloxstrap
private async Task DownloadPackage(Package package) private async Task DownloadPackage(Package package)
{ {
string packageUrl = $"{DeployManager.BaseUrl}/{VersionGuid}-{package.Name}"; if (_cancelFired)
return;
string packageUrl = $"{DeployManager.BaseUrl}/{_versionGuid}-{package.Name}";
string packageLocation = Path.Combine(Directories.Downloads, package.Signature); string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
string robloxPackageLocation = Path.Combine(Directories.LocalAppData, "Roblox", "Downloads", package.Signature); string robloxPackageLocation = Path.Combine(Directories.LocalAppData, "Roblox", "Downloads", package.Signature);
@ -695,7 +722,7 @@ namespace Bloxstrap
else else
{ {
Debug.WriteLine($"{package.Name} is already downloaded, skipping..."); Debug.WriteLine($"{package.Name} is already downloaded, skipping...");
TotalDownloadedBytes += package.PackedSize; _totalDownloadedBytes += package.PackedSize;
UpdateProgressbar(); UpdateProgressbar();
return; return;
} }
@ -707,35 +734,39 @@ namespace Bloxstrap
Debug.WriteLine($"Found existing version of {package.Name} ({robloxPackageLocation})! Copying to Downloads folder..."); Debug.WriteLine($"Found existing version of {package.Name} ({robloxPackageLocation})! Copying to Downloads folder...");
File.Copy(robloxPackageLocation, packageLocation); File.Copy(robloxPackageLocation, packageLocation);
TotalDownloadedBytes += package.PackedSize; _totalDownloadedBytes += package.PackedSize;
UpdateProgressbar(); UpdateProgressbar();
return; return;
} }
if (!File.Exists(packageLocation)) if (!File.Exists(packageLocation))
{ {
Debug.WriteLine($"Downloading {package.Name}..."); Debug.WriteLine($"Downloading {package.Name} ({package.Signature})...");
if (CancelFired)
return;
{ {
var response = await App.HttpClient.GetAsync(packageUrl, HttpCompletionOption.ResponseHeadersRead); var response = await App.HttpClient.GetAsync(packageUrl, HttpCompletionOption.ResponseHeadersRead, _cancelTokenSource.Token);
var buffer = new byte[8192]; var buffer = new byte[4096];
await using var stream = await response.Content.ReadAsStreamAsync(); await using var stream = await response.Content.ReadAsStreamAsync(_cancelTokenSource.Token);
await using var fileStream = new FileStream(packageLocation, FileMode.CreateNew); await using var fileStream = new FileStream(packageLocation, FileMode.CreateNew, FileAccess.Write, FileShare.Delete);
while (true) while (true)
{ {
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (_cancelFired)
{
stream.Close();
fileStream.Close();
return;
}
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, _cancelTokenSource.Token);
if (bytesRead == 0) if (bytesRead == 0)
break; // we're done break; // we're done
await fileStream.WriteAsync(buffer, 0, bytesRead); await fileStream.WriteAsync(buffer, 0, bytesRead, _cancelTokenSource.Token);
TotalDownloadedBytes += bytesRead; _totalDownloadedBytes += bytesRead;
UpdateProgressbar(); UpdateProgressbar();
} }
} }
@ -746,13 +777,12 @@ namespace Bloxstrap
private async void ExtractPackage(Package package) private async void ExtractPackage(Package package)
{ {
if (CancelFired) if (_cancelFired)
return; return;
string packageLocation = Path.Combine(Directories.Downloads, package.Signature); string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); string packageFolder = Path.Combine(_versionFolder, PackageDirectories[package.Name]);
string extractPath; string extractPath;
string? directory;
Debug.WriteLine($"Extracting {package.Name} to {packageFolder}..."); Debug.WriteLine($"Extracting {package.Name} to {packageFolder}...");
@ -760,7 +790,7 @@ namespace Bloxstrap
{ {
foreach (ZipArchiveEntry entry in archive.Entries) foreach (ZipArchiveEntry entry in archive.Entries)
{ {
if (CancelFired) if (_cancelFired)
return; return;
if (entry.FullName.EndsWith('\\')) if (entry.FullName.EndsWith('\\'))
@ -770,7 +800,7 @@ namespace Bloxstrap
//Debug.WriteLine($"[{package.Name}] Writing {extractPath}..."); //Debug.WriteLine($"[{package.Name}] Writing {extractPath}...");
directory = Path.GetDirectoryName(extractPath); string? directory = Path.GetDirectoryName(extractPath);
if (directory is null) if (directory is null)
continue; continue;
@ -783,12 +813,12 @@ namespace Bloxstrap
Debug.WriteLine($"Finished extracting {package.Name}"); Debug.WriteLine($"Finished extracting {package.Name}");
PackagesExtracted += 1; _packagesExtracted += 1;
} }
private void ExtractFileFromPackage(string packageName, string fileName) private void ExtractFileFromPackage(string packageName, string fileName)
{ {
Package? package = VersionPackageManifest.Find(x => x.Name == packageName); Package? package = _versionPackageManifest.Find(x => x.Name == packageName);
if (package is null) if (package is null)
return; return;
@ -796,7 +826,7 @@ namespace Bloxstrap
DownloadPackage(package).GetAwaiter().GetResult(); DownloadPackage(package).GetAwaiter().GetResult();
string packageLocation = Path.Combine(Directories.Downloads, package.Signature); string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); string packageFolder = Path.Combine(_versionFolder, PackageDirectories[package.Name]);
using ZipArchive archive = ZipFile.OpenRead(packageLocation); using ZipArchive archive = ZipFile.OpenRead(packageLocation);

View File

@ -10,9 +10,7 @@ namespace Bloxstrap.Dialogs
{ {
public class BootstrapperDialogForm : Form, IBootstrapperDialog public class BootstrapperDialogForm : Form, IBootstrapperDialog
{ {
public Bootstrapper? Bootstrapper { get; set; } public Bootstrapper Bootstrapper { get; set; } = null!;
protected override bool ShowWithoutActivation => App.IsQuiet;
protected virtual string _message { get; set; } = "Please wait..."; protected virtual string _message { get; set; } = "Please wait...";
protected virtual ProgressBarStyle _progressStyle { get; set; } protected virtual ProgressBarStyle _progressStyle { get; set; }
@ -67,11 +65,6 @@ namespace Bloxstrap.Dialogs
} }
} }
public BootstrapperDialogForm(Bootstrapper? bootstrapper = null)
{
Bootstrapper = bootstrapper;
}
public void ScaleWindow() public void ScaleWindow()
{ {
this.Size = this.MinimumSize = this.MaximumSize = WindowScaling.GetScaledSize(this.Size); this.Size = this.MinimumSize = this.MaximumSize = WindowScaling.GetScaledSize(this.Size);
@ -88,64 +81,16 @@ namespace Bloxstrap.Dialogs
{ {
this.Text = App.ProjectName; this.Text = App.ProjectName;
this.Icon = App.Settings.Prop.BootstrapperIcon.GetIcon(); this.Icon = App.Settings.Prop.BootstrapperIcon.GetIcon();
if (Bootstrapper is null)
{
Message = "Style preview - Click Cancel to close";
CancelEnabled = true;
}
else
{
Bootstrapper.Dialog = this;
Task.Run(RunBootstrapper);
}
} }
public void ShowBootstrapper() => this.ShowDialog();
public async void RunBootstrapper() public virtual void CloseBootstrapper()
{
if (Bootstrapper is null)
return;
#if DEBUG
await Bootstrapper.Run();
#else
try
{
await Bootstrapper.Run();
}
catch (Exception ex)
{
// string message = String.Format("{0}: {1}", ex.GetType(), ex.Message);
string message = ex.ToString();
ShowError(message);
}
#endif
App.Terminate();
}
public void ShowAsPreview()
{
this.ShowDialog();
}
public void ShowAsBootstrapper()
{
System.Windows.Forms.Application.Run(this);
}
public virtual void HideBootstrapper()
{ {
if (this.InvokeRequired) if (this.InvokeRequired)
{ this.Invoke(CloseBootstrapper);
this.Invoke(HideBootstrapper);
}
else else
{ this.Close();
this.Opacity = 0;
this.ShowInTaskbar = false;
}
} }
public virtual void ShowSuccess(string message) public virtual void ShowSuccess(string message)
@ -174,10 +119,8 @@ namespace Bloxstrap.Dialogs
public void ButtonCancel_Click(object? sender, EventArgs e) public void ButtonCancel_Click(object? sender, EventArgs e)
{ {
if (Bootstrapper is null) Bootstrapper.CancelInstall();
this.Close(); this.Close();
else
Task.Run(() => Bootstrapper.CancelButtonClicked());
} }
} }
} }

View File

@ -4,17 +4,15 @@ namespace Bloxstrap.Dialogs
{ {
public interface IBootstrapperDialog public interface IBootstrapperDialog
{ {
Bootstrapper? Bootstrapper { get; set; } public Bootstrapper Bootstrapper { get; set; }
string Message { get; set; } string Message { get; set; }
ProgressBarStyle ProgressStyle { get; set; } ProgressBarStyle ProgressStyle { get; set; }
int ProgressValue { get; set; } int ProgressValue { get; set; }
bool CancelEnabled { get; set; } bool CancelEnabled { get; set; }
void RunBootstrapper(); void ShowBootstrapper();
void ShowAsPreview(); void CloseBootstrapper();
void ShowAsBootstrapper();
void HideBootstrapper();
void ShowSuccess(string message); void ShowSuccess(string message);
void ShowError(string message); void ShowError(string message);
void PromptShutdown(); void PromptShutdown();

View File

@ -32,7 +32,7 @@ namespace Bloxstrap.Dialogs
set => this.buttonCancel.Enabled = value; set => this.buttonCancel.Enabled = value;
} }
public LegacyDialog2009(Bootstrapper? bootstrapper = null) : base(bootstrapper) public LegacyDialog2009()
{ {
InitializeComponent(); InitializeComponent();

View File

@ -33,7 +33,7 @@ namespace Bloxstrap.Dialogs
set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value; set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value;
} }
public LegacyDialog2011(Bootstrapper? bootstrapper = null) : base(bootstrapper) public LegacyDialog2011()
{ {
InitializeComponent(); InitializeComponent();

View File

@ -34,7 +34,7 @@ namespace Bloxstrap.Dialogs
set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value; set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value;
} }
public ProgressDialog(Bootstrapper? bootstrapper = null) : base(bootstrapper) public ProgressDialog()
{ {
InitializeComponent(); InitializeComponent();

View File

@ -13,58 +13,54 @@ namespace Bloxstrap.Dialogs
public partial class VistaDialog : BootstrapperDialogForm public partial class VistaDialog : BootstrapperDialogForm
{ {
private TaskDialogPage Dialog; private TaskDialogPage _dialogPage;
protected override string _message protected sealed override string _message
{ {
get => Dialog.Heading ?? ""; get => _dialogPage.Heading ?? "";
set => Dialog.Heading = value; set => _dialogPage.Heading = value;
} }
protected override ProgressBarStyle _progressStyle protected sealed override ProgressBarStyle _progressStyle
{ {
set set
{ {
if (Dialog.ProgressBar is null) if (_dialogPage.ProgressBar is null)
return; return;
switch (value) _dialogPage.ProgressBar.State = value switch
{ {
case ProgressBarStyle.Continuous: ProgressBarStyle.Continuous => TaskDialogProgressBarState.Normal,
case ProgressBarStyle.Blocks: ProgressBarStyle.Blocks => TaskDialogProgressBarState.Normal,
Dialog.ProgressBar.State = TaskDialogProgressBarState.Normal; ProgressBarStyle.Marquee => TaskDialogProgressBarState.Marquee,
break; _ => _dialogPage.ProgressBar.State
};
case ProgressBarStyle.Marquee:
Dialog.ProgressBar.State = TaskDialogProgressBarState.Marquee;
break;
}
} }
} }
protected override int _progressValue protected sealed override int _progressValue
{ {
get => Dialog.ProgressBar is null ? 0 : Dialog.ProgressBar.Value; get => _dialogPage.ProgressBar?.Value ?? 0;
set set
{ {
if (Dialog.ProgressBar is null) if (_dialogPage.ProgressBar is null)
return; return;
Dialog.ProgressBar.Value = value; _dialogPage.ProgressBar.Value = value;
} }
} }
protected override bool _cancelEnabled protected sealed override bool _cancelEnabled
{ {
get => Dialog.Buttons[0].Enabled; get => _dialogPage.Buttons[0].Enabled;
set => Dialog.Buttons[0].Enabled = value; set => _dialogPage.Buttons[0].Enabled = value;
} }
public VistaDialog(Bootstrapper? bootstrapper = null) : base(bootstrapper) public VistaDialog()
{ {
InitializeComponent(); InitializeComponent();
Dialog = new TaskDialogPage() _dialogPage = new TaskDialogPage()
{ {
Icon = new TaskDialogIcon(App.Settings.Prop.BootstrapperIcon.GetIcon()), Icon = new TaskDialogIcon(App.Settings.Prop.BootstrapperIcon.GetIcon()),
Caption = App.ProjectName, Caption = App.ProjectName,
@ -79,7 +75,7 @@ namespace Bloxstrap.Dialogs
_message = "Please wait..."; _message = "Please wait...";
_cancelEnabled = false; _cancelEnabled = false;
Dialog.Buttons[0].Click += (sender, e) => ButtonCancel_Click(sender, e); _dialogPage.Buttons[0].Click += ButtonCancel_Click;
SetupDialog(); SetupDialog();
} }
@ -100,12 +96,10 @@ namespace Bloxstrap.Dialogs
Buttons = { TaskDialogButton.OK } Buttons = { TaskDialogButton.OK }
}; };
successDialog.Buttons[0].Click += (sender, e) => App.Terminate(); successDialog.Buttons[0].Click += (_, _) => App.Terminate();
if (!App.IsQuiet) _dialogPage.Navigate(successDialog);
Dialog.Navigate(successDialog); _dialogPage = successDialog;
Dialog = successDialog;
} }
} }
@ -134,33 +128,25 @@ namespace Bloxstrap.Dialogs
errorDialog.Buttons[0].Click += (sender, e) => App.Terminate(Bootstrapper.ERROR_INSTALL_FAILURE); errorDialog.Buttons[0].Click += (sender, e) => App.Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
if (!App.IsQuiet) _dialogPage.Navigate(errorDialog);
Dialog.Navigate(errorDialog); _dialogPage = errorDialog;
Dialog = errorDialog;
} }
} }
public override void HideBootstrapper() public override void CloseBootstrapper()
{ {
if (this.InvokeRequired) if (this.InvokeRequired)
{ {
this.Invoke(HideBootstrapper); this.Invoke(CloseBootstrapper);
} }
else else
{ {
if (Dialog.BoundDialog is null) _dialogPage.BoundDialog?.Close();
return; base.CloseBootstrapper();
Dialog.BoundDialog.Close();
} }
} }
private void VistaDialog_Load(object sender, EventArgs e) private void VistaDialog_Load(object sender, EventArgs e) => TaskDialog.ShowDialog(_dialogPage);
{
if (!App.IsQuiet)
TaskDialog.ShowDialog(Dialog);
}
} }
} }

View File

@ -14,28 +14,16 @@ namespace Bloxstrap.Enums
public static class BootstrapperStyleEx public static class BootstrapperStyleEx
{ {
public static void Show(this BootstrapperStyle bootstrapperStyle, Bootstrapper? bootstrapper = null) public static IBootstrapperDialog GetNew(this BootstrapperStyle bootstrapperStyle)
{ {
IBootstrapperDialog dialog = bootstrapperStyle switch return bootstrapperStyle switch
{ {
BootstrapperStyle.VistaDialog => new VistaDialog(bootstrapper), BootstrapperStyle.VistaDialog => new VistaDialog(),
BootstrapperStyle.LegacyDialog2009 => new LegacyDialog2009(bootstrapper), BootstrapperStyle.LegacyDialog2009 => new LegacyDialog2009(),
BootstrapperStyle.LegacyDialog2011 => new LegacyDialog2011(bootstrapper), BootstrapperStyle.LegacyDialog2011 => new LegacyDialog2011(),
BootstrapperStyle.ProgressDialog => new ProgressDialog(bootstrapper), BootstrapperStyle.ProgressDialog => new ProgressDialog(),
_ => new ProgressDialog(bootstrapper) _ => new ProgressDialog()
}; };
if (bootstrapper is null)
{
dialog.ShowAsPreview();
}
else
{
if (App.IsQuiet)
dialog.HideBootstrapper();
dialog.ShowAsBootstrapper();
}
} }
} }
} }

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using Bloxstrap.Dialogs;
using Bloxstrap.Enums; using Bloxstrap.Enums;
using Bloxstrap.Views; using Bloxstrap.Views;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@ -20,7 +21,14 @@ namespace Bloxstrap.ViewModels
public ICommand PreviewBootstrapperCommand => new RelayCommand(PreviewBootstrapper); public ICommand PreviewBootstrapperCommand => new RelayCommand(PreviewBootstrapper);
private void PreviewBootstrapper() => App.Settings.Prop.BootstrapperStyle.Show(); private void PreviewBootstrapper()
{
IBootstrapperDialog dialog = App.Settings.Prop.BootstrapperStyle.GetNew();
dialog.Message = "Style preview - Click Cancel to close";
dialog.CancelEnabled = true;
dialog.ShowBootstrapper();
}
public BootstrapperViewModel(Page page) public BootstrapperViewModel(Page page)
{ {