From 32227cfb553190bbbcfdee0a9d2b6c527575dabd Mon Sep 17 00:00:00 2001 From: pizzaboxer <41478239+pizzaboxer@users.noreply.github.com> Date: Thu, 4 Aug 2022 12:01:12 +0100 Subject: [PATCH] Initial code Version 1.0.0 --- Bloxstrap.sln | 25 + Bloxstrap/Bloxstrap.csproj | 47 ++ Bloxstrap/Bloxstrap.ico | Bin 0 -> 127429 bytes Bloxstrap/Bootstrapper.cs | 655 ++++++++++++++++++ .../LegacyDialogStyle.Designer.cs | 108 +++ .../BootstrapperStyles/LegacyDialogStyle.cs | 161 +++++ .../BootstrapperStyles/LegacyDialogStyle.resx | 60 ++ .../ProgressDialogStyle.Designer.cs | 126 ++++ .../BootstrapperStyles/ProgressDialogStyle.cs | 161 +++++ .../ProgressDialogStyle.resx | 60 ++ .../BootstrapperStyles/TaskDialogStyle.cs | 134 ++++ Bloxstrap/Dialogs/Preferences.Designer.cs | 310 +++++++++ Bloxstrap/Dialogs/Preferences.cs | 235 +++++++ Bloxstrap/Dialogs/Preferences.resx | 63 ++ Bloxstrap/Enums/BootstrapperIcon.cs | 13 + Bloxstrap/Enums/BootstrapperStyle.cs | 9 + Bloxstrap/Helpers/IconManager.cs | 45 ++ Bloxstrap/Helpers/Protocol.cs | 81 +++ Bloxstrap/Helpers/RSMM/ChangeEvent.cs | 16 + Bloxstrap/Helpers/RSMM/FileManifest.cs | 62 ++ Bloxstrap/Helpers/RSMM/Package.cs | 22 + Bloxstrap/Helpers/RSMM/PackageManifest.cs | 85 +++ Bloxstrap/Helpers/RSMM/SystemEvent.cs | 39 ++ Bloxstrap/Program.cs | 115 +++ Bloxstrap/Properties/Resources.Designer.cs | 223 ++++++ Bloxstrap/Properties/Resources.resx | 169 +++++ Bloxstrap/Properties/Settings.Designer.cs | 26 + Bloxstrap/Properties/Settings.settings | 6 + Bloxstrap/Resources/CancelButton.png | Bin 0 -> 768 bytes Bloxstrap/Resources/CancelButtonHover.png | Bin 0 -> 944 bytes Bloxstrap/Resources/Icon2009-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/Icon2009-png.png | Bin 0 -> 17074 bytes Bloxstrap/Resources/Icon2011-ico.ico | Bin 0 -> 5182 bytes Bloxstrap/Resources/Icon2011-png.png | Bin 0 -> 20362 bytes Bloxstrap/Resources/Icon2017-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/Icon2017-png.png | Bin 0 -> 5563 bytes Bloxstrap/Resources/Icon2019-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/Icon2019-png.png | Bin 0 -> 13984 bytes Bloxstrap/Resources/IconBloxstrap-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/IconBloxstrap-png.png | Bin 0 -> 16792 bytes Bloxstrap/Resources/IconEarly2015-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/IconEarly2015-png.png | Bin 0 -> 22754 bytes Bloxstrap/Resources/IconLate2015-ico.ico | Bin 0 -> 4286 bytes Bloxstrap/Resources/IconLate2015-png.png | Bin 0 -> 10100 bytes Bloxstrap/Settings.cs | 68 ++ README.md | 29 + 46 files changed, 3153 insertions(+) create mode 100644 Bloxstrap.sln create mode 100644 Bloxstrap/Bloxstrap.csproj create mode 100644 Bloxstrap/Bloxstrap.ico create mode 100644 Bloxstrap/Bootstrapper.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.Designer.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.resx create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.Designer.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.cs create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.resx create mode 100644 Bloxstrap/Dialogs/BootstrapperStyles/TaskDialogStyle.cs create mode 100644 Bloxstrap/Dialogs/Preferences.Designer.cs create mode 100644 Bloxstrap/Dialogs/Preferences.cs create mode 100644 Bloxstrap/Dialogs/Preferences.resx create mode 100644 Bloxstrap/Enums/BootstrapperIcon.cs create mode 100644 Bloxstrap/Enums/BootstrapperStyle.cs create mode 100644 Bloxstrap/Helpers/IconManager.cs create mode 100644 Bloxstrap/Helpers/Protocol.cs create mode 100644 Bloxstrap/Helpers/RSMM/ChangeEvent.cs create mode 100644 Bloxstrap/Helpers/RSMM/FileManifest.cs create mode 100644 Bloxstrap/Helpers/RSMM/Package.cs create mode 100644 Bloxstrap/Helpers/RSMM/PackageManifest.cs create mode 100644 Bloxstrap/Helpers/RSMM/SystemEvent.cs create mode 100644 Bloxstrap/Program.cs create mode 100644 Bloxstrap/Properties/Resources.Designer.cs create mode 100644 Bloxstrap/Properties/Resources.resx create mode 100644 Bloxstrap/Properties/Settings.Designer.cs create mode 100644 Bloxstrap/Properties/Settings.settings create mode 100644 Bloxstrap/Resources/CancelButton.png create mode 100644 Bloxstrap/Resources/CancelButtonHover.png create mode 100644 Bloxstrap/Resources/Icon2009-ico.ico create mode 100644 Bloxstrap/Resources/Icon2009-png.png create mode 100644 Bloxstrap/Resources/Icon2011-ico.ico create mode 100644 Bloxstrap/Resources/Icon2011-png.png create mode 100644 Bloxstrap/Resources/Icon2017-ico.ico create mode 100644 Bloxstrap/Resources/Icon2017-png.png create mode 100644 Bloxstrap/Resources/Icon2019-ico.ico create mode 100644 Bloxstrap/Resources/Icon2019-png.png create mode 100644 Bloxstrap/Resources/IconBloxstrap-ico.ico create mode 100644 Bloxstrap/Resources/IconBloxstrap-png.png create mode 100644 Bloxstrap/Resources/IconEarly2015-ico.ico create mode 100644 Bloxstrap/Resources/IconEarly2015-png.png create mode 100644 Bloxstrap/Resources/IconLate2015-ico.ico create mode 100644 Bloxstrap/Resources/IconLate2015-png.png create mode 100644 Bloxstrap/Settings.cs create mode 100644 README.md diff --git a/Bloxstrap.sln b/Bloxstrap.sln new file mode 100644 index 00000000..62d830d7 --- /dev/null +++ b/Bloxstrap.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bloxstrap", "Bloxstrap\Bloxstrap.csproj", "{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F989AC04-B48F-4BB4-B940-1E7D082F14DA} + EndGlobalSection +EndGlobal diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj new file mode 100644 index 00000000..cfe3a0c3 --- /dev/null +++ b/Bloxstrap/Bloxstrap.csproj @@ -0,0 +1,47 @@ + + + + WinExe + net6.0-windows + enable + true + enable + AnyCPU + AnyCPU;x86 + Bloxstrap.ico + 1.0.0 + 1.0.0.0 + + + + + + + + + True + True + Resources.resx + + + True + True + Settings.settings + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + \ No newline at end of file diff --git a/Bloxstrap/Bloxstrap.ico b/Bloxstrap/Bloxstrap.ico new file mode 100644 index 0000000000000000000000000000000000000000..6a50aff79c1213e4e7458311660b76a53e9e6b33 GIT binary patch literal 127429 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 WBCUF3 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 literal 0 HcmV?d00001 diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs new file mode 100644 index 00000000..a14dda02 --- /dev/null +++ b/Bloxstrap/Bootstrapper.cs @@ -0,0 +1,655 @@ +using System.Diagnostics; +using System.IO.Compression; +using System.Security.Cryptography; + +using Microsoft.Win32; + +using Bloxstrap.Enums; +using Bloxstrap.Dialogs.BootstrapperStyles; +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap +{ + public class Bootstrapper + { + private string? LaunchCommandLine; + + private string VersionGuid; + private PackageManifest VersionPackageManifest; + private FileManifest VersionFileManifest; + private string VersionFolder; + + private readonly string DownloadsFolder; + private readonly bool FreshInstall; + + private int ProgressIncrement; + private bool CancelFired = false; + + private static readonly HttpClient Client = new(); + + // in case a new package is added, you can find the corresponding directory + // by opening the stock bootstrapper in a hex editor + // TODO - there ideally should be a less static way to do this that's not hardcoded? + private static readonly IReadOnlyDictionary PackageDirectories = new Dictionary() + { + { "RobloxApp.zip", @"" }, + { "shaders.zip", @"shaders\" }, + { "ssl.zip", @"ssl\" }, + + { "content-avatar.zip", @"content\avatar\" }, + { "content-configs.zip", @"content\configs\" }, + { "content-fonts.zip", @"content\fonts\" }, + { "content-sky.zip", @"content\sky\" }, + { "content-sounds.zip", @"content\sounds\" }, + { "content-textures2.zip", @"content\textures\" }, + { "content-models.zip", @"content\models\" }, + + { "content-textures3.zip", @"PlatformContent\pc\textures\" }, + { "content-terrain.zip", @"PlatformContent\pc\terrain\" }, + { "content-platform-fonts.zip", @"PlatformContent\pc\fonts\" }, + + { "extracontent-luapackages.zip", @"ExtraContent\LuaPackages\" }, + { "extracontent-translations.zip", @"ExtraContent\translations\" }, + { "extracontent-models.zip", @"ExtraContent\models\" }, + { "extracontent-textures.zip", @"ExtraContent\textures\" }, + { "extracontent-places.zip", @"ExtraContent\places\" }, + }; + + private static readonly string AppSettings = + "\n" + + "\n" + + " content\n" + + " http://www.roblox.com\n" + + "\n"; + + public event EventHandler PromptShutdownEvent; + public event ChangeEventHandler ShowSuccessEvent; + public event ChangeEventHandler MessageChanged; + public event ChangeEventHandler ProgressBarValueChanged; + public event ChangeEventHandler ProgressBarStyleChanged; + public event ChangeEventHandler CancelEnabledChanged; + + private string _message; + private int _progress = 0; + private ProgressBarStyle _progressStyle = ProgressBarStyle.Marquee; + private bool _cancelEnabled = false; + + public string Message + { + get => _message; + + private set + { + if (_message == value) + return; + + MessageChanged.Invoke(this, new ChangeEventArgs(value)); + + _message = value; + } + } + + public int Progress + { + get => _progress; + + private set + { + if (_progress == value) + return; + + ProgressBarValueChanged.Invoke(this, new ChangeEventArgs(value)); + + _progress = value; + } + } + + public ProgressBarStyle ProgressStyle + { + get => _progressStyle; + + private set + { + if (_progressStyle == value) + return; + + ProgressBarStyleChanged.Invoke(this, new ChangeEventArgs(value)); + + _progressStyle = value; + } + } + + public bool CancelEnabled + { + get => _cancelEnabled; + + private set + { + if (_cancelEnabled == value) + return; + + CancelEnabledChanged.Invoke(this, new ChangeEventArgs(value)); + + _cancelEnabled = value; + } + } + + public Bootstrapper(BootstrapperStyle bootstrapperStyle, string? launchCommandLine = null) + { + Debug.WriteLine("Initializing bootstrapper"); + + FreshInstall = String.IsNullOrEmpty(Program.Settings.VersionGuid); + LaunchCommandLine = launchCommandLine; + DownloadsFolder = Path.Combine(Program.BaseDirectory, "Downloads"); + Client.Timeout = TimeSpan.FromMinutes(10); + + switch (bootstrapperStyle) + { + case BootstrapperStyle.TaskDialog: + new TaskDialogStyle(this); + break; + + case BootstrapperStyle.LegacyDialog: + Application.Run(new LegacyDialogStyle(this)); + break; + + case BootstrapperStyle.ProgressDialog: + Application.Run(new ProgressDialogStyle(this)); + break; + } + } + + public async Task Run() + { + if (LaunchCommandLine == "-uninstall") + { + Uninstall(); + return; + } + + await CheckLatestVersion(); + + if (!Directory.Exists(VersionFolder) || Program.Settings.VersionGuid != VersionGuid) + { + Debug.WriteLineIf(!Directory.Exists(VersionFolder), $"Installing latest version (!Directory.Exists({VersionFolder}))"); + Debug.WriteLineIf(Program.Settings.VersionGuid != VersionGuid, $"Installing latest version ({Program.Settings.VersionGuid} != {VersionGuid})"); + + await InstallLatestVersion(); + } + + // yes, doing this for every start is stupid, but the death sound mod is dynamically toggleable after all + ApplyModifications(); + + if (Program.IsFirstRun) + Program.SettingsManager.ShouldSave = true; + + if (Program.IsFirstRun || FreshInstall) + Register(); + + CheckInstall(); + + await StartRoblox(); + } + + private void CheckIfRunning() + { + Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta"); + + if (processes.Length > 0) + PromptShutdown(); + + try + { + // try/catch just in case process was closed before prompt was answered + + foreach (Process process in processes) + { + process.CloseMainWindow(); + process.Close(); + } + } + catch (Exception) { } + } + + private async Task StartRoblox() + { + string startEventName = Program.ProjectName.Replace(" ", "") + "StartEvent"; + + Message = "Starting Roblox..."; + + // launch time isn't really required for all launches, but it's usually just safest to do this + LaunchCommandLine += " --launchtime=" + DateTimeOffset.Now.ToUnixTimeSeconds() + " -startEvent " + startEventName; + Debug.WriteLine($"Starting game client with command line '{LaunchCommandLine}'"); + + using (SystemEvent startEvent = new(startEventName)) + { + Process.Start(Path.Combine(VersionFolder, "RobloxPlayerBeta.exe"), LaunchCommandLine); + + Debug.WriteLine($"Waiting for {startEventName} event to be fired..."); + bool startEventFired = await startEvent.WaitForEvent(); + + startEvent.Close(); + + if (startEventFired) + { + Debug.WriteLine($"{startEventName} event fired! Exiting in 5 seconds..."); + await Task.Delay(5000); + + Program.Exit(); + } + } + } + + // Bootstrapper Installing + + public static void Register() + { + RegistryKey applicationKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}"); + + // new install location selected, delete old one + string? oldInstallLocation = (string?)applicationKey.GetValue("OldInstallLocation"); + if (!String.IsNullOrEmpty(oldInstallLocation) && oldInstallLocation != Program.BaseDirectory) + { + try + { + if (Directory.Exists(oldInstallLocation)) + Directory.Delete(oldInstallLocation, true); + } + catch (Exception) { } + + applicationKey.DeleteValue("OldInstallLocation"); + } + + applicationKey.SetValue("InstallLocation", Program.BaseDirectory); + applicationKey.Close(); + + // set uninstall key + RegistryKey uninstallKey = Registry.CurrentUser.CreateSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Program.ProjectName}"); + uninstallKey.SetValue("DisplayIcon", $"{Program.FilePath},0"); + uninstallKey.SetValue("DisplayName", Program.ProjectName); + uninstallKey.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd")); + uninstallKey.SetValue("InstallLocation", Program.BaseDirectory); + // uninstallKey.SetValue("NoModify", 1); + uninstallKey.SetValue("NoRepair", 1); + uninstallKey.SetValue("Publisher", Program.ProjectName); + uninstallKey.SetValue("ModifyPath", $"\"{Program.FilePath}\" -preferences"); + uninstallKey.SetValue("UninstallString", $"\"{Program.FilePath}\" -uninstall"); + uninstallKey.Close(); + } + + public static void CheckInstall() + { + // check if launch uri is set to our bootstrapper + // this doesn't go under register, so we check every launch + // just in case the stock bootstrapper changes it back + + Protocol.Register("roblox", "Roblox", Program.FilePath); + Protocol.Register("roblox-player", "Roblox", Program.FilePath); + + // in case the user is reinstalling + if (File.Exists(Program.FilePath) && Program.IsFirstRun) + File.Delete(Program.FilePath); + + // check to make sure bootstrapper is in the install folder + if (!File.Exists(Program.FilePath) && Environment.ProcessPath is not null) + File.Copy(Environment.ProcessPath, Program.FilePath); + } + + private void Uninstall() + { + CheckIfRunning(); + + // lots of try/catches here... lol + + Message = $"Uninstalling {Program.ProjectName}..."; + + Program.SettingsManager.ShouldSave = false; + + // check if stock bootstrapper is still installed + RegistryKey? bootstrapperKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-player"); + if (bootstrapperKey is null) + { + Protocol.Unregister("roblox"); + Protocol.Unregister("roblox-player"); + } + else + { + // revert launch uri handler to stock bootstrapper + + string bootstrapperLocation = (string?)bootstrapperKey.GetValue("InstallLocation") + "RobloxPlayerLauncher.exe"; + + Protocol.Register("roblox", "Roblox", bootstrapperLocation); + Protocol.Register("roblox-player", "Roblox", bootstrapperLocation); + } + + try + { + // delete application key + Registry.CurrentUser.DeleteSubKey($@"Software\{Program.ProjectName}"); + } + catch (Exception) { } + + try + { + // delete installation folder + // (should delete everything except bloxstrap itself) + Directory.Delete(Program.BaseDirectory, true); + } + catch (Exception) { } + + try + { + // delete uninstall key + Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Program.ProjectName}"); + } + catch (Exception) { } + + ShowSuccess($"{Program.ProjectName} has been uninstalled"); + Program.Exit(); + } + + // Roblox Installing + + private async Task CheckLatestVersion() + { + Message = "Connecting to Roblox..."; + + Debug.WriteLine($"Checking latest version..."); + VersionGuid = await Client.GetStringAsync($"{Program.BaseUrlSetup}/version"); + VersionFolder = Path.Combine(Program.BaseDirectory, "Versions", VersionGuid); + Debug.WriteLine($"Latest version is {VersionGuid}"); + + Debug.WriteLine("Getting package manifest..."); + VersionPackageManifest = await PackageManifest.Get(VersionGuid); + + Debug.WriteLine("Getting file manifest..."); + VersionFileManifest = await FileManifest.Get(VersionGuid); + } + + private async Task InstallLatestVersion() + { + CheckIfRunning(); + + if (FreshInstall) + Message = "Installing Roblox..."; + else + Message = "Upgrading Roblox..."; + + Directory.CreateDirectory(Program.BaseDirectory); + + CancelEnabled = true; + + // i believe the original bootstrapper bases the progress bar off zip + // extraction progress, but here i'm doing package download progress + + ProgressStyle = ProgressBarStyle.Continuous; + + ProgressIncrement = (int)Math.Floor((decimal) 1 / VersionPackageManifest.Count * 100); + Debug.WriteLine($"Progress Increment is {ProgressIncrement}"); + + Directory.CreateDirectory(Path.Combine(Program.BaseDirectory, "Downloads")); + + foreach (Package package in VersionPackageManifest) + { + // no await, download all the packages at once + DownloadPackage(package); + } + + do + { + // wait for download to finish (and also round off the progress bar if needed) + + if (Progress == ProgressIncrement * VersionPackageManifest.Count) + Progress = 100; + + await Task.Delay(1000); + } + while (Progress != 100); + + ProgressStyle = ProgressBarStyle.Marquee; + + Debug.WriteLine("Finished downloading"); + + Directory.CreateDirectory(Path.Combine(Program.BaseDirectory, "Versions")); + + foreach (Package package in VersionPackageManifest) + { + // extract all the packages at once (shouldn't be too heavy on cpu?) + ExtractPackage(package); + } + + Debug.WriteLine("Finished extracting packages"); + + Message = "Configuring Roblox..."; + + string appSettingsLocation = Path.Combine(VersionFolder, "AppSettings.xml"); + await File.WriteAllTextAsync(appSettingsLocation, AppSettings); + + if (!FreshInstall) + { + // let's take this opportunity to delete any packages we don't need anymore + foreach (string filename in Directory.GetFiles(DownloadsFolder)) + { + if (!VersionPackageManifest.Exists(package => filename.Contains(package.Signature))) + File.Delete(filename); + } + + // and also to delete our old version folder + Directory.Delete(Path.Combine(Program.BaseDirectory, "Versions", Program.Settings.VersionGuid)); + } + + CancelEnabled = false; + + Program.Settings.VersionGuid = VersionGuid; + } + + private async void ApplyModifications() + { + // i guess we can just assume that if the hash does not match the manifest, then it's a mod + // probably not the best way to do this? don't think file corruption is that much of a worry here + + // TODO - i'm thinking i could have a manifest on my website like rbxManifest.txt + // for integrity checking and to quickly fix/alter stuff (like ouch.ogg being renamed) + // but that probably wouldn't be great to check on every run in case my webserver ever goes down + // interesting idea nonetheless, might add it sometime + + // TODO - i'm hoping i can take this idea of content mods much further + // for stuff like easily installing (community-created?) texture/shader/audio mods + // but for now, let's just keep it at this + + string fileContentName = "ouch.ogg"; + string fileContentLocation = "content\\sounds\\ouch.ogg"; + string fileLocation = Path.Combine(VersionFolder, fileContentLocation); + + string officialDeathSoundHash = VersionFileManifest[fileContentLocation]; + string currentDeathSoundHash = CalculateMD5(fileLocation); + + if (Program.Settings.UseOldDeathSound && currentDeathSoundHash == officialDeathSoundHash) + { + // let's get the old one! + + Debug.WriteLine($"Fetching old death sound..."); + + var response = await Client.GetAsync($"{Program.BaseUrlApplication}/mods/{fileContentLocation}"); + + if (File.Exists(fileLocation)) + File.Delete(fileLocation); + + using (var fileStream = new FileStream(fileLocation, FileMode.CreateNew)) + { + await response.Content.CopyToAsync(fileStream); + } + } + else if (!Program.Settings.UseOldDeathSound && currentDeathSoundHash != officialDeathSoundHash) + { + // who's lame enough to ever do this? + // well, we need to re-extract the one that's in the content-sounds.zip package + + Debug.WriteLine("Fetching current death sound..."); + + var package = VersionPackageManifest.Find(x => x.Name == "content-sounds.zip"); + + if (package is null) + { + Debug.WriteLine("Failed to find content-sounds.zip package! Aborting..."); + return; + } + + DownloadPackage(package); + + string packageLocation = Path.Combine(DownloadsFolder, package.Signature); + string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); + + using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) + { + ZipArchiveEntry? entry = archive.Entries.Where(x => x.FullName == fileContentName).FirstOrDefault(); + + if (entry is null) + { + Debug.WriteLine("Failed to find file entry in content-sounds.zip! Aborting..."); + return; + } + + if (File.Exists(fileLocation)) + File.Delete(fileLocation); + + entry.ExtractToFile(fileLocation); + } + } + } + + private async void DownloadPackage(Package package) + { + string packageUrl = $"{Program.BaseUrlSetup}/{VersionGuid}-{package.Name}"; + string packageLocation = Path.Combine(DownloadsFolder, package.Signature); + string robloxPackageLocation = Path.Combine(Program.LocalAppData, "Roblox", "Downloads", package.Signature); + + if (File.Exists(packageLocation)) + { + FileInfo file = new(packageLocation); + + string calculatedMD5 = CalculateMD5(packageLocation); + if (calculatedMD5 != package.Signature) + { + Debug.WriteLine($"{package.Name} is corrupted ({calculatedMD5} != {package.Signature})! Deleting and re-downloading..."); + file.Delete(); + } + else + { + Debug.WriteLine($"{package.Name} is already downloaded, skipping..."); + Progress += ProgressIncrement; + return; + } + } + else if (File.Exists(robloxPackageLocation)) + { + // let's cheat! if the stock bootstrapper already previously downloaded the file, + // then we can just copy the one from there + + Debug.WriteLine($"Found existing version of {package.Name} ({robloxPackageLocation})! Copying to Downloads folder..."); + File.Copy(robloxPackageLocation, packageLocation); + Progress += ProgressIncrement; + return; + } + + if (!File.Exists(packageLocation)) + { + Debug.WriteLine($"Downloading {package.Name}..."); + + var response = await Client.GetAsync(packageUrl); + + if (CancelFired) + return; + + using (var fileStream = new FileStream(packageLocation, FileMode.CreateNew)) + { + await response.Content.CopyToAsync(fileStream); + } + + Debug.WriteLine($"Finished downloading {package.Name}!"); + Progress += ProgressIncrement; + } + } + + private void ExtractPackage(Package package) + { + if (CancelFired) + return; + + string packageLocation = Path.Combine(DownloadsFolder, package.Signature); + string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); + string extractPath; + + Debug.WriteLine($"Extracting {package.Name} to {packageFolder}..."); + + using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) + { + foreach (ZipArchiveEntry entry in archive.Entries) + { + if (CancelFired) + return; + + if (entry.FullName.EndsWith(@"\")) + continue; + + extractPath = Path.Combine(packageFolder, entry.FullName); + + Debug.WriteLine($"[{package.Name}] Writing {extractPath}..."); + + Directory.CreateDirectory(Path.GetDirectoryName(extractPath)); + + if (File.Exists(extractPath)) + File.Delete(extractPath); + + entry.ExtractToFile(extractPath); + } + } + } + + // Dialog Events + + public void CancelButtonClicked() + { + CancelFired = true; + + try + { + if (Program.IsFirstRun) + Directory.Delete(Program.BaseDirectory, true); + else if (Directory.Exists(VersionFolder)) + Directory.Delete(VersionFolder, true); + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to cleanup install!\n\n{ex}"); + } + + Program.Exit(); + } + + private void ShowSuccess(string message) + { + ShowSuccessEvent.Invoke(this, new ChangeEventArgs(message)); + } + + private void PromptShutdown() + { + PromptShutdownEvent.Invoke(this, new EventArgs()); + } + + // Utilities + + private static string CalculateMD5(string filename) + { + using (MD5 md5 = MD5.Create()) + { + using (FileStream stream = File.OpenRead(filename)) + { + byte[] hash = md5.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.Designer.cs new file mode 100644 index 00000000..2da9d576 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.Designer.cs @@ -0,0 +1,108 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + partial class LegacyDialogStyle + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.Message = new System.Windows.Forms.Label(); + this.ProgressBar = new System.Windows.Forms.ProgressBar(); + this.IconBox = new System.Windows.Forms.PictureBox(); + this.CancelButton = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit(); + this.SuspendLayout(); + // + // Message + // + this.Message.Location = new System.Drawing.Point(55, 23); + this.Message.Name = "Message"; + this.Message.Size = new System.Drawing.Size(287, 17); + this.Message.TabIndex = 0; + this.Message.Text = "Please wait..."; + // + // ProgressBar + // + this.ProgressBar.Location = new System.Drawing.Point(58, 51); + this.ProgressBar.MarqueeAnimationSpeed = 33; + this.ProgressBar.Name = "ProgressBar"; + this.ProgressBar.Size = new System.Drawing.Size(287, 26); + this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.ProgressBar.TabIndex = 1; + // + // IconBox + // + this.IconBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.IconBox.ImageLocation = ""; + this.IconBox.Location = new System.Drawing.Point(19, 16); + this.IconBox.Name = "IconBox"; + this.IconBox.Size = new System.Drawing.Size(32, 32); + this.IconBox.TabIndex = 2; + this.IconBox.TabStop = false; + // + // CancelButton + // + this.CancelButton.Enabled = false; + this.CancelButton.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.CancelButton.Location = new System.Drawing.Point(271, 83); + this.CancelButton.Name = "CancelButton"; + this.CancelButton.Size = new System.Drawing.Size(75, 23); + this.CancelButton.TabIndex = 3; + this.CancelButton.Text = "Cancel"; + this.CancelButton.UseVisualStyleBackColor = true; + this.CancelButton.Visible = false; + this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); + // + // LegacyDialogStyle + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(362, 131); + this.Controls.Add(this.CancelButton); + this.Controls.Add(this.IconBox); + this.Controls.Add(this.ProgressBar); + this.Controls.Add(this.Message); + this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(378, 170); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(378, 170); + this.Name = "LegacyDialogStyle"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "LegacyDialogStyle"; + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private Label Message; + private ProgressBar ProgressBar; + private PictureBox IconBox; + private Button CancelButton; + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.cs new file mode 100644 index 00000000..0a44dea3 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.cs @@ -0,0 +1,161 @@ +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // TODO - universal implementation for winforms-based styles? (to reduce duplicate code) + + // example: https://youtu.be/3K9oCEMHj2s?t=35 + + // so this specifically emulates the 2011 version of the legacy dialog, + // but once winforms code is cleaned up we could also do the 2009 version too + // example: https://youtu.be/VpduiruysuM?t=18 + + public partial class LegacyDialogStyle : Form + { + private Bootstrapper? Bootstrapper; + + public LegacyDialogStyle(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + + if (bootstrapper is not null) + { + Bootstrapper = bootstrapper; + Bootstrapper.PromptShutdownEvent += new EventHandler(PromptShutdown); + Bootstrapper.ShowSuccessEvent += new ChangeEventHandler(ShowSuccess); + Bootstrapper.MessageChanged += new ChangeEventHandler(MessageChanged); + Bootstrapper.ProgressBarValueChanged += new ChangeEventHandler(ProgressBarValueChanged); + Bootstrapper.ProgressBarStyleChanged += new ChangeEventHandler(ProgressBarStyleChanged); + Bootstrapper.CancelEnabledChanged += new ChangeEventHandler(CancelEnabledChanged); + } + + Icon icon = IconManager.GetIconResource(); + + this.Text = Program.ProjectName; + this.Icon = icon; + this.IconBox.Image = icon.ToBitmap(); + + if (Bootstrapper is null) + { + this.Message.Text = "Click the Cancel button to return to preferences"; + this.CancelButton.Enabled = true; + this.CancelButton.Visible = true; + } + else + { + Task.Run(() => RunBootstrapper()); + } + } + + public async void RunBootstrapper() + { + try + { + await Bootstrapper.Run(); + } + catch (Exception ex) + { + // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); + string message = ex.ToString(); + ShowError(message); + + Program.Exit(); + } + } + + private void ShowError(string message) + { + MessageBox.Show( + $"An error occurred while starting Roblox\n\nDetails: {message}", + Program.ProjectName, + MessageBoxButtons.OK, + MessageBoxIcon.Error + ); + } + + private void ShowSuccess(object sender, ChangeEventArgs e) + { + MessageBox.Show( + e.Value, + Program.ProjectName, + MessageBoxButtons.OK, + MessageBoxIcon.Information + ); + } + + private void PromptShutdown(object? sender, EventArgs e) + { + DialogResult result = MessageBox.Show( + "Roblox is currently running, but needs to close. Would you like close Roblox now?", + Program.ProjectName, + MessageBoxButtons.OKCancel, + MessageBoxIcon.Information + ); + + if (result != DialogResult.OK) + Environment.Exit(0); + } + + private void MessageChanged(object sender, ChangeEventArgs e) + { + if (this.InvokeRequired) + { + ChangeEventHandler handler = new(MessageChanged); + this.Message.Invoke(handler, sender, e); + } + else + { + this.Message.Text = e.Value; + } + } + + private void ProgressBarValueChanged(object sender, ChangeEventArgs e) + { + if (this.ProgressBar.InvokeRequired) + { + ChangeEventHandler handler = new(ProgressBarValueChanged); + this.ProgressBar.Invoke(handler, sender, e); + } + else + { + this.ProgressBar.Value = e.Value; + } + } + + private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) + { + if (this.ProgressBar.InvokeRequired) + { + ChangeEventHandler handler = new(this.ProgressBarStyleChanged); + this.ProgressBar.Invoke(handler, sender, e); + } + else + { + this.ProgressBar.Style = e.Value; + } + } + + private void CancelEnabledChanged(object sender, ChangeEventArgs e) + { + if (this.CancelButton.InvokeRequired) + { + ChangeEventHandler handler = new(CancelEnabledChanged); + this.CancelButton.Invoke(handler, sender, e); + } + else + { + this.CancelButton.Enabled = e.Value; + this.CancelButton.Visible = e.Value; + } + } + + private void CancelButton_Click(object sender, EventArgs e) + { + if (Bootstrapper is null) + this.Close(); + else + Task.Run(() => Bootstrapper.CancelButtonClicked()); + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.resx b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialogStyle.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.Designer.cs new file mode 100644 index 00000000..6f8de52a --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.Designer.cs @@ -0,0 +1,126 @@ +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + partial class ProgressDialogStyle + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ProgressBar = new System.Windows.Forms.ProgressBar(); + this.Message = new System.Windows.Forms.Label(); + this.IconBox = new System.Windows.Forms.PictureBox(); + this.CancelButton = new System.Windows.Forms.PictureBox(); + this.panel1 = new System.Windows.Forms.Panel(); + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.CancelButton)).BeginInit(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // ProgressBar + // + this.ProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.ProgressBar.Location = new System.Drawing.Point(29, 241); + this.ProgressBar.MarqueeAnimationSpeed = 20; + this.ProgressBar.Name = "ProgressBar"; + this.ProgressBar.Size = new System.Drawing.Size(460, 20); + this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.ProgressBar.TabIndex = 0; + // + // Message + // + this.Message.Font = new System.Drawing.Font("Tahoma", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.Message.Location = new System.Drawing.Point(29, 199); + this.Message.Name = "Message"; + this.Message.Size = new System.Drawing.Size(460, 18); + this.Message.TabIndex = 1; + this.Message.Text = "Please wait..."; + this.Message.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.Message.UseMnemonic = false; + // + // IconBox + // + this.IconBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.IconBox.ImageLocation = ""; + this.IconBox.Location = new System.Drawing.Point(212, 66); + this.IconBox.Name = "IconBox"; + this.IconBox.Size = new System.Drawing.Size(92, 92); + this.IconBox.TabIndex = 2; + this.IconBox.TabStop = false; + // + // CancelButton + // + this.CancelButton.Enabled = false; + this.CancelButton.Image = global::Bloxstrap.Properties.Resources.CancelButton; + this.CancelButton.Location = new System.Drawing.Point(194, 264); + this.CancelButton.Name = "CancelButton"; + this.CancelButton.Size = new System.Drawing.Size(130, 44); + this.CancelButton.TabIndex = 3; + this.CancelButton.TabStop = false; + this.CancelButton.Visible = false; + this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); + this.CancelButton.MouseEnter += new System.EventHandler(this.CancelButton_MouseEnter); + this.CancelButton.MouseLeave += new System.EventHandler(this.CancelButton_MouseLeave); + // + // panel1 + // + this.panel1.BackColor = System.Drawing.SystemColors.Window; + this.panel1.Controls.Add(this.Message); + this.panel1.Controls.Add(this.IconBox); + this.panel1.Controls.Add(this.CancelButton); + this.panel1.Controls.Add(this.ProgressBar); + this.panel1.Location = new System.Drawing.Point(1, 1); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(518, 318); + this.panel1.TabIndex = 4; + // + // ProgressDialogStyle + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.ClientSize = new System.Drawing.Size(520, 320); + this.Controls.Add(this.panel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.MaximumSize = new System.Drawing.Size(520, 320); + this.MinimumSize = new System.Drawing.Size(520, 320); + this.Name = "ProgressDialogStyle"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "ProgressDialogStyle"; + ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.CancelButton)).EndInit(); + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private ProgressBar ProgressBar; + private Label Message; + private PictureBox IconBox; + private PictureBox CancelButton; + private Panel panel1; + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.cs new file mode 100644 index 00000000..739b26c8 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.cs @@ -0,0 +1,161 @@ +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // TODO - universal implementation for winforms-based styles? (to reduce duplicate code) + + public partial class ProgressDialogStyle : Form + { + private Bootstrapper? Bootstrapper; + + public ProgressDialogStyle(Bootstrapper? bootstrapper = null) + { + InitializeComponent(); + + Bootstrapper = bootstrapper; + + this.Text = Program.ProjectName; + this.Icon = IconManager.GetIconResource(); + this.IconBox.BackgroundImage = IconManager.GetBitmapResource(); + + if (Bootstrapper is null) + { + this.Message.Text = "Click the Cancel button to return to preferences"; + this.CancelButton.Enabled = true; + this.CancelButton.Visible = true; + } + else + { + Bootstrapper.PromptShutdownEvent += new EventHandler(PromptShutdown); + Bootstrapper.ShowSuccessEvent += new ChangeEventHandler(ShowSuccess); + Bootstrapper.MessageChanged += new ChangeEventHandler(MessageChanged); + Bootstrapper.ProgressBarValueChanged += new ChangeEventHandler(ProgressBarValueChanged); + Bootstrapper.ProgressBarStyleChanged += new ChangeEventHandler(ProgressBarStyleChanged); + Bootstrapper.CancelEnabledChanged += new ChangeEventHandler(CancelEnabledChanged); + + Task.Run(() => RunBootstrapper()); + } + } + + public async void RunBootstrapper() + { + try + { + await Bootstrapper.Run(); + } + catch (Exception ex) + { + // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); + string message = ex.ToString(); + ShowError(message); + + Program.Exit(); + } + } + + private void ShowError(string message) + { + MessageBox.Show( + $"An error occurred while starting Roblox\n\nDetails: {message}", + Program.ProjectName, + MessageBoxButtons.OK, + MessageBoxIcon.Error + ); + } + + private void ShowSuccess(object sender, ChangeEventArgs e) + { + MessageBox.Show( + e.Value, + Program.ProjectName, + MessageBoxButtons.OK, + MessageBoxIcon.Information + ); + } + + private void PromptShutdown(object? sender, EventArgs e) + { + DialogResult result = MessageBox.Show( + "Roblox is currently running, but needs to close. Would you like close Roblox now?", + Program.ProjectName, + MessageBoxButtons.OKCancel, + MessageBoxIcon.Information + ); + + if (result != DialogResult.OK) + Environment.Exit(0); + } + + private void MessageChanged(object sender, ChangeEventArgs e) + { + if (this.InvokeRequired) + { + ChangeEventHandler handler = new(MessageChanged); + this.Message.Invoke(handler, sender, e); + } + else + { + this.Message.Text = e.Value; + } + } + + private void ProgressBarValueChanged(object sender, ChangeEventArgs e) + { + if (this.ProgressBar.InvokeRequired) + { + ChangeEventHandler handler = new(ProgressBarValueChanged); + this.ProgressBar.Invoke(handler, sender, e); + } + else + { + this.ProgressBar.Value = e.Value; + } + } + + private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) + { + if (this.ProgressBar.InvokeRequired) + { + ChangeEventHandler handler = new(ProgressBarStyleChanged); + this.ProgressBar.Invoke(handler, sender, e); + } + else + { + this.ProgressBar.Style = e.Value; + } + } + + private void CancelEnabledChanged(object sender, ChangeEventArgs e) + { + if (this.CancelButton.InvokeRequired) + { + ChangeEventHandler handler = new(CancelEnabledChanged); + this.CancelButton.Invoke(handler, sender, e); + } + else + { + this.CancelButton.Enabled = e.Value; + this.CancelButton.Visible = e.Value; + } + } + + private void CancelButton_Click(object sender, EventArgs e) + { + if (Bootstrapper is null) + this.Close(); + else + Task.Run(() => Bootstrapper.CancelButtonClicked()); + } + + private void CancelButton_MouseEnter(object sender, EventArgs e) + { + this.CancelButton.Image = Properties.Resources.CancelButtonHover; + } + + private void CancelButton_MouseLeave(object sender, EventArgs e) + { + this.CancelButton.Image = Properties.Resources.CancelButton; + } + } +} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.resx b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.resx new file mode 100644 index 00000000..f298a7be --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogStyle.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/TaskDialogStyle.cs b/Bloxstrap/Dialogs/BootstrapperStyles/TaskDialogStyle.cs new file mode 100644 index 00000000..019325f7 --- /dev/null +++ b/Bloxstrap/Dialogs/BootstrapperStyles/TaskDialogStyle.cs @@ -0,0 +1,134 @@ +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap.Dialogs.BootstrapperStyles +{ + // example: https://youtu.be/h0_AL95Sc3o?t=48 + + // i suppose a better name for this here would be "VistaDialog" rather than "TaskDialog"? + // having this named as BootstrapperStyles.TaskDialog would conflict with Forms.TaskDialog + // so naming it VistaDialog would let us drop the ~Style suffix on every style name + + // this currently doesn't work because c# is stupid + // technically, task dialogs are treated as winforms controls, but they don't classify as winforms controls at all + // all winforms controls have the ability to be invoked from another thread, but task dialogs don't + // so we're just kind of stuck with this not working in multithreaded use + // (unless we want the bootstrapper to freeze during package extraction) + + // for now, just stick to legacydialog and progressdialog + + public class TaskDialogStyle + { + private Bootstrapper Bootstrapper; + private TaskDialogPage Dialog; + + public TaskDialogStyle(Bootstrapper bootstrapper) + { + Bootstrapper = bootstrapper; + Bootstrapper.ShowSuccessEvent += new ChangeEventHandler(ShowSuccess); + Bootstrapper.MessageChanged += new ChangeEventHandler(MessageChanged); + Bootstrapper.ProgressBarValueChanged += new ChangeEventHandler(ProgressBarValueChanged); + Bootstrapper.ProgressBarStyleChanged += new ChangeEventHandler(ProgressBarStyleChanged); + + Dialog = new TaskDialogPage() + { + Icon = new TaskDialogIcon(IconManager.GetIconResource()), + Caption = Program.ProjectName, + Heading = "Please wait...", + + Buttons = { TaskDialogButton.Cancel }, + ProgressBar = new TaskDialogProgressBar() + { + State = TaskDialogProgressBarState.Marquee + } + }; + + Task.Run(() => RunBootstrapper()); + TaskDialog.ShowDialog(Dialog); + } + + public async void RunBootstrapper() + { + try + { + await Bootstrapper.Run(); + } + catch (Exception ex) + { + // string message = String.Format("{0}: {1}", ex.GetType(), ex.Message); + string message = ex.ToString(); + ShowError(message); + + Program.Exit(); + } + } + + public void ShowError(string message) + { + TaskDialogPage errorDialog = new() + { + Icon = TaskDialogIcon.Error, + Caption = Program.ProjectName, + Heading = "An error occurred while starting Roblox", + Buttons = { TaskDialogButton.Close }, + Expander = new TaskDialogExpander() + { + Text = message, + CollapsedButtonText = "See details", + ExpandedButtonText = "Hide details", + Position = TaskDialogExpanderPosition.AfterText + } + }; + + Dialog.Navigate(errorDialog); + Dialog = errorDialog; + } + + public void ShowSuccess(object sender, ChangeEventArgs e) + { + TaskDialogPage successDialog = new() + { + Icon = TaskDialogIcon.ShieldSuccessGreenBar, + Caption = Program.ProjectName, + Heading = e.Value + }; + + Dialog.Navigate(successDialog); + Dialog = successDialog; + } + + private void MessageChanged(object sender, ChangeEventArgs e) + { + if (Dialog is null) + return; + + Dialog.Heading = e.Value; + } + + private void ProgressBarValueChanged(object sender, ChangeEventArgs e) + { + if (Dialog is null || Dialog.ProgressBar is null) + return; + + Dialog.ProgressBar.Value = e.Value; + } + + private void ProgressBarStyleChanged(object sender, ChangeEventArgs e) + { + if (Dialog is null || Dialog.ProgressBar is null) + return; + + switch (e.Value) + { + case ProgressBarStyle.Continuous: + case ProgressBarStyle.Blocks: + Dialog.ProgressBar.State = TaskDialogProgressBarState.Normal; + break; + + case ProgressBarStyle.Marquee: + Dialog.ProgressBar.State = TaskDialogProgressBarState.Marquee; + break; + } + } + } +} diff --git a/Bloxstrap/Dialogs/Preferences.Designer.cs b/Bloxstrap/Dialogs/Preferences.Designer.cs new file mode 100644 index 00000000..c7f09023 --- /dev/null +++ b/Bloxstrap/Dialogs/Preferences.Designer.cs @@ -0,0 +1,310 @@ +namespace Bloxstrap.Dialogs +{ + partial class Preferences + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.Tabs = new System.Windows.Forms.TabControl(); + this.DialogTab = new System.Windows.Forms.TabPage(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.IconPreview = new System.Windows.Forms.PictureBox(); + this.IconSelection = new System.Windows.Forms.ListBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.StyleSelection = new System.Windows.Forms.ListBox(); + this.InstallationTab = new System.Windows.Forms.TabPage(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.ModifyDeathSoundToggle = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.InstallLocationBrowseButton = new System.Windows.Forms.Button(); + this.InstallLocation = new System.Windows.Forms.TextBox(); + this.SaveButton = new System.Windows.Forms.Button(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.PreviewButton = new System.Windows.Forms.Button(); + this.InstallLocationBrowseDialog = new System.Windows.Forms.FolderBrowserDialog(); + this.Tabs.SuspendLayout(); + this.DialogTab.SuspendLayout(); + this.groupBox3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.IconPreview)).BeginInit(); + this.groupBox2.SuspendLayout(); + this.InstallationTab.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.label1.ForeColor = System.Drawing.Color.Navy; + this.label1.Location = new System.Drawing.Point(8, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(237, 23); + this.label1.TabIndex = 1; + this.label1.Text = "Configure Preferences"; + // + // Tabs + // + this.Tabs.Controls.Add(this.DialogTab); + this.Tabs.Controls.Add(this.InstallationTab); + this.Tabs.Location = new System.Drawing.Point(12, 40); + this.Tabs.Name = "Tabs"; + this.Tabs.SelectedIndex = 0; + this.Tabs.Size = new System.Drawing.Size(442, 176); + this.Tabs.TabIndex = 2; + // + // DialogTab + // + this.DialogTab.Controls.Add(this.groupBox3); + this.DialogTab.Controls.Add(this.groupBox2); + this.DialogTab.Location = new System.Drawing.Point(4, 24); + this.DialogTab.Name = "DialogTab"; + this.DialogTab.Padding = new System.Windows.Forms.Padding(3); + this.DialogTab.Size = new System.Drawing.Size(434, 148); + this.DialogTab.TabIndex = 0; + this.DialogTab.Text = "Bootstrapper"; + this.DialogTab.UseVisualStyleBackColor = true; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.IconPreview); + this.groupBox3.Controls.Add(this.IconSelection); + this.groupBox3.Location = new System.Drawing.Point(192, 3); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(235, 140); + this.groupBox3.TabIndex = 6; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Icon"; + // + // IconPreview + // + this.IconPreview.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.IconPreview.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.IconPreview.Location = new System.Drawing.Point(117, 21); + this.IconPreview.Name = "IconPreview"; + this.IconPreview.Size = new System.Drawing.Size(109, 109); + this.IconPreview.TabIndex = 3; + this.IconPreview.TabStop = false; + // + // IconSelection + // + this.IconSelection.FormattingEnabled = true; + this.IconSelection.ItemHeight = 15; + this.IconSelection.Location = new System.Drawing.Point(9, 21); + this.IconSelection.Name = "IconSelection"; + this.IconSelection.Size = new System.Drawing.Size(100, 109); + this.IconSelection.TabIndex = 4; + this.IconSelection.SelectedIndexChanged += new System.EventHandler(this.IconSelection_SelectedIndexChanged); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.StyleSelection); + this.groupBox2.Location = new System.Drawing.Point(5, 3); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(179, 140); + this.groupBox2.TabIndex = 5; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Style"; + // + // StyleSelection + // + this.StyleSelection.FormattingEnabled = true; + this.StyleSelection.ItemHeight = 15; + this.StyleSelection.Location = new System.Drawing.Point(9, 21); + this.StyleSelection.Name = "StyleSelection"; + this.StyleSelection.Size = new System.Drawing.Size(161, 109); + this.StyleSelection.TabIndex = 3; + this.StyleSelection.SelectedIndexChanged += new System.EventHandler(this.StyleSelection_SelectedIndexChanged); + // + // InstallationTab + // + this.InstallationTab.Controls.Add(this.groupBox4); + this.InstallationTab.Controls.Add(this.groupBox1); + this.InstallationTab.Location = new System.Drawing.Point(4, 24); + this.InstallationTab.Name = "InstallationTab"; + this.InstallationTab.Padding = new System.Windows.Forms.Padding(3); + this.InstallationTab.Size = new System.Drawing.Size(434, 148); + this.InstallationTab.TabIndex = 2; + this.InstallationTab.Text = "Installation"; + this.InstallationTab.UseVisualStyleBackColor = true; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.ModifyDeathSoundToggle); + this.groupBox4.Location = new System.Drawing.Point(5, 59); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(422, 84); + this.groupBox4.TabIndex = 2; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "Modifications"; + // + // ModifyDeathSoundToggle + // + this.ModifyDeathSoundToggle.AutoSize = true; + this.ModifyDeathSoundToggle.Checked = true; + this.ModifyDeathSoundToggle.CheckState = System.Windows.Forms.CheckState.Checked; + this.ModifyDeathSoundToggle.Location = new System.Drawing.Point(9, 21); + this.ModifyDeathSoundToggle.Margin = new System.Windows.Forms.Padding(2); + this.ModifyDeathSoundToggle.Name = "ModifyDeathSoundToggle"; + this.ModifyDeathSoundToggle.Size = new System.Drawing.Size(138, 19); + this.ModifyDeathSoundToggle.TabIndex = 1; + this.ModifyDeathSoundToggle.Text = "Use Old Death Sound"; + this.ModifyDeathSoundToggle.UseVisualStyleBackColor = true; + this.ModifyDeathSoundToggle.CheckedChanged += new System.EventHandler(this.ModifyDeathSoundToggle_CheckedChanged); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.InstallLocationBrowseButton); + this.groupBox1.Controls.Add(this.InstallLocation); + this.groupBox1.Location = new System.Drawing.Point(5, 3); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(422, 53); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Install Location"; + // + // InstallLocationBrowseButton + // + this.InstallLocationBrowseButton.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.InstallLocationBrowseButton.Location = new System.Drawing.Point(328, 20); + this.InstallLocationBrowseButton.Name = "InstallLocationBrowseButton"; + this.InstallLocationBrowseButton.Size = new System.Drawing.Size(86, 25); + this.InstallLocationBrowseButton.TabIndex = 5; + this.InstallLocationBrowseButton.Text = "Browse..."; + this.InstallLocationBrowseButton.UseVisualStyleBackColor = true; + this.InstallLocationBrowseButton.Click += new System.EventHandler(this.InstallLocationBrowseButton_Click); + // + // InstallLocation + // + this.InstallLocation.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.InstallLocation.Location = new System.Drawing.Point(9, 21); + this.InstallLocation.MaxLength = 255; + this.InstallLocation.Name = "InstallLocation"; + this.InstallLocation.Size = new System.Drawing.Size(312, 23); + this.InstallLocation.TabIndex = 4; + // + // SaveButton + // + this.SaveButton.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.SaveButton.Location = new System.Drawing.Point(380, 9); + this.SaveButton.Name = "SaveButton"; + this.SaveButton.Size = new System.Drawing.Size(73, 23); + this.SaveButton.TabIndex = 6; + this.SaveButton.Text = "Save"; + this.SaveButton.UseVisualStyleBackColor = true; + this.SaveButton.Click += new System.EventHandler(this.SaveButton_Click); + // + // panel1 + // + this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panel1.BackColor = System.Drawing.SystemColors.Control; + this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panel1.Controls.Add(this.label2); + this.panel1.Controls.Add(this.PreviewButton); + this.panel1.Controls.Add(this.SaveButton); + this.panel1.Location = new System.Drawing.Point(-1, 227); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(466, 42); + this.panel1.TabIndex = 6; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 13); + this.label2.Margin = new System.Windows.Forms.Padding(0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(221, 15); + this.label2.TabIndex = 6; + this.label2.Text = "made by pizzaboxer - i think this works..."; + // + // PreviewButton + // + this.PreviewButton.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.PreviewButton.Location = new System.Drawing.Point(297, 9); + this.PreviewButton.Name = "PreviewButton"; + this.PreviewButton.Size = new System.Drawing.Size(73, 23); + this.PreviewButton.TabIndex = 5; + this.PreviewButton.Text = "Preview"; + this.PreviewButton.UseVisualStyleBackColor = true; + this.PreviewButton.Click += new System.EventHandler(this.PreviewButton_Click); + // + // Preferences + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Window; + this.ClientSize = new System.Drawing.Size(464, 268); + this.Controls.Add(this.panel1); + this.Controls.Add(this.Tabs); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Preferences"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Preferences"; + this.Tabs.ResumeLayout(false); + this.DialogTab.ResumeLayout(false); + this.groupBox3.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.IconPreview)).EndInit(); + this.groupBox2.ResumeLayout(false); + this.InstallationTab.ResumeLayout(false); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Label label1; + private TabControl Tabs; + private TabPage DialogTab; + private TabPage InstallationTab; + private Button SaveButton; + private Panel panel1; + private ListBox StyleSelection; + private GroupBox groupBox1; + private Button InstallLocationBrowseButton; + private TextBox InstallLocation; + private FolderBrowserDialog InstallLocationBrowseDialog; + private GroupBox groupBox2; + private GroupBox groupBox3; + private PictureBox IconPreview; + private ListBox IconSelection; + private Button PreviewButton; + private Label label2; + private CheckBox ModifyDeathSoundToggle; + private GroupBox groupBox4; + } +} \ No newline at end of file diff --git a/Bloxstrap/Dialogs/Preferences.cs b/Bloxstrap/Dialogs/Preferences.cs new file mode 100644 index 00000000..5756dffd --- /dev/null +++ b/Bloxstrap/Dialogs/Preferences.cs @@ -0,0 +1,235 @@ +using Microsoft.Win32; + +using Bloxstrap.Helpers; +using Bloxstrap.Enums; +using Bloxstrap.Dialogs.BootstrapperStyles; + +namespace Bloxstrap.Dialogs +{ + public partial class Preferences : Form + { + private static readonly IReadOnlyDictionary SelectableStyles = new Dictionary() + { + { "Legacy (2011 - 2014)", BootstrapperStyle.LegacyDialog }, + { "Progress (~2014)", BootstrapperStyle.ProgressDialog }, + }; + + private static readonly IReadOnlyDictionary SelectableIcons = new Dictionary() + { + { "Bloxstrap", BootstrapperIcon.IconBloxstrap }, + { "2009", BootstrapperIcon.Icon2009 }, + { "2011", BootstrapperIcon.Icon2011 }, + { "Early 2015", BootstrapperIcon.IconEarly2015 }, + { "Late 2015", BootstrapperIcon.IconLate2015 }, + { "2017", BootstrapperIcon.Icon2017 }, + { "2019", BootstrapperIcon.Icon2019 }, + }; + + private bool _useOldDeathSound = true; + private BootstrapperStyle? _selectedStyle; + private BootstrapperIcon? _selectedIcon; + + private bool UseOldDeathSound + { + get => _useOldDeathSound; + + set + { + if (_useOldDeathSound == value) + return; + + _useOldDeathSound = value; + + this.ModifyDeathSoundToggle.Checked = value; + } + } + + private BootstrapperStyle SelectedStyle + { + get => (BootstrapperStyle)_selectedStyle; + + set + { + if (_selectedStyle == value) + return; + + _selectedStyle = value; + + int index = SelectableStyles.Values.ToList().IndexOf(value); + this.StyleSelection.SetSelected(index, true); + } + } + + private BootstrapperIcon SelectedIcon + { + get => (BootstrapperIcon)_selectedIcon; + + set + { + if (_selectedIcon == value) + return; + + _selectedIcon = value; + + int index = SelectableIcons.Values.ToList().IndexOf(value); + this.IconSelection.SetSelected(index, true); + this.IconPreview.BackgroundImage = IconManager.GetBitmapResource(value); + } + } + + public Preferences() + { + InitializeComponent(); + + this.Icon = Properties.Resources.IconBloxstrap_ico; + this.Text = Program.ProjectName; + + if (Program.IsFirstRun) + { + this.SaveButton.Text = "Continue"; + this.InstallLocation.Text = Path.Combine(Program.LocalAppData, Program.ProjectName); + } + else + { + this.InstallLocation.Text = Program.BaseDirectory; + } + + foreach (var style in SelectableStyles) + { + this.StyleSelection.Items.Add(style.Key); + } + + foreach (var icon in SelectableIcons) + { + this.IconSelection.Items.Add(icon.Key); + } + + UseOldDeathSound = Program.Settings.UseOldDeathSound; + SelectedStyle = Program.Settings.BootstrapperStyle; + SelectedIcon = Program.Settings.BootstrapperIcon; + } + + private void ShowDialog(MessageBoxIcon icon, string message) + { + MessageBox.Show(message, Program.ProjectName, MessageBoxButtons.OK, icon); + } + + private void InstallLocationBrowseButton_Click(object sender, EventArgs e) + { + DialogResult result = this.InstallLocationBrowseDialog.ShowDialog(); + + if (result == DialogResult.OK) + this.InstallLocation.Text = this.InstallLocationBrowseDialog.SelectedPath; + } + + private void StyleSelection_SelectedIndexChanged(object sender, EventArgs e) + { + string selected = this.StyleSelection.Text; + SelectedStyle = SelectableStyles[selected]; + } + + private void IconSelection_SelectedIndexChanged(object sender, EventArgs e) + { + string selected = this.IconSelection.Text; + SelectedIcon = SelectableIcons[selected]; + } + + private void SaveButton_Click(object sender, EventArgs e) + { + string installLocation = this.InstallLocation.Text; + + if (String.IsNullOrEmpty(installLocation)) + { + ShowDialog(MessageBoxIcon.Error, "You must set an install location"); + return; + } + + try + { + // check if we can write to the directory (a bit hacky but eh) + + string testPath = installLocation; + string testFile = Path.Combine(installLocation, "BloxstrapWriteTest.txt"); + bool testPathExists = Directory.Exists(testPath); + + if (!testPathExists) + Directory.CreateDirectory(testPath); + + File.WriteAllText(testFile, "hi"); + File.Delete(testFile); + + if (!testPathExists) + Directory.Delete(testPath); + } + catch (UnauthorizedAccessException) + { + ShowDialog(MessageBoxIcon.Error, $"{Program.ProjectName} does not have write access to the install location you selected. Please choose another install location."); + return; + } + catch (Exception ex) + { + ShowDialog(MessageBoxIcon.Error, ex.Message); + return; + } + + if (Program.IsFirstRun) + { + // this will be set in the registry after first install + Program.BaseDirectory = installLocation; + } + else if (Program.BaseDirectory != installLocation) + { + ShowDialog(MessageBoxIcon.Information, $"{Program.ProjectName} will install to the new location you've set the next time it runs."); + + Program.Settings.VersionGuid = ""; + + using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}")) + { + registryKey.SetValue("InstallLocation", installLocation); + registryKey.SetValue("OldInstallLocation", Program.BaseDirectory); + } + + // preserve settings + // we don't need to copy the bootstrapper over since the install process will do that automatically + + Program.SettingsManager.Save(); + File.Copy(Path.Combine(Program.BaseDirectory, "Settings.json"), Path.Combine(installLocation, "Settings.json")); + } + + Program.Settings.UseOldDeathSound = UseOldDeathSound; + Program.Settings.BootstrapperStyle = SelectedStyle; + Program.Settings.BootstrapperIcon = SelectedIcon; + + this.Close(); + } + + private void PreviewButton_Click(object sender, EventArgs e) + { + // small hack to get the icon to show in the preview without saving to settings + BootstrapperIcon savedIcon = Program.Settings.BootstrapperIcon; + Program.Settings.BootstrapperIcon = SelectedIcon; + + this.Visible = false; + + switch (SelectedStyle) + { + case BootstrapperStyle.LegacyDialog: + new LegacyDialogStyle().ShowDialog(); + break; + + case BootstrapperStyle.ProgressDialog: + new ProgressDialogStyle().ShowDialog(); + break; + } + + Program.Settings.BootstrapperIcon = savedIcon; + + this.Visible = true; + } + + private void ModifyDeathSoundToggle_CheckedChanged(object sender, EventArgs e) + { + UseOldDeathSound = this.ModifyDeathSoundToggle.Checked; + } + } +} diff --git a/Bloxstrap/Dialogs/Preferences.resx b/Bloxstrap/Dialogs/Preferences.resx new file mode 100644 index 00000000..8e732d63 --- /dev/null +++ b/Bloxstrap/Dialogs/Preferences.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Bloxstrap/Enums/BootstrapperIcon.cs b/Bloxstrap/Enums/BootstrapperIcon.cs new file mode 100644 index 00000000..e0444d4c --- /dev/null +++ b/Bloxstrap/Enums/BootstrapperIcon.cs @@ -0,0 +1,13 @@ +namespace Bloxstrap.Enums +{ + public enum BootstrapperIcon + { + IconBloxstrap, + Icon2009, + Icon2011, + IconEarly2015, + IconLate2015, + Icon2017, + Icon2019 + } +} diff --git a/Bloxstrap/Enums/BootstrapperStyle.cs b/Bloxstrap/Enums/BootstrapperStyle.cs new file mode 100644 index 00000000..fa3e00a7 --- /dev/null +++ b/Bloxstrap/Enums/BootstrapperStyle.cs @@ -0,0 +1,9 @@ +namespace Bloxstrap.Enums +{ + public enum BootstrapperStyle + { + TaskDialog, + LegacyDialog, + ProgressDialog + } +} diff --git a/Bloxstrap/Helpers/IconManager.cs b/Bloxstrap/Helpers/IconManager.cs new file mode 100644 index 00000000..3f873a85 --- /dev/null +++ b/Bloxstrap/Helpers/IconManager.cs @@ -0,0 +1,45 @@ +using Bloxstrap.Enums; + +namespace Bloxstrap.Helpers +{ + internal class IconManager + { + public static Icon GetIconResource() + { + return GetIconResource(Program.Settings.BootstrapperIcon); + } + + public static Icon GetIconResource(BootstrapperIcon icon) + { + switch (icon) + { + case BootstrapperIcon.Icon2009: return Properties.Resources.Icon2009_ico; + case BootstrapperIcon.Icon2011: return Properties.Resources.Icon2011_ico; + case BootstrapperIcon.IconEarly2015: return Properties.Resources.IconEarly2015_ico; + case BootstrapperIcon.IconLate2015: return Properties.Resources.IconLate2015_ico; + case BootstrapperIcon.Icon2017: return Properties.Resources.Icon2017_ico; + case BootstrapperIcon.Icon2019: return Properties.Resources.Icon2019_ico; + case BootstrapperIcon.IconBloxstrap: default: return Properties.Resources.IconBloxstrap_ico; + } + } + + public static Bitmap GetBitmapResource() + { + return GetBitmapResource(Program.Settings.BootstrapperIcon); + } + + public static Bitmap GetBitmapResource(BootstrapperIcon icon) + { + switch (icon) + { + case BootstrapperIcon.Icon2009: return Properties.Resources.Icon2009_png; + case BootstrapperIcon.Icon2011: return Properties.Resources.Icon2011_png; + case BootstrapperIcon.IconEarly2015: return Properties.Resources.IconEarly2015_png; + case BootstrapperIcon.IconLate2015: return Properties.Resources.IconLate2015_png; + case BootstrapperIcon.Icon2017: return Properties.Resources.Icon2017_png; + case BootstrapperIcon.Icon2019: return Properties.Resources.Icon2019_png; + case BootstrapperIcon.IconBloxstrap: default: return Properties.Resources.IconBloxstrap_png; + } + } + } +} diff --git a/Bloxstrap/Helpers/Protocol.cs b/Bloxstrap/Helpers/Protocol.cs new file mode 100644 index 00000000..a64a9f9c --- /dev/null +++ b/Bloxstrap/Helpers/Protocol.cs @@ -0,0 +1,81 @@ +using System.Web; +using Microsoft.Win32; + +namespace Bloxstrap.Helpers +{ + public class Protocol + { + // map uri keys to command line args + private static readonly IReadOnlyDictionary UriKeyArgMap = new Dictionary() + { + // excluding roblox-player, browsertrackerid and channel + { "launchmode", "--" }, + { "gameinfo", "-t " }, + { "placelauncherurl", "-j "}, + // { "launchtime", "--launchtime=" }, we'll set this when launching the game client + { "robloxLocale", "--rloc " }, + { "gameLocale", "--gloc " }, + }; + + public static string Parse(string protocol) + { + string[] keyvalPair; + string key; + string val; + string commandLine = ""; + + foreach (var parameter in protocol.Split('+')) + { + if (!parameter.Contains(':')) + continue; + + keyvalPair = parameter.Split(':'); + key = keyvalPair[0]; + val = keyvalPair[1]; + + if (!UriKeyArgMap.ContainsKey(key)) + continue; + + if (key == "placelauncherurl") + val = HttpUtility.UrlDecode(val).Replace("browserTrackerId", "lol"); + + commandLine += UriKeyArgMap[key] + val + " "; + } + + return commandLine; + } + + public static void Register(string key, string name, string handler) + { + string handlerArgs = $"\"{handler}\" %1"; + RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}"); + RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon"); + RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command"); + + if (uriKey.GetValue("") is null) + { + uriKey.SetValue("", $"URL: {name} Protocol"); + uriKey.SetValue("URL Protocol", ""); + } + + if ((string?)uriCommandKey.GetValue("") != handlerArgs) + { + uriIconKey.SetValue("", handler); + uriCommandKey.SetValue("", handlerArgs); + } + + uriKey.Close(); + uriIconKey.Close(); + uriCommandKey.Close(); + } + + public static void Unregister(string key) + { + try + { + Registry.CurrentUser.DeleteSubKey($@"Software\Classes\{key}"); + } + catch (Exception) { } + } + } +} diff --git a/Bloxstrap/Helpers/RSMM/ChangeEvent.cs b/Bloxstrap/Helpers/RSMM/ChangeEvent.cs new file mode 100644 index 00000000..2a6d2e0f --- /dev/null +++ b/Bloxstrap/Helpers/RSMM/ChangeEvent.cs @@ -0,0 +1,16 @@ +// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Events/ChangeEvent.cs + +namespace Bloxstrap.Helpers.RSMM +{ + public delegate void ChangeEventHandler(object sender, ChangeEventArgs e); + + public class ChangeEventArgs + { + public T Value { get; } + + public ChangeEventArgs(T value) + { + Value = value; + } + } +} diff --git a/Bloxstrap/Helpers/RSMM/FileManifest.cs b/Bloxstrap/Helpers/RSMM/FileManifest.cs new file mode 100644 index 00000000..4905a4fd --- /dev/null +++ b/Bloxstrap/Helpers/RSMM/FileManifest.cs @@ -0,0 +1,62 @@ +// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Bootstrapper/FileManifest.cs + +using System.Runtime.Serialization; + +namespace Bloxstrap.Helpers.RSMM +{ + [Serializable] + internal class FileManifest : Dictionary + { + public string RawData { get; set; } + + protected FileManifest(SerializationInfo info, StreamingContext context) + : base(info, context) { } + + private FileManifest(string data, bool remapExtraContent = false) + { + using (var reader = new StringReader(data)) + { + bool eof = false; + + var readLine = new Func(() => + { + string line = reader.ReadLine(); + + if (line == null) + eof = true; + + return line; + }); + + while (!eof) + { + string path = readLine(); + string signature = readLine(); + + if (eof) + break; + else if (remapExtraContent && path.StartsWith("ExtraContent", Program.StringFormat)) + path = path.Replace("ExtraContent", "content"); + + Add(path, signature); + } + } + + RawData = data; + } + + public static async Task Get(string versionGuid, bool remapExtraContent = false) + { + string fileManifestUrl = $"{Program.BaseUrlSetup}/{versionGuid}-rbxManifest.txt"; + string fileManifestData; + + using (HttpClient http = new()) + { + var download = http.GetStringAsync(fileManifestUrl); + fileManifestData = await download.ConfigureAwait(false); + } + + return new FileManifest(fileManifestData, remapExtraContent); + } + } +} diff --git a/Bloxstrap/Helpers/RSMM/Package.cs b/Bloxstrap/Helpers/RSMM/Package.cs new file mode 100644 index 00000000..9df66c06 --- /dev/null +++ b/Bloxstrap/Helpers/RSMM/Package.cs @@ -0,0 +1,22 @@ +// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Utility/Package.cs + +namespace Bloxstrap.Helpers.RSMM +{ + internal class Package + { + public string Name { get; set; } + public string Signature { get; set; } + public int PackedSize { get; set; } + public int Size { get; set; } + + public bool Exists { get; set; } + public bool ShouldInstall { get; set; } + + internal byte[] Data { get; set; } + + public override string ToString() + { + return $"[{Signature}] {Name}"; + } + } +} diff --git a/Bloxstrap/Helpers/RSMM/PackageManifest.cs b/Bloxstrap/Helpers/RSMM/PackageManifest.cs new file mode 100644 index 00000000..b7708520 --- /dev/null +++ b/Bloxstrap/Helpers/RSMM/PackageManifest.cs @@ -0,0 +1,85 @@ +// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Bootstrapper/PackageManifest.cs + + +namespace Bloxstrap.Helpers.RSMM +{ + internal class PackageManifest : List + { + public string RawData { get; private set; } + + private PackageManifest(string data) + { + using (var reader = new StringReader(data)) + { + string version = reader.ReadLine(); + + if (version != "v0") + { + string errorMsg = $"Unexpected package manifest version: {version} (expected v0!)\n" + + "Please contact MaximumADHD if you see this error."; + + throw new NotSupportedException(errorMsg); + } + + bool eof = false; + + var readLine = new Func(() => + { + string line = reader.ReadLine(); + + if (line == null) + eof = true; + + return line; + }); + + while (!eof) + { + string fileName = readLine(); + string signature = readLine(); + + string rawPackedSize = readLine(); + string rawSize = readLine(); + + if (eof) + break; + + if (!int.TryParse(rawPackedSize, out int packedSize)) + break; + + if (!int.TryParse(rawSize, out int size)) + break; + + if (fileName == "RobloxPlayerLauncher.exe") + break; + + var package = new Package() + { + Name = fileName, + Signature = signature, + PackedSize = packedSize, + Size = size + }; + + Add(package); + } + } + + RawData = data; + } + + public static async Task Get(string versionGuid) + { + string pkgManifestUrl = $"{Program.BaseUrlSetup}/{versionGuid}-rbxPkgManifest.txt"; + string pkgManifestData; + + using (HttpClient http = new()) + { + var getData = http.GetStringAsync(pkgManifestUrl); + pkgManifestData = await getData.ConfigureAwait(false); + } + + return new PackageManifest(pkgManifestData); + } + } +} diff --git a/Bloxstrap/Helpers/RSMM/SystemEvent.cs b/Bloxstrap/Helpers/RSMM/SystemEvent.cs new file mode 100644 index 00000000..ce0a1914 --- /dev/null +++ b/Bloxstrap/Helpers/RSMM/SystemEvent.cs @@ -0,0 +1,39 @@ +// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Utility/SystemEvent.cs + +namespace Bloxstrap.Helpers.RSMM +{ + public class SystemEvent : EventWaitHandle + { + public string Name { get; private set; } + + public SystemEvent(string name, bool init = false, EventResetMode mode = EventResetMode.AutoReset) : base(init, mode, name) + { + if (init) + Reset(); + else + Set(); + + Name = name; + } + + public override string ToString() + { + return Name; + } + + public Task WaitForEvent() + { + return Task.Run(WaitOne); + } + + public Task WaitForEvent(TimeSpan timeout, bool exitContext = false) + { + return Task.Run(() => WaitOne(timeout, exitContext)); + } + + public Task WaitForEvent(int millisecondsTimeout, bool exitContext = false) + { + return Task.Run(() => WaitOne(millisecondsTimeout, exitContext)); + } + } +} diff --git a/Bloxstrap/Program.cs b/Bloxstrap/Program.cs new file mode 100644 index 00000000..b293e03c --- /dev/null +++ b/Bloxstrap/Program.cs @@ -0,0 +1,115 @@ +using Microsoft.Win32; +using System.Diagnostics; +using Bloxstrap.Helpers; + +namespace Bloxstrap +{ + internal static class Program + { + public const StringComparison StringFormat = StringComparison.InvariantCulture; + + // ideally for the application website, i would prefer something other than my own hosted website? + // i don't really have many other options though - github doesn't make much sense for something like this + + public const string ProjectName = "Bloxstrap"; + public const string ProjectRepository = "pizzaboxer/bloxstrap"; + public const string BaseUrlApplication = "https://bloxstrap.pizzaboxer.xyz"; + public const string BaseUrlSetup = "https://s3.amazonaws.com/setup.roblox.com"; + + public static string? BaseDirectory; + public static string LocalAppData { get; private set; } + public static string FilePath { get; private set; } + public static bool IsFirstRun { get; private set; } = false; + + public static SettingsFormat Settings; + public static SettingsManager SettingsManager = new(); + + public static void Exit() + { + SettingsManager.Save(); + Environment.Exit(0); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + + // ensure only one is running + Process[] processes = Process.GetProcessesByName(ProjectName); + if (processes.Length > 1) + return; + + // Task.Run(() => Updater.CheckForUpdates()).Wait(); + // return; + + LocalAppData = Environment.GetEnvironmentVariable("localappdata"); + + RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"); + + if (registryKey is null) + { + IsFirstRun = true; + Settings = SettingsManager.Settings; + Application.Run(new Dialogs.Preferences()); + } + else + { + BaseDirectory = (string?)registryKey.GetValue("InstallLocation"); + registryKey.Close(); + } + + // selection dialog was closed + // (this doesnt account for the registry value not existing but thats basically never gonna happen) + if (BaseDirectory is null) + return; + + SettingsManager.SaveLocation = Path.Combine(BaseDirectory, "Settings.json"); + FilePath = Path.Combine(BaseDirectory, $"{ProjectName}.exe"); + + // we shouldn't save settings on the first run until the first installation is finished, + // just in case the user decides to cancel the install + if (!IsFirstRun) + { + Settings = SettingsManager.Settings; + SettingsManager.ShouldSave = true; + } + + string commandLine = ""; + + if (args.Length > 0) + { + if (args[0] == "-preferences") + { + Application.Run(new Dialogs.Preferences()); + } + else if (args[0].StartsWith("roblox-player:")) + { + commandLine = Protocol.Parse(args[0]); + } + else if (args[0].StartsWith("roblox:")) + { + commandLine = $"--app --deeplink {args[0]}"; + } + else + { + commandLine = String.Join(" ", args); + } + } + else + { + commandLine = "--app"; + } + + if (!String.IsNullOrEmpty(commandLine)) + new Bootstrapper(Settings.BootstrapperStyle, commandLine); + + SettingsManager.Save(); + } + } +} diff --git a/Bloxstrap/Properties/Resources.Designer.cs b/Bloxstrap/Properties/Resources.Designer.cs new file mode 100644 index 00000000..9c57c694 --- /dev/null +++ b/Bloxstrap/Properties/Resources.Designer.cs @@ -0,0 +1,223 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Bloxstrap.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bloxstrap.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CancelButton { + get { + object obj = ResourceManager.GetObject("CancelButton", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CancelButtonHover { + get { + object obj = ResourceManager.GetObject("CancelButtonHover", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon2009_ico { + get { + object obj = ResourceManager.GetObject("Icon2009_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Icon2009_png { + get { + object obj = ResourceManager.GetObject("Icon2009_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon2011_ico { + get { + object obj = ResourceManager.GetObject("Icon2011_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Icon2011_png { + get { + object obj = ResourceManager.GetObject("Icon2011_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon2017_ico { + get { + object obj = ResourceManager.GetObject("Icon2017_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Icon2017_png { + get { + object obj = ResourceManager.GetObject("Icon2017_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon2019_ico { + get { + object obj = ResourceManager.GetObject("Icon2019_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Icon2019_png { + get { + object obj = ResourceManager.GetObject("Icon2019_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon IconBloxstrap_ico { + get { + object obj = ResourceManager.GetObject("IconBloxstrap_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap IconBloxstrap_png { + get { + object obj = ResourceManager.GetObject("IconBloxstrap_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon IconEarly2015_ico { + get { + object obj = ResourceManager.GetObject("IconEarly2015_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap IconEarly2015_png { + get { + object obj = ResourceManager.GetObject("IconEarly2015_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon IconLate2015_ico { + get { + object obj = ResourceManager.GetObject("IconLate2015_ico", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap IconLate2015_png { + get { + object obj = ResourceManager.GetObject("IconLate2015_png", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Bloxstrap/Properties/Resources.resx b/Bloxstrap/Properties/Resources.resx new file mode 100644 index 00000000..924339dc --- /dev/null +++ b/Bloxstrap/Properties/Resources.resx @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\CancelButton.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CancelButtonHover.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2009-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2009-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2011-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2011-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2017-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2017-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2019-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon2019-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconBloxstrap-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconBloxstrap-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconEarly2015-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconEarly2015-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconLate2015-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\IconLate2015-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Bloxstrap/Properties/Settings.Designer.cs b/Bloxstrap/Properties/Settings.Designer.cs new file mode 100644 index 00000000..0e376d22 --- /dev/null +++ b/Bloxstrap/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Bloxstrap.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/Bloxstrap/Properties/Settings.settings b/Bloxstrap/Properties/Settings.settings new file mode 100644 index 00000000..049245f4 --- /dev/null +++ b/Bloxstrap/Properties/Settings.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/Bloxstrap/Resources/CancelButton.png b/Bloxstrap/Resources/CancelButton.png new file mode 100644 index 0000000000000000000000000000000000000000..966103bffc2c603505d9fd9baca2e57141ce9754 GIT binary patch literal 768 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 ALjV8( literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/Icon2011-ico.ico b/Bloxstrap/Resources/Icon2011-ico.ico new file mode 100644 index 0000000000000000000000000000000000000000..486488f3a6973be6b8a6e8427960d18b2deaa8a1 GIT binary patch literal 5182 zcmeH~dt8m#9>$kkx-c%KsYv&GDwIKEMwrQAT!z6ohv+zsnDYrmD7mCuLM~IxAg%JczunIQZ=C5A7~O=nr7@NkZ88DKYcnJr5zYjo&`Lx(HI! zmRiG;h46nQ#5xGElf;pIWH;GJ0?LFKk;2i>{$u*mx}y-zi-qvLFT@VUKTjTzGEz#4 zNj^y@m&nb=^A9}xJru$y^h1tehri1!^NbQhXA#PAd=J|W#OTOaR{L!()5}2B7W_up&?ISQ@TsFpz*@p?^ z6Vcl>4mOqvurNIWOVg9EHam?T-E;79zo!_`uNs~Mt1)tD3Az}rsp0%Ed0k)MT{-UK z)H}%Wt%0`IOy)QYRu((Z(>V=;KPt!I0Xcl`g)he?V)Uqkc>cT=@~EwSfzr|n6ciLA zBjXkhBwfZQgLc8NQz?4bgZ;S2{)ccbbs6!xc5CKiLS9Ra2$&derq+IH#=_1f79aMD z!-&t4Fz$;4I6ItznMn-v^a3#E^Fv5XysFSv_mP}@9p>H6qPGj+)*}m=ZC0}1U*>Zf zw0m9Od5I9)U}qPFK>rsoGkV5%dkj~vmf*>gYKCt>QBgVixFw-=D}VIuvlY{Peneee zy*gsU&~O48O1{6z}-Cxb7lhVHH)w#_L55M z?b~uV_MOnyo`sM5??Fn+EtTg@7fDH1U~Qg`ey)}1)^$7e0*IWux#S-CJ3U0_6|a&$ zCOza9KJ|>o*FHd}w%2gv$hD@BXlQ7_$bAMXo&y8}nSaxmmR7l9&`%FCn z{mv2i&^;E(CkmUTpFXX@>{)4Wvde|3v3!S8L*`u~=eyL7Y-AD7O;|O!qyAte@^Hv}7u^st}XC64BlMFl?-17+>xk!K8v5A&v```QO8KE$2-rR)pul z)bJVR1!nx3^#66grsf64ez6aIdKICE(?05i5INWO6PbVgfkH&HT~A+$#;WCoTziiY zx%MX&o%FQ3h}(M|7cZ31{R!4R&$u6*y0 zg{XqA-nUp0_7HmQ?qS!i9FN~fAh%{czgeVg~4al$u{51&b|wC<1F;ymn#iNTv5ZeysF98MC_j1y)C7U|DDyHmpB~ z-Me#kPQM$a*T3+$IxmuqIaXOT=ViaL_4yz*=SC$7%H5x7Yu6 za~Aic>zw!7s4H_H%(~C;)0bm@wbglm@3g&$+E9kYAvdsfOBVOM%Q$?ffVJFR6kPim z#q{CI%IECcZ&~~G_><=`=Kdzyw2|v>dA$%;(_U3nZOgsbdWx>@u-&${KACQA%W6D4 zHelq)?HE6P8zxPPfv;~Y=FZ)N@bH6(ib}!Go&P~Xd@fF&yon3vAK=FIGS&$dxOcCD zzK{C2NRk_o zeTyYaD_CE%u5z9)L|b#!7=P52jw$yI&S485_K7<7^fB3%wT!~gSx>$@-eurj2Hs`h H%^CO~Vj8P2 literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/Icon2011-png.png b/Bloxstrap/Resources/Icon2011-png.png new file mode 100644 index 0000000000000000000000000000000000000000..0b1d0f34950cd43b9f2581d49b1aee4d2f94e1a2 GIT binary patch literal 20362 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 z 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 literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/Icon2019-png.png b/Bloxstrap/Resources/Icon2019-png.png new file mode 100644 index 0000000000000000000000000000000000000000..99cf28819514c0202a5cff51a7c62b0bb6c9b842 GIT binary patch literal 13984 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 S8Tl77Kvhvwp+e3g{Qm<@@VD~- literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/IconBloxstrap-ico.ico b/Bloxstrap/Resources/IconBloxstrap-ico.ico new file mode 100644 index 0000000000000000000000000000000000000000..3d75f694874a4dc66bba2908286a3763c23f3b56 GIT binary patch literal 4286 zcmc&&XHZmG7Cy-Ay3TH`Zdv7zshYxBYgfim#~e_C2qIAgNuz>*ptNK`ksO=cNE(#^ z6ig^uCL9^mp%F|#+XM+sXlQ~!+X7-D*>}%veJwDc)c%?7`ndh>d-s0#ob#P??*l*q zzw_q<{R=sB0OkRJG6K6m13~*`AG|mB?;r4?1Aq|nr=vDD89E}5LZyEgREEbuonI)_ z#w0*>KseL}vZ0#g1r0u-(3YGA_k-C`;Tr}o%C2E8$-%N;_5w@CSz<4*mCM_N6lTxAnXo?(P;;%wm zt0Fm2e$XGPLu0A^|G4KH{B^OX&rI=7$K1DSMq;uOd6sDsvI5&RI)rRAAVk&h6R>YY zW7}Y3TIQ^V3%SsA>Wmx{`744VfER~xh>U|L$I^h0{PiAw%iFCS+$F}=7lu_0CEezG zIamCGX7sK^PHN+Z34CL@>2JI*{ZWHRqL1o#7ugn=Ip>$T!*bVl~oh>l`c0I1L@~ zDRMCUjV_0+2X?Wt2DDA<-c$ckyOwSuWc3zYzy2p*;=LjC_q`)Q3e5rR@tf@i$^Q(4)0o zK-OS<7@nzG^qchi4dnIDndYyWn&C~!Lm3$UXKc<|=?bm!zMg6>jLDF$Rl|*-aH!EU zOC_r?MpHe@^wT^<`}+2IrU-B5LwnXa#94XW>pY$Fx8cnuZ)G}T#!S_s9!AlgN1lFA zw}-V(rjeAN3{EDYcW~q*|f(DAwwb(N$Wcf2(<2Lu~Twnv`Flo|7u)Z@ZBX{a!%_rHeLx z8qdr#u~kRSEnH6|mJU~M9zOvRO{11~sz%R7)C@<73Tl_;%*VWWr@39+rn&8$zC@@*nFUQ0LXlOgRLt*zu^t;H^+PmD$dhuQF0LXJW2nG8+;0f#C*VXF!t{qN3 zw&E+l;kaBme|u^=v?m;!F{EkI_t|*T+VJ1?r-@^-@|#aO{YQO@Ct{7eocO<@{Y(mqeGZ!Aon_ zg>f37eAK73u2bOecgr?)K0O?`FTe0ElwQ6HJ$bjGA-@0~oWBTVHms#0-J#4QA|Uzij5&)N@MwB}-Md858f(zVqh zzs}V9K#Ap^Z|+*zK_1$8Z{!_R+$)B5oJS<62jPvIFtlm=d$I^MOf@%E9G7a4Ru5mB z(o@-T{_du0`8t<45&`8t--VNNdddRnE_Qd>{A~N>!%02m5ZI+6s2qEUNw!q2xBV6b=Gl`{>{|ix}@V!mv{^+QA1ts zzENZWYJkRY?A|w-O)IrY!wze9QD7JhYnye*);Za8*6vC1R z(4Kkbdnv6S)7jVXIq?<~{*9r zCi2s>2j+Lxez)%)_yo+9QlEV(W^@j!A9`;5c=oTsGbsb!DXc}#hxSg7ALqieXOFFG z*zF@B(6>&fOIEMOtrNLu-cUW!TC{8vp=acG+NLcGe-G|Q47yI9fv)JpZ%3&upF0aN z99Yk~HH~?{1+igZr_(Po4KREwa9>=E^$2rgNW=JLudYSWvpuZP7I$Bb%FyUp4F8L} zTwox%cOS&X4`6t^P4l#TfNOlEqAM*7kA@D`A!G90YT14~q-yxK*TnjFv7KAA(8WjZ ziC^fPhgdhO;*&rSmjaa$vGV*Ep1BBeoXv&0yaIS(BaPRICe2M^1VtkVlhv8M7O zy?hI@%9BWtd#o=_jG)FM<5Z*Y@l2>rqq^6_1)6yH}GNViueprJ#6mXJ26rVb6{_dYMU>uI0glFm5 z&~NFGFV;1`+`8Y>x-p2YTpJz_O&MpPJT4KMq7$J8-$SbLovAe_61ezoRetHp|HaSf z@K0)ywpE=-L7lXM5&=-^{|c0Xu??O9v&Ud2R{t%~@ajSvMZc0sUW mKmMzsA2ehb#C8y`2VuSpqcC4-6corPQj97PB2_|K{QM7{O0h=( literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/IconBloxstrap-png.png b/Bloxstrap/Resources/IconBloxstrap-png.png new file mode 100644 index 0000000000000000000000000000000000000000..ed9c2a76c3b399c55f9e393a78eddc2fa3a8a912 GIT binary patch literal 16792 zcmcJ%WmH^2vo4IgdypUjf)DQQ?(Q}?4DK2%xVw9BcXtTx4oPr#w~xGb?|0X^XPrNH z)@-kS>glTL?%ligOqim)1Tq3X0vH$=vXrE#^2gTy?*a$&vF_(L8vEG5+e>OWfq@}n z{$0Sq(lhbEz+ga@s+!K4aSHRug$iy1xOkxZ)x3uFU zJ8$nMBe68)Bhz4$W0bQO0a{o}dO8ACJmpnQJgrT*OvwcJNqF74KLBij&PF8ewl;Q7 z-0pm2|G?$`*#CPPKt}Qp5oc>YGNHc;Ni^jYNkl-7KoT|vc6t*gRyGn2E(Ruc7FI4U zIud3^CQbk&CxD5Co{62Cm64m7ndDy|vJY*Jre@sAqT>J3^>M^UX5s8?&kX>$xw$d8 zu`qxf%>hhYTwDM~W&krY{f7j-lZTzNkvqMe)0clEhytBV94+mgEkSl9e-Vw0K`zdG zWFJibSp{4B|G?Tg{mV=r1_QVo*#npu82?u3--2>-|GlWK?SG`5oW)!}wETR}HACsR;mR z1GN3XcKVQI`UgLIZV^YIku%6q6$G;Rx1$vQt&v1TJ1SecFKjg6Vv=~+2h&FDFqnAqsK%-9%N**IBD zfk4xL@cJk(3Nmr|YmC3;{~5WaAd?S_{}_~;iOI;=1jx=#&th!EMbBx*%1Y14Xl6ps zYQ)UU$jHvZ@^S6oXbO&&AC1Sz=6^^1%gXcvk%f_i-GrITjQ%6Wx#-!LSdHk7jLn$o z*;viYn2eb?KS*(rk^C>cA|M-(qa4We!@4YFB&H_ZW*|pfqmK}>v^6pZ0_^R~{|592 z`bRA!EuB8R=kc#5q5^dI*Pe|f$v@(b+sNc^xbu;j{PpwS+W+&k<$qzl{~6DJmUpuN zet`Zr&i|*F6UfZj&BzfbWd334|4DoS{%`!9j9mYF_Wv{A|G#DbFXNh67}=QvKYCCA z+28sA{`NKhbPeGD^xFUO`oAMb=p&~8`qb%PmJl{F|Bp@h|LZ_C;xaZeV=`r-=VUQs zqBmn=<)r6gVd0=R;$Uau;4)@sV*#4|lZB1tzxn(V7VzKe{|EE`t=If_;g7EP@9uxb zfRBU!j3Pk0kBU2fj1l-~O^sk++*4AbLaOeWr=1>=L`&IQ_gsw{8;x#tt>@)bVHg@= zg;Wlk+~xZ*OfWS>UmXa^UHyCPV>t!UpqL;y1u=MpB5O?uKJ|a*+~#EQNZ{~1x6Gqb z#*zvvl%!u@+H7v$TAdmtcFEXDq6#Y=XSLf6mD{37tfd0!LuxZCOZ)uZG2R*%^!@^lh^B{Q!6 zPM_oZka5GnBjVFEFtk7mf2)w%(HQufZCGkeCoG;IQ((^UxH!vVQ|r%=T~mjBw!$P` z-fC*!qymekcfcB^MC==B(+OYiXtS5Q*_mWoBFs#!DJ9*%C zwW_iW6g)2B_b8jmcIjl;Bkgb3**$THjatr|IiD}rkZx^F?T{)qQJHSH3VAOWJhnZ^ zxF#jcGi+8`V8-VLqRwkQkG15x4Ma!5EF&3z5eB4 z?uI$%V~LU(?+jg?YpDl#SAUz&*V9odle<2OqJ1k*emwT8UdL23+g~J{5j%5V>3q%G zE4_E*S{=;x2NRl||IH)KtL}{6vv~S-F|GSxym4?zcKWy*V9o+S=1~+=irS`HLu$$YeI`6qmrF;C$#YQudY+w2nS?>?TgQ* z+)O>yxAz+6j_o$MK~H~jEz?C+@Wn~NYPkNDnS zCB>m;b=-;jF>z@eS;hlsv;zXBm4ERF^CGpuU5wuicX!Z0zebRMjb^=4+j@e&o*d zm7f-iLcbu27|~g8pNDT)qaXsTHU3SMdlZ+AawyXv;tP0T&N&Vk1*_l&iyt%sQKBFu z9WouP;x9Z8WkbhREw5K?OWR4A_wW>h(EZh%C?ti>oRK_^XX#O?iOjB(Jx8nv9NXFR zrikZUYjU&n?U-&z%ssnj-PVpL>?xM|G+$!VwQmpWQ_O5)O9syT`QX`**@)7Ig_mGX z{24<-opQ-tN7V|6-gZ$o+_tm#XG!a)s3zn6A=q1!Db~w*B$kI%N&I*FKC9DW{!h+t zwQj#W8QDhdU83dW&uTePX|{tHd)Rwi>m(3NLXQ?2*N_8Gmq74@=9bEI?D8=RHlF5} zf=^u>oF4Oa>ay4};jJ}Mdx6E4-HtlqxZuL?mw>qFg4E9woktvwo?UP&-oCRkwSChn zS4CLC0jj`6)uDCO_sPXb##DzO=zoJ;fuvm-cH+ zmHXf<`^&~kM&n*Et@YND>3jQ9byN<*+M*H%F1yXraFcF4r?3UQaA3D2j|47w=GFKI z{1Udb#&{&aSBnF?n~owHLrVYxIk<%-2HT&tPNXOypStE6MLJNtATcMMil~kcWut@i zLf3qexmcj#`RP4|q8#wzP+*Jp6bnOZZyQi-=|_r^qz5Hw$-6j21OVQcwb?j&58_Oz zzyZnJuUH>mXbR3ut}&z~X5u8VQ`PT+6-Nq1(dIb#x^BE8`0D|_>b&>E3!fqadI@Dx z?u^r5>YezDRY+5&R#E1#YO*g9_ zjygS$Q8^*|jor@SkAa5x(zM+tQK7grE#kQ{r&Hk@0kyBq)gVBW{uBT?8c+OEw`+O8 zbuE<=&x~@B!TAIG@?yZmS`)1G+f?YV`k4Zdvd5aLu3$k}pPP+DjPh5@E!SbDfL;WB zP-UFBn06NgX@kzv-TTB|#jczjrGP7-Q<7m{_mr65twNu`w_i`6r}Brd^4S6tQh?@OC@AXup{pso%^l7YWc#zOKVI(qC(i%|l!9DgM^=TOnpS zFejefW_;(9=@oC^ObU_oZ;b~iKQyF7eMJc&jvf8Xcni%oBU3(N@H?$Z%S0?gBJ@4mrWRbIcpjSQzCoHb_z$;141Yq!*eW8{;Civ8auF)#OkeYOb%lu@$B(BCiAb-M!gg@xXJ``5bRW6E;+=9@Gy{D z7Y>%qi?yEG8_#xPmBDM9Dp`s~Yd~V9&*Q!d^l5zwta`uCNpeH&TXAzaH|GhJo~d=r z?{V!N0t2^1sNB1vSkUs-h-&tAd-*Ao`15e#?f1IZg!J!IaqFXs!-gp=8O1ZXnW#ka zr>py|TXNy&y&;l0MX0VyjjxDaawSR+kx8|3(THX9a_sDege zi{m=fcjk)poInUYmD~WMLAazZ$&6gIOOerc6_KWEaU=zPvg`em%Ymic zeQqbqWapX^t#3~Wv9r#-%^iPp+(8W2;n+5(#rBHZrL&1U_hodkV1L2n06me{7V1h< zCBia}2dTS^(lU>Jkv%(Jypsu#6g(uMPgJxJ{>2ILdkD@len^B>ggxh(bJ_ds?a#Dnn{P(3fLIsWbj96?q;X9J-u(7V8ZV(K`-AWXm$-#6sie{@R9tQb zLO}H4@^@9VplO@RWqhe7{fCAtVq2fPV9cJgN1S%J>Gr#fLFj{Y!R8~`j`makSneNC z6m_pQM)}*hG<9}D9K~_|XPtD{jRD#`W z4#iqy12WLp{iPd^L0L2<_%EXDrkIiQ3;)1$xhk><{s46q z@oIgKcBd2sKD~DZ++?NI=G`422fCII>AsrSNGO#a$PnBjXE^J4dyNU?ES0W10SsJZ zq;n~z*S^fqzkj`dE3*#TW0)kj?rOczoiFZ>88SWEKPe&RCkx+%g)P(Bb zsHrD(QGV|bGO{zDFdi~xwI@#^lu$Spep8)>vprYt+LD}YsXFjl;E#jE-ApMayv%-JpexY)r!}E+y7{s~2+4^r1UUO+M`L~; zCVR$K@W=BIBb})vd~{{4(6l9ZRgLT z;PiOo2`Y1NWP|a)UuubT40=JW^RMt`R=8Bl@rSK_4BegjP6+TM5;0 zkt;aMU(Q&xn&}Pq0ws(zcfvxS)K6 z(e5$Ec*BTQ(k}EDSXa;+&)Dn*h{^?*zvWHTpS!H2mbj<@@cd%e@$w33m4uh~L}F+FX{_LGK*n6d_=QTwLP+Pyts8#yCg3A^%!HI|q@ zu-qWQ3}g6&2rdKw-F!F2z^ZR7&{31WTpruivUAkT)$OT~$1@ZS3X2YZ<&`H{T6?Qe z9oLBGj480(xuQT~p@4hA#`X3RFFuc`fUHXhw^!CwwT-QdS|p*R4n zFq9IGYa5_k$ro1=Gz^;(m#)Q%3AQ7-r)OQ?i~cD0&*QR1f$vMiD0cTgH8?*=uEm@) zWrIlMoE?L|=e=cbm+1{ic3C8yPNEMyhMrBtANjB2|Y@$t6KEFt{YkP*6t0 z$Pwvzthb$9UXD6A&kZ+*14f;Lf&6l$eh5L5;8C1Gy<$nn`bIkm;)5bTA01lC&kxB2 z3%FnGeW=kyBNbYn6O|97UB3xvF5~y0D6?WI9<<(tEI9>oHY|6Zb5P-?7;}RQyeGI% zNf1j)@0OT7x$(wA3nTo2ljb{W(W{!Nc)ZUF-T3``uOA3M#~Ms&VwJGzn-?*e>V^_8 ztTRoHX6i{ZW-I#ISuVf22ct}1q+N;`l4GQa?rXj4E8@qUsnwS2JY7GkMN`%d>l$Cywy+9Nonmabx65%YAAP8;=x10S$-9DYER40hPlF9ui zp%7j9iBG71IT=7V{`}5eNX_Z`GrrLTs7?=XC|rB|lLZ6Tc|~BlNM~n!Q5PZuk9MuV9IT z7%9Iu1quBuTDxC7)8EVtG2kF*3dq5k^Cg$o4L0VyW?4yS%8T5siE`!=Oj$ZNQ9SYC zWb>48tp}o$_0If)8 zR@2{krXmt49Rn^7IZ-fgnuqlON|rjna~_K>%aTMi5(8cmwkV9Q$xc57z=kC9SdgHx zbs+$;#D4hcLX!gLcN;h^C9JEMzZ4GCRborC^N;F_E9$l?A}$Z!qJ@igEWz%}HMi<^ zxQlkb*WU5oXcHAs)J1~8Y?ye;pIhoa#(X7?+6bHwyq4d$>upAd#P4}|O-$?eo8o?F zt;A{>Nqi2vs~O^BA=^-_a=FR@b`aX(%J|yDp@Ol%2hWD)*h&Uz|9E^$nrSvg?MOv# zVWIYOLR^7V%P%$RZa^kgt-ngxdr8qKBXMQdRE z!x&q~#!&9d$2`2sI)B`sRSU+7%|`|hJJl)NqRXUL{!Lp3pJzZSnriX#>PT4L4RFBs z9=-T5mOZYP?SwEVU?G3Hid%e(J69->BaS2OpLe|Cn#X5)u&}QWAxsKYsN6qa68oFP zc5C3$SXSoijf-c9T}PwKFr9C|k=m-vd6D_ZE(NXtyxZhNi+mLv)oE$Cjj;b#NYPkp z%Y7^l4Ny4Uo6Dme4ow2p40*mWh-!SjAZU{;G9xknl}_u`Y5mgWz*V-0Uy7j_s^11< z7y%+h)RSCFVQt*Nr@9*8Q?19-pRLCMO9^=0JhT2neCDz-dmlJ-WuCyg@NHS_2OVF< z-S>p*jcLu+JWtEppATEl58K*& zPUs;EW;d%Bc_YmuxOK0IC8Lm%g5_B?&RWigojhOK%>=BE$s!u=#_|s?jHN8`uTCCbTw3!d${~q&wh$%LdVvZhV68L(R-h# zgtytf_p+7e`URsJ_ZWmw-$FQ)hlvH5bT*=*QaT9nf-idGs6SuQgvOFzCS}mHg*z#i zTSmF>R`aed*Hk2;&)th&{IcH$t!gcHM~T|B@A&%l9NW6zxwPdTZUQ;D9w(t~IM9q- zE_)rj1;eRiwjC90`Zp}Pf~DY!deCazfyet32iyyLuj;q$z5#k7zPsZ`ut(%MOP^Zx zR(&t~Z~WL#7iW%~&&)RkpCc2j*+iFz8t7T&@)}^$>uN+%Pj}2mENCCaG%mDhHD^&x zngRYax#Z-va`-9J<1$Nh2umT!Id2;M5lK!*sf{k9s~KLa3lrV!T$J}TC;Zqcf&slXs@Y=YBGwof& z8_0n z(f!@P%#BQ+#T$v8jW?-0(idr#g_1l(XFhSuSaQloZDd|e}7yT^z0=`|kJB?2}dwQoNKVkDk;c$6SbEk2BcraS@ zUR({q78PM8_;v=FZJmR;FIg>}Vsbv;s-r@SqRT2YqqG(IBCQEh;8&(^Xk;BeZX8}7 zxY1jqR^j@nXSI9IllhGkXL?D5Eh4W;F(L(W1~Hayr;Vj$+F?*6pxGAF$J99=c)WB- z;WeUJp`}?3#6=<5A_W#R%XBk#I(3iiF8C27n`wy8tXDO>5qO!f)o~yo0G=xOjniTa zYi!gmW%PcGcmFM{BLPmX_EMh5pK86~AJ;O*+zXKd5gXRdAY zy|-E(xgn4M5UIEFB28oUOyk7og=uM8J_SK8lrCt{x?gBQ{va*#jIY$zt}h6Bw&r1! z6~mB8cN)7}@!TV$XPvIV+lbS0+$$6*p_KVJJ4S1P$n9S1Z)$EGa$t>hwlVH*i;*jl|r|cz1LClp~XUrNx z))9ZF6mZV`QM;X{skcm@4-94So_{|sUy>&xz(Z3t*VfDyoHN&|?TV`UG*WeKcNfnp z7fVhp1m+N1pm#1N?dN%jaQTF|hFERT%~y9h;ivH=igT4!j%?1YK1*03zCsGD%&UIKYIIh`WNPdblvq)0(IBw1fj1F*d# z4u^@Ww@hBDfR%w478uVUyfX>V@p*m`^qmvPO6B5iaPgvR7HCfsrxlkLtF^iKbCxLZ z@-=(~J>VAm@VH32mN1xMZ29pc*CH^O#`0R-fX`6Wd-Hkjs2Lf$5RMfMKUvED@u-NS zgqBIk{T*&^>tTaBS{QxW3-`(sm!b-nodiF2`eN2$sZn zn$h;uhw8*GV8iZ+hXp`5UWAXU;=#CSj7bfC0uB@PTsyxM3m%XvS&$U)wq1jJ-XOG8 zZ&Qvj2FvqwgewA}*e76GW?_2Cg^jh@KSLB@C1*e0Sf?MQ<4KoJ;=;+HOHBXZwD266 zj+LwIijR?B#A~&DlXMf@y!3dvlge#?%EOX|PnR29MedboH}Gy;uu3QTubUU^a7cv+)6x6?&01%jA){nNP&K8yb;ZVsGmKbRbXS&_`QTQxf|(p z^<=`dICY_}&T}71UhXETwtTeI%TI}zcdiHeYebaX>xY@@jn1=4 zG-jV$TAnd%V>^AOCRv4!>22RYvVy}7*-BF!(}l2UF=`~fXAEHxnoyEH^xnmD4r}kN z8Z)NBr}B{FoQs>rP{cN`pGO(owmV{G#s=|qURd5cB@ zI)l>dnN+!SZoEap9G{9<=Ic~7nm$&cxkw(iU^6dF`OdX_uZiNoeMwQ8SmAY#-YxmM z?l(N>64T!y6?nehvswlAl5BEyYqOz*5}tpf{hn zKxOTFBVk0!z~7VvVnk=U0^J=Kq3B5(v!|d++^#KafW4J&aozQgb z?}>5VjU9Y(<%CC(XA6KNaA)yz+%$1T5&}N9V*}>aY3iw92t>h z$?x5q<0uqI&?3~XE$*5KC56=VW!bGJvN-MGl(4Nq(&hbX6?xFlVlU>c|MIeKZmS$L zS25PTnCh`-h*ty{%2SCq0$7mn#HT7!YbS@N1ByK#&0>jYt-9QB(qo2e9_#}VikMWJrobJYf$<;@BnwlXN&j)Sdc%0?~_Rr^_O~+b})Thb~TtLpIE#72|PG!{8pp*{g4!UWQ=x6g%QVMdT zLM56;o$^o(-vepPW775Ib~r_AU#S@?=stv2kNyo}F}{|zAm@PP5wWHkGK~1?{KUDn zBIO(XHR8br*>yrFrKD{MXK~O$A&uGK72)&y9T_=(E@@F8V?#xs#ny`%N?R5BgZ=M5 zAk4F03~Tnl;Ukxk3wCLG-!vWTZwwgf(_;BwYdemUnQA}Z8#)dWa`0Ef--n_VDn1`I zqeO0LUs|Qgo`&ljFE(mZhF4q^sitAy#jym?3e}?=FDel0x7t=LRA66pHC39Q>AIxE z5O;U$w4i+xr!e;Bbsuzq7G_`te9BUld|{_E;PUdGC`+>47*RKT15tIIoTS~#b2)s|*cKO>Ij zX16?!U7l<{3Gn!CJ?D;IU3N43*B`N*jFb?~Zl2y-jBq{(si5soBU4#!kOD=`Q-$Wk zXaPB7L4CKfb{m;`j=S1zx^U41>WFjlH7AMW%$Ft^0l%em8_@+m_d1%F%AMyfw1wLO zgs8F-Q(0@nL}Db9zRw-pq$6XNZ2#PQ&&n2{Mc(u4*gt`7cD)1ScvZ5sd!!H_H8GM) z8Jnp|%0f$Te4z%071blr!7Hb`xB74P4)GXu1|bpKDZ8QzhTLmFeF`x(Yin#?2uBRr zwg1}s2g3iTFdw~Cp5d#57eiKhHXReoUffUoCZ0&U@6~$7Y?+1z;o zr``IlZZ^la>emzr{LKZ`$j{=Q!}%6x^zHK^CT3)KfzfkDic^UzWoJ!6NB*;l`g)1R-GG^#Y-_1dlIN}CkAmI+^FV2dzM z6bdiBrw_i$Ltk6oBkKXpvC_E8L`ks_wSl8nn@fmt9o5{UTd;axy3VI@?_Hpg7?fXT z1oW@0Tqg5CngQPh8DjmpfMC?$zk3VqoBL(H?|&Uz99=j*8ve%Vrpck5(rHX2m-1j6 z#;6P-FmdyC-%&0sEPorN zBK1I=9N`YrxP8O24v}p%>$~&)+c9f-dG_aOr}cFaq3!PUZR~c3;kdqZo1hM~yar|oK?RRbc z@lwS@NxIX(jM%GKciVMQlfl-UU6qH{Pj3T4DVP+w&QF#aKORv~*{KXLaHrRv#ZnlT zFvn$Ki{fh6U5Cdiyr>XaM}?)JbCL|^(3OF$w|t6NO3I4mfK6r})0yPXErI(Ioty=W zMd!OyEs=Kcu2{HE`8-a+QX50<@YXPV5$k>T+n$J*#~i58vc_o|Z2BAdqM1h$%zkA! z!onc&1m&W--B??PADiPMx-H>4d`d(HuhF;{$wq2bcw{{egsgI3;WEgnRf`cHUiYc? zbMn;rg6;NZCMFIhp@dhcEYV?pap+B(8h)BoDzElX6(v@rN|H&xdwR>SJ>{59zFpK2 z80CK^S#1^BYWI5O^798QA{`MRc-%+zjZGYtdAD*LQu*IN=fOSko~eF0d7zM#BXt&f z&u_Jy>%1G9PmH$hPg9WKyfcDOo-z>Vel#cw^C;kDyj9=v8txGGqKqaRvU88rbG;v% z7GEzQ8qeL}g0O^WkYCXXCP&64g|b#%(_(QsZ1&w^O#gYO&xLKhI;z8A*MnUs( z^b|GUK1Bia#+FjanEt?FG;kK=dmGE`S6XdoXV&>ur=fpL!PtSg}?>yd_XH2h>#0WUIrlKEgz>I(Rjk>1?m0IpP z3=M~z-eqj&{VUftW5sJa{v-v_^s!r@j+a^(KK5HzkqXFzPfgnsJDv=3$P;1kJK~pM zpAba-UeN(QQ7OgLbbVh#N1MR_#GLtBx(Ho`aB1Ql)1tcX7D+{MYE-Pw&P9si->_hc z>EdEno9(bsQdfUM7PHB?xURS?$;!7_4J!4IXVpY-$Rlnfv}Q(|wFemye6=G9G?%;@ zrb->t;(36WyH$Wc&Zw~=%40?9A8C+A4P$^vd~VbGP6&H+JET~EZNo-S>m|7-(nZ`Y z|9nY`h!%@M$kEHXQyR9$ZG@+C!X-ADgy$KhzPqGPrWLLT-$psjID-^?1w}?p)_|Ay zr+x1u@9k~H8{rX(9!w71AkUdj%6!;w5B_nlO3ZAm#;01Ie!KNJR*Y@35LvEdau9V{ zx>k}{-f6pbl%9adbi%GYr^Fmn%mMceOoUIuVHC?qJJ76n#+wmoz4eGpu-wB9dW6PM zx>$V7i-1kT?fM$v`X)!$!P3(iOGX-w{N!mSj!<-1ue?`-oKg5lW-Bt2YK^zh+65U6 zna;|+A|SROD`OtYqBQH?BeB3d(^628=BIG~Rz7_ePM4LBnLfkSPW8P=inAUVRb0O} z{`j;tKX5)`yz)*3m-6CgNwvAq`D8g4Zl;SW%OWO*-bOHuWPHSrng_1yvHy{rbqZj_n}iENK9C=t=6^Y2iZ_o~C2W6Q=KlAm_$Stk6%z4}(X}30eq{ zZ-Jn5((k?!Ag_DaKM}+S-;~|_XjrFDPE3kv;-wqJ7ks+dH{LtWpjsxj%7@g?0?F~b z`cb7rleSu7C!rc|0-An2e{}J6P z1Q#7!902Tag!?(Nmw9Kg(#dnv`3=8fZd@)RGVoiSN}{s`hel#XxQK~Lun3vjiKoYo zfNq=dqkwhuXCG!)o}h;PGu{2G@MGL>y)!e7$BtVa>Fa<`_=Sd_zWvFM>o+TnDkoTP zsSA&@_CTOWZQ1DVufaVNr%Xg4bGn~=c{8r5BTJxZP|Ba&Jcs?tZcD9F$@`@8@zT+0 zdb?KN6D&pJ;T`I#+yvEaIW+aXA9WC!6m+mRqh)EA0&PLyPB=JU{}=MOkS&x}7Zdw) zVFv53!GiF7JPF^Srl-|P20B!4?(7c>RCkHrWEk7j*rKS9dmylFn877vesPf1RiChP$B1Goi<&+ zITxX(X-{8yyiS2~Gx&|_&8uTwlhbxAd$JZd5*U#t1||muKtm=EhXtlet>stelc>{S z_!A!Cm*{RA<1MY5Z!l+DTR4SK4V2ZV25zP+brTP^4?DAXPM*QWGiER9mKyZf2a76A zjd5!5fE38x)Im2u=FwNA?v?oyGC`WM71XStnY9yJ)?{UT#jK+o1=Lw6n+VJ1^MmJE zwi>+z?^fAwA(jUZw*fwgoL(C#!g=VdMxzMtFGr^u;=n&~{K_j26K+*)YP|`;bU`$U zk(-hFV&@hwiLM+U`7J@3JWZ@&{#T1qn+5E2H_Y-EO1sH?W$Fsc=i|lnAwI7Y6$ofi zhw(o~+ds^aRV;n4**|6@_~`?66uz`TZ{yqYrh>dNy5k`g7Z}MrR_EQUJT*2l0RuKR<{e*hU zJR=Ki;P~kgwH7#(X0ox}PtFC4R1z`-pPX1?$(l^|i6`r4wKDVG8!DVCzU_$Euv|^q zIo+^=D0RsjJS0awsE3Pwsc_)vu2XPIygGWdL_oZPT6zPczj3V+UrY6Srb%TXk*Mxx zP<$C&{|~gFNqN!aAIc1-To?QoYc&>X$K!1ad`H;~B3BT#sWYJY{uAr3(uBz`G!L*ga9UXHt;L9u$z>GgH@YAC~e50h(qu$i6 z`?!U?nu+l4+n7FMfH+xJDqf&Dlo*0R`Q$oOHI8hR7a^18ooSUD7SvVu{C4tyvx*$y zdIcZmivE?Dg{{j&<3OkH)}4%0I|g^d0hgJ@rq-2+bY;dunFv-F< zzlL@+YNa1@9H~PQ8c-7DYJr@OPXf=5>kIxa=!P#to|Arfgpv8*g-1QP>HNufN=%Qt zqfH{zX;z2LY2(S|zE99vG>KOP5B$!14WUFE8237*Gjvki*I}Q7G^llKGP~NrsHx=cc{}%Ij|F(jI7I56Lm5(TmncF7QkToU5)G@5%#qg0 zD(>Chxvm>}?n;f@Jgr$ba5D?Hu3pJ%9UHbNRl)hgG*`z0UjLDlSpf)VE!s>8vjn zsxK`{%?Emgbt3WU8YyN%B*K!3v01+cf-kR_eZ$A!41P2_-v=f%7E7J%|B&l|ntGEj!=lED3NhT^zTKRJRJqOFy@ES?&*=~6(fGJ$!OPgN(8js- zj3#fO!s%%O?w!-j%A*h>XVEP?3Q2~9k2{GUrc&;&cBw>&VCIDm9MPdAuxIA|=mb?> ze!uvZMJSrCjB_)cmob?%T3F?02VthVj_)EK1{~)c zrr)c!Hc4S&kg`ed;;oNKOl8Ln{N`{18@1;E`_XQbS)y=v!|>#x@V+y3e`0$bA;1Qc zaVWtKl?tzsl?i+i(Q=*pz&xMU>Z{)jZ$n$Zk-VheoTEmZy?~X5Y(sZ zD#$knj!d~AIc*_rJ=clGxwYYt0HxEZtejws*kno>RL_HSEM7;Q)<7>|NX<^2cY9@N zIzC>jNfgiC0;?)H$39I9RQaEn+Ux?7DN;u6!XIgf%P@6P^~`!PV)T>6zSCuBwECU! zm3uo@ZCpt%An^OViy_hG>Z9ULO)gJ)K30RQg@ni@^Wui^`8sFNs})6l{$i?ZGWdgW zw=f;6j1i1~D~%dmIX6eF%aQPQY@t!4lpY0GaP@>eh#P&$Ov(vbv2Lopv{b<5J-w zOvOYsxHW%U&2F6mK9|*>{nQ-9vxN8IM-W%J&hw7h$t%DbsGXWjiQgt*=M!oFeR0W^ zGg`_d9gMd{cPcY;@DuG-RS@l2kp@})>X&+C*YMy zQfprla$d+t23cKXOHzdOhjG6B=zO36PAk3fMwh%&2*E)J08e3}k#6XdCqhE*pgK}> zQcW|9zcEHyhTwevtXNEluTG5Bkd12fr0|Zb0?4?X=|gPImU$kk;&n3gX5K^@HU5M= zjneDfdo~YL9)OE%r`Fv^O%k5AgHx}PpRCrpv*`zMH|P}1Z|D3$J`9pG>A7FxwKhdx zQ$U8gj@t|m_iLg4tK_bzLWhg8#C<}f;aP|zL++6K& zisl?);re6$Hg5g#GO+fHE3|JfD`D!A89%Zj4PuuYFID1|L?EX)KWWK2NQ#3}k%l4+ zxvZXHg3_I9?)+HqG9*smWoRsP-Ov6mYhf_t?hY}#>uq{m)E0zQ-YMkOQ`YPZg)q!E zV@rihwMU6?{`&hJ2NQ;SaIl2x7DiM&LfmJod;q461rWkdfrj?{O<4h0*UZ5sp*fD? zuK_;AG;@Irb5C+AA2`1woC2&g*FuV4NbV>^U{m z?>MU*TF8G&#AT zn{xH=#f7$Py`*iG--DI)V?;Xm^?vYUbIUKj0IWh9!`t-PxXpEh_z+KH^HgZ54|M78 z+uu2ve@t2}`+PGV{cKQM_An*W-;76m{w10ZzcGkF17y-p8YS)PXM4w`hhU#Yr>g5b zLc$|eHP9jApMGCGC$Rs%TpNUteex*eoL>z^LT;I_L`QK$|M`9JK{ZKy+u{CIGCVgp zKvo9ch}@!C<)$rwIAml%L`GehT%2auTSA!GmjHEi^tXz1a|r;*F2^PDzlJfO5d z3j;H(xFI_5prg$54d|v^NAo&L{5c7%5;LHPwGSS!%|ows$^cVmmD7eKsOp!V^cc}W$YpJ5eq9mmCno{`t%Pi;1#xL=&? zA#L5QCO zQJg)!R>Dtn;6r>X4tgu7X%mv5nA!SktRYBsTx*GY$gOvO1Uj-GC$W4%g& z;bX~!4=WDTQ5=3@?NJa&nSWr~24I1o!IEN0TS0ZnFH6HG5LztKCV((#eL9lLMEuNzimn#duAU8bPO*N!ookSN~%Ech@&JcW%#= z!mj#mP;S@t-h#bfL6a4z633=oqai->X_^pnLUyK+L5gLpOn$ibY7=tL2Ug%Y69eXy z#ON@-?{|_9?2qxVul&N@vrsL7AaXh9Z-NrRzd}6_>^#gCipvEiaAEwksfxTE4;98( zIPOmCE7Ag0OcuLx8%=d8H=BZXb1^W7M|Jo2=7u&e1JDfeqj4&WOK^Jtk&$eWdWzss#fZ_0Qxu6!*mvb*0)P(y8FHOw=UCn3F4~N z4J)}5Yt@c@f+YTFVWc&2T!@WXbnPn&vLdh|QcSA5AkN(c$q{EX z2x_)(Bt>dd=%LWkkhe3oQ-G&}w6vQTb-NZx#f`4tLnEbGESN>F3rF!O-EQ?=k9MYUqMk4l#!opsrZsR5 z6$hvfrM!-*yQ8mlwD?|Kop;PeR(0#9QFrt5t#s3bXK=cHPJ@#r2m-I9tkBQ;yxCGY zxvu&GwSa1UuInS=KMY3-r7Z0&kQe9KfR7V~Go!n#tT9i?-KZPt}-)!rArO;aFgQD~l5glhhUY?t43Ye6XiypMQ5h nW{-*~`8$dN#6NyM`Yv!Hblal)T#@?szZ0ayQR9Ud;)$4pC@O*p3PF%#4nR3XFy1I$h%n#2?vaU7n$!;Uy8rI}`~Tkmj_4X=(W3oI&n+0$S==V zv(poFno&lxq067fjbxbN!yW*1pW6=8GNzYnVOMMPmJe;FpS>%btaVj)^-KIzA}nimCmb|E>WjIp%DnuK8fJVInDt-=l zZYG`Qf>Ta~Qk4#sp@FbZg43M>uWtZvXoO&tk?3yzz2520l}U$9tE-VJn9rIsnmd0xDxY$tkF!8%d9nHdeKAr$#f{9Ewhc z0YLG!f$3pS(b=N+WZTTgU@{(rpjrb3x19F4lkA#Dd#Qv{{T=Ol8aVb#D1|K$wAJ{0 zf*&_;wBq{JR$M%P6Lr2&rnj+k8|^_s0MtayNM z8lciu%XWS1wQ?*-4B*M$MdZIS^1%%7>@+C&4N#F@bH1!ZuswJWxTMdJfhFLzwX**e z{3Zy(67pv~9F{Uxu2hEB9baa|b_LN`9`RxC@ZIR><$Dh53{>`ys`% z#rRXX&A8MMt^J1fUd7l=J14sBbwRx@lkkgSHv4hp(7EusTQ}B2)K$=0t7xq+=)79K zUbe70JVzQxK7W1y?VGT9T@l%8fgC4Nu1F%Pk|{2$h_`BkChgXQKRErpDv&S6D zk9D3S9-(~Jdf{<<5h!m8tsM+fd{t6Tm(Y4Mp;SzPLY;?!{mapK>RK4Ttmq=1>reaE z)={kH%5!-xk#Oh9*dZN~h&R54hq%jP$jNh<<|*CQjW0N@zGkPr8Z7q}#at!&#+T#t zowb`U-@?m-y@(RBprpK0u*;#<`>OL_kp6hby$ z9a4@9=Wfc+y~i$*%}Z&|l@ybkWcx@Q)nW6_XMuFE#uSha^)P9-&+g&aTNvv+3xjSa zqLhmWzXtCN^-@0HmeC)v>cs315feoA9ShtdOx=Z?$9Q7k=8~G5n)%A^OD^;lPh)aQ^HK{Cn6hRDX^^ z)Eopa5GM);hz}2uIC4`>_weC39udI{t zr9<28Am!p^RCt?k_Vjf~wr{)RoA9Jg1Bnk?Z?)mZwN}*EUd4nlKB_~#KaU+?j=!vO@S243dM9eo|M8i zim?;;;9ak5@9;~V1{>GZ5noqPEEXX$@^hJk9Ts1c#h5Z6zGpJ;upBOs>b+7tgdVXz z+1DLQ?8`g@oi1O_qfj2-;ZurZiRYB+V|ec^A6jmP_@d*zjXqjPh6I_5_*Og-bWd^bt2O%E!?BZ%+g#i_ch9j{~ejz-Ec;zos(JBd_#r^|Lw zDkjN$QK*VB>H|N;X^1bvFWwnQYe_l4>1q&3^~h>2l=!#ZY}h{T(Z3clZp(V6PwbXx zwXn#q6o0{hr@eB1cdWBLm3lhL>4@$NNRmhvA|i4*aZW1rnERol9wdo+fCv3{Q!MK# zPB_hB>OVFkmikzmDesuenyj&zRx|Gm-9_x)3 z0G2JT!T0G6D9XQplQmay=|T%ys5Z5?-{0SN>#=NcwajO%xR-M70C9cJkFmDpqRX5T z#`gIdn643vn22r(LpgQYQBIT9z-kU(ryLKm#NUD@K%5kS&FZJV+K1-`c*zGojQ`k= znNzB;^6MIG+f?>))$?B`9^j^Xs>ccqQ391cigEmvfo+o>6GMO}~%zr;g z3*$?J_&$S}C;GhB#b$Z4yU{q>ET}$vmgi@s^8AVxLA@26I-UBT?6=tCdU^9lL}&>0C^d*rr?pRq2^xlRqe zMdC6IeUs@+>E7nUPWuSfVj0^R>UXc-esv*}ym0tKajq1D)4Y44!<<*(vIUym4(a>z z4E55IG|Dk$r+r&vjv~gG9n@>mu86|?JdGxCvQ|HL6vrp@r!O3)U%yX(7w=VoXeKkYjqbFNT&8~))GT2?sXryQ`Fff>k3Nk?GG3c)!3KI0cU(9SAdO&qn z&;!H3puhR+2Md#xLkt6hq++9`>!z!!A_Q`BU^g>&`e?!K<=_mZhJg_g_i{D^*;%+z zeYCK$aTKLFZ||m|vN0E>(cx3&RCSiNu(na~akbF&QPTqX*nt4%G~!}ZB3?pJ1`ZZ( zW>j7d_Ksj7FHxGm`3gbL|0?F7q57M|%}$g?@-Kl@x~l3_(oU`xRDA6GY#=UPJ}LnK zI~P9>F95(w#m&hj$iXSd!NtSI#V^FmDa6f9_0NX}D$Uj0QV1v`_m3>-l_-t1o13!` z2ZyJpC%Y#PyOXOG2NwVU;Naxu;O1t7Qm}!&9o@{l*c`#M|HVPZ0t|AsadxwDa-{l; zquEC%cQ;WQsHXoi!NK|8Y#qV>zyt-1!^_N>gNvQ>FO&WzGza~g&e`47{%_&tAPx(A z3kM5FH!zfz>)*7_)=q9tU~8xUf%U&{|8D}I(5kBbTgHE@i-W_zMS$I8J)ma%1IT|% z4c78@w%`C-fSuf3K^C$eP&H})vc_3R+SS6$&B;~E$;tk|DoXvoRHl-a{;OgDRE#<{ zj^<9DV5a|g#zMx-%|euhn-dB*rvMu_K#PkDT5Cd_yeyntLY$ocB2{%Vx3Tp8Z%GA( zxCH(;QmEIMo4J|&e-fL6ge;w09n7FM+c=n6S#UTzTG3Gb8%ZH)CwnJXs9>meJpWm) zC@rn->SSqS54`~f%1cox%1ZNd0r>gZxY@b>R##P3NYN4OX66X8P?Qm+f#Sn%V`DDF z$p`gTPN{ognu$N#9mlcR@)>wj3_Z02eP%}5rmU{M-NS0@Lme>u%Z2WKibr++T~ z@}U2bBiPL2|BT1~oqznx(Ek>X{|trypLqNOH^|z|(aHjvygB~1hvTm__pi$4_<#Gs z-}n9_B|-uHy9kM%HrktkVU7G2~lUc`q%`KD%jCbDN4nK=cPgu(}(Fu&a#G52o@m@|`c5a;mZgarz z@5#1vcDHEAUw$nw31E>2d~w-hoGg}CEGs)wd!I5fk?h$n2>PxU?~FwH$f02D^EWzTo;~M(-2O}F2 zwSGdf;eBKzoy||7dzo|WXz069??~F}CVyEGz(6Fmr3rU&k>1^Q6pS6XCm-*3d0`R*nYouC?AusmjoOd5~fqUJ|4Df!`uYi(dumdwhFwC z00{&>k`!tE__~`|H&=L$fS08brQfFp>^W+(XhfNa!N%?fb3 zp6DU8uvGQDH?zd%%i(+dZL@PboMYvYj`g(Ts9g=_?B%%Ix7X$XeTqOpw) zOKJ#iFoMz4omf3Bz$EKk)OM%Q77qztFiz*OU}fk!AQntFHP@K70Elhmt=c!wsue$S zWfmZYD9neItO~Vk>n#U>{2&&dmNumP4DaNVwZHwy%YM5OcwD8ktX(7a@Rss%<@$|| zA=kCw)>QGbJ_U{YtdKWZ#KeHp72) z+&X;{NBl{KHNCO`0NUB;DbJH;wHBITsAlt6J>5FkS(8ey>Ec-_R?>s9Se~{EJ=vDK z83aMN-to+Scx?2w$y5Zx2M2e*Umr|f`!SsYAAKn=Lq)bAf3(nhx&rU-s+~HXbGt>a zdtTTiJG&et?Dz+%vz1}_N=I_79*V2#ZhPnhZk4E?aB-qHevMR?g4m28BRLjR#AWq4 zBq>}5oXuvg`a?6dd9|&fh7*r?aOx;=X3vKL^d7$Gy`5FX7v!{>K-G zoCZ5lY%UVA%-mG9F{|u^ey0G-0N?vti>X=8h1m3^LXnGgpIG^&nA zV4M*_qMFZ18l3otVzl3Y>P%hQrBIbDP%W6EE>tk-7zH@OIL{WJuUQ@ zz5+R^5c!xY6eW$&Y?XjX$s%ZDYskQd|#J6E7ADcnJu&lu3?F zF4YeD$e+BD;rnQCu9H@=^7C@U?tJr~_OQ=6;nW{6z8nga_vZ&8s5Q8N)^FU<}m zCWa%(pnj_dx*xVoU&|)v_nZ%1?!MRAbl;$S-pm)BQa177cwYjI;>>CL$HVNcKh@H{ zXIn!O7Mivy;s=g7$2_ex9kvwOb*6P6Wl)hm>+I80&-78}Y>fhxJ%58r@5zOx)t4VD zj>98mU5t*-dqX5marU$m^(4;_0CTTXJFll?Q)s|Kl4w=~B{muxF2@%IeY_Bk#`dSJ zR`!0$fimqjr(^WI*YNbf2T+YandAA%?Xwq3&t^@a|7oG``G(8kl(46$+pIVjbaSrm2+)J;b7%MmF9Vg=OVuvncwVI$0KB@rfugc{0{^-fBGJ! zCrQ+Ir~h^xeDq@A)amoe)+}>NcC>r*x+W+KO7{ykyk(@T8glsrZpL5W&2$@KaJ8t~o7gLrW_NVU)c2acMKTYZE| zLKat&UQTBWp^84=sjZ1LfM|8FsVH>}Wq>GwKEFG<{hFYYiWvdKi6c2ruuUCN`X1g&aEf5jP z>0kTz&YfHz{zW{nd5NBst_CvA65!%2me|) z9QgTjU@^(EM1A{ur-eXqx?mHqvU7R7>h0Xtog%w^PSoBqytjag_e~+Tzz(tEGo4%_ zf$*|WFk1#oF%J2#%r)9#Fzik(^-itE4gn}tyQZMQHP&lT@b?|#wtB9qDNNzsy=W_X zfwg^@JBJU2y!bP{+7urNS(<{OnG_ZHyI7)qjC9uzCBbBd4r^h2)a;okHW-?5k1_1*NdHqau+j;xT~6TxvFpg8Pl%7_0OYy%&{=YH|AtB}05p zMJPw#D1(+o<7Vw@cCM_%hQIoJqW)|a^CY=L=FYT=i|ALrvA;ISu)JU^;#mMEc;@7u zclo*b`QaR$Y18L-!p21l*_&wq9%E3v%fza@_AYvYbkVKR_KKbe4-c)iu%f^%#yPQ~ z?LjVQAssUsD$@8szx0AmyoO$T^jHL+*1ILm&F^VrKR(ESXh%5xSZ`lTG~MTsmOA(D zbMD3jGVCE33o=_=r{qKe$jPc^Bk&vSp3Mf8aUZKI($ss-_DM@=#J0n(HV|2>LB&L&6god;TuY~_1+Sj*B>bbh| z?z=u?Z|sshkteHN6+g%udUqAL6lIk0Yj!Yt$=48=^LYk zAiBwQ9#O77-$kXuoha)PHHT~B#!v1Th7CzSmL@Um^9mX~__i;8f?5;KnMgaH@U#QB z8c(|RJuKUjZEapWTQ5<|VrWO1t6G~kw$-yZ$q=lmTk7yq;C(GEYaVwvI(~BmUgU2D z_OI*qs`Umsz9g0KG42;T5a{8Dxd{l2s}qLVeMgb|fopNxi2AUmQ17 zEIz`Q5)>1)Ed}z>m6$JH=8Xnp2jtABM3QhYHE=%nsPB*+!wn8<^rY(>o!K?75T@fu z;h^;rltv9;et*6DMC5n&p~ipu{pO$RxBrM9^VR4uO_GOS-iZgSZ6GC4o#uV=cL3hGCrnNLZtP%BUhdF!Nx|Rx5ZIq zjarR6(Lfnbuf)i+HeblQz?1TTSBuxHu4bbVp{e_m;(pG_s_Cvzh;UR0VZ`QNR(~HX z*WD0sly>dzyyo90Nc~=6w0qg8cWxBYWZU&G+VSkn89JB|4#j3beH3zFg+p|!v|yb% zVV1Adt^Ii1N%?S*u=zB#^_+>WHw-}UX(Jat8?PE{8I%`-WwhZ^R06+B6V*k$TG^ar zu~e8e6IYw}utcX#Y| zi`^Yfp9ya9<*07JTT{sBRGJScY3y-RsKD>n62h734GBVUCY z!cJWVCVs+TpR@+9S(#jCZTZcbyex%5F?a1A$Rw$4spw+zJ)P0|wzxI?&d3TkjV;HC=Sp>!l()8Toao$@x zH)QKiw9nj2B7}*ze}F^m(Gz;7=KQPLpkh#3MyY^~k@_h7Fh*>O<|l6r60AL%pu8CT zkS-pmwlf`^QDm%(MBOV9H}{o5i@&hgeKihKO5#CNv2_3V=M zc^mMDMmbd`p{1?XTOw0Cd@g`G=-lY`q~l0n>-X2=mo?#+huP}qFL2JU$glS#!`<%3 zJZ^vi3)Bj5%zIe1f)ZI2dG1T%T#0S>eAg}sTCnp1ae?)4(HDGnG(h8p_@%`PR=D&w zWdvjVH>tLw%{7xgEk@uVwL`h}est@=tcY_E^7%&pX7{78w#X@o;pLgQ>0xwk!fC)% z5)#M8)X0Z)7q?q~UU2W1n_-EI3Cys)9rb#ByEFU76-^r#RR_}~LIpHUXALs$R@cFS zWhmgULxTbxJN*fS^nO~fE%(iVx6M0+4@(7W1A+?EQHa8RrV3Oe2l_-;FpPD6oxI<5 zR-Aw6Zt~0*g2=Kh#V#wP7q1`RY}GD^c-@WV1NVShrD+irG8Q7NNjdSnD>SYi6I9mNC=}lKU4;wjkSS6vkR?AtkW-S?tPY8FrS&Yf@ zj$#Z4*#Og)@53Ij^Qz~MJLWSi%7@oPbW1K1x)eiZZ%inR)4FVJGz(e;nNtL})6Ja3EfU}xm`8H-pMq#FG3163xU^ZYs)(I9>G*QJq( z5SZk~N(}g=p@{jo-G4A`zU^qs#NrL#dvRA!ZAy^-?(JPLBFMVZ+E%h?r<8T+ie^t_8v&B8`%SM1Fo5aX>aKGy@jEv-wmW}R`Cb7n0c;}tW7gViH z9QZ@c&^>51Edh~rUG5XO5{>S-vFIy)d3|j50jbJ9pf>gL9Zd%y4+VZGGP3nnn(|?m zR=^*8x?1h44cl_-wDm~oUeyFrCIhCSeTb51QxVd|uMO&0qjxfd0Y>ADFkXby#)vJi z`RSx)tnMuOCOFOErBtLj-3Ujed{)1S(RMydj%mQ!%pJ5Bx^^US}y1lE)I^u z$ePigrF-EJDHJVy>eL=qPOh&Pe~sUu$)K$8#lBXAk-95MQJfn-iiW6NlN(exZAtOey2Tmx72ppGRq2-EBQYutZaQWo z|8Ownk_JO`ajoT)>HqMe-unH_&+|UGPJ?#DYc{X!$z|reL>pxrj%b%2P72DJ;*yqji#V4s9_SQl50X|)_j@#0F9*6tN7yLA_NvV)kgjN zmFGh2*Okm=13H*T@t4tD9&GX~;+d?^k`me?q{st(${;xo+LlDaB%GZP)gTl&x2ot- zhwck4Sa>BuT!qW1z`&*H!F&dvH5y|SfMHmhHGbflZD@S^-f_AJBS zbHI9Bj&kv>xm;wtw(Z(v0xmtZ?0UWz8NG?s}$8{=W|`#+ifk3@q3gs456*nw9_ zxal61D4)d*+U~KVOVXPryoI5I1PVVD$}QOs%fgdou3=q4qcMq9_?#sOD1i^(6K1rQ z);8))J@dD{uBMx~R4kdW>B+I^jkuAe$C`8US9|x!P^vU$Q&KR5QjnTVWE|#^NFAh? zzsd%{c=2>-D=#sIgKBy&fy!4uCn@ATE)ocTz+3+HY^5mhI$OL%Ee^zq+ysb!aE|YbNdP^iNW898{?qT zUqjazXDw0j8V!?kcpVD>TdD8V>S0n>q2jlm?KxB65X9f9>i`;RV$EpR(y`#c=M!w! z4s8>1vX?QPca}GlE8M)?0~t$u=rTR9g<4_2Wc_+N;=K#z~(n>itb3g-O* zpl!9t!A6&FL^JdM8FIE?n;cCO0qr34Dt-1S4@D<@7s+aT_fBd*X(*OHEd->PPHWSb z#mNe1eQ9Yb!-8b{;U?fIg#&qWy@gVyr^u@Kz&PH(J(q79g){9fV~JXYq=c zM^}Jf!b3zTk`!Fl@DU@D-W^USH4l?-XBF20qun1!A#WuA-T1D$(W0yS0i+i00! z)DnD_2J^(g*w$j?5hUK; zkS^E@5#3S(mWCY}M@yM zf;W>+M&I0eqB#YN_r^7~y>`;P@oU2AO2wPY!I(LX5F_7r4P_0kh**-22s9Vrqd7Ir&ic$EhpUJ5#3v!0J)@8Tww>XykD@j3WccGEgw zOo{Ugz8TJYvgd=zXKrsDeRz0dtvy-O8c;qb%R_&BhmqRst03mo=8b_gMoRg5~!>%W%+Z!bG-sWC4ubh?DLbBbx`;g#5eK?FT7YRwAu zXn5^@suEOWxoVLevm#EgIQqCXWU`+@ZJ8`{^)Ev0J3)NVF-t{TCH0WL`D@{|d=s3Gs@ZbS7t%SR7*HOk6i^Ym1o?1Wg_zW+H5EucM|*{nuSJAZNofjf_K(TzRi` zgBHiTsoFA5hj*7b5@@6h5U;elP_(a6g;##TZNOAz+;?huXSWcWpp>oFQ`->J;R0=$ zWcuNv0RYxJCdnjz)ihx$^nAi8VbmkTl)0?SZpdGQo?q0sg;zP^ntBTGD;I(Vqy^is ze#ioC%_oTnSW5s6=n&%(yZf6^;bxwOV_-=6$fGzk*E*CnS-h#oUVr~_kVYKFM^9XrlKHEV;y55R zEX77{p7g8IHkvUBSyHN2stg){>$&Hp${#kv`{HL9FblRS&2l)*lv&vZg2dJl=3dpG zTagfkjl8y=sAiAPA&Y;8uvG!i`oz z5G44ylTiTV^xY?3ytez|p#R=~O?IK#u}GvAt8jsqP;WtP2 zaNxo}{s~7VJ66t7mt-Q_R);R~D-bUNH>Lg79=@vt+wYGFhvj(&4)YXaT}N*2|@e36cr;=IZliU#~(p15t%Y(=4t}BDr)F*zNs9F5d#HWO1lkm zVYoiuWq5VVn?7cYef_MpOz?%~?ULq3NARB?A0S56H(EgOtEA<*xWpGKT{$^lOH*A` zZM)IX%vZf}UCS!H0p&o~kgvcnmjVCRyXl&a!w1Tz7DsY`Ycn`PT|r5SmF>nkKE*pC z@lx%>axER}+BdX5(&SO}Z{?aU13j{D%-sNj2FQ9t%yILuG=Gu+MohR^3&g}$xDHrXaN@p5uB9&3ifQigVJRST6f(gLvA+>B>b^+1uolOQ5{TZ8eYctUw#ls} zVK9{)|Lo`ltJKjxB8)-|vwE&SYSEBYAnlL2-5{)$MyC4gVC zRxd))uf6@KtjnuumM$}k8o(#(>JzQG)$)1Qq$Q~?g8cYfC5AACvN!Cd?~NGq*lPEJ z+TC&)v>9al$Qo_4uh;E*IKeFhd6HW~teLlb<1oJ<|9k2q2&3z*Jkh>juS** zotGxO216;Eysi~#+w>XLyXQR`Ad+Y3d`9FPMA*W6iJ_`#5A@5Y+nY?Qp>yO_gk+h6 zF<;$1;@z6IyQe0SCnxTcFD=p6&VJV6%SWV9f0G}t{6>l-Q%;=Lm~oGGUilu9Hr)(# zw0}osDvi%YG2~CFb`3+9cC?#{?xxmN+>n z>JInQPoz&iXo&SvU=!!61Z^+a7I$!xRbswXod78 z16oHKgr2NPFv-R+@@*N`H&s7{o2ue-5e|JRa@M9vtPAR~s(`smZIw_6R>W(lA9r>g z&7Vv+Q1tVewL;i0+M<UNzA{ zOtOtACNQLxYK729u>7)SekQ72MSgs%tFf+`F4`w-Y}+ek;bV}@%?+; z0j~7!%c@Gh*;_W_`qUNP?U~nSI(er@IBIt|e6d+JjX53-CP_n1AoCXSSDH_RX!HcY zaBD>_zaPttwq!t`;6%Y!Fa(D+UiaL!~5hJ@4YeLDNcTQJhu z>-cN`N_~D8Lb~(a*YVaf_`u%D*ZZoRryr9TaoFE$lX395RPt$V+PUyMUtnTzWGUtd z?y-Cv4wX#^du}pNs|D;oW(NImbvw8`uCNyWy-rlKo=@7fuC$V3o?2o`gGPca{qbo0 zCN1}tityreA}8)RZlm!FmBcY8!f_h;=nqYAr8_Yn4wyV4j|vzRvfu_mkiwKCOjzo` zF37wd)BJ~7B?VsU^BIPD6c9J~&u>Ibgt=^%;NBuHQzhq<>}HoMO%u}%Ob(Mj_l+^l z7Y*X~PJWk=&4DWiPwu+1frpjl2guYkLLOHX4X^ z^qpj*e!;&o8**54?&~Qo>BTl)Ol@0QQ(Tn=itWfj!ImOX#Av*8_ezj@F&&+sEV;Ax z$2FP@D!P$#q*c4bI{3G6U1(1}VC|0TYOfa`De z6cNcI=(48|`;$YHI`{Ak&zN`k)PijHJSMGZXl zC@vBeAnqTB&o8`s{_c3cpSG5gY9K^X4jvCmwvU#Q>x)VTb}1g%dlXL#1k2@In_H0! z9w$mhVI8}#hphU=h>z^fL;HQiq5TBF{X^CiJdZmg+FG~wxdvhqvatO+r+WiL-2*wF zKDntm(i@>ix{4n+V+KSNfbG?@tkN1Ot8w(TQ!$2}+9Rc3pSbwDf0d_o4juC3Pw~AT z=lbpPIe}iKBh|ktS7HeZLWp%j(y<3p4MI4Y@YMF3qN3rd%Qs^d(hYhJ}onR45;SnoU#hc4UgY^;f3g)ee)9-pr#cn`O4RKpEo2=TchY#uRyg& zgL~l)E4mUWuU=80Oe+m_snfY8bQ4UB!qliD9(e+*J`2r0=pemk+EgQh#AlFPbr;z? zK5(YF$|LfeYkUH)P|ir@tH{&QZc&f3P30Fw#U0jf-$FFYvrc@^BlRAoIjBjEn$WX8 zZ@n7k-N-vZ44j@+DFg%HTA3{^W=@C416#B|mZm+~@oc*}GNFqLw-mvt;C{G7F|Uq_ z!w;?a{i$}d8{C2B>9P6#dpJyM07_^1h3NI_uZ||Yw@8R1;d0b5M_&@ow60tOuo#&X zsdV>ZP&C|>4PplIFc)Ti&v4=w_U}Kzu!+pf;TJjQJ@pBD90A^3P}JejL83^RQ_H$` zX^OUS4}PesocO}oDt%W{rkKKjc5FXnt%^Vz_||_+db!XuxTw#jeI^*+ZomEWb1N{> z!RbD{-q>yfyBi_9#P4zP-1Pjn=E!GUmXNRkJ>F!i&r(RGd%JP$$ULFkX_A;Avy;qI zw1Tm|`d^}s!%roDUZ&UkYA(f_C7cZXUqvT&DP(~_%`EkiqRrpylFUBwj3`P#Hd`Fn z^Zu){FYsaTeC9TO7-r<3EHolj&fyzz78=jCVm+e^47B!hDiD39^tpK=c_2&EL(6Exslt!#u_c;}wnItQi{c1>)Jdp@spESk5?a&(9f${0;mdO43dkkwoL;E z#FGI8AH`Owc+f9A9THb?%&M4H zyc0DStzqsknH9&3ayC}w*^*Wgh8TMYbJTImflf)qMCfnXCXXghMQztO-KwGWxr5R) zyeP9GpyEY6_+Zg3KH@hF3ay#MvcBW7Id5FI-ZmKvb9}O@;9b)6%=+z5S`&2?{^#}g z&ildvh?-R{ufJmz-`o>=7MPK{)8F4N;&D_BX_fp`&vIrwnyK~P9uakgIjx|AbVO1T z?LS*BdBV7{mgvdpzNMm|kgWDUb4S)O_8+WjZRB-r4=cnP{=O83E~HkPF??=t_p(Wg z8s6{L1~DXsF1mdvC3>@fY~#jO9Tkq;GgwHeK0o0fx8{t4Ljck~S9Kkm+cHu;dM|rofQk~0# zXL=Gne(tO{Rmr7%k^qiR)`~@fT_Q-5^b?g>X6J{MwGUU<9l_j4DWCHG%^XkC^@qDn zQEAQA9QXRIb_iy0nZ@%CLC7{0Rbt4q&D67^n&Lx15XyKKto-9PX#JHaFi=hH%S^O` z(;cAtDNZ11$ijniI+Gt)Ul;#HVcEr5vE+UCV=(lw(rkae=7Ard)X#5u-_!KZ;{lqL9n)Q?sVt7<)`2ia6Hd^e0LKM% zY8R4`MgT;ij(2x2vRL;QB(hnDmdEKE4 zxIC1B-`2A<9PF4p0jlrf1d_w(IS}}75?$`@r04JXD=BNzy^H~HWY`RwfQd>r*OT|; z-sKCAGPIHkhHi-03bZ!*$}HnaKuY4(+GjCF1k=F}I(gMjE6!gWSi!AEOHEZ+p#iRv zX!&$TSZ@}#eG$UlAr^32#GG!c{2}_et<|URv=O=hZW4;5OEa~A;M7enL<$Ahn{vSZ zPJ}e8oo!-{$l&vN07qGN&iu7(LF*k+U|w$bLyTJpUiGs#69?@&q-Hgh>P9c?C+n6m|D?8{2RDSrmxo>BwXhUO+Z z^MP~Gl2w`fX$HcgE%JeW#L;a1l2b`etrZwW94tQCs-9zI$B9l@wr{%a4K;MSju0z=EYXyqd}O(E^# zlGE(q7#eK7Wo{&pDB9r{EF8EqWgdz#SLVS_RQM5me4Lt^C(p;&iMhx-C{fptG1ju7WsHHb+}2dAY;ZY!UBe5AGz>|?v{Lo) zOBeBeEODnmN^JVd(mU=npC3>Cd4BhuzmnNBSqU{OKfy3+x$GTJrIzI58kU(Z()uaM zbMwWBVt?T?V&ZbWX2*z^I|J*9B21_T7cqB|9O|EAq{S$#rPlhDr?6#x`H;X0s@=bv zYzFV`=8Ho3U`81ClAAjsa-E(Lv%*dQ(kOmrm7Z9hGFkb=jF(qHnPvSO=nTL_o;hU3 z8@rno@molu!q={6Pp?PuYy-=A{KBhXXgWXp_UsE)G+@8FqL3~7#yxi1JdLQd|I$2o z(M-X}?txrtsg`&5XsN} zb;b&`FBrIF;ldjaMW=~;P`p47yO3<sYf9-yZh3U}wB_XKQ zy12%F%3UCfb>BIQt-`u?O5;QHwb_E_l)fX=klJ}ja9G2lkT?$&!sqL66>62mvl9xr z@-%gQ*jinT3+7m)48!;@@~Spp^}%y@ho&tObjZ-j+fmm)O}<`-`7itH%NmKSgC}>3 z%k`H!#_};yqW;tcfdr5O8*+G{vp?U?ej^?{(3Me9aN%^Ou1w;+_fA*=(JwtB*@e$E zCW-t@cO-D-VCnYNy}5j!{YRpW-M&D>r(yxW5sx-sHOq(ty@?*#QoN*#q_%kX=WRJF zAR~EMc4Cf@zp+3u3qb^XcbM9{LJ$G)j?jh46#RuzQIh5M$W9~mYr;|D0^m%ITkRl1 z_b%Bs+~PV&!jtsmbvR*G)MKY(39ifRI=Q(sKDYZ1dV|cs*{*Up%#-De)J2HWKmyi4 z(1>d-dH60RK)#+lXli3D?X;Dzqp|SE?_7=y^?<{Dv z&CCo8g`SE)bhFsF;D-R(2(WhjiR<_4y{@DZXn|z)dTNln(aSb!BX+Jo4Mr~~x!+`~ zRCkr?7*CthpEEBDZl z!{cgC*q>PW;)M0Ye30932MxK(^cM4AFm%wg>e1@~vGaQe>{1vtDA;&z4b@p>Jkx=f zIBUi0VuBi|tmcw!d{gv(Z-X5NYrD9l5 z2n;epxt{nAuO!8dE1OzMV{q{(;pN~>Gi}3vfF*XCzIh=&{geZp@g5C)TJ82!tb=If zab-f3>6W&0B@+k_q)V0GC~P7pPR#;*@@#cbnBOh|jk~ktSZL4hVW%kBn!~b)p}S3f z9k4_kOG>cPVOyoOw*zW9+WO%LEcA@RGesX)Z}S2LyRTWHdrnTgF6~;L9BPV7$4 zKJ-0@P;g5znrV`0YG&C>_GS((<4{bwT9%iuKFp>2xvoCyoki-r>~K6)*fidfDe)Y{ z>Xp>7Quy$tMGvs&V>_$!YPDRBAzwLF13%vV8Xv7JwBYTe&_vcM-78W^!XGW{!e&K~ zmi>CyyYRhTf#7z@5E!w@XH6zJ03UeWb!j{=1#}s>5$Mb^>V84emki$+29`~Jq(0R6 z7KJR*jrY5_5$`xOe9FvYbIR=fwcQa7IE+a@Ch#;rOAa(0>^N3gd)PINsjoB! zOcv&V8TXG$qu^!Ewaf9ukyB7~xI|L=rsFMcO3hvCT)7neS$~!@t%+?S=yLE&zho3) zh`-*{CT}?~R-;ye6~hjRE;kbUvd4Gx+!uxt-kbW)+vE9wBWd7+$8C6!tket`OL&T? zrK8SxCv$&A9xDaw-5mQpKn9CEuT|eC>Jd|8;(_yoDC34PH919ZDv6Hzh!@NFcB=U` zz25&o5AkPm{_{}{hSO$8O_IgQDZ?|6|M(*A1CJ{sn@zpyrqHWi2z0~3 zwQ}>UtvkASfoEh$7M?vSfsvif{-q_%VR^Jw^u>3*FNn?<>8zoWQ~f3_pWc|fJb2d} zs{l0ynoRr%be@(t=Ghik{OQbo$c)>D81FfMPJ0@Wn253EA{ESV10=gCRLUy1R)SaN zLkjWD1(8yBc3NcgDr8kf#lS~`8T&a|Ic9#xH=WrvfveKLj0xP^Z*(uX7|vZL{l zIw5ge#iWGX%FBhod@^-P1{WycT{lS3Vc7i&xmrP`xLXUPa}vMlRp&-)y~BL{#1e3v zuE^|uCOl{HhgD2+8c?a&tNfKqT#JS43mtYj6Sk*tL$v*gtJF;P>`4wt8NXf#CG84B z2I$N>4p{%){_1`H@GQV_Uw~M$`8(b-=QiDIn%b9cUCUd19=vw3rH!NGIh=fcczj{b}G?BXB28uTs`Dmz~9!+dc? zCM;r99&zooqEob$+=B_d;h?3|RQ3lVf4A>;Y?=)3l0%e>ze@<+<_abeHUNi@K=l|# z4Z#obRS7L=VCq8rzwpsz0`Fyea`LSf0plu|H5XnvEUka}3Cxs9a23(Ni`#ZiOVL-R zL#);6!c6d9q-0VI5x+6gs{kg|iBE3iKX&H3S-souee+HayiZ`_bw>HJnJ+%q23_0; z^Xu(9$L{m^a2ijCQ9@XkO5mSX*67LoX?3v3yl5|`hih0J0!QHZ80bK?j)Y)rVs z&-SP=oJwqbyM~WLJ-w{u78r#=$vN*k1_xKxbd{=^b#%$YSaWO`qPNHufM*U3hZFo* zFEf$i&e9GWKjabYeY9YS*X!=DMh&{^udllN-=8ZDMhMd3B=_6&kyjD!7Jk&hXPq}b zE04nXmD8JS+Cl-0rxNEI-7{(jJS;l7>|{4{fo~n>G*eK>ey@zs7*eOdRSP(^B=yDS z6vM$on@3Js>Vwzb%r&=$b+1ag`Wn?ict||%c4h_7u>h3vGWan`{w`sX^SdK~w2t9J z)bzHYBIDr5=XBg9X0pzEtTHve13p;g@E#bXUZtuG6oOA+vIz_x26-Vy=rRTt``JQ& zy^6l3mW5XVufl|I(<$AvL%RPJ*%T)0965UI;I9AhQ}5mNhSz@L(boH@0sye;8P|Mv z&-*^`{d2il7YM<+DCBsWa%;Q6pp;w{2WMD1`U|rN6ppgA)C0>^LWH zz1vsreDgMD@7+r_Gm8u~(dY<4twQVYagKiBiwq5vsOh|K20Um3?C$0?3NsKFX29V) za|LiZ%SeH(k)|$Xv1_~*2tviF(;YfkEdpWOL~2`445jI!g#gO>49iY>Z-dl`BM2_Pe-x&q4rqcN6wbP7{Q| zp`ZNL*OrWkTaM020OFxRC#9S@@QF`f(Tt*zaqVmMzuuUmA_UK@g#Dz%a{gcs=-Cwy zz9Mr5BN(a$4AueylhQu;Feko!J4bK)HmAOQ2hDvCV!H`8$vE-FnRg`LIgwD3 z;a!&!*J~WT`3_DOLQs*Ws9an6q*CYe9h(D9MGB4@%dsq{X>vA%5zns=GORRhXZwHM zQ~eJm8Fw|?%$`1Z@Fnm5%h!zU+IjF?)_G0>0I=%XYj!{U*)Lo>dF04tZK)a7l3QC1 zq!8?i0*18g4{_+(;ztq&@a5xGoii8G#)s>@vq;;(VK3evF~7kW@Q-( zvqhh(1$6LcAS`+qdyW%@qlkc0c}DJFTNF@{qDYtslyC&0;`CHPYo@`P7d)F_@bQC+ z7qSq7p*lyubqA-9&r+@}uL*jTI-kVZ7-+_{ByFKh3NrxQ zljO8rVFsk`*TeU;Kr=VYTF0^Jx|Gt$ z5XWx5gVV=nC|A!e1U=^vAy^kE)&`2UgL}FO_jfvI0TWS7ELH!#cXm=9n4BaGf_q-^ z$&bD?9vWKCA}>qlB><(-kvtk4d}!}K{quF*N_hz9u>kJrrreukJeV4e=Y~cxx+NBp zsu0E8;rX+8??xArEi{7!B59}vmZ7R-u&gLagA@=e>t_fQf89g9Je*k`%KdxQ4E|i3n3|+p1(#RmkdYYgb zQ&T;}<)uPUV2ClUB@=-r2X-eZ_jS7D&axqn8Pb}&+X?r~HEB;x9(>t9{OwP#x$ar# z6>vE(0RUKW<*oy>`yU)R`JLNWj1LTsKCMz=Td9Nrj$}Ezy9xItIr}oh(cCa?J)lA; z1!W;9NpHKIU|H?Za~wV3Vc|ULoB^E|Sj3Ad5YTd#d%FJ2?@t5sJ^4}yloT{_L&I3c zv}9wb==S1Q#c|b2#jzu^=uorj1I!(D+qN?(<3W39$ zgdru_9BRr+vMPD{L}fa>%0X3V9O7`{3{1O z`RVIgM~+`QSgP>sff`p;Dr}4*Rs7(R)119&#-Y@3(puWZB&d=Ul%*gp zOobEkOq+iI3!O*brL$1C^~?YQ?oBef&QaF^(So)pAw<7P;#8J_5L^;z0_bZ2DIB4e z%rr7)Pt7uZ%@veZuRQPP@@OPPp$W!DIR33WICXT2Qnf_w5qtvhMK@(B*c51nmE>?{ zxVzKkp(MdHn_Th3ulV0D{6Ft`$Azrvf&>6y?Q@^~i9H|s@N-Wzn;W`9ustrZC63t` zm)KD%v$a%WP2sRi8p8u=&VAjKy=l%vndNZqeTUR~CYGh3Bt(IYw^$&0T4C86C{6

Vc0jq!D-#szayh1Rvb35hn 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 IM6N<$g5b&?zW@LL literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/IconLate2015-ico.ico b/Bloxstrap/Resources/IconLate2015-ico.ico new file mode 100644 index 0000000000000000000000000000000000000000..8b9eb52d6536c1ef636c55406e3e61bf989412d8 GIT binary patch literal 4286 zcmeHKdr(wW7{7o;Wr|ITk8}Y+1e8R@M=7q4X~=w}MfnKxQR=9r)o3%7rln?TWz$$r zWmKb0md`Ywsab!kO&L~_`Tfl?f=&N40P%$3JPvP-x}x$qyl|`{=n@3 z+i;%?OzD^;BOlL`fIMInpauE>DL{LmB@hG1{?$qX@6a>Hiyach_v1CQ9F&U zxP5|Hg7-Gy5}*Ot$3(z>PqHX!QuR`oZlcsVMfBsh5AQwj;lMa6v>EaHH{dw10=NxO z)2rj%0Uy+>zmbMNSVE6IVWznYEHr1ng_1M$1YcicYzrU`&qor|^z_!dHac|JN&5~s zX!l+RZQo_5FSgXu#|1UC`fVHCKg`7A2xH2DCxIv+6o1S1iQ26>b_~G?R^x?)DO6jR5*j~gK&IOsL zLEy>{WcM( zT_NnV6#t>9<#)>WByi|w&9gM{`}<`tW%e_2{0m{%8vXZ!hMVa6A9aCngT2!!(VBN` z@>+p=H|&#}<7NShO$PYQi(ak_`s0VKJM4s4C7k19olcCsUWBqL_u-5AK(yLa&9xQ@QD!vzv$txpE1(HkEtW|zu-OyM7#{RtoCn4dY|zV+{Lvqpt~ zB*uF_-BdrfSFU-f_dufuy5?bRPRxGBLM~VRe!HQoqx>1=oM)FYc2q)leaQXQ+&n1! zd0e};?X(BO+jr2(bLwu}Syz-b!00)1&J+B0?t+I#jW4BkU34C3`viSH`aEbI2|YQ` z!?m|7z8WjGyN$Gm}q0klN5BI?P-h+M4Yr(au0{Lh% z`RLkpFP*>Wq2oWf=m6qa^17Aa(~bF92@Or^zH03`{)OF8uTK6Fx~7HG66rl_XbwgH2p}*JFM%(dWR=Xyp1d-V;L} zD4`0>L!`!ghR;BZUmS=vRQ&M0VZaa*nM&$w+vO{${n>KupBvB}@BtkKTJSfng^jgr z{H%uO&yH~;0zN|V$0vvdgGZRiTwc$A?!1S(_cHjQeF2aNq(f&GFc@=ikmHZU&-pWb zb~(@ed2q)^;E#gM=3%(=@R|qO&!^m!dar?Sx3ZwN8I#iVWhp)Mm1(^UcAWpd$y3e5 z`4z5RMg8P8h2Hhd9f3a@{9omcDQ)17W8-_(xBDG*;5#QBJ?5elMJ_svH7&;4aP9Zk zm*XZf`2K@^?*i}WX82=p#%<+#%IE06(fBSfd77E${xrDTg^Hc?K+zS`AxYep(Z?X~ zsSO`h%k}Fb>MzI0zN83ohSJMxr{w)>;bK?uvyK_a(?aOgA~9{UfXBB1_$(a=JPK{|;d>4AY{I@h2yLf;D}V{Vt+>ZKU}J=B4iGZ$n-vhjAm9^y zG{7~L^>qTe0etQc0r>w?|0wnd)c>hZ!F-JtVr|hvj7|Vr2(joB04=Z-Ae|5e!~n5? g{5}DF91yEQoK7RZi#vw#DrjLNgO&+Pi5NHApU8B2>Hq)$ literal 0 HcmV?d00001 diff --git a/Bloxstrap/Resources/IconLate2015-png.png b/Bloxstrap/Resources/IconLate2015-png.png new file mode 100644 index 0000000000000000000000000000000000000000..316bf5353876816022422f8b3e759febb163211a GIT binary patch literal 10100 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 literal 0 HcmV?d00001 diff --git a/Bloxstrap/Settings.cs b/Bloxstrap/Settings.cs new file mode 100644 index 00000000..ede8d381 --- /dev/null +++ b/Bloxstrap/Settings.cs @@ -0,0 +1,68 @@ +using System.Diagnostics; +using System.Text.Json; +using Bloxstrap.Enums; + +namespace Bloxstrap +{ + public class SettingsFormat + { + public string VersionGuid { get; set; } + public bool UseOldDeathSound { get; set; } = true; + public BootstrapperStyle BootstrapperStyle { get; set; } = BootstrapperStyle.ProgressDialog; + public BootstrapperIcon BootstrapperIcon { get; set; } = BootstrapperIcon.IconBloxstrap; + } + + public class SettingsManager + { + public SettingsFormat Settings = new(); + public bool ShouldSave = false; + + private string _saveLocation; + public string SaveLocation + { + get => _saveLocation; + + set + { + if (!String.IsNullOrEmpty(_saveLocation)) + return; + + _saveLocation = value; + + string settingsJson = ""; + + if (File.Exists(_saveLocation)) + settingsJson = File.ReadAllText(_saveLocation); + + Debug.WriteLine(settingsJson); + + try + { + Settings = JsonSerializer.Deserialize(settingsJson); + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to fetch settings! Reverting to defaults... ({ex.Message})"); + // Settings = new(); + } + } + } + + public void Save() + { + Debug.WriteLine("Attempting to save..."); + + string SettingsJson = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true }); + Debug.WriteLine(SettingsJson); + + if (!ShouldSave) + { + Debug.WriteLine("ShouldSave set to false, not saving..."); + return; + } + + // save settings + File.WriteAllText(SaveLocation, SettingsJson); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..7d17be04 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Bloxstrap +An open, customizable, feature-packed alternative bootstrapper for Roblox. + +## Usage +This is intended to be a seamless replacement for the stock Roblox bootstrapper, working more or less how you'd expect it to. + +When you first run it, you'll see the preferences menu. If you want to configure preferences again after you've already installed it, open up Apps and Features, look for Bloxstrap and select Modify. + +Bloxstrap can be uninstalled, and reverts to the stock boostrapper when uninstalled, so it's relatively harmless. + +Please keep in mind that **Bloxstrap is in very early development**, and you'll no doubt encounter some bugs. If you do, please submit an issue! + +## Features +Here's some of the features that Bloxstrap provides over the stock Roblox bootstrapper: + +* Bootstrapper style can be customized (including being able to emulate older version looks) +* Install location can be selected (useful if you usually install all your games on a separate drive) +* Support for the old death sound (no messing around with your install folder, and it persists on updates!) + +Some more features that we hope to add include more custom styles, and Discord Rich Presence support. + +## Installing +Bloxstrap requires the [x86 .NET 6 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.7-windows-x86-installer). If you don't already have it installed, you'll be prompted to install it when running Bloxstrap. + +As of right now, Bloxstrap is only supported for Windows. Mac support is possible, but I don't currently have a Mac to test this on. + +## Contributions +* [Roblox Studio Mod Manager](/MaximumADHD/Roblox-Studio-Mod-Manager) by [MaximumADHD](https://www.roblox.com/users/2032622/profile) - some utility code was borrowed to help with Bloxstrap's bootstrapper functionality. They're all under Bloxstrap.Helpers.RSMM, with some having slight modifications. Besides, it's a great project. +* [skulyire](https://www.roblox.com/users/2485612194/profile) - Making the Bloxstrap logo \ No newline at end of file