Initialize stock WPF application

This commit is contained in:
pizzaboxer 2023-01-27 13:22:02 +00:00
parent 0f8cd4a922
commit 44183090e0
89 changed files with 93 additions and 23384 deletions

View File

@ -1,61 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
<PlatformTarget>AnyCPU</PlatformTarget>
<Platforms>AnyCPU;x86</Platforms>
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
<Version>1.7.0</Version>
<FileVersion>1.7.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<Content Include="Bloxstrap.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Mods\OldCursor.png" />
<EmbeddedResource Include="Resources\Mods\OldDeath.ogg" />
<EmbeddedResource Include="Resources\Mods\OldFarCursor.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Dialogs\BootstrapperDialogs\LegacyDialog2009.cs" />
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

View File

@ -1,9 +1,9 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.0.32014.148 VisualStudioVersion = 17.3.32819.101
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bloxstrap", "Bloxstrap\Bloxstrap.csproj", "{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bloxstrap", "Bloxstrap\Bloxstrap.csproj", "{0D75146E-DA24-4B05-B6C9-250C8F81B0C7}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,15 +11,15 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Debug|Any CPU.Build.0 = Debug|Any CPU {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{646D1D58-C9CA-48C9-BBCD-30585A1DAAF1}.Release|Any CPU.Build.0 = Release|Any CPU {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F989AC04-B48F-4BB4-B940-1E7D082F14DA} SolutionGuid = {ED269E5D-8C72-49B4-A76F-51CF163511C1}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -0,0 +1,9 @@
<Application x:Class="Bloxstrap.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Bloxstrap"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace Bloxstrap
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,12 @@
<Window x:Class="Bloxstrap.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Bloxstrap"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Bloxstrap
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@ -1,794 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using Microsoft.Win32;
using Bloxstrap.Enums;
using Bloxstrap.Dialogs.BootstrapperDialogs;
using Bloxstrap.Helpers;
using Bloxstrap.Helpers.Integrations;
using Bloxstrap.Helpers.RSMM;
using Bloxstrap.Models;
using System.Net;
using Bloxstrap.Properties;
namespace Bloxstrap
{
public partial class Bootstrapper
{
#region Properties
// https://learn.microsoft.com/en-us/windows/win32/msi/error-codes
public const int ERROR_SUCCESS = 0;
public const int ERROR_INSTALL_USEREXIT = 1602;
public const int ERROR_INSTALL_FAILURE = 1603;
public const int ERROR_PRODUCT_UNINSTALLED = 1614;
// in case a new package is added, you can find the corresponding directory
// 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 string? LaunchCommandLine;
private string VersionGuid = null!;
private PackageManifest VersionPackageManifest = null!;
private string VersionFolder = null!;
private readonly bool FreshInstall;
private double ProgressIncrement;
private long TotalBytes = 0;
private long TotalDownloadedBytes = 0;
private int PackagesExtracted = 0;
private bool CancelFired = false;
public IBootstrapperDialog Dialog = null!;
#endregion
#region Core
public Bootstrapper(string? launchCommandLine = null)
{
LaunchCommandLine = launchCommandLine;
FreshInstall = String.IsNullOrEmpty(Program.Settings.VersionGuid);
}
// this is called from BootstrapperStyleForm.SetupDialog()
public async Task Run()
{
if (Program.IsQuiet)
Dialog.CloseDialog();
if (Program.IsUninstall)
{
Uninstall();
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
// if roblox needs updating but is running, ignore update for now
if (!Directory.Exists(VersionFolder) && CheckIfRunning(true) || Program.Settings.VersionGuid != VersionGuid && !CheckIfRunning(false))
await InstallLatestVersion();
await ApplyModifications();
if (Program.IsFirstRun)
Program.SettingsManager.ShouldSave = true;
if (Program.IsFirstRun || FreshInstall)
Register();
CheckInstall();
await RbxFpsUnlocker.CheckInstall();
Program.SettingsManager.Save();
if (Program.IsFirstRun && Program.IsNoLaunch)
Dialog.ShowSuccess($"{Program.ProjectName} has successfully installed");
else if (!Program.IsNoLaunch)
await StartRoblox();
}
private async Task CheckForUpdates()
{
string currentVersion = $"Bloxstrap v{Program.Version}";
var releaseInfo = await Utilities.GetJson<GithubRelease>($"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);
Program.SettingsManager.Save();
Process.Start(startInfo);
Environment.Exit(0);
}
private async Task CheckLatestVersion()
{
Dialog.Message = "Connecting to Roblox...";
ClientVersion clientVersion = await DeployManager.GetLastDeploy(Program.Settings.Channel);
VersionGuid = clientVersion.VersionGuid;
VersionFolder = Path.Combine(Directories.Versions, VersionGuid);
VersionPackageManifest = await PackageManifest.Get(VersionGuid);
}
private bool CheckIfRunning(bool shutdown)
{
Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta");
if (processes.Length == 0)
return false;
if (shutdown)
{
Dialog.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) { }
}
return true;
}
private async Task StartRoblox()
{
string startEventName = Program.ProjectName.Replace(" ", "") + "StartEvent";
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();
if (Program.Settings.Channel.ToLower() != DeployManager.DefaultChannel.ToLower())
LaunchCommandLine += " -channel " + Program.Settings.Channel.ToLower();
LaunchCommandLine += " -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()
{
FileName = Path.Combine(Directories.Integrations, @"rbxfpsunlocker\rbxfpsunlocker.exe"),
WorkingDirectory = Path.Combine(Directories.Integrations, "rbxfpsunlocker")
};
rbxFpsUnlocker = Process.Start(startInfo);
if (Program.Settings.RFUAutoclose)
shouldWait = true;
}
// event fired, wait for 3 seconds then close
await Task.Delay(3000);
// now we move onto handling rich presence
if (Program.Settings.UseDiscordRichPresence)
{
richPresence = new DiscordRichPresence();
richPresence.MonitorGameActivity();
shouldWait = true;
}
if (!shouldWait)
return;
// keep bloxstrap open in the background
Dialog.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 (!Dialog.CancelEnabled)
{
Program.Exit(ERROR_INSTALL_USEREXIT);
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(ERROR_INSTALL_USEREXIT);
}
#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("DisplayVersion", Program.Version);
if (uninstallKey.GetValue("InstallDate") is null)
uninstallKey.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
uninstallKey.SetValue("InstallLocation", Directories.Base);
uninstallKey.SetValue("NoRepair", 1);
uninstallKey.SetValue("Publisher", "pizzaboxer");
uninstallKey.SetValue("ModifyPath", $"\"{Directories.App}\" -preferences");
uninstallKey.SetValue("QuietUninstallString", $"\"{Directories.App}\" -uninstall -quiet");
uninstallKey.SetValue("UninstallString", $"\"{Directories.App}\" -uninstall");
uninstallKey.SetValue("URLInfoAbout", $"https://github.com/{Program.ProjectRepository}");
uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{Program.ProjectRepository}/releases/latest");
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(Directories.StartMenu))
{
Directory.CreateDirectory(Directories.StartMenu);
ShellLink.Shortcut.CreateShortcut(Directories.App, "", Directories.App, 0)
.WriteToFile(Path.Combine(Directories.StartMenu, "Play Roblox.lnk"));
ShellLink.Shortcut.CreateShortcut(Directories.App, "-preferences", Directories.App, 0)
.WriteToFile(Path.Combine(Directories.StartMenu, $"Configure {Program.ProjectName}.lnk"));
}
if (Program.Settings.CreateDesktopIcon && !File.Exists(Path.Combine(Directories.Desktop, "Play Roblox.lnk")))
{
ShellLink.Shortcut.CreateShortcut(Directories.App, "", Directories.App, 0)
.WriteToFile(Path.Combine(Directories.Desktop, "Play Roblox.lnk"));
}
}
private void Uninstall()
{
CheckIfRunning(true);
Dialog.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(Directories.StartMenu, true);
// delete desktop shortcut
File.Delete(Path.Combine(Directories.Desktop, "Play Roblox.lnk"));
// 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 e)
{
Debug.WriteLine($"Could not fully uninstall! ({e})");
}
Dialog.ShowSuccess($"{Program.ProjectName} has succesfully uninstalled");
Program.Exit();
}
#endregion
#region Roblox Install
private void UpdateProgressbar()
{
int newProgress = (int)Math.Floor(ProgressIncrement * TotalDownloadedBytes);
Dialog.ProgressValue = newProgress;
}
private async Task InstallLatestVersion()
{
if (FreshInstall)
Dialog.Message = "Installing Roblox...";
else
Dialog.Message = "Upgrading Roblox...";
// check if we have at least 300 megabytes of free disk space
if (Utilities.GetFreeDiskSpace(Directories.Base) < 1024*1024*300)
{
Program.ShowMessageBox($"{Program.ProjectName} requires at least 300 MB of disk space to install Roblox. Please free up some disk space and try again.", MessageBoxIcon.Error);
Program.Exit(ERROR_INSTALL_FAILURE);
return;
}
Directory.CreateDirectory(Directories.Base);
Dialog.CancelEnabled = true;
Dialog.ProgressStyle = ProgressBarStyle.Continuous;
// 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 synchronously
await DownloadPackage(package);
// extract the package immediately after download
ExtractPackage(package);
}
// allow progress bar to 100% before continuing (purely ux reasons lol)
await Task.Delay(1000);
Dialog.ProgressStyle = ProgressBarStyle.Marquee;
Dialog.Message = "Configuring Roblox...";
// wait for all packages to finish extracting
while (PackagesExtracted < VersionPackageManifest.Count)
{
await Task.Delay(100);
}
string appSettingsLocation = Path.Combine(VersionFolder, "AppSettings.xml");
await File.WriteAllTextAsync(appSettingsLocation, AppSettings);
if (!FreshInstall)
{
ReShade.SynchronizeConfigFile();
// 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);
}
string oldVersionFolder = Path.Combine(Directories.Versions, Program.Settings.VersionGuid);
if (VersionGuid != Program.Settings.VersionGuid && Directory.Exists(oldVersionFolder))
{
// and also to delete our old version folder
Directory.Delete(oldVersionFolder, true);
}
}
Dialog.CancelEnabled = false;
Program.Settings.VersionGuid = VersionGuid;
}
private async Task ApplyModifications()
{
Dialog.Message = "Applying Roblox modifications...";
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);
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", "");
await ReShade.CheckModifications();
foreach (string file in Directory.GetFiles(modFolder, "*.*", SearchOption.AllDirectories))
{
// get relative directory path
string relativeFile = file.Substring(modFolder.Length + 1);
// v1.7.0 - README has been moved to the preferences menu now
if (relativeFile == "README.txt")
{
File.Delete(file);
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 = (await File.ReadAllLinesAsync(manifestFile)).ToList();
else
manifestFiles = modFolderFiles;
// copy and overwrite
foreach (string file in modFolderFiles)
{
string fileModFolder = Path.Combine(modFolder, file);
string fileVersionFolder = Path.Combine(VersionFolder, file);
if (File.Exists(fileVersionFolder))
{
if (Utilities.MD5File(fileModFolder) == Utilities.MD5File(fileVersionFolder))
continue;
}
string? directory = Path.GetDirectoryName(fileVersionFolder);
if (directory is null)
continue;
Directory.CreateDirectory(directory);
File.Copy(fileModFolder, fileVersionFolder, true);
File.SetAttributes(fileVersionFolder, File.GetAttributes(fileModFolder) & ~FileAttributes.ReadOnly);
}
// 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))
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
string versionFileLocation = Path.Combine(VersionFolder, fileLocation);
File.Delete(versionFileLocation);
continue;
}
// restore original file
string fileName = fileLocation.Substring(packageDirectory.Value.Length);
ExtractFileFromPackage(packageDirectory.Key, fileName);
}
File.WriteAllLines(manifestFile, modFolderFiles);
}
private static async Task CheckModPreset(bool condition, string location, string name)
{
string modFolderLocation = Path.Combine(Directories.Modifications, location);
byte[] binaryData = string.IsNullOrEmpty(name) ? Array.Empty<byte>() : await ResourceHelper.Get(name);
if (condition)
{
if (!File.Exists(modFolderLocation))
{
string? directory = Path.GetDirectoryName(modFolderLocation);
if (directory is null)
return;
Directory.CreateDirectory(directory);
await File.WriteAllBytesAsync(modFolderLocation, binaryData);
}
}
else if (File.Exists(modFolderLocation) && Utilities.MD5File(modFolderLocation) == Utilities.MD5Data(binaryData))
{
File.Delete(modFolderLocation);
}
}
private async Task DownloadPackage(Package package)
{
string packageUrl = $"{DeployManager.BaseUrl}/{VersionGuid}-{package.Name}";
string packageLocation = Path.Combine(Directories.Downloads, package.Signature);
string robloxPackageLocation = Path.Combine(Directories.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...");
TotalDownloadedBytes += package.PackedSize;
UpdateProgressbar();
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);
TotalDownloadedBytes += package.PackedSize;
UpdateProgressbar();
return;
}
if (!File.Exists(packageLocation))
{
Debug.WriteLine($"Downloading {package.Name}...");
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))
{
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}!");
}
}
private async 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;
string? directory;
Debug.WriteLine($"Extracting {package.Name} to {packageFolder}...");
using (ZipArchive archive = await Task.Run(() => 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 = Path.GetDirectoryName(extractPath);
if (directory is null)
continue;
Directory.CreateDirectory(directory);
await Task.Run(() => entry.ExtractToFile(extractPath, true));
}
}
Debug.WriteLine($"Finished extracting {package.Name}");
PackagesExtracted += 1;
}
private void ExtractFileFromPackage(string packageName, string fileName)
{
Package? package = VersionPackageManifest.Find(x => x.Name == packageName);
if (package is null)
return;
DownloadPackage(package).GetAwaiter().GetResult();
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);
File.Delete(fileLocation);
entry.ExtractToFile(fileLocation);
}
}
#endregion
}
}

