From 92b159d68206ea4f81ddf75897de210d07b83f9b Mon Sep 17 00:00:00 2001 From: pizzaboxer <41478239+pizzaboxer@users.noreply.github.com> Date: Sun, 21 Aug 2022 22:41:16 +0100 Subject: [PATCH] Updates and bugfixes for v1.3.0 - Features - Added integration with rbxfpsunlocker - Added support for user-applicable mods - Added ability to disable auto-update checking - Misc - Removed Bloxstrap branding from Discord Rich Presence - Mod presets for old death sound and mouse cursor are now statically stored as base64 strings, eliminating reliance on the website and the old cursors still existing - Bugfixes - Fixed vista bootstrapper style not hiding properly and improper behavior when closed - Fixed forms not being brought to the front when shown - Code - Reconsolidated Bootstrapper to a single file, using regions instead of partials --- Bloxstrap/Bloxstrap.csproj | 15 +- Bloxstrap/Bootstrapper.cs | 795 ++++++++++++++++++ .../Bootstrapper/Bootstrapper.AppInstall.cs | 125 --- .../Bootstrapper/Bootstrapper.Properties.cs | 130 --- .../Bootstrapper.RobloxInstall.cs | 243 ------ .../Bootstrapper.RobloxModifications.cs | 64 -- Bloxstrap/Bootstrapper/Bootstrapper.cs | 187 ---- .../BootstrapperStyleForm.cs | 6 +- ...trapperStyle.cs => IBootstrapperDialog.cs} | 2 +- .../LegacyDialog2009.Designer.cs | 1 + .../BootstrapperStyles/LegacyDialog2009.cs | 5 + .../LegacyDialog2011.Designer.cs | 3 +- .../BootstrapperStyles/LegacyDialog2011.cs | 5 + .../ProgressDialog.Designer.cs | 1 + .../BootstrapperStyles/ProgressDialog.cs | 5 + .../ProgressDialogDark.Designer.cs | 1 + .../BootstrapperStyles/ProgressDialogDark.cs | 5 + .../VistaDialog.Designer.cs | 4 +- .../Dialogs/BootstrapperStyles/VistaDialog.cs | 1 - Bloxstrap/Dialogs/Preferences.Designer.cs | 140 ++- Bloxstrap/Dialogs/Preferences.cs | 147 ++-- Bloxstrap/Dialogs/Preferences.resx | 6 +- Bloxstrap/Helpers/Directories.cs | 28 + .../{ => Integrations}/DiscordRichPresence.cs | 36 +- .../Helpers/Integrations/RbxFpsUnlocker.cs | 95 +++ Bloxstrap/Helpers/Protocol.cs | 15 +- Bloxstrap/Helpers/RSMM/FileManifest.cs | 2 + Bloxstrap/Helpers/RSMM/PackageManifest.cs | 2 + .../Helpers/{UpdateChecker.cs => Updater.cs} | 27 +- Bloxstrap/Helpers/Utilities.cs | 12 +- Bloxstrap/Program.cs | 35 +- Bloxstrap/Settings.cs | 8 + 32 files changed, 1232 insertions(+), 919 deletions(-) create mode 100644 Bloxstrap/Bootstrapper.cs delete mode 100644 Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs delete mode 100644 Bloxstrap/Bootstrapper/Bootstrapper.Properties.cs delete mode 100644 Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs delete mode 100644 Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs delete mode 100644 Bloxstrap/Bootstrapper/Bootstrapper.cs rename Bloxstrap/Dialogs/BootstrapperStyles/{IBootstrapperStyle.cs => IBootstrapperDialog.cs} (95%) create mode 100644 Bloxstrap/Helpers/Directories.cs rename Bloxstrap/Helpers/{ => Integrations}/DiscordRichPresence.cs (58%) create mode 100644 Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs rename Bloxstrap/Helpers/{UpdateChecker.cs => Updater.cs} (80%) diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj index cda5ea0..c01e437 100644 --- a/Bloxstrap/Bloxstrap.csproj +++ b/Bloxstrap/Bloxstrap.csproj @@ -4,13 +4,14 @@ WinExe net6.0-windows enable - true + true enable AnyCPU AnyCPU;x86 Bloxstrap.ico - 1.2.0 - 1.2.0.0 + 1.3.0 + 1.3.0.0 + True @@ -27,12 +28,8 @@ - - Form - - - Form - + + True True diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs new file mode 100644 index 0000000..f1c2116 --- /dev/null +++ b/Bloxstrap/Bootstrapper.cs @@ -0,0 +1,795 @@ +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net.Http; + +using Microsoft.Win32; + +using Bloxstrap.Enums; +using Bloxstrap.Dialogs.BootstrapperStyles; +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.Integrations; +using Bloxstrap.Helpers.RSMM; + +namespace Bloxstrap +{ + public partial class Bootstrapper + { + #region Properties + private string? LaunchCommandLine; + + private string VersionGuid; + private PackageManifest VersionPackageManifest; + private FileManifest VersionFileManifest; + private string VersionFolder; + + 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"; + + private static readonly string ModReadme = + "This is where you can modify your Roblox files while preserving modifications\n" + + "whenever Roblox updates.\n" + + "\n" + + "For example, Modifications\\content\\sounds\\ouch.ogg will\n" + + "overwrite Versions\\version-xxxxxxxxxxxxxxxx\\content\\sounds\\ouch.ogg\n" + + "\n" + + "If you remove a file mod from here, Bloxstrap will restore the stock version\n" + + "of the file the next time it's launched.\n" + + "\n" + + "Any files added here to the root modification directory are ignored.\n" + + "\n" + + "By default, two mod presets are provided for restoring the old death\n" + + "sound and the old mouse cursor.\n"; + + // TODO: reduce reliance on event handlers for signalling property changes to the bootstrapper dialog + // i mean, chances are we can just use IBootstrapperDialog now? + + // public IBootstrapperDialog BootstrapperDialog; + + public event EventHandler CloseDialogEvent; + 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; + } + } + #endregion + + #region Core + public Bootstrapper() + { + FreshInstall = String.IsNullOrEmpty(Program.Settings.VersionGuid); + Client.Timeout = TimeSpan.FromMinutes(10); + } + + public void Initialize(BootstrapperStyle bootstrapperStyle, string? launchCommandLine = null) + { + LaunchCommandLine = launchCommandLine; + + switch (bootstrapperStyle) + { + case BootstrapperStyle.VistaDialog: + Application.Run(new VistaDialog(this)); + break; + + case BootstrapperStyle.LegacyDialog2009: + Application.Run(new LegacyDialog2009(this)); + break; + + case BootstrapperStyle.LegacyDialog2011: + Application.Run(new LegacyDialog2011(this)); + break; + + case BootstrapperStyle.ProgressDialog: + Application.Run(new ProgressDialog(this)); + break; + + case BootstrapperStyle.ProgressDialogDark: + Application.Run(new ProgressDialogDark(this)); + break; + } + } + + public async Task Run() + { + /* Message = "hi"; + Progress = 42; + ProgressStyle = ProgressBarStyle.Blocks; + CancelEnabled = true; + + BootstrapperDialog.Message = "hi"; + BootstrapperDialog.ProgressValue = 42; + BootstrapperDialog.ProgressStyle = ProgressBarStyle.Blocks; + BootstrapperDialog.CancelEnabled = true; + + return; */ + + 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(); + } + + ApplyModifications(); + + if (Program.IsFirstRun) + Program.SettingsManager.ShouldSave = true; + + if (Program.IsFirstRun || FreshInstall) + Register(); + + CheckInstall(); + + await RbxFpsUnlocker.CheckInstall(); + + await StartRoblox(); + + Program.Exit(); + } + + private async Task CheckLatestVersion() + { + Message = "Connecting to Roblox..."; + + VersionGuid = await Client.GetStringAsync($"{Program.BaseUrlSetup}/version"); + VersionFolder = Path.Combine(Directories.Versions, VersionGuid); + VersionPackageManifest = await PackageManifest.Get(VersionGuid); + VersionFileManifest = await FileManifest.Get(VersionGuid); + } + + 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; + + using (SystemEvent startEvent = new(startEventName)) + { + bool shouldWait = false; + + Process gameClient = Process.Start(Path.Combine(VersionFolder, "RobloxPlayerBeta.exe"), LaunchCommandLine); + Process? rbxFpsUnlocker = null; + DiscordRichPresence? richPresence = null; + + bool startEventFired = await startEvent.WaitForEvent(); + + startEvent.Close(); + + if (!startEventFired) + return; + + if (Program.Settings.RFUEnabled && Process.GetProcessesByName("rbxfpsunlocker").Length == 0) + { + ProcessStartInfo startInfo = new(); + startInfo.FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe"); + startInfo.WorkingDirectory = Path.Combine(Directories.Integrations, "rbxfpsunlocker"); + + rbxFpsUnlocker = Process.Start(startInfo); + + if (Program.Settings.RFUAutoclose) + shouldWait = true; + } + + // event fired, wait for 6 seconds then close + await Task.Delay(6000); + + // now we move onto handling rich presence + // except beta app launch since we have to rely strictly on website launch + if (Program.Settings.UseDiscordRichPresence && !LaunchCommandLine.Contains("--app")) + { + // probably not the most ideal way to do this + string? placeId = Utilities.GetKeyValue(LaunchCommandLine, "placeId=", '&'); + + if (placeId is not null) + { + richPresence = new DiscordRichPresence(); + bool presenceSet = await richPresence.SetPresence(placeId); + + if (presenceSet) + shouldWait = true; + else + richPresence.Dispose(); + } + + } + + if (!shouldWait) + return; + + // keep bloxstrap open in the background + CloseDialog(); + await gameClient.WaitForExitAsync(); + + if (richPresence is not null) + richPresence.Dispose(); + + if (Program.Settings.RFUAutoclose && rbxFpsUnlocker is not null) + rbxFpsUnlocker.Kill(); + } + } + + public void CancelButtonClicked() + { + if (!CancelEnabled) + { + Program.Exit(); + return; + } + + CancelFired = true; + + try + { + if (Program.IsFirstRun) + Directory.Delete(Directories.Base, true); + else if (Directory.Exists(VersionFolder)) + Directory.Delete(VersionFolder, true); + } + catch (Exception) { } + + Program.Exit(); + } + + private void ShowSuccess(string message) + { + ShowSuccessEvent.Invoke(this, new ChangeEventArgs(message)); + } + + private void PromptShutdown() + { + PromptShutdownEvent.Invoke(this, new EventArgs()); + } + + private void CloseDialog() + { + CloseDialogEvent.Invoke(this, new EventArgs()); + } + #endregion + + #region App Install + 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 != Directories.Base) + { + try + { + if (Directory.Exists(oldInstallLocation)) + Directory.Delete(oldInstallLocation, true); + } + catch (Exception) { } + + applicationKey.DeleteValue("OldInstallLocation"); + } + + applicationKey.SetValue("InstallLocation", Directories.Base); + applicationKey.Close(); + + // set uninstall key + RegistryKey uninstallKey = Registry.CurrentUser.CreateSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Program.ProjectName}"); + uninstallKey.SetValue("DisplayIcon", $"{Directories.App},0"); + uninstallKey.SetValue("DisplayName", Program.ProjectName); + uninstallKey.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd")); + uninstallKey.SetValue("InstallLocation", Directories.Base); + uninstallKey.SetValue("NoRepair", 1); + uninstallKey.SetValue("Publisher", Program.ProjectName); + uninstallKey.SetValue("ModifyPath", $"\"{Directories.App}\" -preferences"); + uninstallKey.SetValue("UninstallString", $"\"{Directories.App}\" -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", Directories.App); + Protocol.Register("roblox-player", "Roblox", Directories.App); + + // in case the user is reinstalling + if (File.Exists(Directories.App) && Program.IsFirstRun) + File.Delete(Directories.App); + + // check to make sure bootstrapper is in the install folder + if (!File.Exists(Directories.App) && Environment.ProcessPath is not null) + File.Copy(Environment.ProcessPath, Directories.App); + + // this SHOULD go under Register(), + // but then people who have Bloxstrap v1.0.0 installed won't have this without a reinstall + // maybe in a later version? + if (!Directory.Exists(Program.StartMenu)) + { + Directory.CreateDirectory(Program.StartMenu); + + ShellLink.Shortcut.CreateShortcut(Directories.App, "", Directories.App, 0) + .WriteToFile(Path.Combine(Program.StartMenu, "Play Roblox.lnk")); + + ShellLink.Shortcut.CreateShortcut(Directories.App, "-preferences", Directories.App, 0) + .WriteToFile(Path.Combine(Program.StartMenu, $"Configure {Program.ProjectName}.lnk")); + } + } + + private void Uninstall() + { + CheckIfRunning(); + + 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}"); + + // delete start menu folder + Directory.Delete(Program.StartMenu, true); + + // delete uninstall key + Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Program.ProjectName}"); + + // delete installation folder + // (should delete everything except bloxstrap itself) + Directory.Delete(Directories.Base, true); + } + catch (Exception) { } + + ShowSuccess($"{Program.ProjectName} has been uninstalled"); + } + #endregion + + #region Roblox Install + private async Task InstallLatestVersion() + { + CheckIfRunning(); + + if (FreshInstall) + Message = "Installing Roblox..."; + else + Message = "Upgrading Roblox..."; + + Directory.CreateDirectory(Directories.Base); + + 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); + + Directory.CreateDirectory(Directories.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(Directories.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(Directories.Downloads)) + { + if (!VersionPackageManifest.Exists(package => filename.Contains(package.Signature))) + File.Delete(filename); + } + + if (VersionGuid != Program.Settings.VersionGuid) + { + // and also to delete our old version folder + Directory.Delete(Path.Combine(Directories.Versions, Program.Settings.VersionGuid), true); + } + } + + CancelEnabled = false; + + Program.Settings.VersionGuid = VersionGuid; + } + + private void ApplyModifications() + { + string modFolder = Path.Combine(Directories.Modifications); + string manifestFile = Path.Combine(Directories.Base, "ModManifest.txt"); + + List manifestFiles = new(); + List modFolderFiles = new(); + + if (!Directory.Exists(modFolder)) + { + Directory.CreateDirectory(modFolder); + File.WriteAllText(Path.Combine(modFolder, "README.txt"), ModReadme); + } + + CheckModPreset(Program.Settings.UseOldDeathSound, @"content\sounds\ouch.ogg", Program.Base64OldDeathSound); + CheckModPreset(Program.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowCursor.png", Program.Base64OldArrowCursor); + CheckModPreset(Program.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowFarCursor.png", Program.Base64OldArrowFarCursor); + + foreach (string file in Directory.GetFiles(modFolder, "*.*", SearchOption.AllDirectories)) + { + // get relative directory path + string relativeFile = file.Substring(modFolder.Length + 1); + + // ignore files placed in the root directory + if (!relativeFile.Contains(@"\")) + continue; + + modFolderFiles.Add(relativeFile); + } + + // the manifest is primarily here to keep track of what files have been + // deleted from the modifications folder, so that we know when to restore the + // original files from the downloaded packages + + if (File.Exists(manifestFile)) + manifestFiles = File.ReadAllLines(manifestFile).ToList(); + else + manifestFiles = modFolderFiles; + + // copy and overwrite + foreach (string file in modFolderFiles) + { + string fileModFolder = Path.Combine(modFolder, file); + string fileVersionFolder = Path.Combine(VersionFolder, file); + + if (File.Exists(fileVersionFolder)) + { + if (Utilities.MD5File(fileModFolder) == Utilities.MD5File(fileVersionFolder)) + continue; + } + + File.Copy(fileModFolder, fileVersionFolder, true); + } + + // now we check for files that have been deleted from the mod folder + foreach (string fileLocation in manifestFiles) + { + if (modFolderFiles.Contains(fileLocation)) + continue; + + KeyValuePair packageDirectory; + + try + { + packageDirectory = PackageDirectories.First(x => x.Key != "RobloxApp.zip" && fileLocation.StartsWith(x.Value)); + } + catch (InvalidOperationException) + { + // package doesn't exist, likely mistakenly placed file + continue; + } + + // restore original file + string fileName = fileLocation.Substring(packageDirectory.Value.Length); + ExtractFileFromPackage(packageDirectory.Key, fileName); + } + + File.WriteAllLines(manifestFile, modFolderFiles); + } + + private void CheckModPreset(bool condition, string location, string base64Contents) + { + string modFolderLocation = Path.Combine(Directories.Modifications, location); + + Directory.CreateDirectory(Path.GetDirectoryName(modFolderLocation)); + + if (condition) + { + if (!File.Exists(modFolderLocation)) + { + File.WriteAllBytes(modFolderLocation, Convert.FromBase64String(base64Contents)); + } + } + else if (File.Exists(modFolderLocation)) + { + File.Delete(modFolderLocation); + } + } + + private async void DownloadPackage(Package package) + { + string packageUrl = $"{Program.BaseUrlSetup}/{VersionGuid}-{package.Name}"; + string packageLocation = Path.Combine(Directories.Downloads, package.Signature); + string robloxPackageLocation = Path.Combine(Program.LocalAppData, "Roblox", "Downloads", package.Signature); + + if (File.Exists(packageLocation)) + { + FileInfo file = new(packageLocation); + + string calculatedMD5 = Utilities.MD5File(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(Directories.Downloads, 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); + } + } + } + + private void ExtractFileFromPackage(string packageName, string fileName) + { + Package? package = VersionPackageManifest.Find(x => x.Name == packageName); + + if (package is null) + return; + + DownloadPackage(package); + + string packageLocation = Path.Combine(Directories.Downloads, package.Signature); + string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); + + using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) + { + ZipArchiveEntry? entry = archive.Entries.Where(x => x.FullName == fileName).FirstOrDefault(); + + if (entry is null) + return; + + string fileLocation = Path.Combine(packageFolder, entry.FullName); + + if (File.Exists(fileLocation)) + File.Delete(fileLocation); + + entry.ExtractToFile(fileLocation); + } + } + #endregion + } +} diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs b/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs deleted file mode 100644 index 54741ed..0000000 --- a/Bloxstrap/Bootstrapper/Bootstrapper.AppInstall.cs +++ /dev/null @@ -1,125 +0,0 @@ -using Microsoft.Win32; -using Bloxstrap.Helpers; - -namespace Bloxstrap -{ - partial class Bootstrapper - { - public static void Register() - { - if (Program.BaseDirectory is null) - return; - - 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("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); - - // this SHOULD go under Register(), - // but then people who have Bloxstrap v1.0.0 installed won't have this without a reinstall - // maybe in a later version? - if (!Directory.Exists(Program.StartMenuDirectory)) - { - Directory.CreateDirectory(Program.StartMenuDirectory); - - ShellLink.Shortcut.CreateShortcut(Program.FilePath, "", Program.FilePath, 0) - .WriteToFile(Path.Combine(Program.StartMenuDirectory, "Play Roblox.lnk")); - - ShellLink.Shortcut.CreateShortcut(Program.FilePath, "-preferences", Program.FilePath, 0) - .WriteToFile(Path.Combine(Program.StartMenuDirectory, "Configure Bloxstrap.lnk")); - } - } - - private void Uninstall() - { - if (Program.BaseDirectory is null) - return; - - CheckIfRunning(); - - 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}"); - - // delete start menu folder - Directory.Delete(Program.StartMenuDirectory, true); - - // delete uninstall key - Registry.CurrentUser.DeleteSubKey($@"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Program.ProjectName}"); - - // delete installation folder - // (should delete everything except bloxstrap itself) - Directory.Delete(Program.BaseDirectory, true); - } - catch (Exception) { } - - ShowSuccess($"{Program.ProjectName} has been uninstalled"); - } - } -} diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.Properties.cs b/Bloxstrap/Bootstrapper/Bootstrapper.Properties.cs deleted file mode 100644 index bd36f05..0000000 --- a/Bloxstrap/Bootstrapper/Bootstrapper.Properties.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Bloxstrap.Helpers.RSMM; - -namespace Bloxstrap -{ - partial 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 CloseDialogEvent; - 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; - } - } - } -} diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs deleted file mode 100644 index 151a40d..0000000 --- a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxInstall.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System.Diagnostics; -using System.IO.Compression; - -using Bloxstrap.Helpers; -using Bloxstrap.Helpers.RSMM; - -namespace Bloxstrap -{ - partial class Bootstrapper - { - private async Task CheckLatestVersion() - { - if (Program.BaseDirectory is null) - return; - - Message = "Connecting to Roblox..."; - - VersionGuid = await Client.GetStringAsync($"{Program.BaseUrlSetup}/version"); - VersionFolder = Path.Combine(Program.BaseDirectory, "Versions", VersionGuid); - VersionPackageManifest = await PackageManifest.Get(VersionGuid); - VersionFileManifest = await FileManifest.Get(VersionGuid); - } - - private async Task InstallLatestVersion() - { - if (Program.BaseDirectory is null) - return; - - 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); - - 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); - } - - if (VersionGuid != Program.Settings.VersionGuid) - { - // and also to delete our old version folder - Directory.Delete(Path.Combine(Program.BaseDirectory, "Versions", Program.Settings.VersionGuid), true); - } - } - - 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 - - await ModifyDeathSound(); - await ModifyMouseCursor(); - } - - 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 = Utilities.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); - } - } - } - - private void ExtractFilesFromPackage(string packageName, string[] files) - { - Package? package = VersionPackageManifest.Find(x => x.Name == packageName); - - if (package is null) - return; - - DownloadPackage(package); - - string packageLocation = Path.Combine(DownloadsFolder, package.Signature); - string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]); - - using (ZipArchive archive = ZipFile.OpenRead(packageLocation)) - { - foreach (string fileName in files) - { - ZipArchiveEntry? entry = archive.Entries.Where(x => x.FullName == fileName).FirstOrDefault(); - - if (entry is null) - return; - - string fileLocation = Path.Combine(packageFolder, entry.FullName); - - if (File.Exists(fileLocation)) - File.Delete(fileLocation); - - entry.ExtractToFile(fileLocation); - } - } - } - } -} diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs b/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs deleted file mode 100644 index b5545d5..0000000 --- a/Bloxstrap/Bootstrapper/Bootstrapper.RobloxModifications.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Bloxstrap.Helpers; - -namespace Bloxstrap -{ - partial class Bootstrapper - { - private async Task ModifyDeathSound() - { - string fileContentName = "ouch.ogg"; - string fileContentLocation = "content\\sounds\\ouch.ogg"; - string fileLocation = Path.Combine(VersionFolder, fileContentLocation); - - string officialHash = VersionFileManifest[fileContentLocation]; - string currentHash = Utilities.CalculateMD5(fileLocation); - - if (Program.Settings.UseOldDeathSound && currentHash == officialHash) - { - // let's get the old one! - - 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 && currentHash != officialHash) - { - // who's lame enough to ever do this? - // well, we need to re-extract the one that's in the content-sounds.zip package - - string[] files = { fileContentName }; - ExtractFilesFromPackage("content-sounds.zip", files); - } - } - - private async Task ModifyMouseCursor() - { - string baseFolder = Path.Combine(VersionFolder, "content\\textures\\"); - - string arrowCursor = "Cursors\\KeyboardMouse\\ArrowCursor.png"; - string arrowFarCursor = "Cursors\\KeyboardMouse\\ArrowFarCursor.png"; - - string officialHash = VersionFileManifest["content\\textures\\Cursors\\KeyboardMouse\\ArrowCursor.png"]; - string currentHash = Utilities.CalculateMD5(Path.Combine(baseFolder, arrowCursor)); - - if (Program.Settings.UseOldMouseCursor && currentHash == officialHash) - { - // the old cursors are actually still in the content\textures\ folder, so we can just get them from there - - File.Copy(Path.Combine(baseFolder, "ArrowCursor.png"), Path.Combine(baseFolder, arrowCursor), true); - File.Copy(Path.Combine(baseFolder, "ArrowFarCursor.png"), Path.Combine(baseFolder, arrowFarCursor), true); - } - else if (!Program.Settings.UseOldMouseCursor && currentHash != officialHash) - { - string[] files = { arrowCursor, arrowFarCursor }; - ExtractFilesFromPackage("content-textures2.zip", files); - } - } - } -} diff --git a/Bloxstrap/Bootstrapper/Bootstrapper.cs b/Bloxstrap/Bootstrapper/Bootstrapper.cs deleted file mode 100644 index 3fa20e4..0000000 --- a/Bloxstrap/Bootstrapper/Bootstrapper.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System.Diagnostics; - -using Bloxstrap.Enums; -using Bloxstrap.Dialogs.BootstrapperStyles; -using Bloxstrap.Helpers; -using Bloxstrap.Helpers.RSMM; - -namespace Bloxstrap -{ - public partial class Bootstrapper - { - public Bootstrapper() - { - if (Program.BaseDirectory is null) - return; - - FreshInstall = String.IsNullOrEmpty(Program.Settings.VersionGuid); - DownloadsFolder = Path.Combine(Program.BaseDirectory, "Downloads"); - Client.Timeout = TimeSpan.FromMinutes(10); - } - - public void Initialize(BootstrapperStyle bootstrapperStyle, string? launchCommandLine = null) - { - LaunchCommandLine = launchCommandLine; - - switch (bootstrapperStyle) - { - case BootstrapperStyle.VistaDialog: - Application.Run(new VistaDialog(this)); - break; - - case BootstrapperStyle.LegacyDialog2009: - Application.Run(new LegacyDialog2009(this)); - break; - - case BootstrapperStyle.LegacyDialog2011: - Application.Run(new LegacyDialog2011(this)); - break; - - case BootstrapperStyle.ProgressDialog: - Application.Run(new ProgressDialog(this)); - break; - - case BootstrapperStyle.ProgressDialogDark: - Application.Run(new ProgressDialogDark(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(); - - Program.Exit(); - } - - 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; - - using (SystemEvent startEvent = new(startEventName)) - { - Process gameClient = Process.Start(Path.Combine(VersionFolder, "RobloxPlayerBeta.exe"), LaunchCommandLine); - - bool startEventFired = await startEvent.WaitForEvent(); - - startEvent.Close(); - - if (!startEventFired) - return; - - // event fired, wait for 6 seconds then close - await Task.Delay(6000); - - // now we move onto handling rich presence - // except beta app launch since we have to rely strictly on website launch - if (!Program.Settings.UseDiscordRichPresence || LaunchCommandLine.Contains("--app")) - return; - - // probably not the most ideal way to do this - string? placeId = Utilities.GetKeyValue(LaunchCommandLine, "placeId=", '&'); - - if (placeId is null) - return; - - // keep bloxstrap open to handle rich presence - using (DiscordRichPresence richPresence = new()) - { - bool presenceSet = await richPresence.SetPresence(placeId); - - if (!presenceSet) - return; - - CloseDialog(); - - await gameClient.WaitForExitAsync(); - } - } - } - - public void CancelButtonClicked() - { - if (Program.BaseDirectory is null) - return; - - CancelFired = true; - - try - { - if (Program.IsFirstRun) - Directory.Delete(Program.BaseDirectory, true); - else if (Directory.Exists(VersionFolder)) - Directory.Delete(VersionFolder, true); - } - catch (Exception) { } - - Program.Exit(); - } - - private void ShowSuccess(string message) - { - ShowSuccessEvent.Invoke(this, new ChangeEventArgs(message)); - } - - private void PromptShutdown() - { - PromptShutdownEvent.Invoke(this, new EventArgs()); - } - - private void CloseDialog() - { - CloseDialogEvent.Invoke(this, new EventArgs()); - } - } -} diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs b/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs index 9f9f2a8..912fd24 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/BootstrapperStyleForm.cs @@ -1,11 +1,9 @@ -using System.Diagnostics; - -using Bloxstrap.Helpers; +using Bloxstrap.Helpers; using Bloxstrap.Helpers.RSMM; namespace Bloxstrap.Dialogs.BootstrapperStyles { - public class BootstrapperStyleForm : Form, IBootstrapperStyle + public class BootstrapperStyleForm : Form, IBootstrapperDialog { public Bootstrapper? Bootstrapper { get; set; } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs b/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperDialog.cs similarity index 95% rename from Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs rename to Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperDialog.cs index 25cbe63..de43d71 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperStyle.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/IBootstrapperDialog.cs @@ -2,7 +2,7 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles { - interface IBootstrapperStyle + public interface IBootstrapperDialog { Bootstrapper? Bootstrapper { get; set; } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs index 7e4e43c..9e57fcc 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.Designer.cs @@ -80,6 +80,7 @@ this.Name = "LegacyDialog2009"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "LegacyDialog2009"; + this.Load += new System.EventHandler(this.LegacyDialog2009_Load); this.ResumeLayout(false); } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs index 23d7ca4..101d528 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2009.cs @@ -37,5 +37,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles SetupDialog(); } + + private void LegacyDialog2009_Load(object sender, EventArgs e) + { + this.Activate(); + } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs index d131290..d552f0d 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.Designer.cs @@ -75,7 +75,7 @@ this.buttonCancel.Visible = false; this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click); // - // LegacyDialog + // LegacyDialog2011 // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; @@ -93,6 +93,7 @@ this.Name = "LegacyDialog2011"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "LegacyDialog2011"; + this.Load += new System.EventHandler(this.LegacyDialog2011_Load); ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); this.ResumeLayout(false); diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs index f0144b6..fde89c0 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/LegacyDialog2011.cs @@ -41,5 +41,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles SetupDialog(); } + + private void LegacyDialog2011_Load(object sender, EventArgs e) + { + this.Activate(); + } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs index e53820a..d884efb 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.Designer.cs @@ -108,6 +108,7 @@ this.Name = "ProgressDialog"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "ProgressDialog"; + this.Load += new System.EventHandler(this.ProgressDialog_Load); ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).EndInit(); this.panel1.ResumeLayout(false); diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs index 2dccbfc..00e4d7d 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialog.cs @@ -50,5 +50,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles { this.buttonCancel.Image = Properties.Resources.CancelButton; } + + private void ProgressDialog_Load(object sender, EventArgs e) + { + this.Activate(); + } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs index 59b989b..f08c732 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.Designer.cs @@ -109,6 +109,7 @@ this.Name = "ProgressDialogDark"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "ProgressDialog"; + this.Load += new System.EventHandler(this.ProgressDialogDark_Load); ((System.ComponentModel.ISupportInitialize)(this.IconBox)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).EndInit(); this.panel1.ResumeLayout(false); diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs index 76e3399..3a8b82c 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/ProgressDialogDark.cs @@ -50,5 +50,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles { this.buttonCancel.Image = Properties.Resources.DarkCancelButton; } + + private void ProgressDialogDark_Load(object sender, EventArgs e) + { + this.Activate(); + } } } diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs index b11f2c8..defd569 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.Designer.cs @@ -30,15 +30,17 @@ { this.SuspendLayout(); // - // TestDialog + // VistaDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(0, 0); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Name = "VistaDialog"; + this.Opacity = 0D; this.ShowInTaskbar = false; this.Text = "VistaDialog"; + this.WindowState = System.Windows.Forms.FormWindowState.Minimized; this.Load += new System.EventHandler(this.TestDialog_Load); this.ResumeLayout(false); diff --git a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs index e36b065..5a97ea5 100644 --- a/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs +++ b/Bloxstrap/Dialogs/BootstrapperStyles/VistaDialog.cs @@ -163,7 +163,6 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles private void TestDialog_Load(object sender, EventArgs e) { - this.Hide(); TaskDialog.ShowDialog(Dialog); } } diff --git a/Bloxstrap/Dialogs/Preferences.Designer.cs b/Bloxstrap/Dialogs/Preferences.Designer.cs index b505db7..0ac8886 100644 --- a/Bloxstrap/Dialogs/Preferences.Designer.cs +++ b/Bloxstrap/Dialogs/Preferences.Designer.cs @@ -32,6 +32,10 @@ this.label1 = new System.Windows.Forms.Label(); this.Tabs = new System.Windows.Forms.TabControl(); this.DialogTab = new System.Windows.Forms.TabPage(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.RFUWebsite = new System.Windows.Forms.LinkLabel(); + this.ToggleRFUAutoclose = new System.Windows.Forms.CheckBox(); + this.ToggleRFUEnabled = new System.Windows.Forms.CheckBox(); this.groupBox5 = new System.Windows.Forms.GroupBox(); this.ToggleRPCButtons = new System.Windows.Forms.CheckBox(); this.ToggleDiscordRichPresence = new System.Windows.Forms.CheckBox(); @@ -42,6 +46,8 @@ this.StyleSelection = new System.Windows.Forms.ListBox(); this.InstallationTab = new System.Windows.Forms.TabPage(); this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.LabelModFolderInstall = new System.Windows.Forms.Label(); + this.ButtonOpenModFolder = new System.Windows.Forms.Button(); this.ToggleMouseCursor = new System.Windows.Forms.CheckBox(); this.ToggleDeathSound = new System.Windows.Forms.CheckBox(); this.GroupBoxInstallLocation = new System.Windows.Forms.GroupBox(); @@ -49,11 +55,13 @@ this.InstallLocation = new System.Windows.Forms.TextBox(); this.SaveButton = new System.Windows.Forms.Button(); this.panel1 = new System.Windows.Forms.Panel(); + this.ToggleCheckForUpdates = new System.Windows.Forms.CheckBox(); this.PreviewButton = new System.Windows.Forms.Button(); this.InstallLocationBrowseDialog = new System.Windows.Forms.FolderBrowserDialog(); this.InfoTooltip = new System.Windows.Forms.ToolTip(this.components); this.Tabs.SuspendLayout(); this.DialogTab.SuspendLayout(); + this.groupBox1.SuspendLayout(); this.groupBox5.SuspendLayout(); this.groupBox3.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.IconPreview)).BeginInit(); @@ -86,6 +94,7 @@ // // DialogTab // + this.DialogTab.Controls.Add(this.groupBox1); this.DialogTab.Controls.Add(this.groupBox5); this.DialogTab.Controls.Add(this.groupBox3); this.DialogTab.Controls.Add(this.groupBox2); @@ -97,13 +106,70 @@ this.DialogTab.Text = "Bootstrapper"; this.DialogTab.UseVisualStyleBackColor = true; // + // groupBox1 + // + this.groupBox1.Controls.Add(this.RFUWebsite); + this.groupBox1.Controls.Add(this.ToggleRFUAutoclose); + this.groupBox1.Controls.Add(this.ToggleRFUEnabled); + this.groupBox1.Location = new System.Drawing.Point(192, 146); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(235, 67); + this.groupBox1.TabIndex = 8; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "FPS Unlocker"; + // + // RFUWebsite + // + this.RFUWebsite.BackColor = System.Drawing.Color.White; + this.RFUWebsite.Cursor = System.Windows.Forms.Cursors.Hand; + this.RFUWebsite.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; + this.RFUWebsite.Location = new System.Drawing.Point(174, 0); + this.RFUWebsite.Margin = new System.Windows.Forms.Padding(0); + this.RFUWebsite.Name = "RFUWebsite"; + this.RFUWebsite.Size = new System.Drawing.Size(55, 18); + this.RFUWebsite.TabIndex = 2; + this.RFUWebsite.TabStop = true; + this.RFUWebsite.Tag = ""; + this.RFUWebsite.Text = "(website)"; + this.RFUWebsite.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.RFUWebsite_LinkClicked); + // + // ToggleRFUAutoclose + // + this.ToggleRFUAutoclose.AutoSize = true; + this.ToggleRFUAutoclose.Checked = true; + this.ToggleRFUAutoclose.CheckState = System.Windows.Forms.CheckState.Checked; + this.ToggleRFUAutoclose.Location = new System.Drawing.Point(9, 40); + this.ToggleRFUAutoclose.Name = "ToggleRFUAutoclose"; + this.ToggleRFUAutoclose.Size = new System.Drawing.Size(209, 19); + this.ToggleRFUAutoclose.TabIndex = 1; + this.ToggleRFUAutoclose.Text = "Automatically close on Roblox exit"; + this.InfoTooltip.SetToolTip(this.ToggleRFUAutoclose, "If enabled, rbxfpsunlocker will automatically close when Roblox is closed."); + this.ToggleRFUAutoclose.UseVisualStyleBackColor = true; + this.ToggleRFUAutoclose.CheckedChanged += new System.EventHandler(this.ToggleRFUAutoclose_CheckedChanged); + // + // ToggleRFUEnabled + // + this.ToggleRFUEnabled.AutoSize = true; + this.ToggleRFUEnabled.Checked = true; + this.ToggleRFUEnabled.CheckState = System.Windows.Forms.CheckState.Checked; + this.ToggleRFUEnabled.Location = new System.Drawing.Point(9, 19); + this.ToggleRFUEnabled.Name = "ToggleRFUEnabled"; + this.ToggleRFUEnabled.Size = new System.Drawing.Size(127, 19); + this.ToggleRFUEnabled.TabIndex = 0; + this.ToggleRFUEnabled.Text = "Use rbxfpsunlocker"; + this.InfoTooltip.SetToolTip(this.ToggleRFUEnabled, "If enabled, rbxfpsunlocker is downloaded.\r\nWhen Roblox is started, rbxfpsunlocker" + + " will automatically start too, \r\nbeing minimized to your system tray by default." + + ""); + this.ToggleRFUEnabled.UseVisualStyleBackColor = true; + this.ToggleRFUEnabled.CheckedChanged += new System.EventHandler(this.ToggleRFUEnabled_CheckedChanged); + // // groupBox5 // this.groupBox5.Controls.Add(this.ToggleRPCButtons); this.groupBox5.Controls.Add(this.ToggleDiscordRichPresence); this.groupBox5.Location = new System.Drawing.Point(5, 146); this.groupBox5.Name = "groupBox5"; - this.groupBox5.Size = new System.Drawing.Size(422, 67); + this.groupBox5.Size = new System.Drawing.Size(179, 67); this.groupBox5.TabIndex = 7; this.groupBox5.TabStop = false; this.groupBox5.Text = "Discord Rich Presence"; @@ -113,9 +179,11 @@ this.ToggleRPCButtons.AutoSize = true; this.ToggleRPCButtons.Location = new System.Drawing.Point(9, 40); this.ToggleRPCButtons.Name = "ToggleRPCButtons"; - this.ToggleRPCButtons.Size = new System.Drawing.Size(196, 19); + this.ToggleRPCButtons.Size = new System.Drawing.Size(155, 19); this.ToggleRPCButtons.TabIndex = 1; - this.ToggleRPCButtons.Text = "Hide activity interaction buttons"; + this.ToggleRPCButtons.Text = "Hide interaction buttons"; + this.InfoTooltip.SetToolTip(this.ToggleRPCButtons, "Choose whether the buttons to play/view game details should be hidden from your a" + + "ctivity status."); this.ToggleRPCButtons.UseVisualStyleBackColor = true; this.ToggleRPCButtons.CheckedChanged += new System.EventHandler(this.ToggleRPCButtons_CheckedChanged); // @@ -129,6 +197,9 @@ this.ToggleDiscordRichPresence.Size = new System.Drawing.Size(129, 19); this.ToggleDiscordRichPresence.TabIndex = 0; this.ToggleDiscordRichPresence.Text = "Show game activity"; + this.InfoTooltip.SetToolTip(this.ToggleDiscordRichPresence, "Choose whether to show what game you\'re playing on your Discord activity status.\r" + + "\nThis will only work when you launch a game from the website, and is not support" + + "ed in the Beta App."); this.ToggleDiscordRichPresence.UseVisualStyleBackColor = true; this.ToggleDiscordRichPresence.CheckedChanged += new System.EventHandler(this.ToggleDiscordRichPresence_CheckedChanged); // @@ -161,6 +232,7 @@ this.IconSelection.Name = "IconSelection"; this.IconSelection.Size = new System.Drawing.Size(100, 109); this.IconSelection.TabIndex = 4; + this.InfoTooltip.SetToolTip(this.IconSelection, "Choose what icon the bootstrapper should use."); this.IconSelection.SelectedIndexChanged += new System.EventHandler(this.IconSelection_SelectedIndexChanged); // // groupBox2 @@ -181,6 +253,8 @@ this.StyleSelection.Name = "StyleSelection"; this.StyleSelection.Size = new System.Drawing.Size(161, 109); this.StyleSelection.TabIndex = 3; + this.InfoTooltip.SetToolTip(this.StyleSelection, "Choose how the bootstrapper dialog should look.\r\nYou can use the \'Preview\' button" + + " to preview the bootstrapper look."); this.StyleSelection.SelectedIndexChanged += new System.EventHandler(this.StyleSelection_SelectedIndexChanged); // // InstallationTab @@ -197,15 +271,40 @@ // // groupBox4 // + this.groupBox4.Controls.Add(this.LabelModFolderInstall); + this.groupBox4.Controls.Add(this.ButtonOpenModFolder); this.groupBox4.Controls.Add(this.ToggleMouseCursor); this.groupBox4.Controls.Add(this.ToggleDeathSound); this.groupBox4.Location = new System.Drawing.Point(5, 60); this.groupBox4.Name = "groupBox4"; - this.groupBox4.Size = new System.Drawing.Size(422, 65); + this.groupBox4.Size = new System.Drawing.Size(422, 95); this.groupBox4.TabIndex = 2; this.groupBox4.TabStop = false; this.groupBox4.Text = "Modifications"; // + // LabelModFolderInstall + // + this.LabelModFolderInstall.AutoSize = true; + this.LabelModFolderInstall.Location = new System.Drawing.Point(6, 67); + this.LabelModFolderInstall.Margin = new System.Windows.Forms.Padding(0); + this.LabelModFolderInstall.Name = "LabelModFolderInstall"; + this.LabelModFolderInstall.Size = new System.Drawing.Size(329, 15); + this.LabelModFolderInstall.TabIndex = 7; + this.LabelModFolderInstall.Text = "Other modifications can be added once Bloxstrap is installed."; + this.LabelModFolderInstall.Visible = false; + // + // ButtonOpenModFolder + // + this.ButtonOpenModFolder.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.ButtonOpenModFolder.Location = new System.Drawing.Point(8, 62); + this.ButtonOpenModFolder.Name = "ButtonOpenModFolder"; + this.ButtonOpenModFolder.Size = new System.Drawing.Size(122, 25); + this.ButtonOpenModFolder.TabIndex = 6; + this.ButtonOpenModFolder.Text = "Open Mod Folder"; + this.InfoTooltip.SetToolTip(this.ButtonOpenModFolder, "Open the folder for applying Roblox client modifications."); + this.ButtonOpenModFolder.UseVisualStyleBackColor = true; + this.ButtonOpenModFolder.Click += new System.EventHandler(this.ButtonOpenModFolder_Click); + // // ToggleMouseCursor // this.ToggleMouseCursor.AutoSize = true; @@ -246,9 +345,9 @@ // 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.Location = new System.Drawing.Point(335, 20); this.InstallLocationBrowseButton.Name = "InstallLocationBrowseButton"; - this.InstallLocationBrowseButton.Size = new System.Drawing.Size(86, 25); + this.InstallLocationBrowseButton.Size = new System.Drawing.Size(79, 25); this.InstallLocationBrowseButton.TabIndex = 5; this.InstallLocationBrowseButton.Text = "Browse..."; this.InstallLocationBrowseButton.UseVisualStyleBackColor = true; @@ -260,8 +359,10 @@ 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.Size = new System.Drawing.Size(319, 23); this.InstallLocation.TabIndex = 4; + this.InfoTooltip.SetToolTip(this.InstallLocation, "Choose where Bloxstrap should install to.\r\nThis is useful if you typically instal" + + "l all your games to a separate storage drive."); // // SaveButton // @@ -279,6 +380,7 @@ 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.Controls.Add(this.ToggleCheckForUpdates); this.panel1.Controls.Add(this.PreviewButton); this.panel1.Controls.Add(this.SaveButton); this.panel1.Location = new System.Drawing.Point(-1, 298); @@ -286,6 +388,19 @@ this.panel1.Size = new System.Drawing.Size(466, 42); this.panel1.TabIndex = 6; // + // ToggleCheckForUpdates + // + this.ToggleCheckForUpdates.AutoSize = true; + this.ToggleCheckForUpdates.Checked = true; + this.ToggleCheckForUpdates.CheckState = System.Windows.Forms.CheckState.Checked; + this.ToggleCheckForUpdates.Location = new System.Drawing.Point(14, 12); + this.ToggleCheckForUpdates.Name = "ToggleCheckForUpdates"; + this.ToggleCheckForUpdates.Size = new System.Drawing.Size(179, 19); + this.ToggleCheckForUpdates.TabIndex = 7; + this.ToggleCheckForUpdates.Text = "Check for updates on startup"; + this.ToggleCheckForUpdates.UseVisualStyleBackColor = true; + this.ToggleCheckForUpdates.CheckedChanged += new System.EventHandler(this.ToggleCheckForUpdates_CheckedChanged); + // // PreviewButton // this.PreviewButton.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); @@ -318,8 +433,11 @@ this.Name = "Preferences"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Preferences"; + this.Load += new System.EventHandler(this.Preferences_Load); this.Tabs.ResumeLayout(false); this.DialogTab.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); this.groupBox5.ResumeLayout(false); this.groupBox5.PerformLayout(); this.groupBox3.ResumeLayout(false); @@ -331,6 +449,7 @@ this.GroupBoxInstallLocation.ResumeLayout(false); this.GroupBoxInstallLocation.PerformLayout(); this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); this.ResumeLayout(false); } @@ -360,5 +479,12 @@ private ToolTip InfoTooltip; private CheckBox ToggleMouseCursor; private CheckBox ToggleRPCButtons; + private GroupBox groupBox1; + private LinkLabel RFUWebsite; + private CheckBox ToggleRFUAutoclose; + private CheckBox ToggleRFUEnabled; + private CheckBox ToggleCheckForUpdates; + private Button ButtonOpenModFolder; + private Label LabelModFolderInstall; } } \ No newline at end of file diff --git a/Bloxstrap/Dialogs/Preferences.cs b/Bloxstrap/Dialogs/Preferences.cs index e4577f5..139e11b 100644 --- a/Bloxstrap/Dialogs/Preferences.cs +++ b/Bloxstrap/Dialogs/Preferences.cs @@ -1,8 +1,12 @@ -using Microsoft.Win32; +using System.IO; +using System.Diagnostics; + +using Microsoft.Win32; -using Bloxstrap.Helpers; -using Bloxstrap.Enums; using Bloxstrap.Dialogs.BootstrapperStyles; +using Bloxstrap.Enums; +using Bloxstrap.Helpers; +using Bloxstrap.Helpers.Integrations; namespace Bloxstrap.Dialogs { @@ -30,10 +34,6 @@ namespace Bloxstrap.Dialogs private BootstrapperStyle? _selectedStyle; private BootstrapperIcon? _selectedIcon; - private bool _useDiscordRichPresence = true; - private bool _hideRPCButtons = false; - private bool _useOldDeathSound = true; - private bool _useOldMouseCursor = false; private BootstrapperStyle SelectedStyle { @@ -68,67 +68,6 @@ namespace Bloxstrap.Dialogs } } - private bool UseDiscordRichPresence - { - get => _useDiscordRichPresence; - - set - { - if (_useDiscordRichPresence == value) - return; - - _useDiscordRichPresence = value; - - this.ToggleDiscordRichPresence.Checked = value; - this.ToggleRPCButtons.Enabled = value; - } - } - - private bool HideRPCButtons - { - get => _hideRPCButtons; - - set - { - if (_hideRPCButtons == value) - return; - - _hideRPCButtons = value; - - this.ToggleRPCButtons.Checked = value; - } - } - - private bool UseOldDeathSound - { - get => _useOldDeathSound; - - set - { - if (_useOldDeathSound == value) - return; - - _useOldDeathSound = value; - - this.ToggleDeathSound.Checked = value; - } - } - - private bool UseOldMouseCursor - { - get => _useOldMouseCursor; - - set - { - if (_useOldMouseCursor == value) - return; - - _useOldMouseCursor = value; - - this.ToggleMouseCursor.Checked = value; - } - } - public Preferences() { InitializeComponent(); @@ -142,6 +81,8 @@ namespace Bloxstrap.Dialogs { this.SaveButton.Text = "Install"; this.InstallLocation.Text = Path.Combine(Program.LocalAppData, Program.ProjectName); + this.ButtonOpenModFolder.Visible = false; + this.LabelModFolderInstall.Visible = true; } else { @@ -158,18 +99,22 @@ namespace Bloxstrap.Dialogs this.IconSelection.Items.Add(icon.Key); } - this.InfoTooltip.SetToolTip(this.StyleSelection, "Choose how the bootstrapper dialog should look.\nYou can use the 'Preview' button to preview the bootstrapper look."); - this.InfoTooltip.SetToolTip(this.IconSelection, "Choose what icon the bootstrapper should use."); - this.InfoTooltip.SetToolTip(this.GroupBoxInstallLocation, "Choose where Bloxstrap should install to.\nThis is useful if you typically install all your games to a separate storage drive."); - this.InfoTooltip.SetToolTip(this.ToggleDiscordRichPresence, "Choose whether to show what game you're playing on your Discord activity status.\nThis will only work when you launch a game from the website, and is not supported in the Beta App."); - this.InfoTooltip.SetToolTip(this.ToggleRPCButtons, "Choose whether the buttons to play/view game info should be hidden from activity status."); + if (!Environment.Is64BitOperatingSystem) + this.ToggleRFUEnabled.Enabled = false; SelectedStyle = Program.Settings.BootstrapperStyle; SelectedIcon = Program.Settings.BootstrapperIcon; - UseDiscordRichPresence = Program.Settings.UseDiscordRichPresence; - HideRPCButtons = Program.Settings.HideRPCButtons; - UseOldDeathSound = Program.Settings.UseOldDeathSound; - UseOldMouseCursor = Program.Settings.UseOldMouseCursor; + + this.ToggleCheckForUpdates.Checked = Program.Settings.CheckForUpdates; + + this.ToggleDiscordRichPresence.Checked = Program.Settings.UseDiscordRichPresence; + this.ToggleRPCButtons.Checked = Program.Settings.HideRPCButtons; + + this.ToggleRFUEnabled.Checked = Program.Settings.RFUEnabled; + this.ToggleRFUAutoclose.Checked = Program.Settings.RFUAutoclose; + + this.ToggleDeathSound.Checked = Program.Settings.UseOldDeathSound; + this.ToggleMouseCursor.Checked = Program.Settings.UseOldMouseCursor; } private void InstallLocationBrowseButton_Click(object sender, EventArgs e) @@ -239,7 +184,7 @@ namespace Bloxstrap.Dialogs { Program.SettingsManager.ShouldSave = true; - if (Program.BaseDirectory != installLocation) + if (Program.BaseDirectory is not null && Program.BaseDirectory != installLocation) { Program.ShowMessageBox(MessageBoxIcon.Information, $"{Program.ProjectName} will install to the new location you've set the next time it runs."); @@ -262,18 +207,12 @@ namespace Bloxstrap.Dialogs Program.Settings.BootstrapperStyle = SelectedStyle; Program.Settings.BootstrapperIcon = SelectedIcon; - Program.Settings.UseDiscordRichPresence = UseDiscordRichPresence; - Program.Settings.HideRPCButtons = HideRPCButtons; - Program.Settings.UseOldDeathSound = UseOldDeathSound; - Program.Settings.UseOldMouseCursor = UseOldMouseCursor; 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; @@ -301,29 +240,57 @@ namespace Bloxstrap.Dialogs break; } - Program.Settings.BootstrapperIcon = savedIcon; - this.Visible = true; } private void ToggleDiscordRichPresence_CheckedChanged(object sender, EventArgs e) { - UseDiscordRichPresence = this.ToggleDiscordRichPresence.Checked; + Program.Settings.UseDiscordRichPresence = this.ToggleRPCButtons.Enabled = this.ToggleDiscordRichPresence.Checked; } private void ToggleRPCButtons_CheckedChanged(object sender, EventArgs e) { - HideRPCButtons = this.ToggleRPCButtons.Checked; + Program.Settings.HideRPCButtons = this.ToggleRPCButtons.Checked; + } + + private void ToggleRFUEnabled_CheckedChanged(object sender, EventArgs e) + { + Program.Settings.RFUEnabled = this.ToggleRFUAutoclose.Enabled = this.ToggleRFUEnabled.Checked; + } + + private void ToggleRFUAutoclose_CheckedChanged(object sender, EventArgs e) + { + Program.Settings.RFUAutoclose = this.ToggleRFUAutoclose.Checked; } private void ToggleDeathSound_CheckedChanged(object sender, EventArgs e) { - UseOldDeathSound = this.ToggleDeathSound.Checked; + Program.Settings.UseOldDeathSound = this.ToggleDeathSound.Checked; } private void ToggleMouseCursor_CheckedChanged(object sender, EventArgs e) { - UseOldMouseCursor = this.ToggleMouseCursor.Checked; + Program.Settings.UseOldMouseCursor = this.ToggleMouseCursor.Checked; + } + + private void ToggleCheckForUpdates_CheckedChanged(object sender, EventArgs e) + { + Program.Settings.CheckForUpdates = this.ToggleCheckForUpdates.Checked; + } + + private void RFUWebsite_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Utilities.OpenWebsite($"https://github.com/{RbxFpsUnlocker.ProjectRepository}"); + } + + private void ButtonOpenModFolder_Click(object sender, EventArgs e) + { + Process.Start("explorer.exe", Directories.Modifications); + } + + private void Preferences_Load(object sender, EventArgs e) + { + this.Activate(); } } } diff --git a/Bloxstrap/Dialogs/Preferences.resx b/Bloxstrap/Dialogs/Preferences.resx index 453d499..b2b5813 100644 --- a/Bloxstrap/Dialogs/Preferences.resx +++ b/Bloxstrap/Dialogs/Preferences.resx @@ -57,10 +57,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 - 222, 17 + + 17, 17 + \ No newline at end of file diff --git a/Bloxstrap/Helpers/Directories.cs b/Bloxstrap/Helpers/Directories.cs new file mode 100644 index 0000000..3d49b0f --- /dev/null +++ b/Bloxstrap/Helpers/Directories.cs @@ -0,0 +1,28 @@ +using System.IO; + +namespace Bloxstrap.Helpers +{ + internal class Directories + { + public static string Base { get; private set; } = ""; + public static string Downloads { get; private set; } = ""; + public static string Integrations { get; private set; } = ""; + public static string Versions { get; private set; } = ""; + public static string Modifications { get; private set; } = ""; + + public static string App { get; private set; } = ""; + + public static bool Initialized { get => String.IsNullOrEmpty(Base); } + + public static void Initialize(string baseDirectory) + { + Base = baseDirectory; + Downloads = Path.Combine(Base, "Downloads"); + Integrations = Path.Combine(Base, "Integrations"); + Versions = Path.Combine(Base, "Versions"); + Modifications = Path.Combine(Base, "Modifications"); + + App = Path.Combine(Base, $"{Program.ProjectName}.exe"); + } + } +} diff --git a/Bloxstrap/Helpers/DiscordRichPresence.cs b/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs similarity index 58% rename from Bloxstrap/Helpers/DiscordRichPresence.cs rename to Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs index e57fe53..32dc300 100644 --- a/Bloxstrap/Helpers/DiscordRichPresence.cs +++ b/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs @@ -1,39 +1,35 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using DiscordRPC; -namespace Bloxstrap.Helpers +namespace Bloxstrap.Helpers.Integrations { internal class DiscordRichPresence : IDisposable { readonly DiscordRpcClient RichPresence = new("1005469189907173486"); public async Task SetPresence(string placeId) - { + { string placeName; string placeThumbnail; string creatorName; // null checking could probably be a lot more concrete here - using (HttpClient client = new()) - { - JObject placeInfo = await Utilities.GetJson($"https://economy.roblox.com/v2/assets/{placeId}/details"); + JObject placeInfo = await Utilities.GetJson($"https://economy.roblox.com/v2/assets/{placeId}/details"); - placeName = placeInfo["Name"].Value(); - creatorName = placeInfo["Creator"]["Name"].Value(); + placeName = placeInfo["Name"].Value(); + creatorName = placeInfo["Creator"]["Name"].Value(); - JObject thumbnailInfo = await Utilities.GetJson($"https://thumbnails.roblox.com/v1/places/gameicons?placeIds={placeId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false"); + JObject thumbnailInfo = await Utilities.GetJson($"https://thumbnails.roblox.com/v1/places/gameicons?placeIds={placeId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false"); - if (thumbnailInfo["data"] is null) - return false; + if (thumbnailInfo["data"] is null) + return false; - placeThumbnail = thumbnailInfo["data"][0]["imageUrl"].Value(); - } + placeThumbnail = thumbnailInfo["data"][0]["imageUrl"].Value(); DiscordRPC.Button[]? buttons = null; if (!Program.Settings.HideRPCButtons) - { + { buttons = new DiscordRPC.Button[] { new DiscordRPC.Button() @@ -57,16 +53,14 @@ namespace Bloxstrap.Helpers Details = placeName, State = $"by {creatorName}", Timestamps = new Timestamps() { Start = DateTime.UtcNow }, - + Buttons = buttons, Assets = new Assets() { LargeImageKey = placeThumbnail, LargeImageText = placeName, - SmallImageKey = "bloxstrap", - SmallImageText = "Rich Presence provided by Bloxstrap" - }, - - Buttons = buttons + SmallImageKey = "roblox", + SmallImageText = "Roblox" + } }); return true; diff --git a/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs b/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs new file mode 100644 index 0000000..4d48181 --- /dev/null +++ b/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs @@ -0,0 +1,95 @@ +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Net.Http; + +using Newtonsoft.Json.Linq; + +namespace Bloxstrap.Helpers.Integrations +{ + internal class RbxFpsUnlocker + { + public const string ProjectRepository = "axstin/rbxfpsunlocker"; + + // default settings but with QuickStart set to true and CheckForUpdates set to false + private static readonly string Settings = + "UnlockClient=true\n" + + "UnlockStudio=false\n" + + "FPSCapValues=[30.000000, 60.000000, 75.000000, 120.000000, 144.000000, 165.000000, 240.000000, 360.000000]\n" + + "FPSCapSelection=0\n" + + "FPSCap=0.000000\n" + + "CheckForUpdates=false\n" + + "NonBlockingErrors=true\n" + + "SilentErrors=false\n" + + "QuickStart=true\n"; + + public static async Task CheckInstall() + { + if (Program.BaseDirectory is null) + return; + + string folderLocation = Path.Combine(Program.BaseDirectory, "Integrations", "rbxfpsunlocker"); + string fileLocation = Path.Combine(folderLocation, "rbxfpsunlocker.exe"); + string settingsLocation = Path.Combine(folderLocation, "settings"); + + if (!Program.Settings.RFUEnabled) + { + if (Directory.Exists(folderLocation)) + { + Directory.Delete(folderLocation, true); + } + + return; + } + + DateTime lastReleasePublish; + string downloadUrl; + + try + { + JObject releaseInfo = await Utilities.GetJson($"https://api.github.com/repos/{ProjectRepository}/releases/latest"); + + // so... rbxfpsunlocker does not actually have any version info for the executable + // meaning the best way we can check for a new version is comparing time last download to time last release published + lastReleasePublish = DateTime.Parse(releaseInfo["created_at"].Value()); + downloadUrl = releaseInfo["assets"][0]["browser_download_url"].Value(); + } + catch (Exception ex) + { + Debug.WriteLine($"Failed to fetch latest version info! ({ex.Message})"); + return; + } + + Directory.CreateDirectory(folderLocation); + + if (File.Exists(fileLocation)) + { + DateTime lastDownload = File.GetCreationTimeUtc(fileLocation); + + // no new release published, return + if (lastDownload > lastReleasePublish) + return; + + File.Delete(fileLocation); + } + + Debug.WriteLine("Installing/Updating rbxfpsunlocker..."); + + using (HttpClient client = new()) + { + byte[] bytes = await client.GetByteArrayAsync(downloadUrl); + + using (MemoryStream zipStream = new MemoryStream(bytes)) + { + ZipArchive zip = new ZipArchive(zipStream); + zip.ExtractToDirectory(folderLocation, true); + } + } + + if (!File.Exists(settingsLocation)) + { + await File.WriteAllTextAsync(settingsLocation, Settings); + } + } + } +} diff --git a/Bloxstrap/Helpers/Protocol.cs b/Bloxstrap/Helpers/Protocol.cs index a64a9f9..e24330d 100644 --- a/Bloxstrap/Helpers/Protocol.cs +++ b/Bloxstrap/Helpers/Protocol.cs @@ -1,4 +1,5 @@ -using System.Web; +using System.Text; +using System.Web; using Microsoft.Win32; namespace Bloxstrap.Helpers @@ -9,11 +10,11 @@ namespace Bloxstrap.Helpers private static readonly IReadOnlyDictionary UriKeyArgMap = new Dictionary() { // excluding roblox-player, browsertrackerid and channel - { "launchmode", "--" }, + { "launchmode", "--" }, { "gameinfo", "-t " }, { "placelauncherurl", "-j "}, - // { "launchtime", "--launchtime=" }, we'll set this when launching the game client - { "robloxLocale", "--rloc " }, + // { "launchtime", "--launchtime=" }, we'll set this when launching the game client + { "robloxLocale", "--rloc " }, { "gameLocale", "--gloc " }, }; @@ -22,7 +23,7 @@ namespace Bloxstrap.Helpers string[] keyvalPair; string key; string val; - string commandLine = ""; + StringBuilder commandLine = new(); foreach (var parameter in protocol.Split('+')) { @@ -39,10 +40,10 @@ namespace Bloxstrap.Helpers if (key == "placelauncherurl") val = HttpUtility.UrlDecode(val).Replace("browserTrackerId", "lol"); - commandLine += UriKeyArgMap[key] + val + " "; + commandLine.Append(UriKeyArgMap[key] + val + " "); } - return commandLine; + return commandLine.ToString(); } public static void Register(string key, string name, string handler) diff --git a/Bloxstrap/Helpers/RSMM/FileManifest.cs b/Bloxstrap/Helpers/RSMM/FileManifest.cs index 4905a4f..4ca5aab 100644 --- a/Bloxstrap/Helpers/RSMM/FileManifest.cs +++ b/Bloxstrap/Helpers/RSMM/FileManifest.cs @@ -1,5 +1,7 @@ // https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Bootstrapper/FileManifest.cs +using System.IO; +using System.Net.Http; using System.Runtime.Serialization; namespace Bloxstrap.Helpers.RSMM diff --git a/Bloxstrap/Helpers/RSMM/PackageManifest.cs b/Bloxstrap/Helpers/RSMM/PackageManifest.cs index b770852..29e7035 100644 --- a/Bloxstrap/Helpers/RSMM/PackageManifest.cs +++ b/Bloxstrap/Helpers/RSMM/PackageManifest.cs @@ -1,5 +1,7 @@ // https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Bootstrapper/PackageManifest.cs +using System.IO; +using System.Net.Http; namespace Bloxstrap.Helpers.RSMM { diff --git a/Bloxstrap/Helpers/UpdateChecker.cs b/Bloxstrap/Helpers/Updater.cs similarity index 80% rename from Bloxstrap/Helpers/UpdateChecker.cs rename to Bloxstrap/Helpers/Updater.cs index e02be98..2327fac 100644 --- a/Bloxstrap/Helpers/UpdateChecker.cs +++ b/Bloxstrap/Helpers/Updater.cs @@ -1,18 +1,20 @@ using System.Diagnostics; +using System.IO; + using Newtonsoft.Json.Linq; namespace Bloxstrap.Helpers { - public class UpdateChecker + public class Updater { - public static void CheckInstalledVersion() + public static bool CheckInstalledVersion() { - if (Environment.ProcessPath is null || !File.Exists(Program.FilePath)) - return; + if (Environment.ProcessPath is null || !File.Exists(Directories.App) || Environment.ProcessPath == Directories.App) + return false; // if downloaded version doesn't match, replace installed version with downloaded version FileVersionInfo currentVersionInfo = FileVersionInfo.GetVersionInfo(Environment.ProcessPath); - FileVersionInfo installedVersionInfo = FileVersionInfo.GetVersionInfo(Program.FilePath); + FileVersionInfo installedVersionInfo = FileVersionInfo.GetVersionInfo(Directories.App); if (installedVersionInfo.ProductVersion != currentVersionInfo.ProductVersion) { @@ -25,10 +27,13 @@ namespace Bloxstrap.Helpers if (result == DialogResult.Yes) { - File.Delete(Program.FilePath); - File.Copy(Environment.ProcessPath, Program.FilePath); + File.Delete(Directories.App); + File.Copy(Environment.ProcessPath, Directories.App); + return true; } } + + return false; } public static async Task Check() @@ -36,6 +41,12 @@ namespace Bloxstrap.Helpers if (Environment.ProcessPath is null) return; + if (!Program.IsFirstRun && CheckInstalledVersion()) + return; + + if (!Program.Settings.CheckForUpdates) + return; + FileVersionInfo currentVersionInfo = FileVersionInfo.GetVersionInfo(Environment.ProcessPath); string currentVersion = $"Bloxstrap v{currentVersionInfo.ProductVersion}"; string latestVersion; @@ -67,7 +78,7 @@ namespace Bloxstrap.Helpers if (result == DialogResult.Yes) { - Process.Start(new ProcessStartInfo { FileName = $"https://github.com/{Program.ProjectRepository}/releases/latest", UseShellExecute = true }); + Utilities.OpenWebsite($"https://github.com/{Program.ProjectRepository}/releases/latest"); Program.Exit(); } } diff --git a/Bloxstrap/Helpers/Utilities.cs b/Bloxstrap/Helpers/Utilities.cs index 894cc71..12360d0 100644 --- a/Bloxstrap/Helpers/Utilities.cs +++ b/Bloxstrap/Helpers/Utilities.cs @@ -1,4 +1,7 @@ -using System.Security.Cryptography; +using System.Diagnostics; +using System.IO; +using System.Net.Http; +using System.Security.Cryptography; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -7,6 +10,11 @@ namespace Bloxstrap.Helpers { public class Utilities { + public static void OpenWebsite(string website) + { + Process.Start(new ProcessStartInfo { FileName = website, UseShellExecute = true }); + } + public static async Task GetJson(string url) { using (HttpClient client = new()) @@ -18,7 +26,7 @@ namespace Bloxstrap.Helpers } } - public static string CalculateMD5(string filename) + public static string MD5File(string filename) { using (MD5 md5 = MD5.Create()) { diff --git a/Bloxstrap/Program.cs b/Bloxstrap/Program.cs index 1ea0dca..401f681 100644 --- a/Bloxstrap/Program.cs +++ b/Bloxstrap/Program.cs @@ -1,4 +1,6 @@ using System.Diagnostics; +using System.IO; + using Microsoft.Win32; using Bloxstrap.Helpers; @@ -8,26 +10,29 @@ namespace Bloxstrap { 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"; - + + #region base64 stuff + // TODO: using IPFS as a reliable method for static asset storage instead of base64? + public const string Base64OldDeathSound = "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EEIIIYSMMeeggxBCCKWUUkopGWPOQQghhBBKKKWEEjrnoIMQQgmllFJKKaV0zjkIIYQQSimllFJK6SCEEEIIpZRSSimllNJBCCGEUEoppZRSSiklhBBCCKWUUkoppZRSSgghhBBKKaWUUkoppZQQQgillFJKKaWUUkopIYQQSimllFJKKaWUUkIIpZRSSimllFJKKaWEEEoppZRSSimllFJKCaGUUkoppZRSSimllBJKKaWUUkoppZRSSikllFJKKaWUUkoppZRSSiillFJKKaWUUkoppZRQSimllFJKKaWUUkopoZRSSimllFJKKaWUUgoAADpwAAAIMKLSQuw048ojcEQhwwRUaMhKACAcAABABDoIIYQQQggRcxBCCCGEEEKImIMQQgghhBBCCCGEEEIIpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppRQAdZnhABg9YeMMK0lnhaPBhYasBADSAgAAYxhjjCnIpLMWY60NYxBCB52EFGqoJaaGMQghdFBKSi22WHMGoaRSSkktxliDzT2DUEoppaQWY605F+NBSCWl1GKrteccjO4glJJSSjHWmnPuvWjQSUmptVpz7j0HXzwIpaTWWow9Bx+MMKKUlmKssdYcfBFGGFFKSy3GmnvNvRhjhEopxlp7zrnnXIwRPqUWY6659x58LsL44mLMOffigw8+CGGMkDHm2HPwvRdjjA/CyFxzLsIY44swwvggbK25B1+MEUYYY3zvNfigezHCCCOMMcII3XPRRfhijDFGGF+EAQC5EQ4AiAtGElJnGVYaceMJGCKQQkNWAQAxAAAEMcYgpJBSSinFGGOMMcYYY4wxxhhjjDHGnGPOOeecAADABAcAgAAr2JVZWrVR3NRJXvRB4BM6YjMy5FIqZnIi6JEaarES7NAKbvACsNCQlQAAGQAA5KSUlFotGkLKQWk1iMgg5STFJCJjkILSgqeQMYhJyh1jCiEFqXbQMYUUoxpSCplSCmqqOYaOMagxJ+FSCaUGAABAEAAgICQAwABBwQwAMDhAGDkQ6AggcGgDAAxEyExgUAgNDjIB4AEiQioASExQlC50QQgRpIsgiwcu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"; + public const string Base64OldArrowCursor = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBwoWHS0d8XaOAAAAeUlEQVRo3u3WyQmAUBAEUUMx/yRdEFH/dhG6EKsieKeZniYzMzMzMzMzs5ctWzBgZgk7ACUcAJBwAjDCBYAIdwBCeAIAQgmIE2pAmNACRAltQJDQA8QIfUCIMAJECGNAgFAforI/nWL4GcHvGB4k8CSDRyk8y83s860lExWMmEMyvAAAAABJRU5ErkJggg=="; + public const string Base64OldArrowFarCursor = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBwoWHwRtdYxgAAAAfElEQVRo3u3WwQmAMBQEUetPtXYQERE10VyEHcSZNPBOPztNZmZmZmZmZmYvK7VUGDCzhBWAEjYASNgBGOEAQIQzACFcAQChBcQJPSBMuANECfeAIOEJECM8A0KEESBCGAMChP4Qte9Ppxj+jODvGB4k8CSDRyk8y83s8y1ZdnQ0Empj3AAAAABJRU5ErkJggg=="; + #endregion + public static string? BaseDirectory; - public static string LocalAppData { get; private set; } - public static string FilePath { get; private set; } - public static string StartMenuDirectory { get; private set; } public static bool IsFirstRun { get; private set; } = false; + public static string LocalAppData { get; private set; } = ""; + public static string StartMenu { get; private set; } = ""; + public static SettingsFormat Settings; public static SettingsManager SettingsManager = new(); public static void ShowMessageBox(MessageBoxIcon icon, string message) { - MessageBox.Show(message, Program.ProjectName, MessageBoxButtons.OK, icon); + MessageBox.Show(message, ProjectName, MessageBoxButtons.OK, icon); } public static void Exit() @@ -52,9 +57,8 @@ namespace Bloxstrap return; } - UpdateChecker.Check().Wait(); - LocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + StartMenu = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs", ProjectName); // check if installed RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}"); @@ -76,19 +80,20 @@ namespace Bloxstrap if (BaseDirectory is null) return; - SettingsManager.SaveLocation = Path.Combine(BaseDirectory, "Settings.json"); - FilePath = Path.Combine(BaseDirectory, $"{ProjectName}.exe"); - StartMenuDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs", ProjectName); + Directories.Initialize(BaseDirectory); + + SettingsManager.SaveLocation = Path.Combine(Directories.Base, "Settings.json"); // 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) { - UpdateChecker.CheckInstalledVersion(); Settings = SettingsManager.Settings; SettingsManager.ShouldSave = true; } + Updater.Check().Wait(); + string commandLine = ""; if (args.Length > 0) diff --git a/Bloxstrap/Settings.cs b/Bloxstrap/Settings.cs index 4d4f573..fff91bc 100644 --- a/Bloxstrap/Settings.cs +++ b/Bloxstrap/Settings.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using System.IO; using System.Text.Json; + using Bloxstrap.Enums; namespace Bloxstrap @@ -8,10 +10,16 @@ namespace Bloxstrap { public string VersionGuid { get; set; } + public bool CheckForUpdates { get; set; } = true; + public BootstrapperStyle BootstrapperStyle { get; set; } = BootstrapperStyle.ProgressDialog; public BootstrapperIcon BootstrapperIcon { get; set; } = BootstrapperIcon.IconBloxstrap; + public bool UseDiscordRichPresence { get; set; } = true; public bool HideRPCButtons { get; set; } = false; + public bool RFUEnabled { get; set; } = false; + public bool RFUAutoclose { get; set; } = false; + public bool UseOldDeathSound { get; set; } = true; public bool UseOldMouseCursor { get; set; } = false; }