add studio support

This commit is contained in:
bluepilledgreat 2023-10-04 12:05:53 +01:00
parent bb5b46adf5
commit 1fcd05096a
5 changed files with 140 additions and 73 deletions

View File

@ -30,7 +30,6 @@ namespace Bloxstrap
public static bool IsNoLaunch { get; private set; } = false; public static bool IsNoLaunch { get; private set; } = false;
public static bool IsUpgrade { get; private set; } = false; public static bool IsUpgrade { get; private set; } = false;
public static bool IsMenuLaunch { get; private set; } = false; public static bool IsMenuLaunch { get; private set; } = false;
public static bool IsStudioLaunch { get; private set; } = false;
public static string[] LaunchArgs { get; private set; } = null!; public static string[] LaunchArgs { get; private set; } = null!;
public static BuildMetadataAttribute BuildMetadata = Assembly.GetExecutingAssembly().GetCustomAttribute<BuildMetadataAttribute>()!; public static BuildMetadataAttribute BuildMetadata = Assembly.GetExecutingAssembly().GetCustomAttribute<BuildMetadataAttribute>()!;
@ -155,12 +154,6 @@ namespace Bloxstrap
Logger.WriteLine(LOG_IDENT, "Bloxstrap started with IsUpgrade flag"); Logger.WriteLine(LOG_IDENT, "Bloxstrap started with IsUpgrade flag");
IsUpgrade = true; IsUpgrade = true;
} }
if (Array.IndexOf(LaunchArgs, "-studio") != -1)
{
Logger.WriteLine(LOG_IDENT, "Bloxstrap started with IsStudioLaunch flag");
IsStudioLaunch = true;
}
} }
using (var checker = new InstallChecker()) using (var checker = new InstallChecker())
@ -196,6 +189,7 @@ namespace Bloxstrap
#endif #endif
string commandLine = ""; string commandLine = "";
bool isStudioLaunch = false;
if (IsMenuLaunch) if (IsMenuLaunch)
{ {
@ -234,6 +228,10 @@ namespace Bloxstrap
commandLine = $"--app --deeplink {LaunchArgs[0]}"; commandLine = $"--app --deeplink {LaunchArgs[0]}";
} }
else if (LaunchArgs[0].StartsWith("roblox-studio:") || LaunchArgs[0].StartsWith("roblox-studio-auth:"))
{
commandLine = LaunchArgs[0];
}
else else
{ {
commandLine = "--app"; commandLine = "--app";
@ -251,7 +249,7 @@ namespace Bloxstrap
// start bootstrapper and show the bootstrapper modal if we're not running silently // start bootstrapper and show the bootstrapper modal if we're not running silently
Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper"); Logger.WriteLine(LOG_IDENT, "Initializing bootstrapper");
Bootstrapper bootstrapper = new(commandLine); Bootstrapper bootstrapper = new(commandLine, isStudioLaunch);
IBootstrapperDialog? dialog = null; IBootstrapperDialog? dialog = null;
if (!IsQuiet) if (!IsQuiet)

View File

@ -10,38 +10,6 @@ namespace Bloxstrap
public class Bootstrapper public class Bootstrapper
{ {
#region Properties #region Properties
// 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 = private const string AppSettings =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<Settings>\r\n" + "<Settings>\r\n" +
@ -51,11 +19,30 @@ namespace Bloxstrap
private readonly CancellationTokenSource _cancelTokenSource = new(); private readonly CancellationTokenSource _cancelTokenSource = new();
private static bool FreshInstall => String.IsNullOrEmpty(App.State.Prop.PlayerVersionGuid); 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 string _launchCommandLine;
private bool _studioLaunch;
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 string _latestVersionGuid = null!; private string _latestVersionGuid = null!;
private PackageManifest _versionPackageManifest = null!; private PackageManifest _versionPackageManifest = null!;
@ -68,13 +55,16 @@ namespace Bloxstrap
private int _packagesExtracted = 0; private int _packagesExtracted = 0;
private bool _cancelFired = false; private bool _cancelFired = false;
private IReadOnlyDictionary<string, string> _packageDirectories => _studioLaunch ? PackageMap.Studio : PackageMap.Player;
public IBootstrapperDialog? Dialog = null; public IBootstrapperDialog? Dialog = null;
#endregion #endregion
#region Core #region Core
public Bootstrapper(string launchCommandLine) public Bootstrapper(string launchCommandLine, bool studioLaunch)
{ {
_launchCommandLine = launchCommandLine; _launchCommandLine = launchCommandLine;
_studioLaunch = studioLaunch;
} }
private void SetStatus(string message) private void SetStatus(string message)
@ -183,7 +173,7 @@ namespace Bloxstrap
await CheckLatestVersion(); await CheckLatestVersion();
// install/update roblox if we're running for the first time, needs updating, or the player location doesn't exist // 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.PlayerVersionGuid || !File.Exists(_playerLocation)) if (App.IsFirstRun || _latestVersionGuid != _versionGuid || !File.Exists(_playerLocation))
await InstallLatestVersion(); await InstallLatestVersion();
if (App.IsFirstRun) if (App.IsFirstRun)
@ -199,7 +189,7 @@ namespace Bloxstrap
if (App.IsFirstRun || FreshInstall) if (App.IsFirstRun || FreshInstall)
{ {
Register(); Register();
RegisterProgramSize(); RegisterProgramSize(); // STUDIO TODO
} }
CheckInstall(); CheckInstall();
@ -223,18 +213,20 @@ namespace Bloxstrap
ClientVersion clientVersion; ClientVersion clientVersion;
string binaryType = _studioLaunch ? "WindowsStudio64" : "WindowsPlayer";
try try
{ {
clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel); clientVersion = await RobloxDeployment.GetInfo(App.Settings.Prop.Channel, binaryType: binaryType);
} }
catch (HttpResponseException ex) catch (HttpResponseException ex)
{ {
if (ex.ResponseMessage.StatusCode != HttpStatusCode.NotFound) if (ex.ResponseMessage.StatusCode != HttpStatusCode.NotFound)
throw; 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; 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) if (clientVersion.IsBehindDefaultChannel)
@ -257,7 +249,7 @@ namespace Bloxstrap
App.Logger.WriteLine("Bootstrapper::CheckLatestVersion", $"Changed Roblox channel from {App.Settings.Prop.Channel} to {RobloxDeployment.DefaultChannel}"); App.Logger.WriteLine("Bootstrapper::CheckLatestVersion", $"Changed Roblox channel from {App.Settings.Prop.Channel} to {RobloxDeployment.DefaultChannel}");
App.Settings.Prop.Channel = 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);
} }
} }
@ -502,8 +494,8 @@ namespace Bloxstrap
ProtocolHandler.Register("roblox", "Roblox", Paths.Application); ProtocolHandler.Register("roblox", "Roblox", Paths.Application);
ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application); ProtocolHandler.Register("roblox-player", "Roblox", Paths.Application);
ProtocolHandler.Register("roblox-studio", "Roblox", Paths.Application, "-studio"); ProtocolHandler.Register("roblox-studio", "Roblox", Paths.Application);
ProtocolHandler.Register("roblox-studio-auth", "Roblox", Paths.Application, "-studio"); ProtocolHandler.Register("roblox-studio-auth", "Roblox", Paths.Application);
if (Environment.ProcessPath is not null && Environment.ProcessPath != Paths.Application) if (Environment.ProcessPath is not null && Environment.ProcessPath != Paths.Application)
{ {
@ -798,7 +790,8 @@ namespace Bloxstrap
_isInstalling = true; _isInstalling = true;
SetStatus(FreshInstall ? "Installing Roblox..." : "Upgrading Roblox..."); string extra = _studioLaunch ? " Studio" : "";
SetStatus(FreshInstall ? $"Installing Roblox{extra}..." : $"Upgrading Roblox{extra}...");
Directory.CreateDirectory(Paths.Base); Directory.CreateDirectory(Paths.Base);
Directory.CreateDirectory(Paths.Downloads); Directory.CreateDirectory(Paths.Downloads);
@ -892,12 +885,12 @@ namespace Bloxstrap
} }
} }
string oldVersionFolder = Path.Combine(Paths.Versions, App.State.Prop.PlayerVersionGuid); string oldVersionFolder = Path.Combine(Paths.Versions, _versionGuid);
// move old compatibility flags for the old location // move old compatibility flags for the old location
using (RegistryKey appFlagsKey = Registry.CurrentUser.CreateSubKey($"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers")) 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); string? appFlags = (string?)appFlagsKey.GetValue(oldGameClientLocation);
if (appFlags is not null) if (appFlags is not null)
@ -915,7 +908,7 @@ namespace Bloxstrap
{ {
foreach (DirectoryInfo dir in new DirectoryInfo(Paths.Versions).GetDirectories()) foreach (DirectoryInfo dir in new DirectoryInfo(Paths.Versions).GetDirectories())
{ {
if (dir.Name == _latestVersionGuid || !dir.Name.StartsWith("version-")) if (dir.Name == App.State.Prop.PlayerVersionGuid || dir.Name == App.State.Prop.StudioVersionGuid || !dir.Name.StartsWith("version-"))
continue; continue;
App.Logger.WriteLine(LOG_IDENT, $"Removing old version folder for {dir.Name}"); App.Logger.WriteLine(LOG_IDENT, $"Removing old version folder for {dir.Name}");
@ -933,7 +926,7 @@ namespace Bloxstrap
} }
} }
App.State.Prop.PlayerVersionGuid = _latestVersionGuid; _versionGuid = _latestVersionGuid;
// don't register program size until the program is registered, which will be done after this // don't register program size until the program is registered, which will be done after this
if (!App.IsFirstRun && !FreshInstall) if (!App.IsFirstRun && !FreshInstall)
@ -1228,7 +1221,7 @@ namespace Bloxstrap
if (modFolderFiles.Contains(fileLocation)) if (modFolderFiles.Contains(fileLocation))
continue; 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 // package doesn't exist, likely mistakenly placed file
if (String.IsNullOrEmpty(package.Key)) if (String.IsNullOrEmpty(package.Key))
@ -1436,7 +1429,7 @@ namespace Bloxstrap
return; return;
string packageLocation = Path.Combine(Paths.Downloads, package.Signature); 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, $"Reading {package.Name}...");
@ -1462,7 +1455,7 @@ namespace Bloxstrap
if (directory is not null) if (directory is not null)
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);
var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(PackageDirectories[package.Name], entry.FullName)); var fileManifest = _versionFileManifest.FirstOrDefault(x => x.Name == Path.Combine(_packageDirectories[package.Name], entry.FullName));
string? signature = fileManifest?.Signature; string? signature = fileManifest?.Signature;
if (File.Exists(extractPath)) if (File.Exists(extractPath))
@ -1515,7 +1508,7 @@ namespace Bloxstrap
if (entry is null) if (entry is null)
return; 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); entry.ExtractToFile(extractionPath, true);
} }
#endregion #endregion