View File

@ -1,159 +0,0 @@
using Bloxstrap.Enums;
using Bloxstrap.Helpers;
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
public class BootstrapperDialogForm : Form, IBootstrapperDialog
{
public Bootstrapper? Bootstrapper { get; set; }
protected virtual string _message { get; set; } = "Please wait...";
protected virtual ProgressBarStyle _progressStyle { get; set; }
protected virtual int _progressValue { get; set; }
protected virtual bool _cancelEnabled { get; set; }
public string Message
{
get => _message;
set
{
if (this.InvokeRequired)
this.Invoke(() => _message = value);
else
_message = value;
}
}
public ProgressBarStyle ProgressStyle
{
get => _progressStyle;
set
{
if (this.InvokeRequired)
this.Invoke(() => _progressStyle = value);
else
_progressStyle = value;
}
}
public int ProgressValue
{
get => _progressValue;
set
{
if (this.InvokeRequired)
this.Invoke(() => _progressValue = value);
else
_progressValue = value;
}
}
public bool CancelEnabled
{
get => _cancelEnabled;
set
{
if (this.InvokeRequired)
this.Invoke(() => _cancelEnabled = value);
else
_cancelEnabled = value;
}
}
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)
this.Hide();
this.Text = Program.ProjectName;
this.Icon = Program.Settings.BootstrapperIcon.GetIcon();
if (Bootstrapper is null)
{
Message = "Style Preview - Click Cancel to return";
CancelEnabled = true;
}
else
{
Bootstrapper.Dialog = this;
Task.Run(() => RunBootstrapper());
}
}
public async void RunBootstrapper()
{
if (Bootstrapper is null)
return;
#if DEBUG
await Bootstrapper.Run();
#else
try
{
await Bootstrapper.Run();
}
catch (Exception ex)
{
// string message = String.Format("{0}: {1}", ex.GetType(), ex.Message);
string message = ex.ToString();
ShowError(message);
}
#endif
Program.Exit();
}
public virtual void ShowSuccess(string message)
{
Program.ShowMessageBox(message, MessageBoxIcon.Information);
Program.Exit();
}
public virtual void ShowError(string message)
{
Program.ShowMessageBox($"An error occurred while starting Roblox\n\nDetails: {message}", MessageBoxIcon.Error);
Program.Exit(Bootstrapper.ERROR_INSTALL_FAILURE);
}
public virtual void CloseDialog()
{
if (this.InvokeRequired)
this.Invoke(CloseDialog);
else
this.Hide();
}
public void PromptShutdown()
{
DialogResult result = Program.ShowMessageBox(
"Roblox is currently running, but needs to close. Would you like close Roblox now?",
MessageBoxIcon.Information,
MessageBoxButtons.OKCancel
);
if (result != DialogResult.OK)
Environment.Exit(Bootstrapper.ERROR_INSTALL_USEREXIT);
}
public void ButtonCancel_Click(object? sender, EventArgs e)
{
if (Bootstrapper is null)
this.Close();
else
Task.Run(() => Bootstrapper.CancelButtonClicked());
}
}
}

View File

@ -1,18 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
public interface IBootstrapperDialog
{
Bootstrapper? Bootstrapper { get; set; }
string Message { get; set; }
ProgressBarStyle ProgressStyle { get; set; }
int ProgressValue { get; set; }
bool CancelEnabled { get; set; }
void RunBootstrapper();
void ShowSuccess(string message);
void ShowError(string message);
void CloseDialog();
void PromptShutdown();
}
}

View File

@ -1,94 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
partial class LegacyDialog2009
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.labelMessage = new System.Windows.Forms.Label();
this.ProgressBar = new System.Windows.Forms.ProgressBar();
this.buttonCancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// labelMessage
//
this.labelMessage.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.labelMessage.Location = new System.Drawing.Point(12, 16);
this.labelMessage.Name = "labelMessage";
this.labelMessage.Size = new System.Drawing.Size(287, 17);
this.labelMessage.TabIndex = 0;
this.labelMessage.Text = "Please wait...";
//
// ProgressBar
//
this.ProgressBar.Location = new System.Drawing.Point(15, 47);
this.ProgressBar.MarqueeAnimationSpeed = 33;
this.ProgressBar.Name = "ProgressBar";
this.ProgressBar.Size = new System.Drawing.Size(281, 20);
this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.ProgressBar.TabIndex = 1;
//
// buttonCancel
//
this.buttonCancel.Enabled = false;
this.buttonCancel.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonCancel.Location = new System.Drawing.Point(221, 83);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(75, 23);
this.buttonCancel.TabIndex = 3;
this.buttonCancel.Text = "Cancel";
this.buttonCancel.UseVisualStyleBackColor = true;
this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click);
//
// LegacyDialog2009
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(311, 122);
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.ProgressBar);
this.Controls.Add(this.labelMessage);
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(327, 161);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(327, 161);
this.Name = "LegacyDialog2009";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "LegacyDialog2009";
this.Load += new System.EventHandler(this.LegacyDialog2009_Load);
this.ResumeLayout(false);
}
#endregion
private Label labelMessage;
private ProgressBar ProgressBar;
private Button buttonCancel;
}
}

View File

@ -1,47 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
// windows: https://youtu.be/VpduiruysuM?t=18
// mac: https://youtu.be/ncHhbcVDRgQ?t=63
public partial class LegacyDialog2009 : BootstrapperDialogForm
{
protected override string _message
{
get => labelMessage.Text;
set => labelMessage.Text = value;
}
protected override ProgressBarStyle _progressStyle
{
get => ProgressBar.Style;
set => ProgressBar.Style = value;
}
protected override int _progressValue
{
get => ProgressBar.Value;
set => ProgressBar.Value = value;
}
protected override bool _cancelEnabled
{
get => this.buttonCancel.Enabled;
set => this.buttonCancel.Enabled = value;
}
public LegacyDialog2009(Bootstrapper? bootstrapper = null)
{
InitializeComponent();
Bootstrapper = bootstrapper;
ScaleWindow();
SetupDialog();
}
private void LegacyDialog2009_Load(object sender, EventArgs e)
{
this.Activate();
}
}
}

View File

@ -1,60 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,109 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
partial class LegacyDialog2011
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.labelMessage = new System.Windows.Forms.Label();
this.ProgressBar = new System.Windows.Forms.ProgressBar();
this.IconBox = new System.Windows.Forms.PictureBox();
this.buttonCancel = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit();
this.SuspendLayout();
//
// labelMessage
//
this.labelMessage.Location = new System.Drawing.Point(55, 23);
this.labelMessage.Name = "labelMessage";
this.labelMessage.Size = new System.Drawing.Size(287, 17);
this.labelMessage.TabIndex = 0;
this.labelMessage.Text = "Please wait...";
//
// ProgressBar
//
this.ProgressBar.Location = new System.Drawing.Point(58, 51);
this.ProgressBar.MarqueeAnimationSpeed = 33;
this.ProgressBar.Name = "ProgressBar";
this.ProgressBar.Size = new System.Drawing.Size(287, 26);
this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.ProgressBar.TabIndex = 1;
//
// IconBox
//
this.IconBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.IconBox.ImageLocation = "";
this.IconBox.Location = new System.Drawing.Point(19, 16);
this.IconBox.Name = "IconBox";
this.IconBox.Size = new System.Drawing.Size(32, 32);
this.IconBox.TabIndex = 2;
this.IconBox.TabStop = false;
//
// buttonCancel
//
this.buttonCancel.Enabled = false;
this.buttonCancel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.buttonCancel.Location = new System.Drawing.Point(271, 83);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(75, 23);
this.buttonCancel.TabIndex = 3;
this.buttonCancel.Text = "Cancel";
this.buttonCancel.UseVisualStyleBackColor = true;
this.buttonCancel.Visible = false;
this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click);
//
// LegacyDialog2011
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(362, 131);
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.IconBox);
this.Controls.Add(this.ProgressBar);
this.Controls.Add(this.labelMessage);
this.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(378, 170);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(378, 170);
this.Name = "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);
}
#endregion
private Label labelMessage;
private ProgressBar ProgressBar;
private PictureBox IconBox;
private Button buttonCancel;
}
}

View File

@ -1,51 +0,0 @@
using Bloxstrap.Enums;
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
// https://youtu.be/3K9oCEMHj2s?t=35
public partial class LegacyDialog2011 : BootstrapperDialogForm
{
protected override string _message
{
get => labelMessage.Text;
set => labelMessage.Text = value;
}
protected override ProgressBarStyle _progressStyle
{
get => ProgressBar.Style;
set => ProgressBar.Style = value;
}
protected override int _progressValue
{
get => ProgressBar.Value;
set => ProgressBar.Value = value;
}
protected override bool _cancelEnabled
{
get => this.buttonCancel.Enabled;
set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value;
}
public LegacyDialog2011(Bootstrapper? bootstrapper = null)
{
InitializeComponent();
Bootstrapper = bootstrapper;
// have to convert icon -> bitmap since winforms scaling is poop
this.IconBox.BackgroundImage = Program.Settings.BootstrapperIcon.GetIcon().ToBitmap();
ScaleWindow();
SetupDialog();
}
private void LegacyDialog2011_Load(object sender, EventArgs e)
{
this.Activate();
}
}
}

View File

@ -1,60 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,128 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
partial class ProgressDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ProgressBar = new System.Windows.Forms.ProgressBar();
this.labelMessage = new System.Windows.Forms.Label();
this.IconBox = new System.Windows.Forms.PictureBox();
this.buttonCancel = new System.Windows.Forms.PictureBox();
this.panel1 = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)(this.IconBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.buttonCancel)).BeginInit();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// ProgressBar
//
this.ProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.ProgressBar.Location = new System.Drawing.Point(29, 241);
this.ProgressBar.MarqueeAnimationSpeed = 20;
this.ProgressBar.Name = "ProgressBar";
this.ProgressBar.Size = new System.Drawing.Size(460, 20);
this.ProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.ProgressBar.TabIndex = 0;
//
// labelMessage
//
this.labelMessage.Font = new System.Drawing.Font("Tahoma", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
this.labelMessage.Location = new System.Drawing.Point(29, 199);
this.labelMessage.Name = "labelMessage";
this.labelMessage.Size = new System.Drawing.Size(460, 18);
this.labelMessage.TabIndex = 1;
this.labelMessage.Text = "Please wait...";
this.labelMessage.TextAlign = System.Drawing.ContentAlignment.TopCenter;
this.labelMessage.UseMnemonic = false;
//
// IconBox
//
this.IconBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.IconBox.ImageLocation = "";
this.IconBox.Location = new System.Drawing.Point(212, 66);
this.IconBox.Name = "IconBox";
this.IconBox.Size = new System.Drawing.Size(92, 92);
this.IconBox.TabIndex = 2;
this.IconBox.TabStop = false;
//
// buttonCancel
//
this.buttonCancel.Enabled = false;
this.buttonCancel.Image = global::Bloxstrap.Properties.Resources.CancelButton;
this.buttonCancel.Location = new System.Drawing.Point(194, 264);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(130, 44);
this.buttonCancel.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.buttonCancel.TabIndex = 3;
this.buttonCancel.TabStop = false;
this.buttonCancel.Visible = false;
this.buttonCancel.Click += new System.EventHandler(this.ButtonCancel_Click);
this.buttonCancel.MouseEnter += new System.EventHandler(this.ButtonCancel_MouseEnter);
this.buttonCancel.MouseLeave += new System.EventHandler(this.ButtonCancel_MouseLeave);
//
// panel1
//
this.panel1.BackColor = System.Drawing.SystemColors.Window;
this.panel1.Controls.Add(this.labelMessage);
this.panel1.Controls.Add(this.IconBox);
this.panel1.Controls.Add(this.buttonCancel);
this.panel1.Controls.Add(this.ProgressBar);
this.panel1.Location = new System.Drawing.Point(1, 1);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(518, 318);
this.panel1.TabIndex = 4;
//
// ProgressDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.ActiveBorder;
this.ClientSize = new System.Drawing.Size(520, 320);
this.Controls.Add(this.panel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximumSize = new System.Drawing.Size(520, 320);
this.MinimumSize = new System.Drawing.Size(520, 320);
this.Name = "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);
this.ResumeLayout(false);
}
#endregion
private ProgressBar ProgressBar;
private Label labelMessage;
private PictureBox IconBox;
private PictureBox buttonCancel;
private Panel panel1;
}
}

View File

@ -1,81 +0,0 @@
using Bloxstrap.Enums;
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
// basically just the modern dialog
public partial class ProgressDialog : BootstrapperDialogForm
{
protected override string _message
{
get => labelMessage.Text;
set => labelMessage.Text = value;
}
protected override ProgressBarStyle _progressStyle
{
get => ProgressBar.Style;
set => ProgressBar.Style = value;
}
protected override int _progressValue
{
get => ProgressBar.Value;
set => ProgressBar.Value = value;
}
protected override bool _cancelEnabled
{
get => this.buttonCancel.Enabled;
set => this.buttonCancel.Enabled = this.buttonCancel.Visible = value;
}
public ProgressDialog(Bootstrapper? bootstrapper = null)
{
InitializeComponent();
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
{
this.labelMessage.ForeColor = SystemColors.Window;
this.buttonCancel.Image = Properties.Resources.DarkCancelButton;
this.panel1.BackColor = Color.FromArgb(35, 37, 39);
this.BackColor = Color.FromArgb(25, 27, 29);
}
Bootstrapper = bootstrapper;
this.IconBox.BackgroundImage = Program.Settings.BootstrapperIcon.GetBitmap();
SetupDialog();
}
private void ButtonCancel_MouseEnter(object sender, EventArgs e)
{
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
{
this.buttonCancel.Image = Properties.Resources.DarkCancelButtonHover;
}
else
{
this.buttonCancel.Image = Properties.Resources.CancelButtonHover;
}
}
private void ButtonCancel_MouseLeave(object sender, EventArgs e)
{
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
{
this.buttonCancel.Image = Properties.Resources.DarkCancelButton;
}
else
{
this.buttonCancel.Image = Properties.Resources.CancelButton;
}
}
private void ProgressDialog_Load(object sender, EventArgs e)
{
this.Activate();
}
}
}

