mirror of
https://github.com/bloxstraplabs/bloxstrap.git
synced 2025-04-21 10:01:27 -07:00
Merge pull request #739 from bluepilledgreat/feature/studio
Add studio support
This commit is contained in:
commit
a92ea7ba3d
@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
@ -14,7 +15,8 @@ namespace Bloxstrap
|
||||
{
|
||||
public const string ProjectName = "Bloxstrap";
|
||||
public const string ProjectRepository = "pizzaboxer/bloxstrap";
|
||||
public const string RobloxAppName = "RobloxPlayerBeta";
|
||||
public const string RobloxPlayerAppName = "RobloxPlayerBeta";
|
||||
public const string RobloxStudioAppName = "RobloxStudioBeta";
|
||||
|
||||
// used only for communicating between app and menu - use Directories.Base for anything else
|
||||
public static string BaseDirectory = null!;
|
||||
@ -49,7 +51,9 @@ namespace Bloxstrap
|
||||
)
|
||||
);
|
||||
|
||||
#if RELEASE
|
||||
private static bool _showingExceptionDialog = false;
|
||||
#endif
|
||||
|
||||
public static void Terminate(ErrorCode exitCode = ErrorCode.ERROR_SUCCESS)
|
||||
{
|
||||
@ -120,6 +124,10 @@ namespace Bloxstrap
|
||||
|
||||
LaunchArgs = e.Args;
|
||||
|
||||
#if DEBUG
|
||||
Logger.WriteLine(LOG_IDENT, $"Arguments: {string.Join(' ', LaunchArgs)}");
|
||||
#endif
|
||||
|
||||
HttpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
HttpClient.DefaultRequestHeaders.Add("User-Agent", ProjectRepository);
|
||||
|
||||
@ -189,6 +197,8 @@ namespace Bloxstrap
|
||||
#endif
|
||||
|
||||
string commandLine = "";
|
||||
bool isStudioLaunch = false;
|
||||
bool isStudioAuth = false;
|
||||
|
||||
if (IsMenuLaunch)
|
||||
{
|
||||
@ -227,6 +237,25 @@ namespace Bloxstrap
|
||||
|
||||
commandLine = $"--app --deeplink {LaunchArgs[0]}";
|
||||
}
|
||||
else if (LaunchArgs[0].StartsWith("roblox-studio:"))
|
||||
{
|
||||
commandLine = ProtocolHandler.ParseUri(LaunchArgs[0]);
|
||||
if (!commandLine.Contains("-startEvent"))
|
||||
commandLine += " -startEvent www.roblox.com/robloxQTStudioStartedEvent";
|
||||
isStudioLaunch = true;
|
||||
}
|
||||
else if (LaunchArgs[0].StartsWith("roblox-studio-auth:"))
|
||||
{
|
||||
commandLine = HttpUtility.UrlDecode(LaunchArgs[0]);
|
||||
isStudioLaunch = true;
|
||||
isStudioAuth = true;
|
||||
}
|
||||
else if (LaunchArgs[0] == "-ide")
|
||||
{
|
||||
isStudioLaunch = true;
|
||||
if (LaunchArgs.Length >= 2)
|
||||
commandLine = $"-task EditFile -localPlaceFile \"{LaunchArgs[1]}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
commandLine = "--app";
|
||||
@ -244,7 +273,7 @@ namespace Bloxstrap
|
||||
|
||||
// start bootstrapper and show the bootstrapper modal if we're not running silently
|
||||
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
|
||||
Bootstrapper bootstrapper = new(commandLine);
|
||||
Bootstrapper bootstrapper = new(commandLine, isStudioLaunch, isStudioAuth);
|
||||
IBootstrapperDialog? dialog = null;
|
||||
|
||||
if (!IsQuiet)
|
||||
@ -261,7 +290,7 @@ namespace Bloxstrap
|
||||
|
||||
Mutex? singletonMutex = null;
|
||||
|
||||
if (Settings.Prop.MultiInstanceLaunching)
|
||||
if (Settings.Prop.MultiInstanceLaunching && !isStudioLaunch)
|
||||
{
|
||||
Logger.WriteLine(LOG_IDENT, "Creating singleton mutex");
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -12,38 +12,6 @@ namespace Bloxstrap
|
||||
#region Properties
|
||||
private const int ProgressBarMaximum = 10000;
|
||||
|
||||
// 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\" },
|
||||
|
||||
// the runtime installer is only extracted if it needs installing
|
||||
{ "WebView2.zip", @"" },
|
||||
{ "WebView2RuntimeInstaller.zip", @"WebView2RuntimeInstaller\" },
|
||||
|
||||
{ "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 const string AppSettings =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||
"<Settings>\r\n" +
|
||||
@ -53,15 +21,50 @@ namespace Bloxstrap
|
||||
|
||||
private readonly CancellationTokenSource _cancelTokenSource = new();
|
||||
|
||||
private static bool FreshInstall => String.IsNullOrEmpty(App.State.Prop.VersionGuid);
|
||||
private bool FreshInstall => String.IsNullOrEmpty(_versionGuid);
|
||||
|
||||
private string _playerLocation => Path.Combine(_versionFolder, "RobloxPlayerBeta.exe");
|
||||
private string _playerFileName => _studioLaunch ? "RobloxStudioBeta.exe" : "RobloxPlayerBeta.exe";
|
||||
// TODO: change name
|
||||
private string _playerLocation => Path.Combine(_versionFolder, _playerFileName);
|
||||
|
||||
private string _launchCommandLine;
|
||||
private bool _studioLaunch;
|
||||
private bool _studioAuth;
|
||||
|
||||
private string _versionGuid
|
||||
{
|
||||
get
|
||||
{
|
||||
return _studioLaunch ? App.State.Prop.StudioVersionGuid : App.State.Prop.PlayerVersionGuid;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_studioLaunch)
|
||||
App.State.Prop.StudioVersionGuid = value;
|
||||
else
|
||||
App.State.Prop.PlayerVersionGuid = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int _distributionSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _studioLaunch ? App.State.Prop.StudioSize : App.State.Prop.PlayerSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_studioLaunch)
|
||||
App.State.Prop.StudioSize = value;
|
||||
else
|
||||
App.State.Prop.PlayerSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string _latestVersionGuid = null!;
|
||||
private PackageManifest _versionPackageManifest = null!;
|
||||
private FileManifest _versionFileManifest = null!;
|
||||
private string _versionFolder = null!;
|
||||
|
||||
private bool _isInstalling = false;
|
||||
@ -70,19 +73,32 @@ namespace Bloxstrap
|
||||
private int _packagesExtracted = 0;
|
||||
private bool _cancelFired = false;
|
||||
|
||||
private IReadOnlyDictionary<string, string> _packageDirectories;
|
||||
|
||||
public IBootstrapperDialog? Dialog = null;
|
||||
public bool IsStudioLaunch => _studioLaunch;
|
||||
#endregion
|
||||
|
||||
#region Core
|
||||
public Bootstrapper(string launchCommandLine)
|
||||
public Bootstrapper(string launchCommandLine, bool studioLaunch, bool studioAuth)
|
||||
{
|
||||
_launchCommandLine = launchCommandLine;
|
||||
_studioLaunch = studioLaunch;
|
||||
_studioAuth = studioAuth;
|
||||
|
||||
_packageDirectories = _studioLaunch ? PackageMap.Studio : PackageMap.Player;
|
||||
}
|
||||
|
||||
private void SetStatus(string message)
|
||||
{
|
||||
App.Logger.WriteLine("Bootstrapper::SetStatus", message);
|
||||
|
||||
string productName = "Roblox";
|
||||
if (_studioLaunch)
|
||||
productName += " Studio";
|
||||
|
||||
message = message.Replace("{product}", productName);
|
||||
|
||||
// yea idk
|
||||
if (App.Settings.Prop.BootstrapperStyle == BootstrapperStyle.ByfronDialog)
|
||||
message = message.Replace("...", "");
|
||||
@ -186,7 +202,7 @@ namespace Bloxstrap
|
||||
await CheckLatestVersion();
|
||||
|
||||
// install/update roblox if we're running for the first time, needs updating, or the player location doesn't exist
|
||||
if (App.IsFirstRun || _latestVersionGuid != App.State.Prop.VersionGuid || !File.Exists(_playerLocation))
|
||||
if (App.IsFirstRun || _latestVersionGuid != _versionGuid || !File.Exists(_playerLocation))
|
||||
await InstallLatestVersion();
|
||||
|
||||
if (App.IsFirstRun)
|
||||
@ -226,18 +242,20 @@ namespace Bloxstrap
|
||||
|
||||
ClientVersion clientVersion;
|
||||
|
||||
string binaryType = _studioLaunch ? "WindowsStudio64" : "WindowsPlayer";
|
||||
|
||||
try
|
||||
{
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel);
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel, binaryType: binaryType);
|
||||
}
|
||||
catch (HttpResponseException ex)
|
||||
{
|
||||
if (ex.ResponseMessage.StatusCode != HttpStatusCode.NotFound)
|
||||
throw;
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Reverting enrolled channel to {RobloxDeployment.DefaultChannel} because a WindowsPlayer build does not exist for {App.Settings.Prop.Channel}");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Reverting enrolled channel to {RobloxDeployment.DefaultChannel} because a {binaryType} build does not exist for {App.Settings.Prop.Channel}");
|
||||
App.Settings.Prop.Channel = RobloxDeployment.DefaultChannel;
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel);
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel, binaryType: binaryType);
|
||||
}
|
||||
|
||||
if (clientVersion.IsBehindDefaultChannel)
|
||||
@ -260,21 +278,20 @@ namespace Bloxstrap
|
||||
App.Logger.WriteLine("Bootstrapper::CheckLatestVersion", $"Changed Roblox channel from {App.Settings.Prop.Channel} to {RobloxDeployment.DefaultChannel}");
|
||||
|
||||
App.Settings.Prop.Channel = RobloxDeployment.DefaultChannel;
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel);
|
||||
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel, binaryType: binaryType);
|
||||
}
|
||||
}
|
||||
|
||||
_latestVersionGuid = clientVersion.VersionGuid;
|
||||
_versionFolder = Path.Combine(Paths.Versions, _latestVersionGuid);
|
||||
_versionPackageManifest = await PackageManifest.Get(_latestVersionGuid);
|
||||
_versionFileManifest = await FileManifest.Get(_latestVersionGuid);
|
||||
}
|
||||
|
||||
private async Task StartRoblox()
|
||||
{
|
||||
const string LOG_IDENT = "Bootstrapper::StartRoblox";
|
||||
|
||||
SetStatus("Starting Roblox...");
|
||||
SetStatus("Starting {product}...");
|
||||
|
||||
if (_launchCommandLine == "--app" && App.Settings.Prop.UseDisableAppPatch)
|
||||
{
|
||||
@ -297,14 +314,17 @@ namespace Bloxstrap
|
||||
return;
|
||||
}
|
||||
|
||||
_launchCommandLine = _launchCommandLine.Replace("LAUNCHTIMEPLACEHOLDER", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString());
|
||||
if (!_studioAuth)
|
||||
{
|
||||
_launchCommandLine = _launchCommandLine.Replace("LAUNCHTIMEPLACEHOLDER", DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString());
|
||||
|
||||
_launchCommandLine += " -channel ";
|
||||
_launchCommandLine += " -channel ";
|
||||
|
||||
if (App.Settings.Prop.Channel.ToLowerInvariant() == RobloxDeployment.DefaultChannel.ToLowerInvariant())
|
||||
_launchCommandLine += "production";
|
||||
else
|
||||
_launchCommandLine += App.Settings.Prop.Channel.ToLowerInvariant();
|
||||
if (App.Settings.Prop.Channel.ToLowerInvariant() == RobloxDeployment.DefaultChannel.ToLowerInvariant())
|
||||
_launchCommandLine += "production";
|
||||
else
|
||||
_launchCommandLine += App.Settings.Prop.Channel.ToLowerInvariant();
|
||||
}
|
||||
|
||||
// whether we should wait for roblox to exit to handle stuff in the background or clean up after roblox closes
|
||||
bool shouldWait = false;
|
||||
@ -316,6 +336,13 @@ namespace Bloxstrap
|
||||
WorkingDirectory = _versionFolder
|
||||
};
|
||||
|
||||
if (_studioAuth)
|
||||
{
|
||||
Process.Start(startInfo);
|
||||
Dialog?.CloseBootstrapper();
|
||||
return;
|
||||
}
|
||||
|
||||
// v2.2.0 - byfron will trip if we keep a process handle open for over a minute, so we're doing this now
|
||||
int gameClientPid;
|
||||
using (Process gameClient = Process.Start(startInfo)!)
|
||||
@ -329,7 +356,8 @@ namespace Bloxstrap
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Started Roblox (PID {gameClientPid})");
|
||||
|
||||
using (SystemEvent startEvent = new("www.roblox.com/robloxStartedEvent"))
|
||||
string eventName = _studioLaunch ? "www.roblox.com/robloxQTStudioStartedEvent" : "www.roblox.com/robloxStartedEvent";
|
||||
using (SystemEvent startEvent = new(eventName))
|
||||
{
|
||||
bool startEventFired = await startEvent.WaitForEvent();
|
||||
|
||||
@ -339,7 +367,8 @@ namespace Bloxstrap
|
||||
return;
|
||||
}
|
||||
|
||||
App.NotifyIcon?.SetProcessId(gameClientPid);
|
||||
if (App.Settings.Prop.EnableActivityTracking && !_studioLaunch)
|
||||
App.NotifyIcon?.SetProcessId(gameClientPid);
|
||||
|
||||
if (App.Settings.Prop.EnableActivityTracking)
|
||||
{
|
||||
@ -488,7 +517,10 @@ namespace Bloxstrap
|
||||
using RegistryKey uninstallKey = Registry.CurrentUser.CreateSubKey($"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{App.ProjectName}");
|
||||
|
||||
// sum compressed and uncompressed package sizes and convert to kilobytes
|
||||
int totalSize = (_versionPackageManifest.Sum(x => x.Size) + _versionPackageManifest.Sum(x => x.PackedSize)) / 1000;
|
||||
int distributionSize = (_versionPackageManifest.Sum(x => x.Size) + _versionPackageManifest.Sum(x => x.PackedSize)) / 1000;
|
||||
_distributionSize = distributionSize;
|
||||
|
||||
int totalSize = App.State.Prop.PlayerSize + App.State.Prop.StudioSize;
|
||||
|
||||
uninstallKey.SetValue("EstimatedSize", totalSize);
|
||||
|
||||
@ -507,6 +539,12 @@ namespace Bloxstrap
|
||||
|
||||
ProtocolHandler.Register("roblox", "Roblox", Paths.Application);
|
||||
ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application);
|
||||
ProtocolHandler.Register("roblox-studio", "Roblox", Paths.Application);
|
||||
ProtocolHandler.Register("roblox-studio-auth", "Roblox", Paths.Application);
|
||||
|
||||
ProtocolHandler.RegisterRobloxPlace(Paths.Application);
|
||||
ProtocolHandler.RegisterExtension(".rbxl");
|
||||
ProtocolHandler.RegisterExtension(".rbxlx");
|
||||
|
||||
if (Environment.ProcessPath is not null && Environment.ProcessPath != Paths.Application)
|
||||
{
|
||||
@ -540,6 +578,7 @@ namespace Bloxstrap
|
||||
|
||||
Utility.Shortcut.Create(Paths.Application, "", Path.Combine(Paths.StartMenu, "Play Roblox.lnk"));
|
||||
Utility.Shortcut.Create(Paths.Application, "-menu", Path.Combine(Paths.StartMenu, $"{App.ProjectName} Menu.lnk"));
|
||||
Utility.Shortcut.Create(Paths.Application, "-ide", Path.Combine(Paths.StartMenu, $"Roblox Studio ({App.ProjectName}).lnk"));
|
||||
|
||||
if (App.Settings.Prop.CreateDesktopIcon)
|
||||
{
|
||||
@ -648,7 +687,7 @@ namespace Bloxstrap
|
||||
const string LOG_IDENT = "Bootstrapper::Uninstall";
|
||||
|
||||
// prompt to shutdown roblox if its currently running
|
||||
if (Process.GetProcessesByName(App.RobloxAppName).Any())
|
||||
if (Process.GetProcessesByName(App.RobloxPlayerAppName).Any() || Process.GetProcessesByName(App.RobloxStudioAppName).Any())
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Prompting to shut down all open Roblox instances");
|
||||
|
||||
@ -663,7 +702,13 @@ namespace Bloxstrap
|
||||
|
||||
try
|
||||
{
|
||||
foreach (Process process in Process.GetProcessesByName("RobloxPlayerBeta"))
|
||||
foreach (Process process in Process.GetProcessesByName(App.RobloxPlayerAppName))
|
||||
{
|
||||
process.CloseMainWindow();
|
||||
process.Close();
|
||||
}
|
||||
|
||||
foreach (Process process in Process.GetProcessesByName(App.RobloxStudioAppName))
|
||||
{
|
||||
process.CloseMainWindow();
|
||||
process.Close();
|
||||
@ -680,16 +725,17 @@ namespace Bloxstrap
|
||||
SetStatus($"Uninstalling {App.ProjectName}...");
|
||||
|
||||
App.ShouldSaveConfigs = false;
|
||||
bool robloxStillInstalled = true;
|
||||
bool robloxPlayerStillInstalled = true;
|
||||
bool robloxStudioStillInstalled = true;
|
||||
|
||||
// check if stock bootstrapper is still installed
|
||||
RegistryKey? bootstrapperKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-player");
|
||||
using RegistryKey? bootstrapperKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-player");
|
||||
if (bootstrapperKey is null)
|
||||
{
|
||||
robloxPlayerStillInstalled = false;
|
||||
|
||||
ProtocolHandler.Unregister("roblox");
|
||||
ProtocolHandler.Unregister("roblox-player");
|
||||
|
||||
robloxStillInstalled = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-studio") is not null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -701,6 +747,27 @@ namespace Bloxstrap
|
||||
ProtocolHandler.Register("roblox-player", "Roblox", bootstrapperLocation);
|
||||
}
|
||||
|
||||
using RegistryKey? studioBootstrapperKey = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\roblox-studio");
|
||||
if (studioBootstrapperKey is null)
|
||||
{
|
||||
robloxStudioStillInstalled = false;
|
||||
|
||||
ProtocolHandler.Unregister("roblox-studio");
|
||||
ProtocolHandler.Unregister("roblox-studio-auth");
|
||||
|
||||
ProtocolHandler.Unregister("Roblox.Place");
|
||||
ProtocolHandler.Unregister(".rbxl");
|
||||
ProtocolHandler.Unregister(".rbxlx");
|
||||
}
|
||||
else
|
||||
{
|
||||
string studioLocation = (string?)studioBootstrapperKey.GetValue("InstallLocation") + "RobloxStudioBeta.exe"; // points to studio exe instead of bootstrapper
|
||||
ProtocolHandler.Register("roblox-studio", "Roblox", studioLocation);
|
||||
ProtocolHandler.Register("roblox-studio-auth", "Roblox", studioLocation);
|
||||
|
||||
ProtocolHandler.RegisterRobloxPlace(studioLocation);
|
||||
}
|
||||
|
||||
// if the folder we're installed to does not end with "Bloxstrap", we're installed to a user-selected folder
|
||||
// in which case, chances are they chose to install to somewhere they didn't really mean to (prior to the added warning in 2.4.0)
|
||||
// if so, we're walking on eggshells and have to ensure we only clean up what we need to clean up
|
||||
@ -731,7 +798,7 @@ namespace Bloxstrap
|
||||
|
||||
string robloxFolder = Path.Combine(Paths.LocalAppData, "Roblox");
|
||||
|
||||
if (!robloxStillInstalled && Directory.Exists(robloxFolder))
|
||||
if (!robloxPlayerStillInstalled && !robloxStudioStillInstalled && Directory.Exists(robloxFolder))
|
||||
cleanupSequence.Add(() => Directory.Delete(robloxFolder, true));
|
||||
|
||||
foreach (var process in cleanupSequence)
|
||||
@ -785,7 +852,7 @@ namespace Bloxstrap
|
||||
|
||||
_isInstalling = true;
|
||||
|
||||
SetStatus(FreshInstall ? "Installing Roblox..." : "Upgrading Roblox...");
|
||||
SetStatus(FreshInstall ? "Installing {product}..." : "Upgrading {product}...");
|
||||
|
||||
Directory.CreateDirectory(Paths.Base);
|
||||
Directory.CreateDirectory(Paths.Downloads);
|
||||
@ -832,7 +899,7 @@ namespace Bloxstrap
|
||||
|
||||
// extract the package immediately after download asynchronously
|
||||
// discard is just used to suppress the warning
|
||||
_ = ExtractPackage(package).ContinueWith(AsyncHelpers.ExceptionHandler, $"extracting {package.Name}");
|
||||
_ = Task.Run(() => ExtractPackage(package).ContinueWith(AsyncHelpers.ExceptionHandler, $"extracting {package.Name}"));
|
||||
}
|
||||
|
||||
if (_cancelFired)
|
||||
@ -844,7 +911,7 @@ namespace Bloxstrap
|
||||
if (Dialog is not null)
|
||||
{
|
||||
Dialog.ProgressStyle = ProgressBarStyle.Marquee;
|
||||
SetStatus("Configuring Roblox...");
|
||||
SetStatus("Configuring {product}...");
|
||||
}
|
||||
|
||||
// wait for all packages to finish extracting, with an exception for the webview2 runtime installer
|
||||
@ -881,12 +948,12 @@ namespace Bloxstrap
|
||||
}
|
||||
}
|
||||
|
||||
string oldVersionFolder = Path.Combine(Paths.Versions, App.State.Prop.VersionGuid);
|
||||
string oldVersionFolder = Path.Combine(Paths.Versions, _versionGuid);
|
||||
|
||||
// move old compatibility flags for the old location
|
||||
using (RegistryKey appFlagsKey = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"))
|
||||
{
|
||||
string oldGameClientLocation = Path.Combine(oldVersionFolder, "RobloxPlayerBeta.exe");
|
||||
string oldGameClientLocation = Path.Combine(oldVersionFolder, _playerFileName);
|
||||
string? appFlags = (string?)appFlagsKey.GetValue(oldGameClientLocation);
|
||||
|
||||
if (appFlags is not null)
|
||||
@ -896,34 +963,34 @@ namespace Bloxstrap
|
||||
appFlagsKey.DeleteValue(oldGameClientLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete any old version folders
|
||||
// we only do this if roblox isnt running just in case an update happened
|
||||
// while they were launching a second instance or something idk
|
||||
if (!Process.GetProcessesByName(App.RobloxAppName).Any())
|
||||
_versionGuid = _latestVersionGuid;
|
||||
|
||||
// delete any old version folders
|
||||
// we only do this if roblox isnt running just in case an update happened
|
||||
// while they were launching a second instance or something idk
|
||||
if (!Process.GetProcessesByName(App.RobloxPlayerAppName).Any() && !Process.GetProcessesByName(App.RobloxStudioAppName).Any())
|
||||
{
|
||||
foreach (DirectoryInfo dir in new DirectoryInfo(Paths.Versions).GetDirectories())
|
||||
{
|
||||
foreach (DirectoryInfo dir in new DirectoryInfo(Paths.Versions).GetDirectories())
|
||||
if (dir.Name == App.State.Prop.PlayerVersionGuid || dir.Name == App.State.Prop.StudioVersionGuid || !dir.Name.StartsWith("version-"))
|
||||
continue;
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Removing old version folder for {dir.Name}");
|
||||
|
||||
try
|
||||
{
|
||||
if (dir.Name == _latestVersionGuid || !dir.Name.StartsWith("version-"))
|
||||
continue;
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Removing old version folder for {dir.Name}");
|
||||
|
||||
try
|
||||
{
|
||||
dir.Delete(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to delete version folder!");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
}
|
||||
dir.Delete(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Failed to delete version folder!");
|
||||
App.Logger.WriteException(LOG_IDENT, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App.State.Prop.VersionGuid = _latestVersionGuid;
|
||||
|
||||
// don't register program size until the program is registered, which will be done after this
|
||||
if (!App.IsFirstRun && !FreshInstall)
|
||||
RegisterProgramSize();
|
||||
@ -1010,7 +1077,7 @@ namespace Bloxstrap
|
||||
{
|
||||
const string LOG_IDENT = "Bootstrapper::ApplyModifications";
|
||||
|
||||
if (Process.GetProcessesByName("RobloxPlayerBeta").Any())
|
||||
if (Process.GetProcessesByName(_playerFileName[..^4]).Any())
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Roblox is running, aborting mod check");
|
||||
return;
|
||||
@ -1217,7 +1284,7 @@ namespace Bloxstrap
|
||||
if (modFolderFiles.Contains(fileLocation))
|
||||
continue;
|
||||
|
||||
var package = PackageDirectories.SingleOrDefault(x => x.Value != "" && fileLocation.StartsWith(x.Value));
|
||||
var package = _packageDirectories.SingleOrDefault(x => x.Value != "" && fileLocation.StartsWith(x.Value));
|
||||
|
||||
// package doesn't exist, likely mistakenly placed file
|
||||
if (String.IsNullOrEmpty(package.Key))
|
||||
@ -1417,75 +1484,26 @@ namespace Bloxstrap
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExtractPackage(Package package)
|
||||
private Task ExtractPackage(Package package)
|
||||
{
|
||||
const string LOG_IDENT = "Bootstrapper::ExtractPackage";
|
||||
|
||||
if (_cancelFired)
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
|
||||
string packageLocation = Path.Combine(Paths.Downloads, package.Signature);
|
||||
string packageFolder = Path.Combine(_versionFolder, PackageDirectories[package.Name]);
|
||||
string packageFolder = Path.Combine(_versionFolder, _packageDirectories[package.Name]);
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Reading {package.Name}...");
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Extracting {package.Name}...");
|
||||
|
||||
var archive = await Task.Run(() => ZipFile.OpenRead(packageLocation));
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Read {package.Name}. Extracting to {packageFolder}...");
|
||||
|
||||
// yeah so because roblox is roblox, these packages aren't actually valid zip files
|
||||
// besides the fact that they use backslashes instead of forward slashes for directories,
|
||||
// empty folders that *BEGIN* with a backslash in their fullname, but have an empty name are listed here for some reason...
|
||||
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
if (_cancelFired)
|
||||
return;
|
||||
|
||||
if (String.IsNullOrEmpty(entry.Name))
|
||||
continue;
|
||||
|
||||
string extractPath = Path.Combine(packageFolder, entry.FullName);
|
||||
string? directory = Path.GetDirectoryName(extractPath);
|
||||
|
||||
if (directory is not null)
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(PackageDirectories[package.Name], entry.FullName));
|
||||
string? signature = fileManifest?.Signature;
|
||||
|
||||
if (File.Exists(extractPath))
|
||||
{
|
||||
if (signature is not null && MD5Hash.FromFile(extractPath) == signature)
|
||||
continue;
|
||||
|
||||
File.Delete(extractPath);
|
||||
}
|
||||
|
||||
bool retry = false;
|
||||
|
||||
do
|
||||
{
|
||||
using var entryStream = entry.Open();
|
||||
using var fileStream = new FileStream(extractPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize: 0x1000);
|
||||
await entryStream.CopyToAsync(fileStream);
|
||||
|
||||
if (signature is not null && MD5Hash.FromStream(fileStream) != signature)
|
||||
{
|
||||
if (retry)
|
||||
throw new AssertionException($"Checksum of {entry.FullName} post-extraction did not match manifest");
|
||||
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
while (retry);
|
||||
|
||||
File.SetLastWriteTime(extractPath, entry.LastWriteTime.DateTime);
|
||||
}
|
||||
var fastZip = new ICSharpCode.SharpZipLib.Zip.FastZip();
|
||||
fastZip.ExtractZip(packageLocation, packageFolder, null);
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Finished extracting {package.Name}");
|
||||
|
||||
_packagesExtracted += 1;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task ExtractFileFromPackage(string packageName, string fileName)
|
||||
@ -1504,7 +1522,7 @@ namespace Bloxstrap
|
||||
if (entry is null)
|
||||
return;
|
||||
|
||||
string extractionPath = Path.Combine(_versionFolder, PackageDirectories[package.Name], entry.FullName);
|
||||
string extractionPath = Path.Combine(_versionFolder, _packageDirectories[package.Name], entry.FullName);
|
||||
entry.ExtractToFile(extractionPath, true);
|
||||
}
|
||||
#endregion
|
||||
|
@ -3,7 +3,15 @@
|
||||
public class State
|
||||
{
|
||||
public string LastEnrolledChannel { get; set; } = "";
|
||||
public string VersionGuid { get; set; } = "";
|
||||
|
||||
[Obsolete("Use PlayerVersionGuid instead", true)]
|
||||
public string VersionGuid { set { PlayerVersionGuid = value; } }
|
||||
public string PlayerVersionGuid { get; set; } = "";
|
||||
public string StudioVersionGuid { get; set; } = "";
|
||||
|
||||
public int PlayerSize { get; set; } = 0;
|
||||
public int StudioSize { get; set; } = 0;
|
||||
|
||||
public List<string> ModManifest { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
88
Bloxstrap/PackageMap.cs
Normal file
88
Bloxstrap/PackageMap.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bloxstrap
|
||||
{
|
||||
internal class PackageMap
|
||||
{
|
||||
public static IReadOnlyDictionary<string, string> Player
|
||||
{
|
||||
get { return CombineDictionaries(_common, _playerOnly); }
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<string, string> Studio
|
||||
{
|
||||
get { return CombineDictionaries(_common, _studioOnly); }
|
||||
}
|
||||
|
||||
// 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 IReadOnlyDictionary<string, string> _common = new Dictionary<string, string>()
|
||||
{
|
||||
{ "Libraries.zip", @"" },
|
||||
{ "shaders.zip", @"shaders\" },
|
||||
{ "ssl.zip", @"ssl\" },
|
||||
|
||||
// the runtime installer is only extracted if it needs installing
|
||||
{ "WebView2.zip", @"" },
|
||||
{ "WebView2RuntimeInstaller.zip", @"WebView2RuntimeInstaller\" },
|
||||
|
||||
{ "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 IReadOnlyDictionary<string, string> _playerOnly = new Dictionary<string, string>()
|
||||
{
|
||||
{ "RobloxApp.zip", @"" }
|
||||
};
|
||||
|
||||
private static IReadOnlyDictionary<string, string> _studioOnly = new Dictionary<string, string>()
|
||||
{
|
||||
{ "RobloxStudio.zip", @"" },
|
||||
{ "ApplicationConfig.zip", @"ApplicationConfig\" },
|
||||
{ "content-studio_svg_textures.zip", @"content\studio_svg_textures\"},
|
||||
{ "content-qt_translations.zip", @"content\qt_translations\" },
|
||||
{ "content-api-docs.zip", @"content\api_docs\" },
|
||||
{ "extracontent-scripts.zip", @"ExtraContent\scripts\" },
|
||||
{ "BuiltInPlugins.zip", @"BuiltInPlugins\" },
|
||||
{ "BuiltInStandalonePlugins.zip", @"BuiltInStandalonePlugins\" },
|
||||
{ "LibrariesQt5.zip", @"" },
|
||||
{ "Plugins.zip", @"Plugins\" },
|
||||
{ "Qml.zip", @"Qml\" },
|
||||
{ "StudioFonts.zip", @"StudioFonts\" },
|
||||
{ "redist.zip", @"" },
|
||||
};
|
||||
|
||||
private static Dictionary<string, string> CombineDictionaries(IReadOnlyDictionary<string, string> d1, IReadOnlyDictionary<string, string> d2)
|
||||
{
|
||||
Dictionary<string, string> newD = new Dictionary<string, string>();
|
||||
|
||||
foreach (var d in d1)
|
||||
newD[d.Key] = d.Value;
|
||||
|
||||
foreach (var d in d2)
|
||||
newD[d.Key] = d.Value;
|
||||
|
||||
return newD;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,10 @@
|
||||
"Bloxstrap (Deeplink)": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "roblox://experiences/start?placeId=95206881"
|
||||
},
|
||||
"Bloxstrap (Studio Launch)": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "-ide"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@ namespace Bloxstrap
|
||||
{
|
||||
static class ProtocolHandler
|
||||
{
|
||||
private const string RobloxPlaceKey = "Roblox.Place";
|
||||
|
||||
// map uri keys to command line args
|
||||
private static readonly IReadOnlyDictionary<string, string> UriKeyArgMap = new Dictionary<string, string>()
|
||||
{
|
||||
@ -18,7 +20,12 @@ namespace Bloxstrap
|
||||
{ "browsertrackerid", "-b " },
|
||||
{ "robloxLocale", "--rloc " },
|
||||
{ "gameLocale", "--gloc " },
|
||||
{ "channel", "-channel " }
|
||||
{ "channel", "-channel " },
|
||||
// studio
|
||||
{ "task", "-task " },
|
||||
{ "placeId", "-placeId " },
|
||||
{ "universeId", "-universeId " },
|
||||
{ "userId", "-userId " }
|
||||
};
|
||||
|
||||
public static string ParseUri(string protocol)
|
||||
@ -108,9 +115,10 @@ namespace Bloxstrap
|
||||
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");
|
||||
|
||||
using RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}");
|
||||
using RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon");
|
||||
using RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command");
|
||||
|
||||
if (uriKey.GetValue("") is null)
|
||||
{
|
||||
@ -118,15 +126,44 @@ namespace Bloxstrap
|
||||
uriKey.SetValue("URL Protocol", "");
|
||||
}
|
||||
|
||||
if ((string?)uriCommandKey.GetValue("") != handlerArgs)
|
||||
if (uriCommandKey.GetValue("") as string != handlerArgs)
|
||||
{
|
||||
uriIconKey.SetValue("", handler);
|
||||
uriCommandKey.SetValue("", handlerArgs);
|
||||
}
|
||||
}
|
||||
|
||||
uriKey.Close();
|
||||
uriIconKey.Close();
|
||||
uriCommandKey.Close();
|
||||
public static void RegisterRobloxPlace(string handler)
|
||||
{
|
||||
const string keyValue = "Roblox Place";
|
||||
string handlerArgs = $"\"{handler}\" -ide \"%1\"";
|
||||
string iconValue = $"{handler},0";
|
||||
|
||||
using RegistryKey uriKey = Registry.CurrentUser.CreateSubKey(@"Software\Classes\" + RobloxPlaceKey);
|
||||
using RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon");
|
||||
using RegistryKey uriOpenKey = uriKey.CreateSubKey(@"shell\Open");
|
||||
using RegistryKey uriCommandKey = uriOpenKey.CreateSubKey(@"command");
|
||||
|
||||
if (uriKey.GetValue("") as string != keyValue)
|
||||
uriKey.SetValue("", keyValue);
|
||||
|
||||
if (uriCommandKey.GetValue("") as string != handlerArgs)
|
||||
uriCommandKey.SetValue("", handlerArgs);
|
||||
|
||||
if (uriOpenKey.GetValue("") as string != "Open")
|
||||
uriOpenKey.SetValue("", "Open");
|
||||
|
||||
if (uriIconKey.GetValue("") as string != iconValue)
|
||||
uriIconKey.SetValue("", iconValue);
|
||||
}
|
||||
|
||||
public static void RegisterExtension(string key)
|
||||
{
|
||||
using RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}");
|
||||
uriKey.CreateSubKey(RobloxPlaceKey + @"\ShellNew");
|
||||
|
||||
if (uriKey.GetValue("") as string != RobloxPlaceKey)
|
||||
uriKey.SetValue("", RobloxPlaceKey);
|
||||
}
|
||||
|
||||
public static void Unregister(string key)
|
||||
|
@ -69,22 +69,23 @@
|
||||
return location;
|
||||
}
|
||||
|
||||
public static async Task<ClientVersion> GetInfo(string channel, bool extraInformation = false)
|
||||
public static async Task<ClientVersion> GetInfo(string channel, bool extraInformation = false, string binaryType = "WindowsPlayer")
|
||||
{
|
||||
const string LOG_IDENT = "RobloxDeployment::GetInfo";
|
||||
|
||||
App.Logger.WriteLine(LOG_IDENT, $"Getting deploy info for channel {channel} (extraInformation={extraInformation})");
|
||||
|
||||
string cacheKey = $"{channel}-{binaryType}";
|
||||
ClientVersion clientVersion;
|
||||
|
||||
if (ClientVersionCache.ContainsKey(channel))
|
||||
if (ClientVersionCache.ContainsKey(cacheKey))
|
||||
{
|
||||
App.Logger.WriteLine(LOG_IDENT, "Deploy information is cached");
|
||||
clientVersion = ClientVersionCache[channel];
|
||||
clientVersion = ClientVersionCache[cacheKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
string path = $"/v2/client-version/WindowsPlayer/channel/{channel}";
|
||||
string path = $"/v2/client-version/{binaryType}/channel/{channel}";
|
||||
HttpResponseMessage deployInfoResponse;
|
||||
|
||||
try
|
||||
@ -147,7 +148,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
ClientVersionCache[channel] = clientVersion;
|
||||
ClientVersionCache[cacheKey] = clientVersion;
|
||||
|
||||
return clientVersion;
|
||||
}
|
||||
|
@ -79,7 +79,8 @@ namespace Bloxstrap.UI.Elements.Bootstrapper
|
||||
|
||||
public ByfronDialog()
|
||||
{
|
||||
_viewModel = new ByfronDialogViewModel(this);
|
||||
string version = Utilities.GetRobloxVersion(Bootstrapper?.IsStudioLaunch ?? false);
|
||||
_viewModel = new ByfronDialogViewModel(this, version);
|
||||
DataContext = _viewModel;
|
||||
Title = App.Settings.Prop.BootstrapperTitle;
|
||||
Icon = App.Settings.Prop.BootstrapperIcon.GetIcon().GetImageSource();
|
||||
|
@ -221,9 +221,15 @@
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="MIT License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/LICENSE">
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="1" Margin="0,8,8,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/MaximumADHD/Roblox-Studio-Mod-Manager/blob/main/LICENSE">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="14" Text="Roblox Studio Mod Manager by MaximumADHD" />
|
||||
<TextBlock FontSize="13" Text="RSMM by MaximumADHD" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="MIT License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
<ui:CardAction Grid.Row="1" Grid.Column="2" Margin="0,8,0,0" Command="models:GlobalViewModel.OpenWebpageCommand" CommandParameter="https://github.com/icsharpcode/SharpZipLib/blob/master/LICENSE.txt">
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="13" Text="SharpZipLib by icsharpcode" />
|
||||
<TextBlock Margin="0,2,0,0" FontSize="12" Text="MIT License" Foreground="{DynamicResource TextFillColorTertiaryBrush}" />
|
||||
</StackPanel>
|
||||
</ui:CardAction>
|
||||
|
@ -16,26 +16,11 @@ namespace Bloxstrap.UI.ViewModels.Bootstrapper
|
||||
|
||||
public Visibility VersionTextVisibility => CancelEnabled ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
public string VersionText
|
||||
{
|
||||
get
|
||||
{
|
||||
string playerLocation = Path.Combine(Paths.Versions, App.State.Prop.VersionGuid, "RobloxPlayerBeta.exe");
|
||||
|
||||
if (!File.Exists(playerLocation))
|
||||
return "";
|
||||
|
||||
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(playerLocation);
|
||||
|
||||
if (versionInfo.ProductVersion is null)
|
||||
return "";
|
||||
|
||||
return versionInfo.ProductVersion.Replace(", ", ".");
|
||||
}
|
||||
}
|
||||
|
||||
public ByfronDialogViewModel(IBootstrapperDialog dialog) : base(dialog)
|
||||
public string VersionText { get; init; }
|
||||
|
||||
public ByfronDialogViewModel(IBootstrapperDialog dialog, string version) : base(dialog)
|
||||
{
|
||||
VersionText = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
{
|
||||
public class BehaviourViewModel : NotifyPropertyChangedViewModel
|
||||
{
|
||||
private string _oldVersionGuid = "";
|
||||
private string _oldPlayerVersionGuid = "";
|
||||
private string _oldStudioVersionGuid = "";
|
||||
|
||||
public BehaviourViewModel()
|
||||
{
|
||||
@ -108,17 +109,22 @@
|
||||
|
||||
public bool ForceRobloxReinstallation
|
||||
{
|
||||
get => String.IsNullOrEmpty(App.State.Prop.VersionGuid);
|
||||
// wouldnt it be better to check old version guids?
|
||||
// what about fresh installs?
|
||||
get => String.IsNullOrEmpty(App.State.Prop.PlayerVersionGuid) && String.IsNullOrEmpty(App.State.Prop.StudioVersionGuid);
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_oldVersionGuid = App.State.Prop.VersionGuid;
|
||||
App.State.Prop.VersionGuid = "";
|
||||
_oldPlayerVersionGuid = App.State.Prop.PlayerVersionGuid;
|
||||
_oldStudioVersionGuid = App.State.Prop.StudioVersionGuid;
|
||||
App.State.Prop.PlayerVersionGuid = "";
|
||||
App.State.Prop.StudioVersionGuid = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
App.State.Prop.VersionGuid = _oldVersionGuid;
|
||||
App.State.Prop.PlayerVersionGuid = _oldPlayerVersionGuid;
|
||||
App.State.Prop.StudioVersionGuid = _oldStudioVersionGuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,5 +47,23 @@ namespace Bloxstrap
|
||||
|
||||
return version1.CompareTo(version2);
|
||||
}
|
||||
|
||||
public static string GetRobloxVersion(bool studio)
|
||||
{
|
||||
string versionGuid = studio ? App.State.Prop.StudioVersionGuid : App.State.Prop.PlayerVersionGuid;
|
||||
string fileName = studio ? "RobloxStudioBeta.exe" : "RobloxPlayerBeta.exe";
|
||||
|
||||
string playerLocation = Path.Combine(Paths.Versions, versionGuid, fileName);
|
||||
|
||||
if (!File.Exists(playerLocation))
|
||||
return "";
|
||||
|
||||
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(playerLocation);
|
||||
|
||||
if (versionInfo.ProductVersion is null)
|
||||
return "";
|
||||
|
||||
return versionInfo.ProductVersion.Replace(", ", ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user