diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml
new file mode 100644
index 0000000..d11fd2a
--- /dev/null
+++ b/.github/workflows/winget.yml
@@ -0,0 +1,15 @@
+name: Publish to Winget
+
+on:
+ release:
+ types: [released]
+
+jobs:
+ publish:
+ # Action can only be run on windows
+ runs-on: windows-latest
+ steps:
+ - uses: vedantmgoyal2009/winget-releaser@v1
+ with:
+ identifier: pizzaboxer.Bloxstrap
+ token: ${{ secrets.WINGET_TOKEN }}
diff --git a/Bloxstrap/Bloxstrap.csproj b/Bloxstrap/Bloxstrap.csproj
index aaeec16..e8c835a 100644
--- a/Bloxstrap/Bloxstrap.csproj
+++ b/Bloxstrap/Bloxstrap.csproj
@@ -10,14 +10,20 @@
AnyCPU
AnyCPU;x86
Bloxstrap.ico
- 1.5.6
- 1.5.6.0
+ 1.6.2
+ 1.6.2.0
+
+
+
+
+
+
diff --git a/Bloxstrap/Bloxstrap.ico b/Bloxstrap/Bloxstrap.ico
index 35ffd33..93904fb 100644
Binary files a/Bloxstrap/Bloxstrap.ico and b/Bloxstrap/Bloxstrap.ico differ
diff --git a/Bloxstrap/Bootstrapper.cs b/Bloxstrap/Bootstrapper.cs
index 18cf310..af19d50 100644
--- a/Bloxstrap/Bootstrapper.cs
+++ b/Bloxstrap/Bootstrapper.cs
@@ -12,6 +12,7 @@
using Bloxstrap.Helpers.RSMM;
using Bloxstrap.Models;
using System.Net;
+using Bloxstrap.Properties;
namespace Bloxstrap
{
@@ -81,7 +82,10 @@ public partial class Bootstrapper
private readonly bool FreshInstall;
- private int ProgressIncrement;
+ private double ProgressIncrement;
+ private long TotalBytes = 0;
+ private long TotalDownloadedBytes = 0;
+ private int PackagesExtracted = 0;
private bool CancelFired = false;
public IBootstrapperDialog Dialog = null!;
@@ -106,6 +110,11 @@ public async Task Run()
return;
}
+#if !DEBUG
+ if (!Program.IsFirstRun && Program.Settings.CheckForUpdates)
+ await CheckForUpdates();
+#endif
+
await CheckLatestVersion();
// if bloxstrap is installing for the first time but is running, prompt to close roblox
@@ -113,7 +122,7 @@ public async Task Run()
if (!Directory.Exists(VersionFolder) && CheckIfRunning(true) || Program.Settings.VersionGuid != VersionGuid && !CheckIfRunning(false))
await InstallLatestVersion();
- ApplyModifications();
+ await ApplyModifications();
if (Program.IsFirstRun)
Program.SettingsManager.ShouldSave = true;
@@ -136,6 +145,50 @@ public async Task Run()
Program.Exit();
}
+ private async Task CheckForUpdates()
+ {
+ string currentVersion = $"Bloxstrap v{Program.Version}";
+
+ var releaseInfo = await Utilities.GetJson($"https://api.github.com/repos/{Program.ProjectRepository}/releases/latest");
+
+ if (releaseInfo is null || releaseInfo.Name is null || releaseInfo.Assets is null || currentVersion == releaseInfo.Name)
+ return;
+
+ Dialog.Message = "Getting the latest Bloxstrap...";
+
+ // 64-bit is always the first option
+ GithubReleaseAsset asset = releaseInfo.Assets[Environment.Is64BitOperatingSystem ? 0 : 1];
+ string downloadLocation = Path.Combine(Directories.Updates, asset.Name);
+
+ Directory.CreateDirectory(Directories.Updates);
+
+ Debug.WriteLine($"Downloading {releaseInfo.Name}...");
+
+ if (!File.Exists(downloadLocation))
+ {
+ var response = await Program.HttpClient.GetAsync(asset.BrowserDownloadUrl);
+
+ using (var fileStream = new FileStream(Path.Combine(Directories.Updates, asset.Name), FileMode.CreateNew))
+ {
+ await response.Content.CopyToAsync(fileStream);
+ }
+ }
+
+ Debug.WriteLine($"Starting {releaseInfo.Name}...");
+
+ ProcessStartInfo startInfo = new()
+ {
+ FileName = downloadLocation,
+ };
+
+ foreach (string arg in Program.LaunchArgs)
+ startInfo.ArgumentList.Add(arg);
+
+ Process.Start(startInfo);
+
+ Program.Exit();
+ }
+
private async Task CheckLatestVersion()
{
Dialog.Message = "Connecting to Roblox...";
@@ -179,6 +232,12 @@ private async Task StartRoblox()
Dialog.Message = "Starting Roblox...";
+ if (LaunchCommandLine == "--app" && Program.Settings.UseDisableAppPatch)
+ {
+ Utilities.OpenWebsite("https://www.roblox.com/games");
+ return;
+ }
+
// launch time isn't really required for all launches, but it's usually just safest to do this
LaunchCommandLine += " --launchtime=" + DateTimeOffset.Now.ToUnixTimeMilliseconds();
@@ -204,9 +263,11 @@ private async Task StartRoblox()
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");
+ ProcessStartInfo startInfo = new()
+ {
+ FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe"),
+ WorkingDirectory = Path.Combine(Directories.Integrations, "rbxfpsunlocker")
+ };
rbxFpsUnlocker = Process.Start(startInfo);
@@ -218,10 +279,6 @@ private async Task StartRoblox()
await Task.Delay(3000);
// now we move onto handling rich presence
- // this is going to be an issue for desktop app launches as this gets the place id from the command line,
- // but the desktop app launch can switch games without having to close the client
- // i might be able to experiment with reading from the latest log file in realtime to circumvent this,
- // but i have no idea how reliable it will be. todo?
if (Program.Settings.UseDiscordRichPresence)
{
richPresence = new DiscordRichPresence();
@@ -266,9 +323,9 @@ public void CancelButtonClicked()
Program.Exit(ERROR_INSTALL_USEREXIT);
}
- #endregion
+#endregion
- #region App Install
+#region App Install
public static void Register()
{
RegistryKey applicationKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}");
@@ -384,13 +441,19 @@ private void Uninstall()
}
catch (Exception) { }
- Dialog.ShowSuccess($"{Program.ProjectName} has been uninstalled");
+ Dialog.ShowSuccess($"{Program.ProjectName} has succesfully uninstalled");
- Environment.Exit(ERROR_PRODUCT_UNINSTALLED);
+ Program.Exit();
+ }
+#endregion
+
+#region Roblox Install
+ private void UpdateProgressbar()
+ {
+ int newProgress = (int)Math.Floor(ProgressIncrement * TotalDownloadedBytes);
+ Dialog.ProgressValue = newProgress;
}
- #endregion
- #region Roblox Install
private async Task InstallLatestVersion()
{
if (FreshInstall)
@@ -402,50 +465,40 @@ private async Task InstallLatestVersion()
Dialog.CancelEnabled = true;
- // i believe the bootstrapper bases the progress bar off
- // bytes downloaded / bytes total according to rbxPkgManifest?
- // i'm too lazy for that, so here it's just based off how many packages
- // have finished downloading
-
Dialog.ProgressStyle = ProgressBarStyle.Continuous;
- ProgressIncrement = (int)Math.Floor((decimal)1 / VersionPackageManifest.Count * 100);
+ // compute total bytes to download
+
+ foreach (Package package in VersionPackageManifest)
+ TotalBytes += package.PackedSize;
+
+ ProgressIncrement = (double)1 / TotalBytes * 100;
Directory.CreateDirectory(Directories.Downloads);
+ Directory.CreateDirectory(Directories.Versions);
foreach (Package package in VersionPackageManifest)
{
- // download all the packages at once
- DownloadPackage(package);
- }
-
- do
- {
- // wait for download to finish (and also round off the progress bar if needed)
-
- if (Dialog.ProgressValue == ProgressIncrement * VersionPackageManifest.Count)
- Dialog.ProgressValue = 100;
+ // download all the packages synchronously
+ await DownloadPackage(package);
- await Task.Delay(1000);
+ // extract the package immediately after download
+ ExtractPackage(package);
}
- while (Dialog.ProgressValue != 100);
- Dialog.ProgressStyle = ProgressBarStyle.Marquee;
+ // allow progress bar to 100% before continuing (purely ux reasons lol)
+ await Task.Delay(1000);
- Debug.WriteLine("Finished downloading");
+ Dialog.ProgressStyle = ProgressBarStyle.Marquee;
Dialog.Message = "Configuring Roblox...";
- Directory.CreateDirectory(Directories.Versions);
-
- foreach (Package package in VersionPackageManifest)
+ // wait for all packages to finish extracting
+ while (PackagesExtracted < VersionPackageManifest.Count)
{
- // extract all the packages at once (shouldn't be too heavy on cpu?)
- ExtractPackage(package);
+ await Task.Delay(100);
}
- Debug.WriteLine("Finished extracting packages");
-
string appSettingsLocation = Path.Combine(VersionFolder, "AppSettings.xml");
await File.WriteAllTextAsync(appSettingsLocation, AppSettings);
@@ -472,7 +525,7 @@ private async Task InstallLatestVersion()
Program.Settings.VersionGuid = VersionGuid;
}
- private void ApplyModifications()
+ private async Task ApplyModifications()
{
Dialog.Message = "Applying Roblox modifications...";
@@ -485,13 +538,13 @@ private void ApplyModifications()
if (!Directory.Exists(modFolder))
{
Directory.CreateDirectory(modFolder);
- File.WriteAllText(Path.Combine(modFolder, "README.txt"), ModReadme);
+ await File.WriteAllTextAsync(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);
- CheckModPreset(Program.Settings.UseDisableAppPatch, @"ExtraContent\places\Mobile.rbxl", "");
+ await CheckModPreset(Program.Settings.UseOldDeathSound, @"content\sounds\ouch.ogg", "OldDeath.ogg");
+ await CheckModPreset(Program.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowCursor.png", "OldCursor.png");
+ await CheckModPreset(Program.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowFarCursor.png", "OldFarCursor.png");
+ await CheckModPreset(Program.Settings.UseDisableAppPatch, @"ExtraContent\places\Mobile.rbxl", "");
foreach (string file in Directory.GetFiles(modFolder, "*.*", SearchOption.AllDirectories))
{
@@ -510,7 +563,7 @@ private void ApplyModifications()
// original files from the downloaded packages
if (File.Exists(manifestFile))
- manifestFiles = File.ReadAllLines(manifestFile).ToList();
+ manifestFiles = (await File.ReadAllLinesAsync(manifestFile)).ToList();
else
manifestFiles = modFolderFiles;
@@ -537,8 +590,7 @@ private void ApplyModifications()
File.SetAttributes(fileVersionFolder, File.GetAttributes(fileModFolder) & ~FileAttributes.ReadOnly);
}
- // now check for files that have been deleted from the mod folder
- // according to the manifest
+ // now check for files that have been deleted from the mod folder according to the manifest
foreach (string fileLocation in manifestFiles)
{
if (modFolderFiles.Contains(fileLocation))
@@ -564,10 +616,10 @@ private void ApplyModifications()
File.WriteAllLines(manifestFile, modFolderFiles);
}
- private static void CheckModPreset(bool condition, string location, string base64Contents)
+ private static async Task CheckModPreset(bool condition, string location, string name)
{
string modFolderLocation = Path.Combine(Directories.Modifications, location);
- byte[] binaryData = Convert.FromBase64String(base64Contents);
+ byte[] binaryData = string.IsNullOrEmpty(name) ? Array.Empty() : await ResourceHelper.Get(name);
if (condition)
{
@@ -580,7 +632,7 @@ private static void CheckModPreset(bool condition, string location, string base6
Directory.CreateDirectory(directory);
- File.WriteAllBytes(modFolderLocation, binaryData);
+ await File.WriteAllBytesAsync(modFolderLocation, binaryData);
}
}
else if (File.Exists(modFolderLocation) && Utilities.MD5File(modFolderLocation) == Utilities.MD5Data(binaryData))
@@ -589,7 +641,7 @@ private static void CheckModPreset(bool condition, string location, string base6
}
}
- private async void DownloadPackage(Package package)
+ private async Task DownloadPackage(Package package)
{
string packageUrl = $"{DeployManager.BaseUrl}/{VersionGuid}-{package.Name}";
string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
@@ -603,12 +655,13 @@ private async void DownloadPackage(Package package)
if (calculatedMD5 != package.Signature)
{
Debug.WriteLine($"{package.Name} is corrupted ({calculatedMD5} != {package.Signature})! Deleting and re-downloading...");
- file.Delete();
+ file.Delete();
}
else
{
Debug.WriteLine($"{package.Name} is already downloaded, skipping...");
- Dialog.ProgressValue += ProgressIncrement;
+ TotalDownloadedBytes += package.PackedSize;
+ UpdateProgressbar();
return;
}
}
@@ -619,7 +672,8 @@ private async void DownloadPackage(Package package)
Debug.WriteLine($"Found existing version of {package.Name} ({robloxPackageLocation})! Copying to Downloads folder...");
File.Copy(robloxPackageLocation, packageLocation);
- Dialog.ProgressValue += ProgressIncrement;
+ TotalDownloadedBytes += package.PackedSize;
+ UpdateProgressbar();
return;
}
@@ -627,22 +681,34 @@ private async void DownloadPackage(Package package)
{
Debug.WriteLine($"Downloading {package.Name}...");
- var response = await Program.HttpClient.GetAsync(packageUrl);
-
if (CancelFired)
return;
+ var response = await Program.HttpClient.GetAsync(packageUrl, HttpCompletionOption.ResponseHeadersRead);
+
+ var buffer = new byte[8192];
+
+ using (var stream = await response.Content.ReadAsStreamAsync())
using (var fileStream = new FileStream(packageLocation, FileMode.CreateNew))
{
- await response.Content.CopyToAsync(fileStream);
+ while (true)
+ {
+ var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
+ if (bytesRead == 0)
+ break; // we're done
+
+ await fileStream.WriteAsync(buffer, 0, bytesRead);
+
+ TotalDownloadedBytes += bytesRead;
+ UpdateProgressbar();
+ }
}
Debug.WriteLine($"Finished downloading {package.Name}!");
- Dialog.ProgressValue += ProgressIncrement;
}
}
- private void ExtractPackage(Package package)
+ private async void ExtractPackage(Package package)
{
if (CancelFired)
return;
@@ -654,7 +720,7 @@ private void ExtractPackage(Package package)
Debug.WriteLine($"Extracting {package.Name} to {packageFolder}...");
- using (ZipArchive archive = ZipFile.OpenRead(packageLocation))
+ using (ZipArchive archive = await Task.Run(() => ZipFile.OpenRead(packageLocation)))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
@@ -666,7 +732,7 @@ private void ExtractPackage(Package package)
extractPath = Path.Combine(packageFolder, entry.FullName);
- Debug.WriteLine($"[{package.Name}] Writing {extractPath}...");
+ //Debug.WriteLine($"[{package.Name}] Writing {extractPath}...");
directory = Path.GetDirectoryName(extractPath);
@@ -678,9 +744,13 @@ private void ExtractPackage(Package package)
if (File.Exists(extractPath))
File.Delete(extractPath);
- entry.ExtractToFile(extractPath);
+ await Task.Run(() => entry.ExtractToFile(extractPath));
}
}
+
+ Debug.WriteLine($"Finished extracting {package.Name}");
+
+ PackagesExtracted += 1;
}
private void ExtractFileFromPackage(string packageName, string fileName)
@@ -690,7 +760,7 @@ private void ExtractFileFromPackage(string packageName, string fileName)
if (package is null)
return;
- DownloadPackage(package);
+ DownloadPackage(package).GetAwaiter().GetResult();
string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
string packageFolder = Path.Combine(VersionFolder, PackageDirectories[package.Name]);
@@ -710,6 +780,6 @@ private void ExtractFileFromPackage(string packageName, string fileName)
entry.ExtractToFile(fileLocation);
}
}
- #endregion
+#endregion
}
}
diff --git a/Bloxstrap/Dialogs/BootstrapperDialogs/BootstrapperDialogForm.cs b/Bloxstrap/Dialogs/BootstrapperDialogs/BootstrapperDialogForm.cs
index adac9b6..425df1b 100644
--- a/Bloxstrap/Dialogs/BootstrapperDialogs/BootstrapperDialogForm.cs
+++ b/Bloxstrap/Dialogs/BootstrapperDialogs/BootstrapperDialogForm.cs
@@ -1,4 +1,5 @@
using Bloxstrap.Enums;
+using Bloxstrap.Helpers;
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
@@ -59,6 +60,18 @@ public bool CancelEnabled
}
}
+ public void ScaleWindow()
+ {
+ this.Size = this.MinimumSize = this.MaximumSize = WindowScaling.GetScaledSize(this.Size);
+
+ foreach (Control control in this.Controls)
+ {
+ control.Size = WindowScaling.GetScaledSize(control.Size);
+ control.Location = WindowScaling.GetScaledPoint(control.Location);
+ control.Padding = WindowScaling.GetScaledPadding(control.Padding);
+ }
+ }
+
public void SetupDialog()
{
if (Program.IsQuiet)
diff --git a/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2009.cs b/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2009.cs
index b09109b..24224d8 100644
--- a/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2009.cs
+++ b/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2009.cs
@@ -35,6 +35,7 @@ public LegacyDialog2009(Bootstrapper? bootstrapper = null)
Bootstrapper = bootstrapper;
+ ScaleWindow();
SetupDialog();
}
diff --git a/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2011.cs b/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2011.cs
index c9dcfdd..8b5e8d5 100644
--- a/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2011.cs
+++ b/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2011.cs
@@ -37,8 +37,9 @@ public LegacyDialog2011(Bootstrapper? bootstrapper = null)
Bootstrapper = bootstrapper;
// have to convert icon -> bitmap since winforms scaling is poop
- this.IconBox.Image = Program.Settings.BootstrapperIcon.GetIcon().ToBitmap();
+ this.IconBox.BackgroundImage = Program.Settings.BootstrapperIcon.GetIcon().ToBitmap();
+ ScaleWindow();
SetupDialog();
}
diff --git a/Bloxstrap/Dialogs/Preferences.xaml b/Bloxstrap/Dialogs/Preferences.xaml
index e956848..d0dcc11 100644
--- a/Bloxstrap/Dialogs/Preferences.xaml
+++ b/Bloxstrap/Dialogs/Preferences.xaml
@@ -34,7 +34,7 @@
-
+
@@ -107,6 +107,7 @@
+
@@ -117,18 +118,22 @@
-
-
+
+
+
-
-
-
-
-
+
+
+ Leave a star on GitHub!
+
+
+
+
+
diff --git a/Bloxstrap/Dialogs/Preferences.xaml.cs b/Bloxstrap/Dialogs/Preferences.xaml.cs
index 2efceeb..f6479f2 100644
--- a/Bloxstrap/Dialogs/Preferences.xaml.cs
+++ b/Bloxstrap/Dialogs/Preferences.xaml.cs
@@ -155,6 +155,12 @@ private void ButtonConfirm_Click(object sender, EventArgs e)
this.Close();
}
+
+ private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
+ {
+ Utilities.OpenWebsite(e.Uri.AbsoluteUri);
+ e.Handled = true;
+ }
}
public class PreferencesViewModel : INotifyPropertyChanged
diff --git a/Bloxstrap/Helpers/Directories.cs b/Bloxstrap/Helpers/Directories.cs
index 93462e2..c69a88a 100644
--- a/Bloxstrap/Helpers/Directories.cs
+++ b/Bloxstrap/Helpers/Directories.cs
@@ -9,6 +9,7 @@ class Directories
public static string Integrations { get; private set; } = "";
public static string Versions { get; private set; } = "";
public static string Modifications { get; private set; } = "";
+ public static string Updates { get; private set; } = "";
public static string App { get; private set; } = "";
@@ -21,6 +22,7 @@ public static void Initialize(string baseDirectory)
Integrations = Path.Combine(Base, "Integrations");
Versions = Path.Combine(Base, "Versions");
Modifications = Path.Combine(Base, "Modifications");
+ Updates = Path.Combine(Base, "Updates");
App = Path.Combine(Base, $"{Program.ProjectName}.exe");
}
diff --git a/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs b/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs
index 810491d..bfa77ec 100644
--- a/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs
+++ b/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs
@@ -14,7 +14,7 @@ class DiscordRichPresence : IDisposable
const string GameJoiningEntry = "[FLog::Output] ! Joining game";
const string GameJoinedEntry = "[FLog::Network] serverId:";
- const string GameDisconnectedEntry = "[FLog::Network] Client:Disconnect";
+ const string GameDisconnectedEntry = "[FLog::Network] Time to disconnect replication data:";
const string GameJoiningEntryPattern = @"! Joining game '([0-9a-f\-]{36})' place ([0-9]+) at ([0-9\.]+)";
const string GameJoinedEntryPattern = @"serverId: ([0-9\.]+)\|([0-9]+)";
@@ -76,12 +76,12 @@ public async void MonitorGameActivity()
{
// okay, here's the process:
//
- // - read the latest log file from %localappdata%\roblox\logs approx every 30 sec or so
+ // - tail the latest log file from %localappdata%\roblox\logs
// - check for specific lines to determine player's game activity as shown below:
//
// - get the place id, job id and machine address from '! Joining game '{{JOBID}}' place {{PLACEID}} at {{MACHINEADDRESS}}' entry
// - confirm place join with 'serverId: {{MACHINEADDRESS}}|{{MACHINEPORT}}' entry
- // - check for leaves/disconnects with 'Client:Disconnect' entry
+ // - check for leaves/disconnects with 'Time to disconnect replication data: {{TIME}}' entry
//
// we'll tail the log file continuously, monitoring for any log entries that we need to determine the current game activity
@@ -123,7 +123,6 @@ public async void MonitorGameActivity()
}
// no need to close the event, its going to be finished with when the program closes anyway
- // ...rr im too lazy to fix the event still be updating when its closed... lol
}
public async Task SetPresence()
@@ -175,6 +174,7 @@ public async Task SetPresence()
public void Dispose()
{
+ RichPresence.ClearPresence();
RichPresence.Dispose();
}
}
diff --git a/Bloxstrap/Helpers/Protocol.cs b/Bloxstrap/Helpers/Protocol.cs
index c94e1e0..dc369b7 100644
--- a/Bloxstrap/Helpers/Protocol.cs
+++ b/Bloxstrap/Helpers/Protocol.cs
@@ -1,4 +1,5 @@
-using System.Text;
+using System.Diagnostics;
+using System.Text;
using System.Web;
using Microsoft.Win32;
@@ -97,9 +98,12 @@ public static void Unregister(string key)
{
try
{
- Registry.CurrentUser.DeleteSubKey($@"Software\Classes\{key}");
+ Registry.CurrentUser.DeleteSubKeyTree($@"Software\Classes\{key}");
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine($"Failed to unregister {key}: {e}");
}
- catch (Exception) { }
}
}
}
diff --git a/Bloxstrap/Helpers/RSMM/PackageManifest.cs b/Bloxstrap/Helpers/RSMM/PackageManifest.cs
index 4404809..a3dbd59 100644
--- a/Bloxstrap/Helpers/RSMM/PackageManifest.cs
+++ b/Bloxstrap/Helpers/RSMM/PackageManifest.cs
@@ -7,76 +7,49 @@ namespace Bloxstrap.Helpers.RSMM
{
internal class PackageManifest : List
{
- public string RawData { get; private set; }
-
private PackageManifest(string data)
{
- using (var reader = new StringReader(data))
- {
- string version = reader.ReadLine();
+ using StringReader reader = new StringReader(data);
+ string? version = reader.ReadLine();
- if (version != "v0")
- {
- string errorMsg = $"Unexpected package manifest version: {version} (expected v0!)\n" +
- "Please contact MaximumADHD if you see this error.";
+ if (version != "v0")
+ throw new NotSupportedException($"Unexpected package manifest version: {version} (expected v0!)");
- throw new NotSupportedException(errorMsg);
- }
+ while (true)
+ {
+ string? fileName = reader.ReadLine();
+ string? signature = reader.ReadLine();
- bool eof = false;
+ string? rawPackedSize = reader.ReadLine();
+ string? rawSize = reader.ReadLine();
- var readLine = new Func(() =>
- {
- string line = reader.ReadLine();
+ if (string.IsNullOrEmpty(fileName) ||
+ string.IsNullOrEmpty(signature) ||
+ string.IsNullOrEmpty(rawPackedSize) ||
+ string.IsNullOrEmpty(rawSize))
+ break;
- if (line == null)
- eof = true;
+ // ignore launcher
+ if (fileName == "RobloxPlayerLauncher.exe")
+ break;
- return line;
- });
+ int packedSize = int.Parse(rawPackedSize);
+ int size = int.Parse(rawSize);
- while (!eof)
+ Add(new Package
{
- string fileName = readLine();
- string signature = readLine();
-
- string rawPackedSize = readLine();
- string rawSize = readLine();
-
- if (eof)
- break;
-
- if (!int.TryParse(rawPackedSize, out int packedSize))
- break;
-
- if (!int.TryParse(rawSize, out int size))
- break;
-
- if (fileName == "RobloxPlayerLauncher.exe")
- break;
-
- var package = new Package()
- {
- Name = fileName,
- Signature = signature,
- PackedSize = packedSize,
- Size = size
- };
-
- Add(package);
- }
+ Name = fileName,
+ Signature = signature,
+ PackedSize = packedSize,
+ Size = size
+ });
}
-
- RawData = data;
}
public static async Task Get(string versionGuid)
{
string pkgManifestUrl = $"{DeployManager.BaseUrl}/{versionGuid}-rbxPkgManifest.txt";
- string pkgManifestData;
-
- var getData = Program.HttpClient.GetStringAsync(pkgManifestUrl);
- pkgManifestData = await getData.ConfigureAwait(false);
+ var pkgManifestData = await Program.HttpClient.GetStringAsync(pkgManifestUrl);
return new PackageManifest(pkgManifestData);
}
diff --git a/Bloxstrap/Helpers/ResourceHelper.cs b/Bloxstrap/Helpers/ResourceHelper.cs
new file mode 100644
index 0000000..f61776a
--- /dev/null
+++ b/Bloxstrap/Helpers/ResourceHelper.cs
@@ -0,0 +1,25 @@
+using System.IO;
+using System.Reflection;
+
+namespace Bloxstrap.Helpers
+{
+ internal class ResourceHelper
+ {
+ static readonly Assembly assembly = Assembly.GetExecutingAssembly();
+ static readonly string[] resourceNames = assembly.GetManifestResourceNames();
+
+ public static async Task Get(string name)
+ {
+ string path = resourceNames.Single(str => str.EndsWith(name));
+
+ using (Stream stream = assembly.GetManifestResourceStream(path)!)
+ {
+ using (MemoryStream memoryStream = new())
+ {
+ await stream.CopyToAsync(memoryStream);
+ return memoryStream.ToArray();
+ }
+ }
+ }
+ }
+}
diff --git a/Bloxstrap/Helpers/Updater.cs b/Bloxstrap/Helpers/Updater.cs
index c3964aa..272a729 100644
--- a/Bloxstrap/Helpers/Updater.cs
+++ b/Bloxstrap/Helpers/Updater.cs
@@ -17,78 +17,52 @@ public static void CheckInstalledVersion()
if (Environment.ProcessPath is null || !File.Exists(Directories.App) || Environment.ProcessPath == Directories.App)
return;
+ bool isAutoUpgrade = Environment.ProcessPath.StartsWith(Directories.Updates);
+
// if downloaded version doesn't match, replace installed version with downloaded version
FileVersionInfo currentVersionInfo = FileVersionInfo.GetVersionInfo(Environment.ProcessPath);
FileVersionInfo installedVersionInfo = FileVersionInfo.GetVersionInfo(Directories.App);
- if (installedVersionInfo.ProductVersion != currentVersionInfo.ProductVersion)
+ if (installedVersionInfo.ProductVersion == currentVersionInfo.ProductVersion)
+ return;
+
+
+ DialogResult result;
+
+ // silently upgrade version if the command line flag is set or if we're launching from an auto update
+ if (Program.IsUpgrade || isAutoUpgrade)
+ {
+ result = DialogResult.Yes;
+ }
+ else
{
- DialogResult result = Program.ShowMessageBox(
- $"The version of {Program.ProjectName} you've launched is different to the version you currently have installed.\nWould you like to update your currently installed version?",
+ result = Program.ShowMessageBox(
+ $"The version of {Program.ProjectName} you've launched is different to the version you currently have installed.\nWould you like to upgrade your currently installed version?",
MessageBoxIcon.Question,
MessageBoxButtons.YesNo
);
-
- if (result == DialogResult.Yes)
- {
- File.Delete(Directories.App);
- File.Copy(Environment.ProcessPath, Directories.App);
-
- Bootstrapper.Register();
-
- Program.ShowMessageBox(
- $"{Program.ProjectName} has been updated to v{currentVersionInfo.ProductVersion}",
- MessageBoxIcon.Information,
- MessageBoxButtons.OK
- );
-
- new Preferences().ShowDialog();
-
- Program.Exit();
- }
}
- return;
- }
- public static async Task Check()
- {
- if (Environment.ProcessPath is null || Program.IsUninstall || Program.IsQuiet && Program.IsFirstRun)
+ if (result != DialogResult.Yes)
return;
- if (!Program.IsFirstRun)
- CheckInstalledVersion();
-
- if (!Program.Settings.CheckForUpdates)
+ File.Delete(Directories.App);
+ File.Copy(Environment.ProcessPath, Directories.App);
+
+ Bootstrapper.Register();
+
+ if (Program.IsQuiet || isAutoUpgrade)
return;
-
- FileVersionInfo currentVersionInfo = FileVersionInfo.GetVersionInfo(Environment.ProcessPath);
- string currentVersion = $"Bloxstrap v{currentVersionInfo.ProductVersion}";
- string latestVersion;
- string releaseNotes;
-
- var releaseInfo = await Utilities.GetJson($"https://api.github.com/repos/{Program.ProjectRepository}/releases/latest");
-
- if (releaseInfo is null || releaseInfo.Name is null || releaseInfo.Body is null)
- return;
-
- latestVersion = releaseInfo.Name;
- releaseNotes = releaseInfo.Body;
-
- if (currentVersion != latestVersion)
- {
- DialogResult result = Program.ShowMessageBox(
- $"A new version of {Program.ProjectName} is available\n\n[{latestVersion}]\n{releaseNotes}\n\nWould you like to download it?",
- MessageBoxIcon.Question,
- MessageBoxButtons.YesNo
- );
-
- if (result == DialogResult.Yes)
- {
- Utilities.OpenWebsite($"https://github.com/{Program.ProjectRepository}/releases/latest");
- Program.Exit(Bootstrapper.ERROR_INSTALL_USEREXIT);
- }
- }
+
+ Program.ShowMessageBox(
+ $"{Program.ProjectName} has been updated to v{currentVersionInfo.ProductVersion}",
+ MessageBoxIcon.Information,
+ MessageBoxButtons.OK
+ );
+
+ new Preferences().ShowDialog();
+ Program.Exit();
}
}
}
\ No newline at end of file
diff --git a/Bloxstrap/Helpers/Utilities.cs b/Bloxstrap/Helpers/Utilities.cs
index 6b2e93e..11530c4 100644
--- a/Bloxstrap/Helpers/Utilities.cs
+++ b/Bloxstrap/Helpers/Utilities.cs
@@ -1,6 +1,5 @@
using System.Diagnostics;
using System.IO;
-using System.Net.Http;
using System.Security.Cryptography;
using System.Text.Json;
diff --git a/Bloxstrap/Helpers/WindowScaling.cs b/Bloxstrap/Helpers/WindowScaling.cs
new file mode 100644
index 0000000..9954f34
--- /dev/null
+++ b/Bloxstrap/Helpers/WindowScaling.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace Bloxstrap.Helpers
+{
+ public class WindowScaling
+ {
+ public static double GetFactor()
+ {
+ return Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;
+ }
+
+ public static int GetScaledNumber(int number)
+ {
+ return (int)Math.Ceiling(number * GetFactor());
+ }
+
+ public static System.Drawing.Size GetScaledSize(System.Drawing.Size size)
+ {
+ return new System.Drawing.Size(GetScaledNumber(size.Width), GetScaledNumber(size.Height));
+ }
+
+ public static System.Drawing.Point GetScaledPoint(System.Drawing.Point point)
+ {
+ return new System.Drawing.Point(GetScaledNumber(point.X), GetScaledNumber(point.Y));
+ }
+
+ public static Padding GetScaledPadding(Padding padding)
+ {
+ return new Padding(GetScaledNumber(padding.Left), GetScaledNumber(padding.Top), GetScaledNumber(padding.Right), GetScaledNumber(padding.Bottom));
+ }
+ }
+}
diff --git a/Bloxstrap/Program.cs b/Bloxstrap/Program.cs
index ddb7acb..b9f23f6 100644
--- a/Bloxstrap/Program.cs
+++ b/Bloxstrap/Program.cs
@@ -24,18 +24,13 @@ internal static class Program
public const string ProjectName = "Bloxstrap";
public const string ProjectRepository = "6ixfalls/bloxstrap";
- #region base64 stuff
- // TODO: using IPFS as a reliable method for static asset storage instead of base64?
- public const string Base64OldDeathSound = "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EEIIIYSMMeeggxBCCKWUUkopGWPOQQghhBBKKKWEEjrnoIMQQgmllFJKKaV0zjkIIYQQSimllFJK6SCEEEIIpZRSSimllNJBCCGEUEoppZRSSiklhBBCCKWUUkoppZRSSgghhBBKKaWUUkoppZQQQgillFJKKaWUUkopIYQQSimllFJKKaWUUkIIpZRSSimllFJKKaWEEEoppZRSSimllFJKCaGUUkoppZRSSimllBJKKaWUUkoppZRSSikllFJKKaWUUkoppZRSSiillFJKKaWUUkoppZRQSimllFJKKaWUUkopoZRSSimllFJKKaWUUgoAADpwAAAIMKLSQuw048ojcEQhwwRUaMhKACAcAABABDoIIYQQQggRcxBCCCGEEEKImIMQQgghhBBCCCGEEEIIpZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppRQAdZnhABg9YeMMK0lnhaPBhYasBADSAgAAYxhjjCnIpLMWY60NYxBCB52EFGqoJaaGMQghdFBKSi22WHMGoaRSSkktxliDzT2DUEoppaQWY605F+NBSCWl1GKrteccjO4glJJSSjHWmnPuvWjQSUmptVpz7j0HXzwIpaTWWow9Bx+MMKKUlmKssdYcfBFGGFFKSy3GmnvNvRhjhEopxlp7zrnnXIwRPqUWY6659x58LsL44mLMOffigw8+CGGMkDHm2HPwvRdjjA/CyFxzLsIY44swwvggbK25B1+MEUYYY3zvNfigezHCCCOMMcII3XPRRfhijDFGGF+EAQC5EQ4AiAtGElJnGVYaceMJGCKQQkNWAQAxAAAEMcYgpJBSSinFGGOMMcYYY4wxxhhjjDHGnGPOOeecAADABAcAgAAr2JVZWrVR3NRJXvRB4BM6YjMy5FIqZnIi6JEaarES7NAKbvACsNCQlQAAGQAA5KSUlFotGkLKQWk1iMgg5STFJCJjkILSgqeQMYhJyh1jCiEFqXbQMYUUoxpSCplSCmqqOYaOMagxJ+FSCaUGAABAEAAgICQAwABBwQwAMDhAGDkQ6AggcGgDAAxEyExgUAgNDjIB4AEiQioASExQlC50QQgRpIsgiwcu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";
- public const string Base64OldArrowCursor = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBwoWHS0d8XaOAAAAeUlEQVRo3u3WyQmAUBAEUUMx/yRdEFH/dhG6EKsieKeZniYzMzMzMzMzs5ctWzBgZgk7ACUcAJBwAjDCBYAIdwBCeAIAQgmIE2pAmNACRAltQJDQA8QIfUCIMAJECGNAgFAforI/nWL4GcHvGB4k8CSDRyk8y83s860lExWMmEMyvAAAAABJRU5ErkJggg==";
- public const string Base64OldArrowFarCursor = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfdBwoWHwRtdYxgAAAAfElEQVRo3u3WwQmAMBQEUetPtXYQERE10VyEHcSZNPBOPztNZmZmZmZmZmYvK7VUGDCzhBWAEjYASNgBGOEAQIQzACFcAQChBcQJPSBMuANECfeAIOEJECM8A0KEESBCGAMChP4Qte9Ppxj+jODvGB4k8CSDRyk8y83s8y1ZdnQ0Empj3AAAAABJRU5ErkJggg==";
- #endregion
-
public static string BaseDirectory = null!;
public static bool IsFirstRun { get; private set; } = false;
public static bool IsQuiet { get; private set; } = false;
public static bool IsUninstall { get; private set; } = false;
public static bool IsNoLaunch { get; private set; } = false;
+ public static bool IsUpgrade { get; private set; } = false;
+ public static string[] LaunchArgs { get; private set; } = null!;
public static string LocalAppData { get; private set; } = null!;
public static string StartMenu { get; private set; } = null!;
@@ -71,6 +66,8 @@ static void Main(string[] args)
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
+ LaunchArgs = args;
+
HttpClient.Timeout = TimeSpan.FromMinutes(5);
HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository);
@@ -87,6 +84,9 @@ static void Main(string[] args)
if (Array.IndexOf(args, "-nolaunch") != -1)
IsNoLaunch = true;
+
+ if (Array.IndexOf(args, "-upgrade") != -1)
+ IsUpgrade = true;
}
// check if installed
@@ -126,12 +126,13 @@ static void Main(string[] args)
}
#if !DEBUG
- Updater.Check().Wait();
+ if (!IsUninstall && !IsFirstRun)
+ Updater.CheckInstalledVersion();
#endif
string commandLine = "";
-#if false //DEBUG
+#if false//DEBUG
new Preferences().ShowDialog();
#else
if (args.Length > 0)
@@ -156,15 +157,12 @@ static void Main(string[] args)
}
else
{
- commandLine = String.Join(" ", args);
+ commandLine = "--app";
}
}
else
{
- if (Settings.UseDisableAppPatch)
- Utilities.OpenWebsite("https://www.roblox.com/games");
- else
- commandLine = "--app";
+ commandLine = "--app";
}
#endif
diff --git a/Bloxstrap/Resources/Icon2009-ico.ico b/Bloxstrap/Resources/Icon2009-ico.ico
index 34164b3..f9f0a6b 100644
Binary files a/Bloxstrap/Resources/Icon2009-ico.ico and b/Bloxstrap/Resources/Icon2009-ico.ico differ
diff --git a/Bloxstrap/Resources/Icon2011-ico.ico b/Bloxstrap/Resources/Icon2011-ico.ico
index 486488f..a2876df 100644
Binary files a/Bloxstrap/Resources/Icon2011-ico.ico and b/Bloxstrap/Resources/Icon2011-ico.ico differ
diff --git a/Bloxstrap/Resources/Icon2017-ico.ico b/Bloxstrap/Resources/Icon2017-ico.ico
index 67a46ad..885a743 100644
Binary files a/Bloxstrap/Resources/Icon2017-ico.ico and b/Bloxstrap/Resources/Icon2017-ico.ico differ
diff --git a/Bloxstrap/Resources/Icon2019-ico.ico b/Bloxstrap/Resources/Icon2019-ico.ico
index 9424b47..d218fb9 100644
Binary files a/Bloxstrap/Resources/Icon2019-ico.ico and b/Bloxstrap/Resources/Icon2019-ico.ico differ
diff --git a/Bloxstrap/Resources/Icon2022-ico.ico b/Bloxstrap/Resources/Icon2022-ico.ico
index a05eb33..877b76b 100644
Binary files a/Bloxstrap/Resources/Icon2022-ico.ico and b/Bloxstrap/Resources/Icon2022-ico.ico differ
diff --git a/Bloxstrap/Resources/IconBloxstrap-ico.ico b/Bloxstrap/Resources/IconBloxstrap-ico.ico
index 18cbcf6..1b6988c 100644
Binary files a/Bloxstrap/Resources/IconBloxstrap-ico.ico and b/Bloxstrap/Resources/IconBloxstrap-ico.ico differ
diff --git a/Bloxstrap/Resources/IconEarly2015-ico.ico b/Bloxstrap/Resources/IconEarly2015-ico.ico
index 22b9983..de52a19 100644
Binary files a/Bloxstrap/Resources/IconEarly2015-ico.ico and b/Bloxstrap/Resources/IconEarly2015-ico.ico differ
diff --git a/Bloxstrap/Resources/IconLate2015-ico.ico b/Bloxstrap/Resources/IconLate2015-ico.ico
index 8b9eb52..634a089 100644
Binary files a/Bloxstrap/Resources/IconLate2015-ico.ico and b/Bloxstrap/Resources/IconLate2015-ico.ico differ
diff --git a/Bloxstrap/Resources/Mods/OldCursor.png b/Bloxstrap/Resources/Mods/OldCursor.png
new file mode 100644
index 0000000..694c26a
Binary files /dev/null and b/Bloxstrap/Resources/Mods/OldCursor.png differ
diff --git a/Bloxstrap/Resources/Mods/OldDeath.ogg b/Bloxstrap/Resources/Mods/OldDeath.ogg
new file mode 100644
index 0000000..193677b
Binary files /dev/null and b/Bloxstrap/Resources/Mods/OldDeath.ogg differ
diff --git a/Bloxstrap/Resources/Mods/OldFarCursor.png b/Bloxstrap/Resources/Mods/OldFarCursor.png
new file mode 100644
index 0000000..daf5471
Binary files /dev/null and b/Bloxstrap/Resources/Mods/OldFarCursor.png differ
diff --git a/README.md b/README.md
index 8d41f90..bec8b70 100644
--- a/README.md
+++ b/README.md
@@ -6,32 +6,41 @@
An open-source, feature-packed alternative bootstrapper for Roblox.
-This a drop-in replacement for the stock Roblox bootstrapper, working more or less how you'd expect it to, while providing additional useful features. Keep in mind - this does not touch or modify the game client itself, it's just a launcher!
+This a drop-in replacement for the stock Roblox bootstrapper, working more or less how you'd expect it to, while providing additional useful features.
+
+Keep in mind - this does not touch or modify the game client by itself, it's just a launcher!
If you encounter a bug, or would like to suggest a feature, please submit an issue!
Bloxstrap is only supported for PCs running Windows.
## Features
-Here's some of the features that Bloxstrap provides over the stock Roblox bootstrapper:
+Here's a list of the features that Bloxstrap provides over the stock Roblox bootstrapper:
-* A customizable launcher (including dark theme!)
+* Ability to disable the Desktop app
* Persistent file modifications (re-adds the old death sound!)
-* Support for opting in to pre-release testing channels
-* Painless Discord Rich Presence support
+* Painless support for Discord Rich Presence
* Support for silent automatic FPS unlocking with [rbxfpsunlocker](https://github.com/axstin/rbxfpsunlocker)
+* A customizable launcher (including dark theme!)
+* Support for opting in to pre-release testing channels
+
## Installing
Download the [latest release of Bloxstrap](https://github.com/pizzaboxer/bloxstrap/releases/latest), and run it. Configure your preferences if needed, and install. That's about it!
+Alternatively, you can install Bloxstrap via Winget:
+```
+> winget install bloxstrap
+```
+
You will also need the [.NET 6 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.9-windows-x64-installer). If you don't already have it installed, you'll be prompted to install it anyway.
It's not unlikely that Windows Smartscreen will show a popup when you run Bloxstrap for the first time. This happens because it's an unknown program, not because it's actually detected as being malicious. To dismiss it, just click on "More info" and then "Run anyway".
Once installed, Bloxstrap is added to your Start Menu, where you can change your preferences if needed.
-## Contributions
-* [Roblox Studio Mod Manager](https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager) - some utilities was borrowed to help with Bloxstrap's bootstrapper functionality. Besides, it's a great project.
+## Other Contributions
+* [Roblox Studio Mod Manager](https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager) - some utilities were borrowed to help with Bloxstrap's bootstrapper functionality.
* [skulyire](https://www.roblox.com/users/2485612194/profile) - Making the Bloxstrap logo.
* [rbxfpsunlocker](https://github.com/axstin/rbxfpsunlocker) - Used for Bloxstrap's FPS unlocking.
* [WPFDarkTheme](https://github.com/AngryCarrot789/WPFDarkTheme) - Used for making the Preferences menu.