View File

@ -1,60 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,51 +0,0 @@
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
partial class VistaDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// 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.VistaDialog_Load);
this.ResumeLayout(false);
}
#endregion
}
}

View File

@ -1,165 +0,0 @@
using Bloxstrap.Enums;
namespace Bloxstrap.Dialogs.BootstrapperDialogs
{
// https://youtu.be/h0_AL95Sc3o?t=48
// a bit hacky, but this is actually a hidden form
// since taskdialog is part of winforms, it can't really be properly used without a form
// for example, cross-threaded calls to ui controls can't really be done outside of a form
public partial class VistaDialog : BootstrapperDialogForm
{
private TaskDialogPage Dialog;
protected override string _message
{
get => Dialog.Heading ?? "";
set => Dialog.Heading = value;
}
protected override ProgressBarStyle _progressStyle
{
set
{
if (Dialog.ProgressBar is null)
return;
switch (value)
{
case ProgressBarStyle.Continuous:
case ProgressBarStyle.Blocks:
Dialog.ProgressBar.State = TaskDialogProgressBarState.Normal;
break;
case ProgressBarStyle.Marquee:
Dialog.ProgressBar.State = TaskDialogProgressBarState.Marquee;
break;
}
}
}
protected override int _progressValue
{
get => Dialog.ProgressBar is null ? 0 : Dialog.ProgressBar.Value;
set
{
if (Dialog.ProgressBar is null)
return;
Dialog.ProgressBar.Value = value;
}
}
protected override bool _cancelEnabled
{
get => Dialog.Buttons[0].Enabled;
set => Dialog.Buttons[0].Enabled = value;
}
public VistaDialog(Bootstrapper? bootstrapper = null)
{
InitializeComponent();
Bootstrapper = bootstrapper;
Dialog = new TaskDialogPage()
{
Icon = new TaskDialogIcon(Program.Settings.BootstrapperIcon.GetIcon()),
Caption = Program.ProjectName,
Buttons = { TaskDialogButton.Cancel },
ProgressBar = new TaskDialogProgressBar()
{
State = TaskDialogProgressBarState.Marquee
}
};
_message = "Please wait...";
_cancelEnabled = false;
Dialog.Buttons[0].Click += (sender, e) => ButtonCancel_Click(sender, e);
SetupDialog();
}
public override void ShowSuccess(string message)
{
if (this.InvokeRequired)
{
this.Invoke(ShowSuccess, message);
}
else
{
TaskDialogPage successDialog = new()
{
Icon = TaskDialogIcon.ShieldSuccessGreenBar,
Caption = Program.ProjectName,
Heading = message,
Buttons = { TaskDialogButton.OK }
};
successDialog.Buttons[0].Click += (sender, e) => Program.Exit();
if (!Program.IsQuiet)
Dialog.Navigate(successDialog);
Dialog = successDialog;
}
}
public override void ShowError(string message)
{
if (this.InvokeRequired)
{
this.Invoke(ShowError, message);
}
else
{
TaskDialogPage errorDialog = new()
{
Icon = TaskDialogIcon.Error,
Caption = Program.ProjectName,
Heading = "An error occurred while starting Roblox",
Buttons = { TaskDialogButton.Close },
Expander = new TaskDialogExpander()
{
Text = message,
CollapsedButtonText = "See details",
ExpandedButtonText = "Hide details",
Position = TaskDialogExpanderPosition.AfterText
}
};
errorDialog.Buttons[0].Click += (sender, e) => Program.Exit(Bootstrapper.ERROR_INSTALL_FAILURE);
if (!Program.IsQuiet)
Dialog.Navigate(errorDialog);
Dialog = errorDialog;
}
}
public override void CloseDialog()
{
if (this.InvokeRequired)
{
this.Invoke(CloseDialog);
}
else
{
if (Dialog.BoundDialog is null)
return;
Dialog.BoundDialog.Close();
}
}
private void VistaDialog_Load(object sender, EventArgs e)
{
if (!Program.IsQuiet)
TaskDialog.ShowDialog(Dialog);
}
}
}

View File

@ -1,60 +0,0 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1,39 +0,0 @@
<Window x:Class="Bloxstrap.Dialogs.Menu.ModHelp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Bloxstrap.Dialogs.Menu"
mc:Ignorable="d"
Style="{DynamicResource MainWindowStyle}"
Title="Modification Help"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--<ResourceDictionary Source="Themes\ColourfulDarkTheme.xaml" />-->
<ResourceDictionary Source="Themes\LightTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Width="420" Height="260">
<StackPanel Margin="10">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Modification Help" VerticalAlignment="Top" FontSize="18" />
<StackPanel Margin="10">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="The Modifications folder is where you can modify your Roblox files while ensuring that they're preserved whenever Roblox updates." />
<TextBlock HorizontalAlignment="Left" Margin="0,10,0,0" TextWrapping="Wrap" Text="For example, Modifications\content\sounds\ouch.ogg will automatically override Versions\Version-{id}\content\sounds\ouch.ogg" />
<TextBlock HorizontalAlignment="Left" Margin="0,10,0,0" TextWrapping="Wrap" Text="When you remove a file from the folder, Bloxstrap restores the original version of the file the next time Roblox launches." />
<TextBlock HorizontalAlignment="Left" Margin="0,10,0,0" TextWrapping="Wrap" Text="The folder is also used for handling presets and files for ReShade, so if you find any files or folders that already exist, you can just ignore them." />
</StackPanel>
</StackPanel>
<Border Background="{DynamicResource ControlSelectedBackground}" BorderBrush="{DynamicResource ControlSelectedBorderBrush}" BorderThickness="1" Margin="-1,0,-1,0" Height="42" VerticalAlignment="Bottom">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="ButtonClose" Content="Close" Width="66" Height="23" HorizontalAlignment="Right" Margin="0,0,10,0" Click="ButtonClose_Click" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@ -1,33 +0,0 @@
using System.Windows;
using Bloxstrap.Enums;
namespace Bloxstrap.Dialogs.Menu
{
/// <summary>
/// Interaction logic for ModHelp.xaml
/// </summary>
public partial class ModHelp : Window
{
public ModHelp()
{
InitializeComponent();
SetTheme();
}
public void SetTheme()
{
string theme = "Light";
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
theme = "ColourfulDark";
this.Resources.MergedDictionaries[0] = new ResourceDictionary() { Source = new Uri($"Dialogs/Menu/Themes/{theme}Theme.xaml", UriKind.Relative) };
}
private void ButtonClose_Click(object sender, EventArgs e)
{
this.Close();
}
}
}

View File

