mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-19 00:51:30 -07:00
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
This commit is contained in:
parent
85de225226
commit
92b159d682
@ -4,13 +4,14 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Platforms>AnyCPU;x86</Platforms>
|
||||
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
|
||||
<Version>1.2.0</Version>
|
||||
<FileVersion>1.2.0.0</FileVersion>
|
||||
<Version>1.3.0</Version>
|
||||
<FileVersion>1.3.0.0</FileVersion>
|
||||
<UseWPF>True</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -27,12 +28,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Dialogs\BootstrapperStyles\LegacyDialog2009.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Dialogs\BootstrapperStyles\ProgressDialogDark.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Dialogs\BootstrapperStyles\LegacyDialog2009.cs" />
|
||||
<Compile Update="Dialogs\BootstrapperStyles\ProgressDialogDark.cs" />
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
|
795
Bloxstrap/Bootstrapper.cs
Normal file
795
Bloxstrap/Bootstrapper.cs
Normal file
@ -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<string, string> PackageDirectories = new Dictionary<string, string>()
|
||||
{
|
||||
{ "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 =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<Settings>\n" +
|
||||
" <ContentFolder>content</ContentFolder>\n" +
|
||||
" <BaseUrl>http://www.roblox.com</BaseUrl>\n" +
|
||||
"</Settings>\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<string> ShowSuccessEvent;
|
||||
public event ChangeEventHandler<string> MessageChanged;
|
||||
public event ChangeEventHandler<int> ProgressBarValueChanged;
|
||||
public event ChangeEventHandler<ProgressBarStyle> ProgressBarStyleChanged;
|
||||
public event ChangeEventHandler<bool> 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<string>(value));
|
||||
|
||||
_message = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Progress
|
||||
{
|
||||
get => _progress;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_progress == value)
|
||||
return;
|
||||
|
||||
ProgressBarValueChanged.Invoke(this, new ChangeEventArgs<int>(value));
|
||||
|
||||
_progress = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ProgressBarStyle ProgressStyle
|
||||
{
|
||||
get => _progressStyle;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_progressStyle == value)
|
||||
return;
|
||||
|
||||
ProgressBarStyleChanged.Invoke(this, new ChangeEventArgs<ProgressBarStyle>(value));
|
||||
|
||||
_progressStyle = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CancelEnabled
|
||||
{
|
||||
get => _cancelEnabled;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_cancelEnabled == value)
|
||||
return;
|
||||
|
||||
CancelEnabledChanged.Invoke(this, new ChangeEventArgs<bool>(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<string>(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<string> manifestFiles = new();
|
||||
List<string> 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<string>();
|
||||
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<string, string> 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
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string, string> PackageDirectories = new Dictionary<string, string>()
|
||||
{
|
||||
{ "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 =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<Settings>\n" +
|
||||
" <ContentFolder>content</ContentFolder>\n" +
|
||||
" <BaseUrl>http://www.roblox.com</BaseUrl>\n" +
|
||||
"</Settings>\n";
|
||||
|
||||
public event EventHandler CloseDialogEvent;
|
||||
public event EventHandler PromptShutdownEvent;
|
||||
public event ChangeEventHandler<string> ShowSuccessEvent;
|
||||
public event ChangeEventHandler<string> MessageChanged;
|
||||
public event ChangeEventHandler<int> ProgressBarValueChanged;
|
||||
public event ChangeEventHandler<ProgressBarStyle> ProgressBarStyleChanged;
|
||||
public event ChangeEventHandler<bool> 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<string>(value));
|
||||
|
||||
_message = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Progress
|
||||
{
|
||||
get => _progress;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_progress == value)
|
||||
return;
|
||||
|
||||
ProgressBarValueChanged.Invoke(this, new ChangeEventArgs<int>(value));
|
||||
|
||||
_progress = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ProgressBarStyle ProgressStyle
|
||||
{
|
||||
get => _progressStyle;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_progressStyle == value)
|
||||
return;
|
||||
|
||||
ProgressBarStyleChanged.Invoke(this, new ChangeEventArgs<ProgressBarStyle>(value));
|
||||
|
||||
_progressStyle = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CancelEnabled
|
||||
{
|
||||
get => _cancelEnabled;
|
||||
|
||||
private set
|
||||
{
|
||||
if (_cancelEnabled == value)
|
||||
return;
|
||||
|
||||
CancelEnabledChanged.Invoke(this, new ChangeEventArgs<bool>(value));
|
||||
|
||||
_cancelEnabled = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string>(message));
|
||||
}
|
||||
|
||||
private void PromptShutdown()
|
||||
{
|
||||
PromptShutdownEvent.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
private void CloseDialog()
|
||||
{
|
||||
CloseDialogEvent.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
{
|
||||
interface IBootstrapperStyle
|
||||
public interface IBootstrapperDialog
|
||||
{
|
||||
Bootstrapper? Bootstrapper { get; set; }
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -37,5 +37,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
|
||||
SetupDialog();
|
||||
}
|
||||
|
||||
private void LegacyDialog2009_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -41,5 +41,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
|
||||
SetupDialog();
|
||||
}
|
||||
|
||||
private void LegacyDialog2011_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -50,5 +50,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
{
|
||||
this.buttonCancel.Image = Properties.Resources.CancelButton;
|
||||
}
|
||||
|
||||
private void ProgressDialog_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -50,5 +50,10 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
{
|
||||
this.buttonCancel.Image = Properties.Resources.DarkCancelButton;
|
||||
}
|
||||
|
||||
private void ProgressDialogDark_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -163,7 +163,6 @@ namespace Bloxstrap.Dialogs.BootstrapperStyles
|
||||
|
||||
private void TestDialog_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Hide();
|
||||
TaskDialog.ShowDialog(Dialog);
|
||||
}
|
||||
}
|
||||
|
140
Bloxstrap/Dialogs/Preferences.Designer.cs
generated
140
Bloxstrap/Dialogs/Preferences.Designer.cs
generated
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,10 +57,10 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="InstallLocationBrowseDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="InfoTooltip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>222, 17</value>
|
||||
</metadata>
|
||||
<metadata name="InstallLocationBrowseDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
28
Bloxstrap/Helpers/Directories.cs
Normal file
28
Bloxstrap/Helpers/Directories.cs
Normal file
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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<bool> 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<string>();
|
||||
creatorName = placeInfo["Creator"]["Name"].Value<string>();
|
||||
placeName = placeInfo["Name"].Value<string>();
|
||||
creatorName = placeInfo["Creator"]["Name"].Value<string>();
|
||||
|
||||
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<string>();
|
||||
}
|
||||
placeThumbnail = thumbnailInfo["data"][0]["imageUrl"].Value<string>();
|
||||
|
||||
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;
|
95
Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs
Normal file
95
Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs
Normal file
@ -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<string>());
|
||||
downloadUrl = releaseInfo["assets"][0]["browser_download_url"].Value<string>();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<string, string> UriKeyArgMap = new Dictionary<string, string>()
|
||||
{
|
||||
// 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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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<JObject> 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())
|
||||
{
|
||||
|
File diff suppressed because one or more lines are too long
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user