85
Bloxstrap/PackageMap.cs Normal file
View File

@ -0,0 +1,85 @@
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>()
{
{ "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\" },
{ "BuiltInPlugins.zip", @"BuiltInPlugins\" },
{ "BuiltInStandalonePlugins.zip", @"BuiltInStandalonePlugins\" },
{ "LibrariesQt5.zip", @"" },
{ "Plugins.zip", @"Plugins\" },
{ "Qml.zip", @"Qml\" },
{ "StudioFonts.zip", @"StudioFonts\" },
};
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;
}
}
}

View File

@ -105,18 +105,9 @@ namespace Bloxstrap
App.State.Save(); App.State.Save();
} }
private static string ConstructHandlerArgs(string handler, string? extraArgs = null) public static void Register(string key, string name, string handler)
{ {
string handlerArgs = $"\"{handler}\""; string handlerArgs = $"\"{handler}\" %1";
if (!string.IsNullOrEmpty(extraArgs))
handlerArgs += $" {extraArgs}";
handlerArgs += " %1";
return handlerArgs;
}
public static void Register(string key, string name, string handler, string? extraArgs = null)
{
string handlerArgs = ConstructHandlerArgs(handler, extraArgs);
RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}"); RegistryKey uriKey = Registry.CurrentUser.CreateSubKey($@"Software\Classes\{key}");
RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon"); RegistryKey uriIconKey = uriKey.CreateSubKey("DefaultIcon");
RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command"); RegistryKey uriCommandKey = uriKey.CreateSubKey(@"shell\open\command");

View File

@ -69,7 +69,7 @@
return location; 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"; const string LOG_IDENT = "RobloxDeployment::GetInfo";
@ -84,7 +84,7 @@
} }
else else
{ {
string path = $"/v2/client-version/WindowsPlayer/channel/{channel}"; string path = $"/v2/client-version/{binaryType}/channel/{channel}";
HttpResponseMessage deployInfoResponse; HttpResponseMessage deployInfoResponse;
try try