@ -1,177 +0,0 @@
<Window x:Class="Bloxstrap.Dialogs.Menu.Preferences"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Bloxstrap.Dialogs.Menu"
mc:Ignorable="d"
Style="{DynamicResource MainWindowStyle}"
Title="PreferencesWPF"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--<ResourceDictionary Source="Themes\ColourfulDarkTheme.xaml" />-->
<ResourceDictionary Source="Themes\LightTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Width="480" Height="360">
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Configure Bloxstrap" VerticalAlignment="Top" FontSize="18"/>
<TextBlock HorizontalAlignment="Right" Margin="0,14,10,0" TextWrapping="Wrap" Text="{Binding BloxstrapVersion, Mode=OneTime}" VerticalAlignment="Top" FontSize="12"/>
<TabControl TabStripPlacement="Left" Margin="10,40,10,51" Padding="0">
<TabItem Padding="5">
<TabItem.Header>
<TextBlock Text="Integrations" FontSize="13" />
</TabItem.Header>
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Grid.Column="0" Header="Discord Rich Presence" Margin="10,10,5,5">
<StackPanel VerticalAlignment="Center">
<CheckBox x:Name="CheckBoxDRPEnabled" Content=" Show game activity" ToolTip="Choose whether the game you're currently playing should be shown on your Discord game activity." Margin="5" IsChecked="{Binding DRPEnabled, Mode=TwoWay}" />
<CheckBox x:Name="CheckBoxDRPButtons" Content=" Allow activity joining" ToolTip="Choose whether people can join the game you're in through your Discord game activity." Margin="5" IsEnabled="{Binding IsChecked, ElementName=CheckBoxDRPEnabled, Mode=OneWay}" IsChecked="{Binding DRPButtons, Mode=TwoWay}" />
</StackPanel>
</GroupBox>
<GroupBox Grid.Column="1" Header="FPS Unlocking" Margin="5,10,10,5">
<StackPanel VerticalAlignment="Center">
<CheckBox x:Name="CheckBoxRFUEnabled" Content=" Use rbxfpsunlocker" ToolTip="Choose whether rbxfpsunlocker should be downloaded and launched whenever Roblox starts." Margin="5" IsChecked="{Binding RFUEnabled, Mode=TwoWay}" />
<CheckBox x:Name="CheckBoxRFUAutoclose" Content=" Exit when Roblox closes" ToolTip="Choose whether rbxfpsunlocker should stop running when Roblox closes." Margin="5" IsEnabled="{Binding IsChecked, ElementName=CheckBoxRFUEnabled, Mode=OneWay}" IsChecked="{Binding RFUAutoclose, Mode=TwoWay}" />
</StackPanel>
</GroupBox>
</Grid>
<GroupBox Header="ReShade" Margin="10,5,10,0">
<StackPanel VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="155" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" x:Name="CheckBoxUseReShade" Content=" Use ReShade" ToolTip="Choose whether to use ReShade to enhance your graphics with shaders." Margin="5" IsChecked="{Binding UseReShade, Mode=TwoWay}" />
<CheckBox Grid.Column="1" x:Name="CheckBoxUseReShadeExtraviPresets" Content=" Use Extravi's shader presets" ToolTip="Choose whether to use Extravi's shader presets with ReShade." Margin="5" IsEnabled="{Binding IsChecked, ElementName=CheckBoxUseReShade, Mode=OneWay}" IsChecked="{Binding UseReShadeExtraviPresets, Mode=TwoWay}" />
</Grid>
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="ButtonOpenReShadeFolder" ToolTip="{Binding ReShadeFolderButtonTooltip, Mode=OneTime}" IsEnabled="{Binding ReShadeFolderButtonEnabled, Mode=OneTime}" Content="Open ReShade folder" Height="23" Margin="0,0,5,0" Click="ButtonOpenReShadeFolder_Click" />
<Button Grid.Column="1" x:Name="ButtonOpenReShadeHelp" Content="Help" Height="23" Margin="5,0,0,0" Click="ButtonOpenReShadeHelp_Click" />
</Grid>
</StackPanel>
</GroupBox>
</StackPanel>
</TabItem>
<TabItem Padding="5">
<TabItem.Header>
<TextBlock Text="Modifications" FontSize="13" />
</TabItem.Header>
<StackPanel>
<GroupBox Header="Presets" Margin="10,10,10,5">
<StackPanel VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<CheckBox x:Name="CheckBoxModDeathSound" Content=" Use old death sound" Margin="5" IsChecked="{Binding ModOldDeathSound, Mode=TwoWay}" />
<CheckBox x:Name="CheckBoxModMouseCursor" Content=" Use old mouse cursor" Margin="5" IsChecked="{Binding ModOldMouseCursor, Mode=TwoWay}" />
</StackPanel>
<StackPanel Grid.Column="1">
<CheckBox x:Name="CheckBoxDisableAppPatch" Content=" Disable desktop app" Margin="5" IsChecked="{Binding ModDisableAppPatch, Mode=TwoWay}" />
</StackPanel>
</Grid>
</StackPanel>
</GroupBox>
<Grid Margin="10,5,10,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="ButtonOpenModFolder" Content="Open Modifications folder" ToolTip="{Binding ModFolderButtonTooltip, Mode=OneTime}" IsEnabled="{Binding ModFolderButtonEnabled, Mode=OneTime}" Height="23" Margin="0,0,5,0" Click="ButtonOpenModFolder_Click" />
<Button Grid.Column="1" x:Name="ButtonOpenModHelp" Content="Help" Height="23" Margin="5,0,0,0" Click="ButtonOpenModHelp_Click" />
</Grid>
</StackPanel>
</TabItem>
<TabItem Padding="5">
<TabItem.Header>
<TextBlock Text="Installation" FontSize="13" />
</TabItem.Header>
<StackPanel>
<GroupBox Header="Location" Margin="10,10,10,5">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" x:Name="TextBoxInstallLocation" Padding="0,1,0,2" Margin="0,0,5,0" Text="{Binding InstallLocation, Mode=OneWay}" />
<Button Grid.Column="1" x:Name="ButtonInstallLocationBrowse" Content="Browse..." Margin="5,0,0,0" Click="ButtonLocationBrowse_Click" />
</Grid>
</GroupBox>
<GroupBox Header="Channel" Margin="10,5,10,5">
<StackPanel VerticalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="130" />
</Grid.ColumnDefinitions>
<ComboBox x:Name="ComboBoxChannel" ToolTip="Choose which release channel to download Roblox from. If you're not sure what this does, just leave it as LIVE." Margin="5" ItemsSource="{Binding Channels, Mode=OneWay}" Text="{Binding Channel, Mode=TwoWay}" />
<CheckBox Grid.Column="1" x:Name="CheckBoxShowAllChannels" Content=" Show all channels" VerticalAlignment="Center" Margin="5" IsChecked="{Binding ShowAllChannels, Mode=TwoWay}" />
</Grid>
<TextBlock x:Name="TextBlockChannelInfo" Text="{Binding ChannelInfo, Mode=OneWay}" TextWrapping="Wrap" Margin="5" />
<CheckBox x:Name="TextBoxPromptChannelChange" Content=" Prompt on Roblox-forced channel change" ToolTip="Choose whether to be prompted when Roblox mandates a channel change through the website. Otherwise, the channel changes automatically." Margin="5" IsChecked="{Binding PromptChannelChange, Mode=TwoWay}" />
</StackPanel>
</GroupBox>
</StackPanel>
</TabItem>
<TabItem Padding="5">
<TabItem.Header>
<TextBlock Text="Bloxstrap" FontSize="13" />
</TabItem.Header>
<StackPanel>
<GroupBox Header="Style" Margin="10,10,10,0">
<StackPanel Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Theme*" VerticalAlignment="Center" Margin="0,0,0,5" />
<ComboBox Grid.Row="0" Grid.Column="1" x:Name="ComboBoxTheme" VerticalAlignment="Center" Margin="0,0,0,0" ItemsSource="{Binding Themes.Keys, Mode=OneTime}" Text="{Binding Theme, Mode=TwoWay}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="*Dark theme only applies to the Progress dialog" FontSize="10" VerticalAlignment="Center" Margin="0,0,0,5" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="Dialog" VerticalAlignment="Center" Margin="0,5,0,5" />
<ComboBox Grid.Row="2" Grid.Column="1" x:Name="ComboBoxDialog" VerticalAlignment="Center" Margin="0,5,0,5" ItemsSource="{Binding Dialogs.Keys, Mode=OneTime}" Text="{Binding Dialog, Mode=TwoWay}" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="Icon" VerticalAlignment="Center" Margin="0,5,0,5" />
<ComboBox Grid.Row="3" Grid.Column="1" x:Name="ComboBoxIcon" VerticalAlignment="Center" Margin="0,5,0,5" ItemsSource="{Binding Icons.Keys, Mode=OneTime}" Text="{Binding Icon, Mode=TwoWay}" />
</Grid>
<Button x:Name="ButtonPreview" Content="Preview" Margin="0,5,0,0" VerticalAlignment="Bottom" Height="23" Click="ButtonPreview_Click" />
</StackPanel>
</GroupBox>
<CheckBox Content=" Create desktop icon" IsChecked="{Binding CreateDesktopIcon, Mode=TwoWay}" Margin="10,10,10,0" />
<CheckBox Content=" Check for Bloxstrap updates on startup" IsChecked="{Binding CheckForUpdates, Mode=TwoWay}" Margin="10,10,10,0" />
</StackPanel>
</TabItem>
</TabControl>
<Border Background="{DynamicResource ControlSelectedBackground}" BorderBrush="{DynamicResource ControlSelectedBorderBrush}" BorderThickness="1" Margin="-1,0,-1,0" Height="42" VerticalAlignment="Bottom">
<Grid>
<TextBlock Text="Like Bloxstrap? " VerticalAlignment="Center" Margin="10,0,0,0" ><Hyperlink NavigateUri="https://github.com/pizzaboxer/bloxstrap" RequestNavigate="Hyperlink_RequestNavigate">Leave a star on GitHub!</Hyperlink></TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="ButtonConfirm" Content="{Binding ConfirmButtonText, Mode=OneTime}" Width="66" Height="23" HorizontalAlignment="Right" Margin="0,0,10,0" Click="ButtonConfirm_Click" />
<Button x:Name="ButtonCancel" Content="Cancel" Width="66" Height="23" HorizontalAlignment="Right" Margin="0,0,10,0" Click="ButtonCancel_Click" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@ -1,422 +0,0 @@
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using Bloxstrap.Enums;
using Bloxstrap.Helpers;
using Bloxstrap.Models;
namespace Bloxstrap.Dialogs.Menu
{
/// <summary>
/// Interaction logic for PreferencesWPF.xaml
/// </summary>
public partial class Preferences : Window
{
public readonly PreferencesViewModel ViewModel;
public Preferences()
{
InitializeComponent();
SetTheme();
ViewModel = new(this);
this.DataContext = ViewModel;
Program.SettingsManager.ShouldSave = false;
this.Icon = Imaging.CreateBitmapSourceFromHIcon(
Properties.Resources.IconBloxstrap_ico.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions()
);
this.Title = Program.ProjectName;
// just in case i guess?
if (!Environment.Is64BitOperatingSystem)
this.CheckBoxRFUEnabled.IsEnabled = false;
}
public void SetTheme()
{
string theme = "Light";
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
theme = "ColourfulDark";
this.Resources.MergedDictionaries[0] = new ResourceDictionary() { Source = new Uri($"Dialogs/Menu/Themes/{theme}Theme.xaml", UriKind.Relative) };
}
private void ButtonOpenReShadeFolder_Click(object sender, EventArgs e)
{
Process.Start("explorer.exe", Directories.ReShade);
}
private void ButtonOpenReShadeHelp_Click(object sender, EventArgs e)
{
new ReShadeHelp().Show();
}
private void ButtonOpenModFolder_Click(object sender, EventArgs e)
{
Process.Start("explorer.exe", Directories.Modifications);
}
private void ButtonOpenModHelp_Click(object sender, EventArgs e)
{
new ModHelp().Show();
}
private void ButtonLocationBrowse_Click(object sender, EventArgs e)
{
using (var dialog = new FolderBrowserDialog())
{
DialogResult result = dialog.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
ViewModel.InstallLocation = dialog.SelectedPath;
}
}
private void ButtonPreview_Click(object sender, EventArgs e)
{
//this.Visible = false;
Program.Settings.BootstrapperStyle.Show();
//this.Visible = true;
}
private void ButtonCancel_Click(object sender, EventArgs e)
{
this.Close();
}
private void ButtonConfirm_Click(object sender, EventArgs e)
{
string installLocation = this.TextBoxInstallLocation.Text;
if (String.IsNullOrEmpty(installLocation))
{
Program.ShowMessageBox("You must set an install location", MessageBoxIcon.Error);
return;
}
try
{
// check if we can write to the directory (a bit hacky but eh)
string testPath = installLocation;
string testFile = Path.Combine(installLocation, "BloxstrapWriteTest.txt");
bool testPathExists = Directory.Exists(testPath);
if (!testPathExists)
Directory.CreateDirectory(testPath);
File.WriteAllText(testFile, "hi");
File.Delete(testFile);
if (!testPathExists)
Directory.Delete(testPath);
}
catch (UnauthorizedAccessException)
{
Program.ShowMessageBox($"{Program.ProjectName} does not have write access to the install location you selected. Please choose another install location.", MessageBoxIcon.Error);
return;
}
catch (Exception ex)
{
Program.ShowMessageBox(ex.Message, MessageBoxIcon.Error);
return;
}
if (Program.IsFirstRun)
{
// this will be set in the registry after first install
Program.BaseDirectory = installLocation;
}
else
{
Program.SettingsManager.ShouldSave = true;
if (Program.BaseDirectory is not null && Program.BaseDirectory != installLocation)
{
Program.ShowMessageBox($"{Program.ProjectName} will install to the new location you've set the next time it runs.", MessageBoxIcon.Information);
Program.Settings.VersionGuid = "";
using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{Program.ProjectName}"))
{
registryKey.SetValue("InstallLocation", installLocation);
registryKey.SetValue("OldInstallLocation", Program.BaseDirectory);
}
// preserve settings
// we don't need to copy the bootstrapper over since the install process will do that automatically
Program.SettingsManager.Save();
File.Copy(Path.Combine(Program.BaseDirectory, "Settings.json"), Path.Combine(installLocation, "Settings.json"));
}
}
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
{
private readonly Preferences _window;
public event PropertyChangedEventHandler? PropertyChanged;
public string BloxstrapVersion { get; } = $"Version {Program.Version}";
#region Integrations
public bool DRPEnabled
{
get => Program.Settings.UseDiscordRichPresence;
set
{
// if user wants discord rpc, auto-enable buttons by default
_window.CheckBoxDRPButtons.IsChecked = value;
Program.Settings.UseDiscordRichPresence = value;
}
}
public bool DRPButtons
{
get => !Program.Settings.HideRPCButtons;
set => Program.Settings.HideRPCButtons = !value;
}
public bool RFUEnabled
{
get => Program.Settings.RFUEnabled;
set
{
// if user wants to use rbxfpsunlocker, auto-enable autoclosing by default
_window.CheckBoxRFUAutoclose.IsChecked = value;
Program.Settings.RFUEnabled = value;
}
}
public bool RFUAutoclose
{
get => Program.Settings.RFUAutoclose;
set => Program.Settings.RFUAutoclose = value;
}
public bool UseReShade
{
get => Program.Settings.UseReShade;
set
{
// if user wants to use reshade, auto-enable use of extravi's presets by default
_window.CheckBoxUseReShadeExtraviPresets.IsChecked = value;
Program.Settings.UseReShade = value;
}
}
public bool UseReShadeExtraviPresets
{
get => Program.Settings.UseReShadeExtraviPresets;
set => Program.Settings.UseReShadeExtraviPresets = value;
}
public bool ReShadeFolderButtonEnabled { get; } = !Program.IsFirstRun;
public string ReShadeFolderButtonTooltip { get; } = Program.IsFirstRun ? "Bloxstrap must first be installed before managing ReShade" : "This is the folder that contains all your ReShade resources for presets, shaders and textures.";
#endregion
#region Modifications
public bool ModOldDeathSound
{
get => Program.Settings.UseOldDeathSound;
set => Program.Settings.UseOldDeathSound = value;
}
public bool ModOldMouseCursor
{
get => Program.Settings.UseOldMouseCursor;
set => Program.Settings.UseOldMouseCursor = value;
}
public bool ModDisableAppPatch
{
get => Program.Settings.UseDisableAppPatch;
set => Program.Settings.UseDisableAppPatch = value;
}
public bool ModFolderButtonEnabled { get; } = !Program.IsFirstRun;
public string ModFolderButtonTooltip { get; } = Program.IsFirstRun ? "Bloxstrap must first be installed before managing mods" : "This is the folder that contains all your file modifications, including presets and any ReShade files needed.";
#endregion
#region Installation
private string installLocation = Program.IsFirstRun ? Path.Combine(Directories.LocalAppData, Program.ProjectName) : Program.BaseDirectory;
public string InstallLocation
{
get => installLocation;
set
{
installLocation = value;
OnPropertyChanged();
}
}
private bool showAllChannels = !DeployManager.ChannelsAbstracted.Contains(Program.Settings.Channel);
public bool ShowAllChannels
{
get => showAllChannels;
set
{
if (value)
{
Channels = DeployManager.ChannelsAll;
}
else
{
Channels = DeployManager.ChannelsAbstracted;
Channel = DeployManager.DefaultChannel;
OnPropertyChanged("Channel");
}
showAllChannels = value;
}
}
private IEnumerable<string> channels = DeployManager.ChannelsAbstracted.Contains(Program.Settings.Channel) ? DeployManager.ChannelsAbstracted : DeployManager.ChannelsAll;
public IEnumerable<string> Channels
{
get => channels;
set
{
channels = value;
OnPropertyChanged();
}
}
public string Channel
{
get => Program.Settings.Channel;
set
{
Task.Run(() => GetChannelInfo(value));
Program.Settings.Channel = value;
}
}
private string channelInfo = "Getting latest version info, please wait...\n";
public string ChannelInfo
{
get => channelInfo;
set
{
channelInfo = value;
OnPropertyChanged();
}
}
public bool PromptChannelChange
{
get => Program.Settings.PromptChannelChange;
set => Program.Settings.PromptChannelChange = value;
}
#endregion
#region Bloxstrap
public IReadOnlyDictionary<string, Theme> Themes { get; set; } = new Dictionary<string, Theme>()
{
{ "System Default", Enums.Theme.Default },
{ "Light", Enums.Theme.Light },
{ "Dark", Enums.Theme.Dark },
};
public string Theme
{
get => Themes.FirstOrDefault(x => x.Value == Program.Settings.Theme).Key;
set
{
Program.Settings.Theme = Themes[value];
_window.SetTheme();
}
}
public IReadOnlyDictionary<string, BootstrapperStyle> Dialogs { get; set; } = new Dictionary<string, BootstrapperStyle>()
{
{ "Vista (2009 - 2011)", BootstrapperStyle.VistaDialog },
{ "Legacy (2009 - 2011)", BootstrapperStyle.LegacyDialog2009 },
{ "Legacy (2011 - 2014)", BootstrapperStyle.LegacyDialog2011 },
{ "Progress (~2014)", BootstrapperStyle.ProgressDialog },
};
public string Dialog
{
get => Dialogs.FirstOrDefault(x => x.Value == Program.Settings.BootstrapperStyle).Key;
set => Program.Settings.BootstrapperStyle = Dialogs[value];
}
public IReadOnlyDictionary<string, BootstrapperIcon> Icons { get; set; } = new Dictionary<string, BootstrapperIcon>()
{
{ "Bloxstrap", BootstrapperIcon.IconBloxstrap },
{ "2009", BootstrapperIcon.Icon2009 },
{ "2011", BootstrapperIcon.Icon2011 },
{ "2015", BootstrapperIcon.IconEarly2015 },
{ "2016", BootstrapperIcon.IconLate2015 },
{ "2017", BootstrapperIcon.Icon2017 },
{ "2019", BootstrapperIcon.Icon2019 },
{ "2022", BootstrapperIcon.Icon2022 }
};
public string Icon
{
get => Icons.FirstOrDefault(x => x.Value == Program.Settings.BootstrapperIcon).Key;
set => Program.Settings.BootstrapperIcon = Icons[value];
}
public bool CreateDesktopIcon
{
get => Program.Settings.CreateDesktopIcon;
set => Program.Settings.CreateDesktopIcon = value;
}
public bool CheckForUpdates
{
get => Program.Settings.CheckForUpdates;
set => Program.Settings.CheckForUpdates = value;
}
#endregion
public string ConfirmButtonText { get; } = Program.IsFirstRun ? "Install" : "Save";
public PreferencesViewModel(Preferences window)
{
_window = window;
Task.Run(() => GetChannelInfo(Program.Settings.Channel));
}
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private async Task GetChannelInfo(string channel)
{
ChannelInfo = "Getting latest version info, please wait...\n";
ClientVersion info = await DeployManager.GetLastDeploy(channel, true);
string? strTimestamp = info.Timestamp?.ToString("MM/dd/yyyy h:mm:ss tt", Program.CultureFormat);
ChannelInfo = $"Version: v{info.Version} ({info.VersionGuid})\nDeployed: {strTimestamp}";
}
}
}

View File

@ -1,54 +0,0 @@
<Window x:Class="Bloxstrap.Dialogs.Menu.ReShadeHelp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Bloxstrap.Dialogs.Menu"
mc:Ignorable="d"
Style="{DynamicResource MainWindowStyle}"
Title="ReShade Help"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--<ResourceDictionary Source="Themes\ColourfulDarkTheme.xaml" />-->
<ResourceDictionary Source="Themes\LightTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Width="420" Height="220">
<StackPanel Margin="10">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="ReShade Help" VerticalAlignment="Top" FontSize="18" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Toggle Menu" FontSize="16" />
<TextBlock HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" Text="Shift + Tab" />
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Toggle Shaders" FontSize="16" />
<TextBlock HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" Text="Shift + F6" />
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Take Screenshot" FontSize="16" />
<TextBlock HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" Text="Print Screen" />
</StackPanel>
</Grid>
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="If you're using a compact keyboard, you may have to hold down the Fn key when pressing F6." />
<TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Any screenshots you take are saved to the Screenshots folder in the ReShade folder." />
</StackPanel>
<Border Background="{DynamicResource ControlSelectedBackground}" BorderBrush="{DynamicResource ControlSelectedBorderBrush}" BorderThickness="1" Margin="-1,0,-1,0" Height="42" VerticalAlignment="Bottom">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="ButtonClose" Content="Close" Width="66" Height="23" HorizontalAlignment="Right" Margin="0,0,10,0" Click="ButtonClose_Click" />
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>

View File

@ -1,33 +0,0 @@
using System.Windows;
using Bloxstrap.Enums;
namespace Bloxstrap.Dialogs.Menu
{
/// <summary>
/// Interaction logic for ReShadeHelp.xaml
/// </summary>
public partial class ReShadeHelp : Window
{
public ReShadeHelp()
{
InitializeComponent();
SetTheme();
}
public void SetTheme()
{
string theme = "Light";
if (Program.Settings.Theme.GetFinal() == Theme.Dark)
theme = "ColourfulDark";
this.Resources.MergedDictionaries[0] = new ResourceDictionary() { Source = new Uri($"Dialogs/Menu/Themes/{theme}Theme.xaml", UriKind.Relative) };
}
private void ButtonClose_Click(object sender, EventArgs e)
{
this.Close();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
using System.Windows;
namespace REghZyFramework.Themes {
public partial class ColourfulDarkTheme {
private void CloseWindow_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
CloseWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void AutoMinimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MaximizeRestore(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void Minimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MinimizeWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
public void CloseWind(Window window) => window.Close();
public void MaximizeRestore(Window window) {
if (window.WindowState == WindowState.Maximized)
window.WindowState = WindowState.Normal;
else if (window.WindowState == WindowState.Normal)
window.WindowState = WindowState.Maximized;
}
public void MinimizeWind(Window window) => window.WindowState = WindowState.Minimized;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
using System.Windows;
namespace REghZyFramework.Themes {
public partial class ColourfulLightTheme {
private void CloseWindow_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
CloseWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void AutoMinimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MaximizeRestore(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void Minimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MinimizeWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
public void CloseWind(Window window) => window.Close();
public void MaximizeRestore(Window window) {
if (window.WindowState == WindowState.Maximized)
window.WindowState = WindowState.Normal;
else if (window.WindowState == WindowState.Normal)
window.WindowState = WindowState.Maximized;
}
public void MinimizeWind(Window window) => window.WindowState = WindowState.Minimized;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
using System.Windows;
namespace REghZyFramework.Themes {
public partial class DarkTheme {
private void CloseWindow_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
CloseWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void AutoMinimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MaximizeRestore(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void Minimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MinimizeWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
public void CloseWind(Window window) => window.Close();
public void MaximizeRestore(Window window) {
if (window.WindowState == WindowState.Maximized)
window.WindowState = WindowState.Normal;
else if (window.WindowState == WindowState.Normal)
window.WindowState = WindowState.Maximized;
}
public void MinimizeWind(Window window) => window.WindowState = WindowState.Minimized;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
using System.Windows;
namespace REghZyFramework.Themes {
public partial class LightTheme {
private void CloseWindow_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
CloseWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void AutoMinimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MaximizeRestore(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
private void Minimize_Event(object sender, RoutedEventArgs e) {
if (e.Source != null)
try {
MinimizeWind(Window.GetWindow((FrameworkElement) e.Source));
}
catch {
}
}
public void CloseWind(Window window) => window.Close();
public void MaximizeRestore(Window window) {
if (window.WindowState == WindowState.Maximized)
window.WindowState = WindowState.Normal;
else if (window.WindowState == WindowState.Normal)
window.WindowState = WindowState.Maximized;
}
public void MinimizeWind(Window window) => window.WindowState = WindowState.Minimized;
}
}

View File

@ -1,79 +0,0 @@
namespace Bloxstrap.Enums
{
public enum BootstrapperIcon
{
IconBloxstrap,
Icon2009,
Icon2011,
IconEarly2015,
IconLate2015,
Icon2017,
Icon2019,
Icon2022
}
public static class BootstrapperIconEx
{
public static Icon GetIcon(this BootstrapperIcon icon)
{
switch (icon)
{
case BootstrapperIcon.Icon2009:
return Properties.Resources.Icon2009_ico;
case BootstrapperIcon.Icon2011:
return Properties.Resources.Icon2011_ico;
case BootstrapperIcon.IconEarly2015:
return Properties.Resources.IconEarly2015_ico;
case BootstrapperIcon.IconLate2015:
return Properties.Resources.IconLate2015_ico;
case BootstrapperIcon.Icon2017:
return Properties.Resources.Icon2017_ico;
case BootstrapperIcon.Icon2019:
return Properties.Resources.Icon2019_ico;
case BootstrapperIcon.Icon2022:
return Properties.Resources.Icon2022_ico;
case BootstrapperIcon.IconBloxstrap:
default:
return Properties.Resources.IconBloxstrap_ico;
}
}
public static Bitmap GetBitmap(this BootstrapperIcon icon)
{
switch (icon)
{
case BootstrapperIcon.Icon2009:
return Properties.Resources.Icon2009_png;
case BootstrapperIcon.Icon2011:
return Properties.Resources.Icon2011_png;
case BootstrapperIcon.IconEarly2015:
return Properties.Resources.IconEarly2015_png;
case BootstrapperIcon.IconLate2015:
return Properties.Resources.IconLate2015_png;
case BootstrapperIcon.Icon2017:
return Properties.Resources.Icon2017_png;
case BootstrapperIcon.Icon2019:
return Properties.Resources.Icon2019_png;
case BootstrapperIcon.Icon2022:
return Properties.Resources.Icon2022_png;
case BootstrapperIcon.IconBloxstrap:
default:
return Properties.Resources.IconBloxstrap_png;
}
}
}
}

View File

@ -1,49 +0,0 @@
using Bloxstrap.Dialogs.BootstrapperDialogs;
namespace Bloxstrap.Enums
{
public enum BootstrapperStyle
{
VistaDialog,
LegacyDialog2009,
LegacyDialog2011,
ProgressDialog,
}
public static class BootstrapperStyleEx
{
public static void Show(this BootstrapperStyle bootstrapperStyle, Bootstrapper? bootstrapper = null)
{
Form dialog;
switch (bootstrapperStyle)
{
case BootstrapperStyle.VistaDialog:
dialog = new VistaDialog(bootstrapper);
break;
case BootstrapperStyle.LegacyDialog2009:
dialog = new LegacyDialog2009(bootstrapper);
break;
case BootstrapperStyle.LegacyDialog2011:
dialog = new LegacyDialog2011(bootstrapper);
break;
case BootstrapperStyle.ProgressDialog:
default:
dialog = new ProgressDialog(bootstrapper);
break;
}
if (bootstrapper is null)
{
dialog.ShowDialog();
}
else
{
Application.Run(dialog);
}
}
}
}

View File

@ -1,32 +0,0 @@
using Microsoft.Win32;
namespace Bloxstrap.Enums
{
public enum Theme
{
Default,
Light,
Dark
}
public static class DialogThemeEx
{
public static Theme GetFinal(this Theme dialogTheme)
{
if (dialogTheme != Theme.Default)
return dialogTheme;
RegistryKey? key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize");
if (key is not null)
{
var value = key.GetValue("AppsUseLightTheme");
if (value is not null && (int)value == 0)
return Theme.Dark;
}
return Theme.Light;
}
}
}

View File

@ -1,89 +0,0 @@
using System.Net.Http;
using System.Text.Json;
using Bloxstrap.Models;
namespace Bloxstrap.Helpers
{
public class DeployManager
{
#region Properties
public const string DefaultBaseUrl = "https://setup.rbxcdn.com";
public static string BaseUrl { get; private set; } = DefaultBaseUrl;
public const string DefaultChannel = "LIVE";
public static string Channel { set => BaseUrl = BuildBaseUrl(value); }
// basically any channel that has had a deploy within the past month with a windowsplayer build
public static readonly List<string> ChannelsAbstracted = new List<string>()
{
"LIVE",
"ZNext",
"ZCanary",
"ZIntegration"
};
// why not?
public static readonly List<string> ChannelsAll = new List<string>()
{
"LIVE",
"ZAvatarTeam",
"ZAvatarRelease",
"ZCanary",
"ZCanary1",
"ZCanary2",
"ZCanary3",
"ZCanaryApps",
"ZFlag",
"ZIntegration",
"ZIntegration1",
"ZLive",
"ZLive1",
"ZNext",
"ZSocialTeam",
"ZStudioInt1",
"ZStudioInt2"
};
#endregion
private static string BuildBaseUrl(string channel)
{
if (channel == DefaultChannel)
return DefaultBaseUrl;
else
return $"{DefaultBaseUrl}/channel/{channel.ToLower()}";
}
public static async Task<ClientVersion> GetLastDeploy(string channel, bool timestamp = false)
{
HttpResponseMessage deployInfoResponse = await Program.HttpClient.GetAsync($"https://clientsettings.roblox.com/v2/client-version/WindowsPlayer/channel/{channel}");
if (!deployInfoResponse.IsSuccessStatusCode)
{
// 400 = Invalid binaryType.
// 404 = Could not find version details for binaryType.
// 500 = Error while fetching version information.
// either way, we throw
throw new Exception($"Could not get latest deploy for channel {channel}");
}
string rawJson = await deployInfoResponse.Content.ReadAsStringAsync();
ClientVersion clientVersion = JsonSerializer.Deserialize<ClientVersion>(rawJson)!;
// for preferences
if (timestamp)
{
string channelUrl = BuildBaseUrl(channel);
// get an approximate deploy time from rbxpkgmanifest's last modified date
HttpResponseMessage pkgResponse = await Program.HttpClient.GetAsync($"{channelUrl}/{clientVersion.VersionGuid}-rbxPkgManifest.txt");
if (pkgResponse.Content.Headers.TryGetValues("last-modified", out var values))
{
string lastModified = values.First();
clientVersion.Timestamp = DateTime.Parse(lastModified);
}
}
return clientVersion;
}
}
}

View File

@ -1,38 +0,0 @@
using System.IO;
namespace Bloxstrap.Helpers
{
class Directories
{
// note that these are directories that aren't tethered to the basedirectory
// so these can safely be called before initialization
public static string LocalAppData { get => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); }
public static string Desktop { get => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); }
public static string StartMenu { get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs", Program.ProjectName); }
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 Updates { get; private set; } = "";
public static string ReShade { 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");
Updates = Path.Combine(Base, "Updates");
ReShade = Path.Combine(Base, "ReShade");
App = Path.Combine(Base, $"{Program.ProjectName}.exe");
}
}
}

View File

@ -1,205 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using Bloxstrap.Models;
using DiscordRPC;
namespace Bloxstrap.Helpers.Integrations
{
class DiscordRichPresence : IDisposable
{
readonly DiscordRpcClient RichPresence = new("1005469189907173486");
const string GameJoiningEntry = "[FLog::Output] ! Joining game";
const string GameJoinedEntry = "[FLog::Network] serverId:";
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]+)";
// these are values to use assuming the player isn't currently in a game
bool ActivityInGame = false;
long ActivityPlaceId = 0;
string ActivityJobId = "";
string ActivityMachineAddress = ""; // we're only really using this to confirm a place join. todo: maybe this could be used to see server location/ping?
public DiscordRichPresence()
{
RichPresence.Initialize();
}
private async Task ExamineLogEntry(string entry)
{
Debug.WriteLine(entry);
if (entry.Contains(GameJoiningEntry) && !ActivityInGame && ActivityPlaceId == 0)
{
Match match = Regex.Match(entry, GameJoiningEntryPattern);
if (match.Groups.Count != 4)
return;
ActivityInGame = false;
ActivityPlaceId = Int64.Parse(match.Groups[2].Value);
ActivityJobId = match.Groups[1].Value;
ActivityMachineAddress = match.Groups[3].Value;
Debug.WriteLine($"[DiscordRichPresence] Joining Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
}
else if (entry.Contains(GameJoinedEntry) && !ActivityInGame && ActivityPlaceId != 0)
{
Match match = Regex.Match(entry, GameJoinedEntryPattern);
if (match.Groups.Count != 3 || match.Groups[1].Value != ActivityMachineAddress)
return;
Debug.WriteLine($"[DiscordRichPresence] Joined Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
ActivityInGame = true;
await SetPresence();
}
else if (entry.Contains(GameDisconnectedEntry) && ActivityInGame && ActivityPlaceId != 0)
{
Debug.WriteLine($"[DiscordRichPresence] Disconnected from Game ({ActivityPlaceId}/{ActivityJobId}/{ActivityMachineAddress})");
ActivityInGame = false;
ActivityPlaceId = 0;
ActivityJobId = "";
ActivityMachineAddress = "";
await SetPresence();
}
}
public async void MonitorGameActivity()
{
// okay, here's the process:
//
// - 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 '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
string logDirectory = Path.Combine(Directories.LocalAppData, "Roblox\\logs");
if (!Directory.Exists(logDirectory))
return;
FileInfo logFileInfo;
// we need to make sure we're fetching the absolute latest log file
// if roblox doesn't start quickly enough, we can wind up fetching the previous log file
// good rule of thumb is to find a log file that was created in the last 15 seconds or so
while (true)
{
logFileInfo = new DirectoryInfo(logDirectory).GetFiles().OrderByDescending(x => x.CreationTime).First();
if (logFileInfo.CreationTime.AddSeconds(15) > DateTime.Now)
break;
await Task.Delay(1000);
}
FileStream logFileStream = logFileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
AutoResetEvent logUpdatedEvent = new(false);
FileSystemWatcher logWatcher = new()
{
Path = logDirectory,
Filter = Path.GetFileName(logFileInfo.FullName),
EnableRaisingEvents = true
};
logWatcher.Changed += (s, e) => logUpdatedEvent.Set();
using (StreamReader sr = new(logFileStream))
{
string? log = null;
while (true)
{
log = await sr.ReadLineAsync();
if (String.IsNullOrEmpty(log))
{
logUpdatedEvent.WaitOne(1000);
}
else
{
//Debug.WriteLine(log);
await ExamineLogEntry(log);
}
}
}
// no need to close the event, its going to be finished with when the program closes anyway
}
public async Task<bool> SetPresence()
{
if (!ActivityInGame)
{
RichPresence.ClearPresence();
return true;
}
string placeThumbnail = "roblox";
var placeInfo = await Utilities.GetJson<RobloxAsset>($"https://economy.roblox.com/v2/assets/{ActivityPlaceId}/details");
if (placeInfo is null || placeInfo.Creator is null)
return false;
var thumbnailInfo = await Utilities.GetJson<RobloxThumbnails>($"https://thumbnails.roblox.com/v1/places/gameicons?placeIds={ActivityPlaceId}&returnPolicy=PlaceHolder&size=512x512&format=Png&isCircular=false");
if (thumbnailInfo is not null)
placeThumbnail = thumbnailInfo.Data![0].ImageUrl!;
List<DiscordRPC.Button> buttons = new()
{
new DiscordRPC.Button()
{
Label = "See Details",
Url = $"https://www.roblox.com/games/{ActivityPlaceId}"
}
};
if (!Program.Settings.HideRPCButtons)
{
buttons.Insert(0, new DiscordRPC.Button()
{
Label = "Join",
Url = $"https://www.roblox.com/games/start?placeId={ActivityPlaceId}&gameInstanceId={ActivityJobId}&launchData=%7B%7D"
});
}
RichPresence.SetPresence(new RichPresence()
{
Details = placeInfo.Name,
State = $"by {placeInfo.Creator.Name}",
Timestamps = new Timestamps() { Start = DateTime.UtcNow },
Buttons = buttons.ToArray(),
Assets = new Assets()
{
LargeImageKey = placeThumbnail,
LargeImageText = placeInfo.Name,
SmallImageKey = "roblox",
SmallImageText = "Roblox"
}
});
return true;
}
public void Dispose()
{
RichPresence.ClearPresence();
RichPresence.Dispose();
}
}
}

View File

@ -1,109 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using Bloxstrap.Models;
namespace Bloxstrap.Helpers.Integrations
{
internal class RbxFpsUnlocker
{
public const string ApplicationName = "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 void CheckIfRunning()
{
Process[] processes = Process.GetProcessesByName(ApplicationName);
if (processes.Length == 0)
return;
try
{
// try/catch just in case process was closed before prompt was answered
foreach (Process process in processes)
{
if (process.MainModule is null || process.MainModule.FileName is null)
continue;
if (!process.MainModule.FileName.Contains(Program.BaseDirectory))
continue;
process.Kill();
process.Close();
}
}
catch (Exception) { }
}
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))
{
CheckIfRunning();
Directory.Delete(folderLocation, true);
}
return;
}
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{ProjectRepository}/releases/latest");
if (releaseInfo is null || releaseInfo.Assets is null)
return;
string downloadUrl = releaseInfo.Assets[0].BrowserDownloadUrl;
Directory.CreateDirectory(folderLocation);
if (File.Exists(fileLocation))
{
// no new release published, return
if (Program.Settings.RFUVersion == releaseInfo.TagName)
return;
CheckIfRunning();
File.Delete(fileLocation);
}
Debug.WriteLine("Installing/Updating rbxfpsunlocker...");
{
byte[] bytes = await Program.HttpClient.GetByteArrayAsync(downloadUrl);
using MemoryStream zipStream = new(bytes);
using ZipArchive archive = new(zipStream);
archive.ExtractToDirectory(folderLocation, true);
}
if (!File.Exists(settingsLocation))
await File.WriteAllTextAsync(settingsLocation, Settings);
Program.Settings.RFUVersion = releaseInfo.TagName;
}
}
}

View File

@ -1,398 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using Bloxstrap.Models;
using IniParser;
using IniParser.Model;
namespace Bloxstrap.Helpers.Integrations
{
internal class ReShade
{
// i havent even started this and i know for a fact this is gonna be a mess of an integration lol
// there's a lot of nuances involved in how reshade functionality is supposed to work (shader management, config management, etc)
// it's gonna be a bit of a pain in the ass, and i'm expecting a lot of bugs to arise from this...
// well, looks like v1.7.0 is gonna be held back for quite a while lol
// also, this is going to be fairly restrictive without a lot of heavy work
// reshade's official installer gives you a list of shader packs and lets you choose which ones you want to install
// and here we're effectively choosing for the user... hm...
// i mean, it should be fine? importing shaders is still gonna be a thing, though maybe not as simple, but most people would be looking to use extravi's presets anyway
private static string ShadersFolder { get => Path.Combine(Directories.ReShade, "Shaders"); }
private static string TexturesFolder { get => Path.Combine(Directories.ReShade, "Textures"); }
private static string ConfigLocation { get => Path.Combine(Directories.Modifications, "ReShade.ini"); }
// the base url that we're fetching all our remote configs and resources and stuff from
private const string BaseUrl = "https://raw.githubusercontent.com/Extravi/extravi.github.io/main/update";
// this is a list of selectable shaders to download:
// this should be formatted as { FolderName, GithubRepositoryUrl }
private static readonly IReadOnlyDictionary<string, string> Shaders = new Dictionary<string, string>()
{
{ "Stock", "https://github.com/crosire/reshade-shaders/archive/refs/heads/master.zip" },
// shaders required for extravi's presets:
{ "AlucardDH", "https://github.com/AlucardDH/dh-reshade-shaders/archive/refs/heads/master.zip" },
{ "AstrayFX", "https://github.com/BlueSkyDefender/AstrayFX/archive/refs/heads/master.zip" },
{ "Depth3D", "https://github.com/BlueSkyDefender/Depth3D/archive/refs/heads/master.zip" },
{ "Glamarye", "https://github.com/rj200/Glamarye_Fast_Effects_for_ReShade/archive/refs/heads/main.zip" },
{ "NiceGuy", "https://github.com/mj-ehsan/NiceGuy-Shaders/archive/refs/heads/main.zip" },
{ "prod80", "https://github.com/prod80/prod80-ReShade-Repository/archive/refs/heads/master.zip" },
{ "qUINT", "https://github.com/martymcmodding/qUINT/archive/refs/heads/master.zip" },
};
private static readonly string[] ExtraviPresetsShaders = new string[]
{
"AlucardDH",
"AstrayFX",
"Depth3D",
"Glamarye",
"NiceGuy",
"prod80",
"qUINT",
};
private static string GetSearchPath(string type, string name)
{
return $",..\\..\\ReShade\\{type}\\{name}";
}
public static async Task DownloadConfig()
{
Debug.WriteLine("[ReShade] Downloading/Upgrading config file...");
{
byte[] bytes = await Program.HttpClient.GetByteArrayAsync($"{BaseUrl}/config.zip");
using MemoryStream zipStream = new(bytes);
using ZipArchive archive = new(zipStream);
archive.Entries.Where(x => x.FullName == "ReShade.ini").First().ExtractToFile(ConfigLocation, true);
// when we extract the file we have to make sure the last modified date is overwritten
// or else it will synchronize with the config in the version folder
// really the config adjustments below should do this for us, but this is just to be safe
File.SetLastWriteTime(ConfigLocation, DateTime.Now);
// we also gotta download the editor fonts
foreach (ZipArchiveEntry entry in archive.Entries.Where(x => x.FullName.EndsWith(".ttf")))
entry.ExtractToFile(Path.Combine(Directories.ReShade, "Fonts", entry.FullName), true);
}
// now we have to adjust the config file to use the paths that we need
// some of these can be removed later when the config file is better adjusted for bloxstrap by default
FileIniDataParser parser = new();
IniData data = parser.ReadFile(ConfigLocation);
data["GENERAL"]["EffectSearchPaths"] = "..\\..\\ReShade\\Shaders";
data["GENERAL"]["TextureSearchPaths"] = "..\\..\\ReShade\\Textures";
data["GENERAL"]["PresetPath"] = data["GENERAL"]["PresetPath"].Replace(".\\reshade-presets\\", "..\\..\\ReShade\\Presets\\");
data["SCREENSHOT"]["SavePath"] = "..\\..\\ReShade\\Screenshots";
data["STYLE"]["EditorFont"] = data["STYLE"]["EditorFont"].Replace(".\\", "..\\..\\ReShade\\Fonts\\");
data["STYLE"]["Font"] = data["STYLE"]["Font"].Replace(".\\", "..\\..\\ReShade\\Fonts\\");
// add search paths for shaders and textures
foreach (string name in Directory.GetDirectories(ShadersFolder).Select(x => Path.GetRelativePath(ShadersFolder, x)).ToArray())
data["GENERAL"]["EffectSearchPaths"] += GetSearchPath("Shaders", name);
foreach (string name in Directory.GetDirectories(TexturesFolder).Select(x => Path.GetRelativePath(TexturesFolder, x)).ToArray())
data["GENERAL"]["TextureSearchPaths"] += GetSearchPath("Textures", name);
parser.WriteFile(ConfigLocation, data);
}
public static void SynchronizeConfigFile()
{
Debug.WriteLine($"[ReShade] Synchronizing configuration file...");
// yeah, this is going to be a bit of a pain
// keep in mind the config file is going to be in two places: the mod folder and the version folder
// so we have to make sure the two below scenaros work flawlessly:
// - if the user manually updates their reshade config in the mod folder or it gets updated, it must be copied to the version folder
// - if the user updates their reshade settings ingame, the updated config must be copied to the mod folder
// the easiest way to manage this is to just compare the modification dates of the two
// anyway, this is where i'm expecting most of the bugs to arise from
// config synchronization will be done whenever roblox updates or whenever we launch roblox
string modFolderConfigPath = ConfigLocation;
string versionFolderConfigPath = Path.Combine(Directories.Versions, Program.Settings.VersionGuid, "ReShade.ini");
// we shouldn't be here if the mod config doesn't already exist
if (!File.Exists(modFolderConfigPath))
{
Debug.WriteLine($"[ReShade] ReShade.ini in modifications folder does not exist, aborting sync");
return;
}
// copy to the version folder if it doesn't already exist there
if (!File.Exists(versionFolderConfigPath))
{
Debug.WriteLine($"[ReShade] ReShade.ini in version folder does not exist, synchronized with modifications folder");
File.Copy(modFolderConfigPath, versionFolderConfigPath);
}
// if both the mod and version configs match, then we don't need to do anything
if (Utilities.MD5File(modFolderConfigPath) == Utilities.MD5File(versionFolderConfigPath))
{
Debug.WriteLine($"[ReShade] ReShade.ini in version and modifications folder match");
return;
}
FileInfo modFolderConfigFile = new(modFolderConfigPath);
FileInfo versionFolderConfigFile = new(versionFolderConfigPath);
if (modFolderConfigFile.LastWriteTime > versionFolderConfigFile.LastWriteTime)
{
// overwrite version config if mod config was modified most recently
Debug.WriteLine($"[ReShade] ReShade.ini in version folder is older, synchronized with modifications folder");
File.Copy(modFolderConfigPath, versionFolderConfigPath, true);
}
else if (versionFolderConfigFile.LastWriteTime > modFolderConfigFile.LastWriteTime)
{
// overwrite mod config if version config was modified most recently
Debug.WriteLine($"[ReShade] ReShade.ini in modifications folder is older, synchronized with version folder");
File.Copy(versionFolderConfigPath, modFolderConfigPath, true);
}
}
public static async Task DownloadShaders(string name)
{
string downloadUrl = Shaders.First(x => x.Key == name).Value;
// not all shader packs have a textures folder, so here we're determining if they exist purely based on if they have a Shaders folder
if (Directory.Exists(Path.Combine(Directories.ReShade, "Shaders", name)))
return;
Debug.WriteLine($"[ReShade] Downloading shaders for {name}");
{
byte[] bytes = await Program.HttpClient.GetByteArrayAsync(downloadUrl);
using MemoryStream zipStream = new(bytes);
using ZipArchive archive = new(zipStream);
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith('/'))
continue;
// github branch zips have a root folder of the name of the branch, so let's just remove that
string fullPath = entry.FullName.Substring(entry.FullName.IndexOf('/') + 1);
// skip file if it's not in the Shaders or Textures folder
if (!fullPath.StartsWith("Shaders") && !fullPath.StartsWith("Textures"))
continue;
// ingore shaders with compiler errors
if (fullPath.EndsWith("dh_Lain.fx") || fullPath.EndsWith("dh_rtgi.fx"))
continue;
// and now we do it again because of how we're handling folder management
// e.g. reshade-shaders-master/Shaders/Vignette.fx should go to ReShade/Shaders/Stock/Vignette.fx
// so in this case, relativePath should just be "Vignette.fx"
string relativePath = fullPath.Substring(fullPath.IndexOf('/') + 1);
// now we stitch it all together
string extractionPath = Path.Combine(
Directories.ReShade,
fullPath.StartsWith("Shaders") ? "Shaders" : "Textures",
name,
relativePath
);
// make sure the folder that we're extracting it to exists
Directory.CreateDirectory(Path.GetDirectoryName(extractionPath)!);
// and now extract
await Task.Run(() => entry.ExtractToFile(extractionPath));
}
}
// now we have to update ReShade.ini and add the installed shaders to the search paths
FileIniDataParser parser = new();
IniData data = parser.ReadFile(ConfigLocation);
if (!data["GENERAL"]["EffectSearchPaths"].Contains(name))
data["GENERAL"]["EffectSearchPaths"] += GetSearchPath("Shaders", name);
// not every shader pack has a textures folder
if (Directory.Exists(Path.Combine(Directories.ReShade, "Textures", name)) && !data["GENERAL"]["TextureSearchPaths"].Contains(name))
data["GENERAL"]["TextureSearchPaths"] += GetSearchPath("Textures", name);
parser.WriteFile(ConfigLocation, data);
}
public static void DeleteShaders(string name)
{
Debug.WriteLine($"[ReShade] Deleting shaders for {name}");
string shadersPath = Path.Combine(Directories.ReShade, "Shaders", name);
string texturesPath = Path.Combine(Directories.ReShade, "Textures", name);
if (Directory.Exists(shadersPath))
Directory.Delete(shadersPath, true);
if (Directory.Exists(texturesPath))
Directory.Delete(texturesPath, true);
if (!File.Exists(ConfigLocation))
return;
// now we have to update ReShade.ini and remove the installed shaders from the search paths
FileIniDataParser parser = new();
IniData data = parser.ReadFile(ConfigLocation);
string shaderSearchPaths = data["GENERAL"]["EffectSearchPaths"];
string textureSearchPaths = data["GENERAL"]["TextureSearchPaths"];
if (shaderSearchPaths.Contains(name))
{
string searchPath = GetSearchPath("Shaders", name);
data["GENERAL"]["EffectSearchPaths"] = shaderSearchPaths.Remove(shaderSearchPaths.IndexOf(searchPath), searchPath.Length);
}
if (textureSearchPaths.Contains(name))
{
string searchPath = GetSearchPath("Textures", name);
data["GENERAL"]["TextureSearchPaths"] = textureSearchPaths.Remove(textureSearchPaths.IndexOf(searchPath), searchPath.Length);
}
parser.WriteFile(ConfigLocation, data);
}
public static async Task InstallExtraviPresets()
{
Debug.WriteLine("[ReShade] Installing Extravi's presets...");
foreach (string name in ExtraviPresetsShaders)
await DownloadShaders(name);
byte[] bytes = await Program.HttpClient.GetByteArrayAsync($"{BaseUrl}/reshade-presets.zip");
using MemoryStream zipStream = new(bytes);
using ZipArchive archive = new(zipStream);
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith('/'))
continue;
// remove containing folder
string filename = entry.FullName.Substring(entry.FullName.IndexOf('/') + 1);
await Task.Run(() => entry.ExtractToFile(Path.Combine(Directories.ReShade, "Presets", filename), true));
}
}
public static void UninstallExtraviPresets()
{
Debug.WriteLine("[ReShade] Uninstalling Extravi's presets...");
FileInfo[] presets = new DirectoryInfo(Path.Combine(Directories.ReShade, "Presets")).GetFiles();
foreach (FileInfo preset in presets)
{
if (preset.Name.StartsWith("Extravi"))
preset.Delete();
}
foreach (string name in ExtraviPresetsShaders)
DeleteShaders(name);
}
public static async Task CheckModifications()
{
Debug.WriteLine("[ReShade] Checking ReShade modifications... ");
string injectorLocation = Path.Combine(Directories.Modifications, "dxgi.dll");
// initialize directories
Directory.CreateDirectory(Directories.ReShade);
Directory.CreateDirectory(Path.Combine(Directories.ReShade, "Fonts"));
Directory.CreateDirectory(Path.Combine(Directories.ReShade, "Screenshots"));
Directory.CreateDirectory(Path.Combine(Directories.ReShade, "Shaders"));
Directory.CreateDirectory(Path.Combine(Directories.ReShade, "Textures"));
Directory.CreateDirectory(Path.Combine(Directories.ReShade, "Presets"));
if (!Program.Settings.UseReShadeExtraviPresets)
{
UninstallExtraviPresets();
Program.Settings.ExtraviPresetsVersion = "";
}
if (!Program.Settings.UseReShade)
{
Debug.WriteLine("[ReShade] Uninstalling ReShade...");
// delete any stock config files
File.Delete(injectorLocation);
File.Delete(ConfigLocation);
Program.Settings.ReShadeConfigVersion = "";
DeleteShaders("Stock");
return;
}
// the version manfiest contains the version of reshade available for download and the last date the presets were updated
var versionManifest = await Utilities.GetJson<ReShadeVersionManifest>("https://raw.githubusercontent.com/Extravi/extravi.github.io/main/update/version.json");
bool shouldFetchReShade = false;
bool shouldFetchConfig = false;
if (!File.Exists(injectorLocation))
{
shouldFetchReShade = true;
}
else if (versionManifest is not null)
{
// check if an update for reshade is available
FileVersionInfo injectorVersionInfo = FileVersionInfo.GetVersionInfo(injectorLocation);
if (injectorVersionInfo.ProductVersion != versionManifest.ReShade)
shouldFetchReShade = true;
}
// check if we should download a fresh copy of the config
// extravi may need to update the config ota, in which case we'll redownload it
if (!File.Exists(ConfigLocation) || versionManifest is not null && Program.Settings.ReShadeConfigVersion != versionManifest.ConfigFile)
shouldFetchConfig = true;
if (shouldFetchReShade)
{
Debug.WriteLine("[ReShade] Installing/Upgrading ReShade...");
{
byte[] bytes = await Program.HttpClient.GetByteArrayAsync($"{BaseUrl}/dxgi.zip");
using MemoryStream zipStream = new(bytes);
using ZipArchive archive = new(zipStream);
archive.ExtractToDirectory(Directories.Modifications, true);
}
}
if (shouldFetchConfig)
{
await DownloadConfig();
if (versionManifest is not null)
Program.Settings.ReShadeConfigVersion = versionManifest.ConfigFile;
}
await DownloadShaders("Stock");
if (Program.Settings.UseReShadeExtraviPresets && Program.Settings.ExtraviPresetsVersion != versionManifest!.Presets)
{
await InstallExtraviPresets();
Program.Settings.ExtraviPresetsVersion = versionManifest.Presets;
}
SynchronizeConfigFile();
}
}
}

View File

@ -1,109 +0,0 @@
using System.Diagnostics;
using System.Text;
using System.Web;
using Microsoft.Win32;
namespace Bloxstrap.Helpers
{
public class Protocol
{
// map uri keys to command line args
private static readonly IReadOnlyDictionary<string, string> UriKeyArgMap = new Dictionary<string, string>()
{
// excluding roblox-player and browsertrackerid
{ "launchmode", "--" },
{ "gameinfo", "-t " },
{ "placelauncherurl", "-j "},
// { "launchtime", "--launchtime=" }, we'll set this when launching the game client
{ "robloxLocale", "--rloc " },
{ "gameLocale", "--gloc " },
{ "channel", "-channel " }
};
public static string ParseUri(string protocol)
{
string[] keyvalPair;
string key;
string val;
StringBuilder commandLine = new();
foreach (var parameter in protocol.Split('+'))
{
if (!parameter.Contains(':'))
continue;
keyvalPair = parameter.Split(':');
key = keyvalPair[0];
val = keyvalPair[1];
if (!UriKeyArgMap.ContainsKey(key) || String.IsNullOrEmpty(val))
continue;
if (key == "launchmode" && val == "play")
val = "app";
if (key == "placelauncherurl")
val = HttpUtility.UrlDecode(val).Replace("browserTrackerId", "lol");
if (key == "channel")
{
if (val.ToLower() != Program.Settings.Channel.ToLower())
{
DialogResult result = !Program.Settings.PromptChannelChange ? DialogResult.Yes : Program.ShowMessageBox(
$"{Program.ProjectName} was launched with the Roblox build channel set to {val}, however your current preferred channel is {Program.Settings.Channel}.\n\n" +
$"Would you like to switch channels from {Program.Settings.Channel} to {val}?",
MessageBoxIcon.Question,
MessageBoxButtons.YesNo
);
if (result == DialogResult.Yes)
Program.Settings.Channel = val;
}
// we'll set the arg when launching
continue;
}
commandLine.Append(UriKeyArgMap[key] + val + " ");
}
return commandLine.ToString();
}
public static void Register(string key, string name, string handler)
{
string handlerArgs = $"\"{handler}\" %1";
RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}");
RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon");
RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command");
if (uriKey.GetValue("") is null)
{
uriKey.SetValue("", $"URL: {name} Protocol");
uriKey.SetValue("URL Protocol", "");
}
if ((string?)uriCommandKey.GetValue("") != handlerArgs)
{
uriIconKey.SetValue("", handler);
uriCommandKey.SetValue("", handlerArgs);
}
uriKey.Close();
uriIconKey.Close();
uriCommandKey.Close();
}
public static void Unregister(string key)
{
try
{
Registry.CurrentUser.DeleteSubKeyTree($@"Software\Classes\{key}");
}
catch (Exception e)
{
Debug.WriteLine($"Failed to unregister {key}: {e}");
}
}
}
}

View File

@ -1,17 +0,0 @@
// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Utility/Package.cs
namespace Bloxstrap.Helpers.RSMM
{
internal class Package
{
public string Name { get; set; } = "";
public string Signature { get; set; } = "";
public int PackedSize { get; set; }
public int Size { get; set; }
public override string ToString()
{
return $"[{Signature}] {Name}";
}
}
}

View File

@ -1,57 +0,0 @@
// 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
{
internal class PackageManifest : List<Package>
{
private PackageManifest(string data)
{
using StringReader reader = new StringReader(data);
string? version = reader.ReadLine();
if (version != "v0")
throw new NotSupportedException($"Unexpected package manifest version: {version} (expected v0!)");
while (true)
{
string? fileName = reader.ReadLine();
string? signature = reader.ReadLine();
string? rawPackedSize = reader.ReadLine();
string? rawSize = reader.ReadLine();
if (string.IsNullOrEmpty(fileName) ||
string.IsNullOrEmpty(signature) ||
string.IsNullOrEmpty(rawPackedSize) ||
string.IsNullOrEmpty(rawSize))
break;
// ignore launcher
if (fileName == "RobloxPlayerLauncher.exe")
break;
int packedSize = int.Parse(rawPackedSize);
int size = int.Parse(rawSize);
Add(new Package
{
Name = fileName,
Signature = signature,
PackedSize = packedSize,
Size = size
});
}
}
public static async Task<PackageManifest> Get(string versionGuid)
{
string pkgManifestUrl = $"{DeployManager.BaseUrl}/{versionGuid}-rbxPkgManifest.txt";
var pkgManifestData = await Program.HttpClient.GetStringAsync(pkgManifestUrl);
return new PackageManifest(pkgManifestData);
}
}
}

View File

@ -1,39 +0,0 @@
// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Utility/SystemEvent.cs
namespace Bloxstrap.Helpers.RSMM
{
public class SystemEvent : EventWaitHandle
{
public string Name { get; private set; }
public SystemEvent(string name, bool init = false, EventResetMode mode = EventResetMode.AutoReset) : base(init, mode, name)
{
if (init)
Reset();
else
Set();
Name = name;
}
public override string ToString()
{
return Name;
}
public Task<bool> WaitForEvent()
{
return Task.Run(WaitOne);
}
public Task<bool> WaitForEvent(TimeSpan timeout, bool exitContext = false)
{
return Task.Run(() => WaitOne(timeout, exitContext));
}
public Task<bool> WaitForEvent(int millisecondsTimeout, bool exitContext = false)
{
return Task.Run(() => WaitOne(millisecondsTimeout, exitContext));
}
}
}

View File

@ -1,25 +0,0 @@
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<byte[]> 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();
}
}
}
}
}

