Port files to WPF application
@ -1,10 +1,21 @@
|
||||
using System;
|
||||
using Bloxstrap.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Diagnostics;
|
||||
using Bloxstrap.Dialogs.Menu;
|
||||
using Bloxstrap.Enums;
|
||||
using Bloxstrap.Helpers;
|
||||
using Microsoft.Win32;
|
||||
using System.IO;
|
||||
|
||||
namespace Bloxstrap
|
||||
{
|
||||
@ -13,5 +24,154 @@ namespace Bloxstrap
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
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 MessageBoxResult ShowMessageBox(string message, MessageBoxImage icon = MessageBoxImage.None, MessageBoxButton buttons = MessageBoxButton.OK)
|
||||
{
|
||||
if (IsQuiet)
|
||||
return MessageBoxResult.None;
|
||||
|
||||
return MessageBox.Show(message, ProjectName, buttons, icon);
|
||||
}
|
||||
|
||||
public static void Terminate(int code = Bootstrapper.ERROR_SUCCESS)
|
||||
{
|
||||
SettingsManager.Save();
|
||||
Environment.Exit(code);
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
|
||||
LaunchArgs = e.Args;
|
||||
|
||||
HttpClient.Timeout = TimeSpan.FromMinutes(5);
|
||||
HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository);
|
||||
|
||||
if (LaunchArgs.Length > 0)
|
||||
{
|
||||
if (Array.IndexOf(LaunchArgs, "-quiet") != -1)
|
||||
IsQuiet = true;
|
||||
|
||||
if (Array.IndexOf(LaunchArgs, "-uninstall") != -1)
|
||||
IsUninstall = true;
|
||||
|
||||
if (Array.IndexOf(LaunchArgs, "-nolaunch") != -1)
|
||||
IsNoLaunch = true;
|
||||
|
||||
if (Array.IndexOf(LaunchArgs, "-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 (LaunchArgs.Length > 0)
|
||||
{
|
||||
if (LaunchArgs[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.", MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
new Preferences().ShowDialog();
|
||||
}
|
||||
else if (LaunchArgs[0].StartsWith("roblox-player:"))
|
||||
{
|
||||
commandLine = Protocol.ParseUri(LaunchArgs[0]);
|
||||
}
|
||||
else if (LaunchArgs[0].StartsWith("roblox:"))
|
||||
{
|
||||
commandLine = $"--app --deeplink {LaunchArgs[0]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
commandLine = "--app";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
commandLine = "--app";
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!String.IsNullOrEmpty(commandLine))
|
||||
{
|
||||
DeployManager.Channel = Settings.Channel;
|
||||
Settings.BootstrapperStyle.Show(new Bootstrapper(commandLine));
|
||||
}
|
||||
|
||||
SettingsManager.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,26 @@
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<ApplicationIcon>Bloxstrap.ico</ApplicationIcon>
|
||||
<Version>2.0.0</Version>
|
||||
<FileVersion>2.0.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>
|
||||
|
||||
</Project>
|
||||
|
BIN
Bloxstrap/Bloxstrap/Bloxstrap.ico
Normal file
After Width: | Height: | Size: 120 KiB |
800
Bloxstrap/Bloxstrap/Bootstrapper.cs
Normal file
@ -0,0 +1,800 @@
|
||||
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;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows;
|
||||
|
||||
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(App.Settings.VersionGuid);
|
||||
}
|
||||
|
||||
// this is called from BootstrapperStyleForm.SetupDialog()
|
||||
public async Task Run()
|
||||
{
|
||||
if (App.IsQuiet)
|
||||
Dialog.CloseDialog();
|
||||
|
||||
if (App.IsUninstall)
|
||||
{
|
||||
Uninstall();
|
||||
return;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
if (!App.IsFirstRun && App.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) || App.Settings.VersionGuid != VersionGuid && !CheckIfRunning(false))
|
||||
await InstallLatestVersion();
|
||||
|
||||
await ApplyModifications();
|
||||
|
||||
if (App.IsFirstRun)
|
||||
App.SettingsManager.ShouldSave = true;
|
||||
|
||||
if (App.IsFirstRun || FreshInstall)
|
||||
Register();
|
||||
|
||||
CheckInstall();
|
||||
|
||||
await RbxFpsUnlocker.CheckInstall();
|
||||
|
||||
App.SettingsManager.Save();
|
||||
|
||||
if (App.IsFirstRun && App.IsNoLaunch)
|
||||
Dialog.ShowSuccess($"{App.ProjectName} has successfully installed");
|
||||
else if (!App.IsNoLaunch)
|
||||
await StartRoblox();
|
||||
}
|
||||
|
||||
private async Task CheckForUpdates()
|
||||
{
|
||||
string currentVersion = $"Bloxstrap v{App.Version}";
|
||||
|
||||
var releaseInfo = await Utilities.GetJson<GithubRelease>($"https://api.github.com/repos/{App.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 App.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 App.LaunchArgs)
|
||||
startInfo.ArgumentList.Add(arg);
|
||||
|
||||
App.SettingsManager.Save();
|
||||
|
||||
Process.Start(startInfo);
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private async Task CheckLatestVersion()
|
||||
{
|
||||
Dialog.Message = "Connecting to Roblox...";
|
||||
|
||||
ClientVersion clientVersion = await DeployManager.GetLastDeploy(App.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 = App.ProjectName.Replace(" ", "") + "StartEvent";
|
||||
|
||||
Dialog.Message = "Starting Roblox...";
|
||||
|
||||
if (LaunchCommandLine == "--app" && App.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 (App.Settings.Channel.ToLower() != DeployManager.DefaultChannel.ToLower())
|
||||
LaunchCommandLine += " -channel " + App.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 (App.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 (App.Settings.RFUAutoclose)
|
||||
shouldWait = true;
|
||||
}
|
||||
|
||||
// event fired, wait for 3 seconds then close
|
||||
await Task.Delay(3000);
|
||||
|
||||
// now we move onto handling rich presence
|
||||
if (App.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 (App.Settings.RFUAutoclose && rbxFpsUnlocker is not null)
|
||||
rbxFpsUnlocker.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelButtonClicked()
|
||||
{
|
||||
if (!Dialog.CancelEnabled)
|
||||
{
|
||||
App.Terminate(ERROR_INSTALL_USEREXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
CancelFired = true;
|
||||
|
||||
try
|
||||
{
|
||||
if (App.IsFirstRun)
|
||||
Directory.Delete(Directories.Base, true);
|
||||
else if (Directory.Exists(VersionFolder))
|
||||
Directory.Delete(VersionFolder, true);
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
App.Terminate(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region App Install
|
||||
public static void Register()
|
||||
{
|
||||
RegistryKey applicationKey = Registry.CurrentUser.CreateSubKey($@"Software\{App.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\{App.ProjectName}");
|
||||
uninstallKey.SetValue("DisplayIcon", $"{Directories.App},0");
|
||||
uninstallKey.SetValue("DisplayName", App.ProjectName);
|
||||
uninstallKey.SetValue("DisplayVersion", App.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/{App.ProjectRepository}");
|
||||
uninstallKey.SetValue("URLUpdateInfo", $"https://github.com/{App.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) && App.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 {App.ProjectName}.lnk"));
|
||||
}
|
||||
|
||||
if (App.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 {App.ProjectName}...";
|
||||
|
||||
App.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\{App.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\{App.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($"{App.ProjectName} has succesfully uninstalled");
|
||||
|
||||
App.Terminate();
|
||||
}
|
||||
#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)
|
||||
{
|
||||
App.ShowMessageBox($"{App.ProjectName} requires at least 300 MB of disk space to install Roblox. Please free up some disk space and try again.", MessageBoxImage.Error);
|
||||
App.Terminate(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, App.Settings.VersionGuid);
|
||||
|
||||
if (VersionGuid != App.Settings.VersionGuid && Directory.Exists(oldVersionFolder))
|
||||
{
|
||||
// and also to delete our old version folder
|
||||
Directory.Delete(oldVersionFolder, true);
|
||||
}
|
||||
}
|
||||
|
||||
Dialog.CancelEnabled = false;
|
||||
|
||||
App.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(App.Settings.UseOldDeathSound, @"content\sounds\ouch.ogg", "OldDeath.ogg");
|
||||
await CheckModPreset(App.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowCursor.png", "OldCursor.png");
|
||||
await CheckModPreset(App.Settings.UseOldMouseCursor, @"content\textures\Cursors\KeyboardMouse\ArrowFarCursor.png", "OldFarCursor.png");
|
||||
await CheckModPreset(App.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 App.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
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
using Bloxstrap.Enums;
|
||||
using Bloxstrap.Helpers;
|
||||
using System.Windows.Forms;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System;
|
||||
|
||||
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 (App.IsQuiet)
|
||||
this.Hide();
|
||||
|
||||
this.Text = App.ProjectName;
|
||||
this.Icon = App.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
|
||||
|
||||
App.Terminate();
|
||||
}
|
||||
|
||||
public virtual void ShowSuccess(string message)
|
||||
{
|
||||
App.ShowMessageBox(message, MessageBoxImage.Information);
|
||||
App.Terminate();
|
||||
}
|
||||
|
||||
public virtual void ShowError(string message)
|
||||
{
|
||||
App.ShowMessageBox($"An error occurred while starting Roblox\n\nDetails: {message}", MessageBoxImage.Error);
|
||||
App.Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||
}
|
||||
|
||||
public virtual void CloseDialog()
|
||||
{
|
||||
if (this.InvokeRequired)
|
||||
this.Invoke(CloseDialog);
|
||||
else
|
||||
this.Hide();
|
||||
}
|
||||
|
||||
public void PromptShutdown()
|
||||
{
|
||||
MessageBoxResult result = App.ShowMessageBox(
|
||||
"Roblox is currently running, but needs to close. Would you like close Roblox now?",
|
||||
MessageBoxImage.Information,
|
||||
MessageBoxButton.OKCancel
|
||||
);
|
||||
|
||||
if (result != MessageBoxResult.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());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
96
Bloxstrap/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2009.Designer.cs
generated
Normal file
@ -0,0 +1,96 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<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>
|
111
Bloxstrap/Bloxstrap/Dialogs/BootstrapperDialogs/LegacyDialog2011.Designer.cs
generated
Normal file
@ -0,0 +1,111 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
using Bloxstrap.Enums;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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 = App.Settings.BootstrapperIcon.GetIcon().ToBitmap();
|
||||
|
||||
ScaleWindow();
|
||||
SetupDialog();
|
||||
}
|
||||
|
||||
private void LegacyDialog2011_Load(object sender, EventArgs e)
|
||||
{
|
||||
this.Activate();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<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>
|
130
Bloxstrap/Bloxstrap/Dialogs/BootstrapperDialogs/ProgressDialog.Designer.cs
generated
Normal file
@ -0,0 +1,130 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
using Bloxstrap.Enums;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
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 (App.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 = App.Settings.BootstrapperIcon.GetBitmap();
|
||||
|
||||
SetupDialog();
|
||||
}
|
||||
|
||||
private void ButtonCancel_MouseEnter(object sender, EventArgs e)
|
||||
{
|
||||
if (App.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 (App.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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<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>
|
51
Bloxstrap/Bloxstrap/Dialogs/BootstrapperDialogs/VistaDialog.Designer.cs
generated
Normal file
@ -0,0 +1,51 @@
|
||||
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
|
||||
}
|
||||
}
|
167
Bloxstrap/Bloxstrap/Dialogs/BootstrapperDialogs/VistaDialog.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using Bloxstrap.Enums;
|
||||
using System.Windows.Forms;
|
||||
using System;
|
||||
|
||||
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(App.Settings.BootstrapperIcon.GetIcon()),
|
||||
Caption = App.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 = App.ProjectName,
|
||||
Heading = message,
|
||||
Buttons = { TaskDialogButton.OK }
|
||||
};
|
||||
|
||||
successDialog.Buttons[0].Click += (sender, e) => App.Terminate();
|
||||
|
||||
if (!App.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 = App.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) => App.Terminate(Bootstrapper.ERROR_INSTALL_FAILURE);
|
||||
|
||||
if (!App.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 (!App.IsQuiet)
|
||||
TaskDialog.ShowDialog(Dialog);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<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>
|
39
Bloxstrap/Bloxstrap/Dialogs/Menu/ModHelp.xaml
Normal file
@ -0,0 +1,39 @@
|
||||
<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>
|
34
Bloxstrap/Bloxstrap/Dialogs/Menu/ModHelp.xaml.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Windows;
|
||||
|
||||
using Bloxstrap.Enums;
|
||||
using System;
|
||||
|
||||
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 (App.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();
|
||||
}
|
||||
}
|
||||
}
|
177
Bloxstrap/Bloxstrap/Dialogs/Menu/Preferences.xaml
Normal file
@ -0,0 +1,177 @@
|
||||
<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>
|
427
Bloxstrap/Bloxstrap/Dialogs/Menu/Preferences.xaml.cs
Normal file
@ -0,0 +1,427 @@
|
||||
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 System;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using Bloxstrap.Enums;
|
||||
using Bloxstrap.Helpers;
|
||||
using Bloxstrap.Models;
|
||||
using System.Linq;
|
||||
|
||||
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;
|
||||
|
||||
App.SettingsManager.ShouldSave = false;
|
||||
|
||||
this.Icon = Imaging.CreateBitmapSourceFromHIcon(
|
||||
Properties.Resources.IconBloxstrap_ico.Handle,
|
||||
Int32Rect.Empty,
|
||||
BitmapSizeOptions.FromEmptyOptions()
|
||||
);
|
||||
|
||||
this.Title = App.ProjectName;
|
||||
|
||||
// just in case i guess?
|
||||
if (!Environment.Is64BitOperatingSystem)
|
||||
this.CheckBoxRFUEnabled.IsEnabled = false;
|
||||
}
|
||||
|
||||
public void SetTheme()
|
||||
{
|
||||
string theme = "Light";
|
||||
|
||||
if (App.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;
|
||||
App.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))
|
||||
{
|
||||
App.ShowMessageBox("You must set an install location", MessageBoxImage.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)
|
||||
{
|
||||
App.ShowMessageBox($"{App.ProjectName} does not have write access to the install location you selected. Please choose another install location.", MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.ShowMessageBox(ex.Message, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (App.IsFirstRun)
|
||||
{
|
||||
// this will be set in the registry after first install
|
||||
App.BaseDirectory = installLocation;
|
||||
}
|
||||
else
|
||||
{
|
||||
App.SettingsManager.ShouldSave = true;
|
||||
|
||||
if (App.BaseDirectory is not null && App.BaseDirectory != installLocation)
|
||||
{
|
||||
App.ShowMessageBox($"{App.ProjectName} will install to the new location you've set the next time it runs.", MessageBoxImage.Information);
|
||||
|
||||
App.Settings.VersionGuid = "";
|
||||
|
||||
using (RegistryKey registryKey = Registry.CurrentUser.CreateSubKey($@"Software\{App.ProjectName}"))
|
||||
{
|
||||
registryKey.SetValue("InstallLocation", installLocation);
|
||||
registryKey.SetValue("OldInstallLocation", App.BaseDirectory);
|
||||
}
|
||||
|
||||
// preserve settings
|
||||
// we don't need to copy the bootstrapper over since the install process will do that automatically
|
||||
|
||||
App.SettingsManager.Save();
|
||||
|
||||
File.Copy(Path.Combine(App.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 {App.Version}";
|
||||
|
||||
#region Integrations
|
||||
public bool DRPEnabled
|
||||
{
|
||||
get => App.Settings.UseDiscordRichPresence;
|
||||
set
|
||||
{
|
||||
// if user wants discord rpc, auto-enable buttons by default
|
||||
_window.CheckBoxDRPButtons.IsChecked = value;
|
||||
App.Settings.UseDiscordRichPresence = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool DRPButtons
|
||||
{
|
||||
get => !App.Settings.HideRPCButtons;
|
||||
set => App.Settings.HideRPCButtons = !value;
|
||||
}
|
||||
|
||||
public bool RFUEnabled
|
||||
{
|
||||
get => App.Settings.RFUEnabled;
|
||||
set
|
||||
{
|
||||
// if user wants to use rbxfpsunlocker, auto-enable autoclosing by default
|
||||
_window.CheckBoxRFUAutoclose.IsChecked = value;
|
||||
App.Settings.RFUEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RFUAutoclose
|
||||
{
|
||||
get => App.Settings.RFUAutoclose;
|
||||
set => App.Settings.RFUAutoclose = value;
|
||||
}
|
||||
|
||||
public bool UseReShade
|
||||
{
|
||||
get => App.Settings.UseReShade;
|
||||
set
|
||||
{
|
||||
// if user wants to use reshade, auto-enable use of extravi's presets by default
|
||||
_window.CheckBoxUseReShadeExtraviPresets.IsChecked = value;
|
||||
App.Settings.UseReShade = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseReShadeExtraviPresets
|
||||
{
|
||||
get => App.Settings.UseReShadeExtraviPresets;
|
||||
set => App.Settings.UseReShadeExtraviPresets = value;
|
||||
}
|
||||
|
||||
public bool ReShadeFolderButtonEnabled { get; } = !App.IsFirstRun;
|
||||
public string ReShadeFolderButtonTooltip { get; } = App.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 => App.Settings.UseOldDeathSound;
|
||||
set => App.Settings.UseOldDeathSound = value;
|
||||
}
|
||||
|
||||
public bool ModOldMouseCursor
|
||||
{
|
||||
get => App.Settings.UseOldMouseCursor;
|
||||
set => App.Settings.UseOldMouseCursor = value;
|
||||
}
|
||||
|
||||
public bool ModDisableAppPatch
|
||||
{
|
||||
get => App.Settings.UseDisableAppPatch;
|
||||
set => App.Settings.UseDisableAppPatch = value;
|
||||
}
|
||||
|
||||
public bool ModFolderButtonEnabled { get; } = !App.IsFirstRun;
|
||||
public string ModFolderButtonTooltip { get; } = App.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 = App.IsFirstRun ? Path.Combine(Directories.LocalAppData, App.ProjectName) : App.BaseDirectory;
|
||||
public string InstallLocation
|
||||
{
|
||||
get => installLocation;
|
||||
set
|
||||
{
|
||||
installLocation = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private bool showAllChannels = !DeployManager.ChannelsAbstracted.Contains(App.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(App.Settings.Channel) ? DeployManager.ChannelsAbstracted : DeployManager.ChannelsAll;
|
||||
public IEnumerable<string> Channels
|
||||
{
|
||||
get => channels;
|
||||
set
|
||||
{
|
||||
channels = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Channel
|
||||
{
|
||||
get => App.Settings.Channel;
|
||||
set
|
||||
{
|
||||
Task.Run(() => GetChannelInfo(value));
|
||||
App.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 => App.Settings.PromptChannelChange;
|
||||
set => App.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 == App.Settings.Theme).Key;
|
||||
set
|
||||
{
|
||||
App.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 == App.Settings.BootstrapperStyle).Key;
|
||||
set => App.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 == App.Settings.BootstrapperIcon).Key;
|
||||
set => App.Settings.BootstrapperIcon = Icons[value];
|
||||
}
|
||||
|
||||
public bool CreateDesktopIcon
|
||||
{
|
||||
get => App.Settings.CreateDesktopIcon;
|
||||
set => App.Settings.CreateDesktopIcon = value;
|
||||
}
|
||||
|
||||
public bool CheckForUpdates
|
||||
{
|
||||
get => App.Settings.CheckForUpdates;
|
||||
set => App.Settings.CheckForUpdates = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public string ConfirmButtonText { get; } = App.IsFirstRun ? "Install" : "Save";
|
||||
|
||||
public PreferencesViewModel(Preferences window)
|
||||
{
|
||||
_window = window;
|
||||
Task.Run(() => GetChannelInfo(App.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", App.CultureFormat);
|
||||
|
||||
ChannelInfo = $"Version: v{info.Version} ({info.VersionGuid})\nDeployed: {strTimestamp}";
|
||||
}
|
||||
}
|
||||
}
|
54
Bloxstrap/Bloxstrap/Dialogs/Menu/ReShadeHelp.xaml
Normal file
@ -0,0 +1,54 @@
|
||||
<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>
|
34
Bloxstrap/Bloxstrap/Dialogs/Menu/ReShadeHelp.xaml.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System.Windows;
|
||||
|
||||
using Bloxstrap.Enums;
|
||||
using System;
|
||||
|
||||
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 (App.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();
|
||||
}
|
||||
}
|
||||
}
|
4505
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/ColourfulDarkTheme.xaml
Normal file
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
}
|
4555
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/ColourfulLightTheme.xaml
Normal file
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
}
|
4644
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/DarkTheme.xaml
Normal file
43
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/DarkTheme.xaml.cs
Normal file
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
}
|
4461
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/LightTheme.xaml
Normal file
43
Bloxstrap/Bloxstrap/Dialogs/Menu/Themes/LightTheme.xaml.cs
Normal file
@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
}
|
81
Bloxstrap/Bloxstrap/Enums/BootstrapperIcon.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System.Drawing;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
Bloxstrap/Bloxstrap/Enums/BootstrapperStyle.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using Bloxstrap.Dialogs.BootstrapperDialogs;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
Bloxstrap/Bloxstrap/Enums/Theme.cs
Normal file
@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
93
Bloxstrap/Bloxstrap/Helpers/DeployManager.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using Bloxstrap.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
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 App.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 App.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;
|
||||
}
|
||||
}
|
||||
}
|
39
Bloxstrap/Bloxstrap/Helpers/Directories.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
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", "Bloxstrap"); }
|
||||
|
||||
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, $"{"Bloxstrap"}.exe");
|
||||
}
|
||||
}
|
||||
}
|
210
Bloxstrap/Bloxstrap/Helpers/Integrations/DiscordRichPresence.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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 (!App.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();
|
||||
}
|
||||
}
|
||||
}
|
111
Bloxstrap/Bloxstrap/Helpers/Integrations/RbxFpsUnlocker.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Bloxstrap.Models;
|
||||
using System;
|
||||
|
||||
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(App.BaseDirectory))
|
||||
continue;
|
||||
|
||||
process.Kill();
|
||||
process.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public static async Task CheckInstall()
|
||||
{
|
||||
if (App.BaseDirectory is null)
|
||||
return;
|
||||
|
||||
string folderLocation = Path.Combine(App.BaseDirectory, "Integrations\\rbxfpsunlocker");
|
||||
string fileLocation = Path.Combine(folderLocation, "rbxfpsunlocker.exe");
|
||||
string settingsLocation = Path.Combine(folderLocation, "settings");
|
||||
|
||||
if (!App.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 (App.Settings.RFUVersion == releaseInfo.TagName)
|
||||
return;
|
||||
|
||||
CheckIfRunning();
|
||||
File.Delete(fileLocation);
|
||||
}
|
||||
|
||||
Debug.WriteLine("Installing/Updating rbxfpsunlocker...");
|
||||
|
||||
{
|
||||
byte[] bytes = await App.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);
|
||||
|
||||
App.Settings.RFUVersion = releaseInfo.TagName;
|
||||
}
|
||||
}
|
||||
}
|
402
Bloxstrap/Bloxstrap/Helpers/Integrations/ReShade.cs
Normal file
@ -0,0 +1,402 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
using Bloxstrap.Models;
|
||||
|
||||
using IniParser;
|
||||
using IniParser.Model;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
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 App.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, App.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 App.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 App.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 (!App.Settings.UseReShadeExtraviPresets)
|
||||
{
|
||||
UninstallExtraviPresets();
|
||||
App.Settings.ExtraviPresetsVersion = "";
|
||||
}
|
||||
|
||||
if (!App.Settings.UseReShade)
|
||||
{
|
||||
Debug.WriteLine("[ReShade] Uninstalling ReShade...");
|
||||
|
||||
// delete any stock config files
|
||||
File.Delete(injectorLocation);
|
||||
File.Delete(ConfigLocation);
|
||||
|
||||
App.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 && App.Settings.ReShadeConfigVersion != versionManifest.ConfigFile)
|
||||
shouldFetchConfig = true;
|
||||
|
||||
if (shouldFetchReShade)
|
||||
{
|
||||
Debug.WriteLine("[ReShade] Installing/Upgrading ReShade...");
|
||||
|
||||
{
|
||||
byte[] bytes = await App.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)
|
||||
App.Settings.ReShadeConfigVersion = versionManifest.ConfigFile;
|
||||
}
|
||||
|
||||
await DownloadShaders("Stock");
|
||||
|
||||
if (App.Settings.UseReShadeExtraviPresets && App.Settings.ExtraviPresetsVersion != versionManifest!.Presets)
|
||||
{
|
||||
await InstallExtraviPresets();
|
||||
App.Settings.ExtraviPresetsVersion = versionManifest.Presets;
|
||||
}
|
||||
|
||||
SynchronizeConfigFile();
|
||||
}
|
||||
}
|
||||
}
|
112
Bloxstrap/Bloxstrap/Helpers/Protocol.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Microsoft.Win32;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using System;
|
||||
|
||||
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() != App.Settings.Channel.ToLower())
|
||||
{
|
||||
MessageBoxResult result = !App.Settings.PromptChannelChange ? MessageBoxResult.Yes : App.ShowMessageBox(
|
||||
$"{App.ProjectName} was launched with the Roblox build channel set to {val}, however your current preferred channel is {App.Settings.Channel}.\n\n" +
|
||||
$"Would you like to switch channels from {App.Settings.Channel} to {val}?",
|
||||
MessageBoxImage.Question,
|
||||
MessageBoxButton.YesNo
|
||||
);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
App.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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
Bloxstrap/Bloxstrap/Helpers/RSMM/Package.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// 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}";
|
||||
}
|
||||
}
|
||||
}
|
60
Bloxstrap/Bloxstrap/Helpers/RSMM/PackageManifest.cs
Normal file
@ -0,0 +1,60 @@
|
||||
// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Bootstrapper/PackageManifest.cs
|
||||
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
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 App.HttpClient.GetStringAsync(pkgManifestUrl);
|
||||
|
||||
return new PackageManifest(pkgManifestData);
|
||||
}
|
||||
}
|
||||
}
|
43
Bloxstrap/Bloxstrap/Helpers/RSMM/SystemEvent.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/ProjectSrc/Utility/SystemEvent.cs
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
27
Bloxstrap/Bloxstrap/Helpers/ResourceHelper.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
Bloxstrap/Bloxstrap/Helpers/Updater.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
using Bloxstrap.Dialogs.Menu;
|
||||
using System.Windows;
|
||||
using System;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
MessageBoxResult result;
|
||||
|
||||
// silently upgrade version if the command line flag is set or if we're launching from an auto update
|
||||
if (App.IsUpgrade || isAutoUpgrade)
|
||||
{
|
||||
result = MessageBoxResult.Yes;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = App.ShowMessageBox(
|
||||
$"The version of {App.ProjectName} you've launched is different to the version you currently have installed.\nWould you like to upgrade your currently installed version?",
|
||||
MessageBoxImage.Question,
|
||||
MessageBoxButton.YesNo
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
return;
|
||||
|
||||
File.Delete(Directories.App);
|
||||
File.Copy(Environment.ProcessPath, Directories.App);
|
||||
|
||||
Bootstrapper.Register();
|
||||
|
||||
if (App.IsQuiet || isAutoUpgrade)
|
||||
return;
|
||||
|
||||
App.ShowMessageBox(
|
||||
$"{App.ProjectName} has been updated to v{currentVersionInfo.ProductVersion}",
|
||||
MessageBoxImage.Information,
|
||||
MessageBoxButton.OK
|
||||
);
|
||||
|
||||
new Preferences().ShowDialog();
|
||||
App.Terminate();
|
||||
}
|
||||
}
|
||||
}
|
84
Bloxstrap/Bloxstrap/Helpers/Utilities.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
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 App.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];
|
||||
}
|
||||
}
|
||||
}
|
38
Bloxstrap/Bloxstrap/Helpers/WindowScaling.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
19
Bloxstrap/Bloxstrap/Models/ClientVersion.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System;
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
32
Bloxstrap/Bloxstrap/Models/GithubRelease.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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!;
|
||||
}
|
||||
}
|
9
Bloxstrap/Bloxstrap/Models/ReShadeVersionManifest.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Bloxstrap.Models
|
||||
{
|
||||
public class ReShadeVersionManifest
|
||||
{
|
||||
public string ReShade { get; set; } = null!;
|
||||
public string Presets { get; set; } = null!;
|
||||
public string ConfigFile { get; set; } = null!;
|
||||
}
|
||||
}
|
13
Bloxstrap/Bloxstrap/Models/RobloxAsset.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Bloxstrap.Models
|
||||
{
|
||||
public class RobloxAsset
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public RobloxAssetCreator? Creator { get; set; }
|
||||
}
|
||||
|
||||
public class RobloxAssetCreator
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
17
Bloxstrap/Bloxstrap/Models/RobloxThumbnails.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Bloxstrap.Models
|
||||
{
|
||||
public class RobloxThumbnails
|
||||
{
|
||||
[JsonPropertyName("data")]
|
||||
public List<RobloxThumbnail>? Data { get; set; }
|
||||
}
|
||||
|
||||
public class RobloxThumbnail
|
||||
{
|
||||
[JsonPropertyName("imageUrl")]
|
||||
public string? ImageUrl { get; set; }
|
||||
}
|
||||
}
|
41
Bloxstrap/Bloxstrap/Models/SettingsFormat.cs
Normal file
@ -0,0 +1,41 @@
|
||||
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;
|
||||
}
|
||||
}
|
263
Bloxstrap/Bloxstrap/Properties/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,263 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
181
Bloxstrap/Bloxstrap/Properties/Resources.resx
Normal file
@ -0,0 +1,181 @@
|
||||
<?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>
|
26
Bloxstrap/Bloxstrap/Properties/Settings.Designer.cs
generated
Normal file
@ -0,0 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
Bloxstrap/Bloxstrap/Properties/Settings.settings
Normal file
@ -0,0 +1,6 @@
|
||||
<?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>
|
8
Bloxstrap/Bloxstrap/Properties/launchSettings.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Bloxstrap": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-foo -bar"
|
||||
}
|
||||
}
|
||||
}
|
BIN
Bloxstrap/Bloxstrap/Resources/CancelButton.png
Normal file
After Width: | Height: | Size: 768 B |
BIN
Bloxstrap/Bloxstrap/Resources/CancelButtonHover.png
Normal file
After Width: | Height: | Size: 944 B |
BIN
Bloxstrap/Bloxstrap/Resources/DarkCancelButton.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/DarkCancelButtonHover.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2009-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2009-png.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2011-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2011-png.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2017-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2017-png.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2019-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2019-png.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2022-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Icon2022-png.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconBloxstrap-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconBloxstrap-png.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconEarly2015-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconEarly2015-png.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconLate2015-ico.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/IconLate2015-png.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
Bloxstrap/Bloxstrap/Resources/Mods/OldCursor.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
Bloxstrap/Bloxstrap/Resources/Mods/OldDeath.ogg
Normal file
BIN
Bloxstrap/Bloxstrap/Resources/Mods/OldFarCursor.png
Normal file
After Width: | Height: | Size: 235 B |
83
Bloxstrap/Bloxstrap/SettingsManager.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
using Bloxstrap.Models;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|