View File

@ -1,63 +0,0 @@
using System.Diagnostics;
using System.IO;
using Bloxstrap.Dialogs.Menu;
namespace Bloxstrap.Helpers
{
public class Updater
{
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)
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
{
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)
return;
File.Delete(Directories.App);
File.Copy(Environment.ProcessPath, Directories.App);
Bootstrapper.Register();
if (Program.IsQuiet || isAutoUpgrade)
return;
Program.ShowMessageBox(
$"{Program.ProjectName} has been updated to v{currentVersionInfo.ProductVersion}",
MessageBoxIcon.Information,
MessageBoxButtons.OK
);
new Preferences().ShowDialog();
Program.Exit();
}
}
}

View File

@ -1,81 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text.Json;
namespace Bloxstrap.Helpers
{
public class Utilities
{
public static bool IsDirectoryEmpty(string path)
{
return !Directory.EnumerateFileSystemEntries(path).Any();
}
public static long GetFreeDiskSpace(string path)
{
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (path.StartsWith(drive.Name))
return drive.AvailableFreeSpace;
}
return -1;
}
public static void OpenWebsite(string website)
{
Process.Start(new ProcessStartInfo { FileName = website, UseShellExecute = true });
}
public static async Task<T?> GetJson<T>(string url)
{
try
{
string json = await Program.HttpClient.GetStringAsync(url);
return JsonSerializer.Deserialize<T>(json);
}
catch (Exception)
{
return default;
}
}
public static string MD5File(string filename)
{
using (MD5 md5 = MD5.Create())
{
using (FileStream stream = File.OpenRead(filename))
{
byte[] hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}
public static string MD5Data(byte[] data)
{
using (MD5 md5 = MD5.Create())
{
byte[] hash = md5.ComputeHash(data);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
// quick and hacky way of getting a value from any key/value pair formatted list
// (command line args, uri params, etc)
public static string? GetKeyValue(string subject, string key, char delimiter)
{
if (subject.LastIndexOf(key) == -1)
return null;
string substr = subject.Substring(subject.LastIndexOf(key) + key.Length);
if (!substr.Contains(delimiter))
return null;
return substr.Split(delimiter)[0];
}
}
}

View File

@ -1,37 +0,0 @@
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));
}
}
}

View File

@ -1,18 +0,0 @@
using System.Text.Json.Serialization;
namespace Bloxstrap.Models
{
public class ClientVersion
{
[JsonPropertyName("version")]
public string Version { get; set; } = null!;
[JsonPropertyName("clientVersionUpload")]
public string VersionGuid { get; set; } = null!;
[JsonPropertyName("bootstrapperVersion")]
public string BootstrapperVersion { get; set; } = null!;
public DateTime? Timestamp { get; set; }
}
}

View File

@ -1,31 +0,0 @@
using System.Text.Json.Serialization;
namespace Bloxstrap.Models
{
public class GithubRelease
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; } = null!;
[JsonPropertyName("name")]
public string Name { get; set; } = null!;
[JsonPropertyName("body")]
public string Body { get; set; } = null!;
[JsonPropertyName("created_at")]
public string CreatedAt { get; set; } = null!;
[JsonPropertyName("assets")]
public List<GithubReleaseAsset>? Assets { get; set; }
}
public class GithubReleaseAsset
{
[JsonPropertyName("browser_download_url")]
public string BrowserDownloadUrl { get; set; } = null!;
[JsonPropertyName("name")]
public string Name { get; set; } = null!;
}
}

View File

@ -1,9 +0,0 @@
namespace Bloxstrap.Models
{
public class ReShadeVersionManifest
{
public string ReShade { get; set; } = null!;
public string Presets { get; set; } = null!;
public string ConfigFile { get; set; } = null!;
}
}

View File

@ -1,13 +0,0 @@
namespace Bloxstrap.Models
{
public class RobloxAsset
{
public string? Name { get; set; }
public RobloxAssetCreator? Creator { get; set; }
}
public class RobloxAssetCreator
{
public string? Name { get; set; }
}
}

View File

@ -1,16 +0,0 @@
using System.Text.Json.Serialization;
namespace Bloxstrap.Models
{
public class RobloxThumbnails
{
[JsonPropertyName("data")]
public List<RobloxThumbnail>? Data { get; set; }
}
public class RobloxThumbnail
{
[JsonPropertyName("imageUrl")]
public string? ImageUrl { get; set; }
}
}

View File

@ -1,41 +0,0 @@
using Bloxstrap.Enums;
using Bloxstrap.Helpers;
namespace Bloxstrap.Models
{
public class SettingsFormat
{
// could these be moved to a separate file (something like State.json)?
// the only problem is i havent yet figured out a way to boil down the settings handler to reduce boilerplate
// as the Program class needs a Settings and a SettingsManager property
// once i figure that out, then ig i could move these
public string VersionGuid { get; set; } = "";
public string RFUVersion { get; set; } = "";
public string ReShadeConfigVersion { get; set; } = "";
public string ExtraviPresetsVersion { get; set; } = "";
// bloxstrap configuration
public BootstrapperStyle BootstrapperStyle { get; set; } = BootstrapperStyle.ProgressDialog;
public BootstrapperIcon BootstrapperIcon { get; set; } = BootstrapperIcon.IconBloxstrap;
public Theme Theme { get; set; } = Theme.Default;
public bool CheckForUpdates { get; set; } = true;
public bool CreateDesktopIcon { get; set; } = true;
// channel configuration
public string Channel { get; set; } = DeployManager.DefaultChannel;
public bool PromptChannelChange { get; set; } = false;
// integration configuration
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 UseReShade { get; set; } = false;
public bool UseReShadeExtraviPresets { get; set; } = false;
// mod preset configuration
public bool UseOldDeathSound { get; set; } = true;
public bool UseOldMouseCursor { get; set; } = false;
public bool UseDisableAppPatch { get; set; } = false;
}
}

View File

@ -1,172 +0,0 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Net;
using System.Reflection;
using Microsoft.Win32;
using Bloxstrap.Enums;
using Bloxstrap.Helpers;
using Bloxstrap.Models;
using Bloxstrap.Dialogs.Menu;
namespace Bloxstrap
{
internal static class Program
{
public const StringComparison StringFormat = StringComparison.InvariantCulture;
public static readonly CultureInfo CultureFormat = CultureInfo.InvariantCulture;
public const string ProjectName = "Bloxstrap";
public const string ProjectRepository = "pizzaboxer/bloxstrap";
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 Version = Assembly.GetExecutingAssembly().GetName().Version!.ToString()[..^2];
public static SettingsManager SettingsManager = new();
public static SettingsFormat Settings = SettingsManager.Settings;
public static readonly HttpClient HttpClient = new(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.All });
// shorthand
public static DialogResult ShowMessageBox(string message, MessageBoxIcon icon = MessageBoxIcon.None, MessageBoxButtons buttons = MessageBoxButtons.OK)
{
if (IsQuiet)
return DialogResult.None;
return MessageBox.Show(message, ProjectName, buttons, icon);
}
public static void Exit(int code = Bootstrapper.ERROR_SUCCESS)
{
SettingsManager.Save();
Environment.Exit(code);
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
LaunchArgs = args;
HttpClient.Timeout = TimeSpan.FromMinutes(5);
HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository);
if (args.Length > 0)
{
if (Array.IndexOf(args, "-quiet") != -1)
IsQuiet = true;
if (Array.IndexOf(args, "-uninstall") != -1)
IsUninstall = true;
if (Array.IndexOf(args, "-nolaunch") != -1)
IsNoLaunch = true;
if (Array.IndexOf(args, "-upgrade") != -1)
IsUpgrade = true;
}
// check if installed
RegistryKey? registryKey = Registry.CurrentUser.OpenSubKey($@"Software\{ProjectName}");
if (registryKey is null)
{
IsFirstRun = true;
Settings = SettingsManager.Settings;
if (IsQuiet)
BaseDirectory = Path.Combine(Directories.LocalAppData, ProjectName);
else
new Preferences().ShowDialog();
}
else
{
BaseDirectory = (string)registryKey.GetValue("InstallLocation")!;
registryKey.Close();
}
// preferences dialog was closed, and so base directory was never set
// (this doesnt account for the registry value not existing but thats basically never gonna happen)
if (String.IsNullOrEmpty(BaseDirectory))
return;
Directories.Initialize(BaseDirectory);
SettingsManager.SaveLocation = Path.Combine(Directories.Base, "Settings.json");
// we shouldn't save settings on the first run until the first installation is finished,
// just in case the user decides to cancel the install
if (!IsFirstRun)
{
Settings = SettingsManager.Settings;
SettingsManager.ShouldSave = true;
}
#if !DEBUG
if (!IsUninstall && !IsFirstRun)
Updater.CheckInstalledVersion();
#endif
string commandLine = "";
#if false//DEBUG
new Preferences().ShowDialog();
#else
if (args.Length > 0)
{
if (args[0] == "-preferences")
{
if (Process.GetProcessesByName(ProjectName).Length > 1)
{
ShowMessageBox($"{ProjectName} is already running. Please close any currently open Bloxstrap or Roblox window before opening the configuration menu.", MessageBoxIcon.Error);
return;
}
new Preferences().ShowDialog();
}
else if (args[0].StartsWith("roblox-player:"))
{
commandLine = Protocol.ParseUri(args[0]);
}
else if (args[0].StartsWith("roblox:"))
{
commandLine = $"--app --deeplink {args[0]}";
}
else
{
commandLine = "--app";
}
}
else
{
commandLine = "--app";
}
#endif
if (!String.IsNullOrEmpty(commandLine))
{
DeployManager.Channel = Settings.Channel;
Settings.BootstrapperStyle.Show(new Bootstrapper(commandLine));
}
SettingsManager.Save();
}
}
}

View File

@ -1,263 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Bloxstrap.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Bloxstrap.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap CancelButton {
get {
object obj = ResourceManager.GetObject("CancelButton", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap CancelButtonHover {
get {
object obj = ResourceManager.GetObject("CancelButtonHover", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap DarkCancelButton {
get {
object obj = ResourceManager.GetObject("DarkCancelButton", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap DarkCancelButtonHover {
get {
object obj = ResourceManager.GetObject("DarkCancelButtonHover", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon2009_ico {
get {
object obj = ResourceManager.GetObject("Icon2009_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Icon2009_png {
get {
object obj = ResourceManager.GetObject("Icon2009_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon2011_ico {
get {
object obj = ResourceManager.GetObject("Icon2011_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Icon2011_png {
get {
object obj = ResourceManager.GetObject("Icon2011_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon2017_ico {
get {
object obj = ResourceManager.GetObject("Icon2017_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Icon2017_png {
get {
object obj = ResourceManager.GetObject("Icon2017_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon2019_ico {
get {
object obj = ResourceManager.GetObject("Icon2019_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Icon2019_png {
get {
object obj = ResourceManager.GetObject("Icon2019_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon Icon2022_ico {
get {
object obj = ResourceManager.GetObject("Icon2022_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap Icon2022_png {
get {
object obj = ResourceManager.GetObject("Icon2022_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon IconBloxstrap_ico {
get {
object obj = ResourceManager.GetObject("IconBloxstrap_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap IconBloxstrap_png {
get {
object obj = ResourceManager.GetObject("IconBloxstrap_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon IconEarly2015_ico {
get {
object obj = ResourceManager.GetObject("IconEarly2015_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap IconEarly2015_png {
get {
object obj = ResourceManager.GetObject("IconEarly2015_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon IconLate2015_ico {
get {
object obj = ResourceManager.GetObject("IconLate2015_ico", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap IconLate2015_png {
get {
object obj = ResourceManager.GetObject("IconLate2015_png", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@ -1,181 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=6.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="CancelButton" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CancelButton.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="CancelButtonHover" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\CancelButtonHover.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DarkCancelButton" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DarkCancelButton.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="DarkCancelButtonHover" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\DarkCancelButtonHover.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2009_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2009-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2009_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2009-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2011_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2011-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2011_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2011-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2017_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2017-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2017_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2017-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2019_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2019-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2019_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2019-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2022_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2022-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Icon2022_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Icon2022-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconBloxstrap_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconBloxstrap-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconBloxstrap_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconBloxstrap-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconEarly2015_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconEarly2015-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconEarly2015_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconEarly2015-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconLate2015_ico" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconLate2015-ico.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="IconLate2015_png" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\IconLate2015-png.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Bloxstrap.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -1,6 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 944 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 B

View File

@ -1,81 +0,0 @@
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using Bloxstrap.Models;
namespace Bloxstrap
{
public class SettingsManager
{
public SettingsFormat Settings = new();
public bool ShouldSave = false;
private bool IsSaving = false;
private string? _saveLocation;
public string? SaveLocation
{
get => _saveLocation;
set
{
if (!String.IsNullOrEmpty(_saveLocation))
return;
_saveLocation = value;
string settingsJson = "";
if (File.Exists(_saveLocation))
settingsJson = File.ReadAllText(_saveLocation);
Debug.WriteLine(settingsJson);
try
{
var settings = JsonSerializer.Deserialize<SettingsFormat>(settingsJson);
if (settings is null)
throw new Exception("Deserialization returned null");
Settings = settings;
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to fetch settings! Reverting to defaults... ({ex.Message})");
// Settings = new();
}
}
}
public void Save()
{
if (IsSaving)
{
// sometimes Save() is called at the same time from both Main() and Exit(),
// so this is here to avoid the program exiting before saving
Thread.Sleep(1000);
return;
}
IsSaving = true;
Debug.WriteLine("Attempting to save...");
string SettingsJson = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true });
Debug.WriteLine(SettingsJson);
if (!ShouldSave || SaveLocation is null)
{
Debug.WriteLine("ShouldSave set to false, not saving...");
return;
}
// save settings
File.WriteAllText(SaveLocation, SettingsJson);
IsSaving = false;
}
}
}