├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml ├── dependabot.yml └── workflows │ ├── ci-debug.yml │ ├── ci-release.yml │ └── winget.yml ├── .gitignore ├── .gitmodules ├── Bloxstrap.sln ├── Bloxstrap ├── App.xaml ├── App.xaml.cs ├── AppData │ ├── CommonAppData.cs │ ├── IAppData.cs │ ├── RobloxPlayerData.cs │ └── RobloxStudioData.cs ├── AssemblyInfo.cs ├── Bloxstrap.csproj ├── Bloxstrap.ico ├── Bootstrapper.cs ├── Enums │ ├── BootstrapperIcon.cs │ ├── BootstrapperStyle.cs │ ├── CursorType.cs │ ├── CustomThemeTemplate.cs │ ├── EmojiType.cs │ ├── ErrorCode.cs │ ├── FlagPresets │ │ ├── InGameMenuVersion.cs │ │ ├── LightingMode.cs │ │ ├── MSAAMode.cs │ │ ├── RenderingMode.cs │ │ └── TextureQuality.cs │ ├── GenericTriState.cs │ ├── LaunchMode.cs │ ├── NextAction.cs │ ├── ServerType.cs │ ├── Theme.cs │ ├── VersionComparison.cs │ └── WebEnvironment.cs ├── Exceptions │ ├── AssertionException.cs │ ├── ChecksumFailedException.cs │ ├── CustomThemeException.cs │ ├── InvalidChannelException.cs │ └── InvalidHTTPResponseException.cs ├── Extensions │ ├── BootstrapperIconEx.cs │ ├── BootstrapperStyleEx.cs │ ├── CustomThemeTemplateEx.cs │ ├── DateTimeEx.cs │ ├── EmojiTypeEx.cs │ ├── HttpClientEx.cs │ ├── IconEx.cs │ ├── RegistryKeyEx.cs │ ├── ResourceManagerEx.cs │ ├── ServerTypeEx.cs │ ├── TEnumEx.cs │ └── ThemeEx.cs ├── FastFlagManager.cs ├── GlobalCache.cs ├── GlobalUsings.cs ├── HttpClientLoggingHandler.cs ├── Installer.cs ├── Integrations │ ├── ActivityWatcher.cs │ └── DiscordRichPresence.cs ├── JsonManager.cs ├── LaunchHandler.cs ├── LaunchSettings.cs ├── Locale.cs ├── Logger.cs ├── Models │ ├── APIs │ │ ├── Config │ │ │ ├── Supporter.cs │ │ │ ├── SupporterData.cs │ │ │ └── SupporterGroup.cs │ │ ├── GitHub │ │ │ ├── GitHubReleaseAsset.cs │ │ │ └── GithubRelease.cs │ │ ├── IPInfoResponse.cs │ │ └── Roblox │ │ │ ├── ApiArrayResponse.cs │ │ │ ├── ClientFlagSettings.cs │ │ │ ├── ClientVersion.cs │ │ │ ├── GameCreator.cs │ │ │ ├── GameDetailResponse.cs │ │ │ ├── GetUserResponse.cs │ │ │ ├── ThumbnailBatchResponse.cs │ │ │ ├── ThumbnailRequest.cs │ │ │ ├── ThumbnailResponse.cs │ │ │ └── UniverseIdResponse.cs │ ├── Attributes │ │ ├── BuildMetadataAttribute.cs │ │ ├── EnumNameAttribute.cs │ │ └── EnumSortAttribute.cs │ ├── BloxstrapRPC │ │ ├── Message.cs │ │ ├── RichPresence.cs │ │ └── RichPresenceImage.cs │ ├── BootstrapperIconEntry.cs │ ├── CustomIntegration.cs │ ├── DeployInfo.cs │ ├── Entities │ │ ├── ActivityData.cs │ │ ├── ModPresetFileData.cs │ │ ├── UniverseDetails.cs │ │ └── UserDetails.cs │ ├── FastFlag.cs │ ├── FontFace.cs │ ├── FontFamily.cs │ ├── LaunchFlag.cs │ ├── Manifest │ │ ├── FileManifest.cs │ │ ├── ManifestFile.cs │ │ ├── Package.cs │ │ └── PackageManifest.cs │ ├── Persistable │ │ ├── AppState.cs │ │ ├── RobloxState.cs │ │ ├── Settings.cs │ │ ├── State.cs │ │ └── WindowState.cs │ ├── SettingTasks │ │ ├── Base │ │ │ ├── BaseTask.cs │ │ │ ├── BoolBaseTask.cs │ │ │ ├── EnumBaseTask.cs │ │ │ └── StringBaseTask.cs │ │ ├── EmojiModPresetTask.cs │ │ ├── EnumModPresetTask.cs │ │ ├── ExtractIconsTask.cs │ │ ├── FontModPresetTask.cs │ │ ├── ModPresetTask.cs │ │ └── ShortcutTask.cs │ ├── ThumbnailCacheEntry.cs │ └── WatcherData.cs ├── MultiInstanceWatcher.cs ├── NativeMethods.txt ├── Paths.cs ├── Properties │ ├── PublishProfiles │ │ └── Publish-x64.pubxml │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ ├── Settings.settings │ └── launchSettings.json ├── Resource.cs ├── Resources │ ├── BootstrapperStyles │ │ └── ByfronDialog │ │ │ ├── ByfronLogoDark.jpg │ │ │ ├── ByfronLogoLight.jpg │ │ │ └── Matt.png │ ├── CancelButton.png │ ├── CancelButtonHover.png │ ├── CustomBootstrapperSchema.json │ ├── CustomBootstrapperTemplate_Blank.xml │ ├── CustomBootstrapperTemplate_Simple.xml │ ├── DarkCancelButton.png │ ├── DarkCancelButtonHover.png │ ├── Fonts │ │ ├── NotoSansThai-VariableFont_wdth,wght.ttf │ │ └── Rubik-VariableFont_wght.ttf │ ├── Icon2008.ico │ ├── Icon2011.ico │ ├── Icon2017.ico │ ├── Icon2019.ico │ ├── Icon2022.ico │ ├── IconBloxstrap.ico │ ├── IconBloxstrapClassic.ico │ ├── IconEarly2015.ico │ ├── IconLate2015.ico │ ├── MessageBox │ │ ├── Error.png │ │ ├── FullQuality │ │ │ ├── Error.png │ │ │ ├── Information.png │ │ │ ├── Question.png │ │ │ └── Warning.png │ │ ├── Information.png │ │ ├── Question.png │ │ └── Warning.png │ ├── Mods │ │ ├── Cursor │ │ │ ├── From2006 │ │ │ │ ├── ArrowCursor.png │ │ │ │ └── ArrowFarCursor.png │ │ │ └── From2013 │ │ │ │ ├── ArrowCursor.png │ │ │ │ └── ArrowFarCursor.png │ │ ├── OldAvatarBackground.rbxl │ │ └── Sounds │ │ │ ├── Empty.mp3 │ │ │ ├── OldDeath.ogg │ │ │ ├── OldGetUp.mp3 │ │ │ ├── OldJump.mp3 │ │ │ └── OldWalk.mp3 │ ├── Strings.Designer.cs │ ├── Strings.ar.resx │ ├── Strings.bg.resx │ ├── Strings.bs.resx │ ├── Strings.cs.resx │ ├── Strings.da.resx │ ├── Strings.de.resx │ ├── Strings.en-US.resx │ ├── Strings.es-ES.resx │ ├── Strings.fa.resx │ ├── Strings.fi.resx │ ├── Strings.fil.resx │ ├── Strings.fr.resx │ ├── Strings.hr.resx │ ├── Strings.hu.resx │ ├── Strings.id.resx │ ├── Strings.it.resx │ ├── Strings.ja.resx │ ├── Strings.ko.resx │ ├── Strings.lt.resx │ ├── Strings.lv.resx │ ├── Strings.ms.resx │ ├── Strings.nl.resx │ ├── Strings.pl.resx │ ├── Strings.pt-BR.resx │ ├── Strings.resx │ ├── Strings.ro.resx │ ├── Strings.ru.resx │ ├── Strings.sv-SE.resx │ ├── Strings.th.resx │ ├── Strings.tr.resx │ ├── Strings.uk.resx │ ├── Strings.vi.resx │ ├── Strings.zh-CN.resx │ ├── Strings.zh-HK.resx │ └── Strings.zh-TW.resx ├── RobloxInterfaces │ ├── ApplicationSettings.cs │ └── Deployment.cs ├── UI │ ├── Converters │ │ ├── EnumNameConverter.cs │ │ ├── RangeConverter.cs │ │ └── StringFormatConverter.cs │ ├── Elements │ │ ├── About │ │ │ ├── MainWindow.xaml │ │ │ ├── MainWindow.xaml.cs │ │ │ └── Pages │ │ │ │ ├── AboutPage.xaml │ │ │ │ ├── AboutPage.xaml.cs │ │ │ │ ├── LicensesPage.xaml │ │ │ │ ├── LicensesPage.xaml.cs │ │ │ │ ├── SupportersPage.xaml │ │ │ │ ├── SupportersPage.xaml.cs │ │ │ │ ├── TranslatorsPage.xaml │ │ │ │ └── TranslatorsPage.xaml.cs │ │ ├── Base │ │ │ └── WpfUiWindow.cs │ │ ├── Bootstrapper │ │ │ ├── Base │ │ │ │ ├── BaseFunctions.cs │ │ │ │ └── WinFormsDialogBase.cs │ │ │ ├── ByfronDialog.xaml │ │ │ ├── ByfronDialog.xaml.cs │ │ │ ├── ClassicFluentDialog.xaml │ │ │ ├── ClassicFluentDialog.xaml.cs │ │ │ ├── CustomDialog.Converters.cs │ │ │ ├── CustomDialog.Creator.cs │ │ │ ├── CustomDialog.Elements.cs │ │ │ ├── CustomDialog.Utilities.cs │ │ │ ├── CustomDialog.xaml │ │ │ ├── CustomDialog.xaml.cs │ │ │ ├── FluentDialog.xaml │ │ │ ├── FluentDialog.xaml.cs │ │ │ ├── LegacyDialog2008.Designer.cs │ │ │ ├── LegacyDialog2008.cs │ │ │ ├── LegacyDialog2008.resx │ │ │ ├── LegacyDialog2011.Designer.cs │ │ │ ├── LegacyDialog2011.cs │ │ │ ├── LegacyDialog2011.resx │ │ │ ├── ProgressDialog.Designer.cs │ │ │ ├── ProgressDialog.cs │ │ │ ├── ProgressDialog.resx │ │ │ ├── VistaDialog.Designer.cs │ │ │ ├── VistaDialog.cs │ │ │ └── VistaDialog.resx │ │ ├── ContextMenu │ │ │ ├── MenuContainer.xaml │ │ │ ├── MenuContainer.xaml.cs │ │ │ ├── ServerHistory.xaml │ │ │ ├── ServerHistory.xaml.cs │ │ │ ├── ServerInformation.xaml │ │ │ └── ServerInformation.xaml.cs │ │ ├── Controls │ │ │ ├── Expander.xaml │ │ │ ├── Expander.xaml.cs │ │ │ ├── MarkdownTextBlock.cs │ │ │ ├── OptionControl.xaml │ │ │ └── OptionControl.xaml.cs │ │ ├── Dialogs │ │ │ ├── AddCustomThemeDialog.xaml │ │ │ ├── AddCustomThemeDialog.xaml.cs │ │ │ ├── AddFastFlagDialog.xaml │ │ │ ├── AddFastFlagDialog.xaml.cs │ │ │ ├── ConnectivityDialog.xaml │ │ │ ├── ConnectivityDialog.xaml.cs │ │ │ ├── ExceptionDialog.xaml │ │ │ ├── ExceptionDialog.xaml.cs │ │ │ ├── FluentMessageBox.xaml │ │ │ ├── FluentMessageBox.xaml.cs │ │ │ ├── LanguageSelectorDialog.xaml │ │ │ ├── LanguageSelectorDialog.xaml.cs │ │ │ ├── LaunchMenuDialog.xaml │ │ │ ├── LaunchMenuDialog.xaml.cs │ │ │ ├── UninstallerDialog.xaml │ │ │ └── UninstallerDialog.xaml.cs │ │ ├── Editor │ │ │ ├── BootstrapperEditorWindow.xaml │ │ │ └── BootstrapperEditorWindow.xaml.cs │ │ ├── Installer │ │ │ ├── MainWindow.xaml │ │ │ ├── MainWindow.xaml.cs │ │ │ └── Pages │ │ │ │ ├── CompletionPage.xaml │ │ │ │ ├── CompletionPage.xaml.cs │ │ │ │ ├── InstallPage.xaml │ │ │ │ ├── InstallPage.xaml.cs │ │ │ │ ├── WelcomePage.xaml │ │ │ │ └── WelcomePage.xaml.cs │ │ └── Settings │ │ │ ├── MainWindow.xaml │ │ │ ├── MainWindow.xaml.cs │ │ │ └── Pages │ │ │ ├── AppearancePage.xaml │ │ │ ├── AppearancePage.xaml.cs │ │ │ ├── BloxstrapPage.xaml │ │ │ ├── BloxstrapPage.xaml.cs │ │ │ ├── BootstrapperPage.xaml │ │ │ ├── BootstrapperPage.xaml.cs │ │ │ ├── FastFlagEditorPage.xaml │ │ │ ├── FastFlagEditorPage.xaml.cs │ │ │ ├── FastFlagEditorWarningPage.xaml │ │ │ ├── FastFlagEditorWarningPage.xaml.cs │ │ │ ├── FastFlagsPage.xaml │ │ │ ├── FastFlagsPage.xaml.cs │ │ │ ├── IntegrationsPage.xaml │ │ │ ├── IntegrationsPage.xaml.cs │ │ │ ├── ModsPage.xaml │ │ │ ├── ModsPage.xaml.cs │ │ │ ├── ShortcutsPage.xaml │ │ │ └── ShortcutsPage.xaml.cs │ ├── Frontend.cs │ ├── IBootstrapperDialog.cs │ ├── NotifyIconWrapper.cs │ ├── Style │ │ ├── Dark.xaml │ │ ├── Default.xaml │ │ ├── Editor-Theme-Dark.xshd │ │ ├── Editor-Theme-Light.xshd │ │ └── Light.xaml │ ├── Utility │ │ ├── Rendering.cs │ │ ├── TaskbarProgress.cs │ │ └── WindowScaling.cs │ └── ViewModels │ │ ├── About │ │ ├── AboutViewModel.cs │ │ └── SupportersViewModel.cs │ │ ├── Bootstrapper │ │ ├── BootstrapperDialogViewModel.cs │ │ ├── ByfronDialogViewModel.cs │ │ ├── ClassicFluentDialogViewModel.cs │ │ └── FluentDialogViewModel.cs │ │ ├── ContextMenu │ │ ├── ServerHistoryViewModel.cs │ │ └── ServerInformationViewModel.cs │ │ ├── Dialogs │ │ ├── AddCustomThemeViewModel.cs │ │ ├── LanguageSelectorViewModel.cs │ │ ├── LaunchMenuViewModel.cs │ │ └── UninstallerViewModel.cs │ │ ├── Editor │ │ └── BootstrapperEditorWindowViewModel.cs │ │ ├── GlobalViewModel.cs │ │ ├── Installer │ │ ├── CompletionViewModel.cs │ │ ├── InstallViewModel.cs │ │ ├── MainWindowViewModel.cs │ │ └── WelcomeViewModel.cs │ │ ├── NotifyPropertyChangedViewModel.cs │ │ └── Settings │ │ ├── AppearanceViewModel.cs │ │ ├── BehaviourViewModel.cs │ │ ├── BloxstrapViewModel.cs │ │ ├── FastFlagEditorWarningViewModel.cs │ │ ├── FastFlagsViewModel.cs │ │ ├── IntegrationsViewModel.cs │ │ ├── MainWindowViewModel.cs │ │ ├── ModsViewModel.cs │ │ └── ShortcutsViewModel.cs ├── Utilities.cs ├── Utility │ ├── AsyncMutex.cs │ ├── Filesystem.cs │ ├── FixedCapacityList.cs │ ├── Http.cs │ ├── InterProcessLock.cs │ ├── MD5Hash.cs │ ├── PathValidator.cs │ ├── Shortcut.cs │ ├── Thumbnails.cs │ └── WindowsRegistry.cs ├── Watcher.cs └── app.manifest ├── Images ├── Bloxstrap-full-dark.png ├── Bloxstrap-full-light.png └── Bloxstrap.png ├── LICENSE ├── README.md └── Scripts └── Translations ├── find-unused.py └── prep.py /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | title: "[REQ] " 3 | description: Suggest a feature that should be added 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | ### **Preliminary instructions** 9 | - Please first check to see if your idea has already been suggested. You can check by [searching all previous issues](https://github.com/pizzaboxer/bloxstrap/issues?q=is%3Aissue). 10 | - If your feature suggestion is to do with Roblox itself, please consider that what's possible is heavily constrained by what [FastFlags](https://github.com/pizzaboxer/bloxstrap/wiki/A-guide-to-FastFlags) are available. 11 | - Don't ask for support on Linux or Mac. That's not happening anytime soon, sorry. 12 | - type: checkboxes 13 | id: terms 14 | attributes: 15 | label: Acknowledgement of preliminary instructions 16 | options: 17 | - label: I have read and acknowledged the preliminary instructions. 18 | required: true 19 | - type: textarea 20 | id: what-happened 21 | attributes: 22 | label: What idea do you have? 23 | description: Provide a comprehensive description of what you think can be improved! 24 | validations: 25 | required: true 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "nuget" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "gitsubmodule" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | -------------------------------------------------------------------------------- /.github/workflows/ci-debug.yml: -------------------------------------------------------------------------------- 1 | name: CI (Debug) 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: windows-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v4 10 | with: 11 | submodules: true 12 | 13 | - uses: actions/setup-dotnet@v4 14 | with: 15 | dotnet-version: '6.0.x' 16 | 17 | - name: Restore dependencies 18 | run: dotnet restore 19 | 20 | - name: Build 21 | run: dotnet build --no-restore 22 | 23 | - name: Publish 24 | run: dotnet publish -p:PublishSingleFile=true -p:CommitHash=${{ github.sha }} -p:CommitRef=${{ github.ref_type }}/${{ github.ref_name }} -r win-x64 -c Debug --self-contained false .\Bloxstrap\Bloxstrap.csproj 25 | 26 | - name: Upload Artifact 27 | uses: actions/upload-artifact@v4 28 | with: 29 | name: Bloxstrap (Debug) (${{ github.sha }}) 30 | path: .\Bloxstrap\bin\Debug\net6.0-windows\win-x64\publish\* -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Winget 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: vedantmgoyal9/winget-releaser@main 12 | with: 13 | identifier: pizzaboxer.Bloxstrap 14 | token: ${{ secrets.WINGET_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "wpfui"] 2 | path = wpfui 3 | url = https://github.com/bloxstraplabs/wpfui.git 4 | -------------------------------------------------------------------------------- /Bloxstrap.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.3.32819.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bloxstrap", "Bloxstrap\Bloxstrap.csproj", "{0D75146E-DA24-4B05-B6C9-250C8F81B0C7}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wpf.Ui", "wpfui\src\Wpf.Ui\Wpf.Ui.csproj", "{1ADC87D1-8963-4100-845A-18477824718E}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {0D75146E-DA24-4B05-B6C9-250C8F81B0C7}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {1ADC87D1-8963-4100-845A-18477824718E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {1ADC87D1-8963-4100-845A-18477824718E}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {1ADC87D1-8963-4100-845A-18477824718E}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {1ADC87D1-8963-4100-845A-18477824718E}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | RESX_NeutralResourcesLanguage = en-GB 30 | SolutionGuid = {ED269E5D-8C72-49B4-A76F-51CF163511C1} 31 | EndGlobalSection 32 | EndGlobal 33 | -------------------------------------------------------------------------------- /Bloxstrap/App.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | pack://application:,,,/Resources/Fonts/#Rubik Light 19 | 20 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Bloxstrap/AppData/IAppData.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.AppData 2 | { 3 | internal interface IAppData 4 | { 5 | string ProductName { get; } 6 | 7 | string BinaryType { get; } 8 | 9 | string RegistryName { get; } 10 | 11 | string ExecutableName { get; } 12 | 13 | string Directory { get; } 14 | 15 | string ExecutablePath { get; } 16 | 17 | AppState State { get; } 18 | 19 | IReadOnlyDictionary PackageDirectoryMap { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Bloxstrap/AppData/RobloxPlayerData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.AppData 8 | { 9 | public class RobloxPlayerData : CommonAppData, IAppData 10 | { 11 | public string ProductName => "Roblox"; 12 | 13 | public string BinaryType => "WindowsPlayer"; 14 | 15 | public string RegistryName => "RobloxPlayer"; 16 | 17 | public override string ExecutableName => "RobloxPlayerBeta.exe"; 18 | 19 | public override AppState State => App.RobloxState.Prop.Player; 20 | 21 | public override IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() 22 | { 23 | { "RobloxApp.zip", @"" } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Bloxstrap/AppData/RobloxStudioData.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.AppData 2 | { 3 | public class RobloxStudioData : CommonAppData, IAppData 4 | { 5 | public string ProductName => "Roblox Studio"; 6 | 7 | public string BinaryType => "WindowsStudio64"; 8 | 9 | public string RegistryName => "RobloxStudio"; 10 | 11 | public override string ExecutableName => "RobloxStudioBeta.exe"; 12 | 13 | public override AppState State => App.RobloxState.Prop.Studio; 14 | 15 | public override IReadOnlyDictionary PackageDirectoryMap { get; set; } = new Dictionary() 16 | { 17 | { "RobloxStudio.zip", @"" }, 18 | { "LibrariesQt5.zip", @"" }, 19 | 20 | { "content-studio_svg_textures.zip", @"content\studio_svg_textures\"}, 21 | { "content-qt_translations.zip", @"content\qt_translations\" }, 22 | { "content-api-docs.zip", @"content\api_docs\" }, 23 | 24 | { "extracontent-scripts.zip", @"ExtraContent\scripts\" }, 25 | 26 | { "BuiltInPlugins.zip", @"BuiltInPlugins\" }, 27 | { "BuiltInStandalonePlugins.zip", @"BuiltInStandalonePlugins\" }, 28 | 29 | { "ApplicationConfig.zip", @"ApplicationConfig\" }, 30 | { "Plugins.zip", @"Plugins\" }, 31 | { "Qml.zip", @"Qml\" }, 32 | { "StudioFonts.zip", @"StudioFonts\" }, 33 | { "RibbonConfig.zip", @"RibbonConfig\" } 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Bloxstrap/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /Bloxstrap/Bloxstrap.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Bloxstrap.ico -------------------------------------------------------------------------------- /Bloxstrap/Enums/BootstrapperIcon.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum BootstrapperIcon 4 | { 5 | [EnumName(StaticName = "Bloxstrap")] 6 | IconBloxstrap, 7 | [EnumName(StaticName = "2008")] 8 | Icon2008, 9 | [EnumName(StaticName = "2011")] 10 | Icon2011, 11 | IconEarly2015, 12 | IconLate2015, 13 | [EnumName(StaticName = "2017")] 14 | Icon2017, 15 | [EnumName(StaticName = "2019")] 16 | Icon2019, 17 | [EnumName(StaticName = "2022")] 18 | Icon2022, 19 | [EnumName(FromTranslation = "Common.Custom")] 20 | IconCustom, 21 | [EnumName(FromTranslation = "Enums.BootstrapperStyle.ClassicFluentDialog")] 22 | IconBloxstrapClassic 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/BootstrapperStyle.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum BootstrapperStyle 4 | { 5 | VistaDialog, 6 | LegacyDialog2008, 7 | LegacyDialog2011, 8 | ProgressDialog, 9 | ClassicFluentDialog, 10 | ByfronDialog, 11 | [EnumName(StaticName = "Bloxstrap")] 12 | FluentDialog, 13 | FluentAeroDialog, 14 | CustomDialog 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/CursorType.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum CursorType 4 | { 5 | [EnumSort(Order = 1)] 6 | [EnumName(FromTranslation = "Common.Default")] 7 | Default, 8 | 9 | [EnumSort(Order = 3)] 10 | From2006, 11 | 12 | [EnumSort(Order = 2)] 13 | From2013 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/CustomThemeTemplate.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum CustomThemeTemplate 4 | { 5 | Blank, 6 | Simple 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/EmojiType.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum EmojiType 4 | { 5 | Default, 6 | Catmoji, 7 | Windows11, 8 | Windows10, 9 | Windows8 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/ErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | // https://learn.microsoft.com/en-us/windows/win32/msi/error-codes 4 | // https://i-logic.com/serial/errorcodes.htm 5 | // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/705fb797-2175-4a90-b5a3-3918024b10b8 6 | // just the ones that we're interested in 7 | 8 | public enum ErrorCode 9 | { 10 | ERROR_SUCCESS = 0, 11 | ERROR_INVALID_FUNCTION = 1, 12 | ERROR_FILE_NOT_FOUND = 2, 13 | 14 | ERROR_CANCELLED = 1223, 15 | ERROR_INSTALL_USEREXIT = 1602, 16 | ERROR_INSTALL_FAILURE = 1603, 17 | 18 | CO_E_APPNOTFOUND = -2147221003 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/FlagPresets/InGameMenuVersion.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums.FlagPresets 2 | { 3 | public enum InGameMenuVersion 4 | { 5 | [EnumName(FromTranslation = "Common.Default")] 6 | Default, 7 | V1, 8 | V2, 9 | V4, 10 | V4Chrome 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/FlagPresets/LightingMode.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums.FlagPresets 2 | { 3 | public enum LightingMode 4 | { 5 | Default, 6 | Voxel, 7 | ShadowMap, 8 | Future 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/FlagPresets/MSAAMode.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums.FlagPresets 2 | { 3 | public enum MSAAMode 4 | { 5 | [EnumName(FromTranslation = "Common.Automatic")] 6 | Default, 7 | [EnumName(StaticName = "1x")] 8 | x1, 9 | [EnumName(StaticName = "2x")] 10 | x2, 11 | [EnumName(StaticName = "4x")] 12 | x4 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/FlagPresets/RenderingMode.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums.FlagPresets 2 | { 3 | public enum RenderingMode 4 | { 5 | [EnumName(FromTranslation = "Common.Automatic")] 6 | Default, 7 | D3D11, 8 | D3D10, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/FlagPresets/TextureQuality.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums.FlagPresets 2 | { 3 | public enum TextureQuality 4 | { 5 | [EnumName(FromTranslation = "Common.Automatic")] 6 | Default, 7 | Level0, 8 | Level1, 9 | Level2, 10 | Level3 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/GenericTriState.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum GenericTriState 4 | { 5 | Successful, 6 | Failed, 7 | Unknown 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/LaunchMode.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum LaunchMode 4 | { 5 | None, 6 | /// 7 | /// Launch mode will be determined inside the bootstrapper. Only works if the VersionFlag is set. 8 | /// 9 | Unknown, 10 | Player, 11 | Studio, 12 | StudioAuth 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/NextAction.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum NextAction 4 | { 5 | Terminate, 6 | LaunchSettings, 7 | LaunchRoblox, 8 | LaunchRobloxStudio 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/ServerType.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum ServerType 4 | { 5 | Public, 6 | Private, 7 | Reserved 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/Theme.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | public enum Theme 4 | { 5 | [EnumName(FromTranslation = "Common.SystemDefault")] 6 | Default, 7 | Light, 8 | Dark 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/VersionComparison.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Enums 2 | { 3 | enum VersionComparison 4 | { 5 | LessThan = -1, 6 | Equal = 0, 7 | GreaterThan = 1 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Enums/WebEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Bloxstrap.Enums 4 | { 5 | [JsonConverter(typeof(JsonStringEnumConverter))] 6 | public enum WebEnvironment 7 | { 8 | [Description("prod")] 9 | Production, 10 | 11 | [Description("stage")] 12 | Staging, 13 | 14 | [Description("dev")] 15 | Dev, 16 | 17 | [Description("pizza")] 18 | DevPizza, 19 | 20 | [Description("matt")] 21 | DevMatt, 22 | 23 | [Description("local")] 24 | Local 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Bloxstrap/Exceptions/AssertionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Exceptions 8 | { 9 | internal class AssertionException : Exception 10 | { 11 | public AssertionException(string message) 12 | : base($"{message}\n\nThis is very likely just an off-chance error. Please report this first, and then start {App.ProjectName} again.") 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Bloxstrap/Exceptions/ChecksumFailedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Exceptions 8 | { 9 | internal class ChecksumFailedException : Exception 10 | { 11 | public ChecksumFailedException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Bloxstrap/Exceptions/CustomThemeException.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.Extensions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Bloxstrap.Exceptions 9 | { 10 | internal class CustomThemeException : Exception 11 | { 12 | /// 13 | /// The exception message in English (for logging) 14 | /// 15 | public string EnglishMessage { get; } = null!; 16 | 17 | public CustomThemeException(string translationString) 18 | : base(Strings.ResourceManager.GetStringSafe(translationString)) 19 | { 20 | EnglishMessage = Strings.ResourceManager.GetStringSafe(translationString, new CultureInfo("en-GB")); 21 | } 22 | 23 | public CustomThemeException(Exception innerException, string translationString) 24 | : base(Strings.ResourceManager.GetStringSafe(translationString), innerException) 25 | { 26 | EnglishMessage = Strings.ResourceManager.GetStringSafe(translationString, new CultureInfo("en-GB")); 27 | } 28 | 29 | public CustomThemeException(string translationString, params object?[] args) 30 | : base(string.Format(Strings.ResourceManager.GetStringSafe(translationString), args)) 31 | { 32 | EnglishMessage = string.Format(Strings.ResourceManager.GetStringSafe(translationString, new CultureInfo("en-GB")), args); 33 | } 34 | 35 | public CustomThemeException(Exception innerException, string translationString, params object?[] args) 36 | : base(string.Format(Strings.ResourceManager.GetStringSafe(translationString), args), innerException) 37 | { 38 | EnglishMessage = string.Format(Strings.ResourceManager.GetStringSafe(translationString, new CultureInfo("en-GB")), args); 39 | } 40 | 41 | public override string ToString() 42 | { 43 | StringBuilder sb = new StringBuilder(GetType().ToString()); 44 | 45 | if (!string.IsNullOrEmpty(Message)) 46 | sb.Append($": {Message}"); 47 | 48 | if (!string.IsNullOrEmpty(EnglishMessage) && Message != EnglishMessage) 49 | sb.Append($" ({EnglishMessage})"); 50 | 51 | if (InnerException != null) 52 | sb.Append($"\r\n ---> {InnerException}\r\n "); 53 | 54 | if (StackTrace != null) 55 | sb.Append($"\r\n{StackTrace}"); 56 | 57 | return sb.ToString(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Bloxstrap/Exceptions/InvalidChannelException.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Exceptions 2 | { 3 | public class InvalidChannelException : Exception 4 | { 5 | public HttpStatusCode? StatusCode; 6 | 7 | public InvalidChannelException(HttpStatusCode? statusCode) : base() 8 | => StatusCode = statusCode; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Exceptions/InvalidHTTPResponseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Exceptions 8 | { 9 | internal class InvalidHTTPResponseException : Exception 10 | { 11 | public InvalidHTTPResponseException(string message) : base(message) { } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/BootstrapperStyleEx.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Extensions 2 | { 3 | static class BootstrapperStyleEx 4 | { 5 | public static IBootstrapperDialog GetNew(this BootstrapperStyle bootstrapperStyle) => Frontend.GetBootstrapperDialog(bootstrapperStyle); 6 | 7 | public static IReadOnlyCollection Selections => new BootstrapperStyle[] 8 | { 9 | BootstrapperStyle.FluentDialog, 10 | BootstrapperStyle.FluentAeroDialog, 11 | BootstrapperStyle.ClassicFluentDialog, 12 | BootstrapperStyle.ByfronDialog, 13 | BootstrapperStyle.ProgressDialog, 14 | BootstrapperStyle.LegacyDialog2011, 15 | BootstrapperStyle.LegacyDialog2008, 16 | BootstrapperStyle.VistaDialog, 17 | BootstrapperStyle.CustomDialog 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/CustomThemeTemplateEx.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Bloxstrap.Extensions 4 | { 5 | static class CustomThemeTemplateEx 6 | { 7 | const string EXAMPLES_URL = "https://github.com/bloxstraplabs/custom-bootstrapper-examples"; 8 | 9 | public static string GetFileName(this CustomThemeTemplate template) 10 | { 11 | return $"CustomBootstrapperTemplate_{template}.xml"; 12 | } 13 | 14 | public static string GetFileContents(this CustomThemeTemplate template) 15 | { 16 | string contents = Encoding.UTF8.GetString(Resource.Get(template.GetFileName()).Result); 17 | 18 | switch (template) 19 | { 20 | case CustomThemeTemplate.Blank: 21 | { 22 | string moreText = string.Format(Strings.CustomTheme_Templates_Blank_MoreExamples, EXAMPLES_URL); 23 | return contents.Replace("{0}", Strings.CustomTheme_Templates_Blank_UIElements).Replace("{1}", moreText); 24 | } 25 | case CustomThemeTemplate.Simple: 26 | { 27 | string moreText = string.Format(Strings.CustomTheme_Templates_Simple_MoreExamples, EXAMPLES_URL); 28 | return contents.Replace("{0}", moreText); 29 | } 30 | default: 31 | Debug.Assert(false); 32 | return contents; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/DateTimeEx.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Extensions 2 | { 3 | static class DateTimeEx 4 | { 5 | public static string ToFriendlyString(this DateTime dateTime) 6 | { 7 | return dateTime.ToString("dddd, d MMMM yyyy 'at' h:mm:ss tt", CultureInfo.InvariantCulture); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/EmojiTypeEx.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Extensions 2 | { 3 | static class EmojiTypeEx 4 | { 5 | public static IReadOnlyDictionary Filenames => new Dictionary 6 | { 7 | { EmojiType.Catmoji, "Catmoji.ttf" }, 8 | { EmojiType.Windows11, "Win1122H2SegoeUIEmoji.ttf" }, 9 | { EmojiType.Windows10, "Win10April2018SegoeUIEmoji.ttf" }, 10 | { EmojiType.Windows8, "Win8.1SegoeUIEmoji.ttf" }, 11 | }; 12 | 13 | public static IReadOnlyDictionary Hashes => new Dictionary 14 | { 15 | { EmojiType.Catmoji, "98138f398a8cde897074dd2b8d53eca0" }, 16 | { EmojiType.Windows11, "d50758427673578ddf6c9edcdbf367f5" }, 17 | { EmojiType.Windows10, "d8a7eecbebf9dfdf622db8ccda63aff5" }, 18 | { EmojiType.Windows8, "2b01c6caabbe95afc92aa63b9bf100f3" }, 19 | }; 20 | 21 | public static string GetHash(this EmojiType emojiType) => Hashes[emojiType]; 22 | 23 | public static string GetUrl(this EmojiType emojiType) 24 | { 25 | if (emojiType == EmojiType.Default) 26 | return ""; 27 | 28 | return $"https://github.com/bloxstraplabs/rbxcustom-fontemojis/releases/download/my-phone-is-78-percent/{Filenames[emojiType]}"; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/IconEx.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Media.Imaging; 3 | using System.Windows.Media; 4 | 5 | namespace Bloxstrap.Extensions 6 | { 7 | public static class IconEx 8 | { 9 | public static Icon GetSized(this Icon icon, int width, int height) => new(icon, new Size(width, height)); 10 | 11 | public static ImageSource GetImageSource(this Icon icon, bool handleException = true) 12 | { 13 | using MemoryStream stream = new(); 14 | icon.Save(stream); 15 | 16 | if (handleException) 17 | { 18 | try 19 | { 20 | return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 21 | } 22 | catch (Exception ex) 23 | { 24 | App.Logger.WriteException("IconEx::GetImageSource", ex); 25 | Frontend.ShowMessageBox(String.Format(Strings.Dialog_IconLoadFailed, ex.Message)); 26 | return BootstrapperIcon.IconBloxstrap.GetIcon().GetImageSource(false); 27 | } 28 | } 29 | else 30 | { 31 | return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/RegistryKeyEx.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | 3 | namespace Bloxstrap.Extensions 4 | { 5 | public static class RegistryKeyEx 6 | { 7 | public static void SetValueSafe(this RegistryKey registryKey, string? name, object value) 8 | { 9 | try 10 | { 11 | App.Logger.WriteLine("RegistryKeyEx::SetValueSafe", $"Writing '{value}' to {registryKey}\\{name}"); 12 | registryKey.SetValue(name, value); 13 | } 14 | catch (UnauthorizedAccessException) 15 | { 16 | Frontend.ShowMessageBox(Strings.Dialog_RegistryWriteError, System.Windows.MessageBoxImage.Error); 17 | App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE); 18 | } 19 | } 20 | 21 | public static void DeleteValueSafe(this RegistryKey registryKey, string name) 22 | { 23 | try 24 | { 25 | App.Logger.WriteLine("RegistryKeyEx::DeleteValueSafe", $"Deleting {registryKey}\\{name}"); 26 | registryKey.DeleteValue(name); 27 | } 28 | catch (UnauthorizedAccessException) 29 | { 30 | Frontend.ShowMessageBox(Strings.Dialog_RegistryWriteError, System.Windows.MessageBoxImage.Error); 31 | App.Terminate(ErrorCode.ERROR_INSTALL_FAILURE); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/ResourceManagerEx.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | 3 | namespace Bloxstrap.Extensions 4 | { 5 | static class ResourceManagerEx 6 | { 7 | /// 8 | /// Returns the value of the specified string resource.
9 | /// If the resource is not found, the resource name will be returned. 10 | ///
11 | public static string GetStringSafe(this ResourceManager manager, string name) => manager.GetStringSafe(name, null); 12 | 13 | /// 14 | /// Returns the value of the string resource localized for the specified culture.
15 | /// If the resource is not found, the resource name will be returned. 16 | ///
17 | public static string GetStringSafe(this ResourceManager manager, string name, CultureInfo? culture) 18 | { 19 | string? resourceValue = manager.GetString(name, culture); 20 | 21 | return resourceValue ?? name; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/ServerTypeEx.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Extensions 2 | { 3 | static class ServerTypeEx 4 | { 5 | public static string ToTranslatedString(this ServerType value) => value switch 6 | { 7 | ServerType.Public => Strings.Enums_ServerType_Public, 8 | ServerType.Private => Strings.Enums_ServerType_Private, 9 | ServerType.Reserved => Strings.Enums_ServerType_Reserved, 10 | _ => "?" 11 | }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/TEnumEx.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Reflection; 3 | 4 | namespace Bloxstrap.Extensions 5 | { 6 | internal static class TEnumEx 7 | { 8 | public static string? GetDescription(this TEnum e) 9 | { 10 | string? enumName = e?.ToString(); 11 | if (enumName == null) 12 | return null; 13 | 14 | FieldInfo? field = e?.GetType().GetField(enumName); 15 | if (field == null) 16 | return null; 17 | 18 | DescriptionAttribute? attribute = field.GetCustomAttribute(); 19 | return attribute?.Description; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bloxstrap/Extensions/ThemeEx.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | 3 | namespace Bloxstrap.Extensions 4 | { 5 | public static class ThemeEx 6 | { 7 | public static Theme GetFinal(this Theme dialogTheme) 8 | { 9 | if (dialogTheme != Theme.Default) 10 | return dialogTheme; 11 | 12 | using var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"); 13 | 14 | if (key?.GetValue("AppsUseLightTheme") is int value && value == 0) 15 | return Theme.Dark; 16 | 17 | return Theme.Light; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Bloxstrap/GlobalCache.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap 2 | { 3 | public static class GlobalCache 4 | { 5 | public static readonly Dictionary ServerLocation = new(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Bloxstrap/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Diagnostics; 4 | global using System.Globalization; 5 | global using System.IO; 6 | global using System.Text; 7 | global using System.Text.Json; 8 | global using System.Text.Json.Serialization; 9 | global using System.Text.RegularExpressions; 10 | global using System.Linq; 11 | global using System.Net; 12 | global using System.Net.Http; 13 | global using System.Threading; 14 | global using System.Threading.Tasks; 15 | 16 | global using Bloxstrap.Enums; 17 | global using Bloxstrap.Exceptions; 18 | global using Bloxstrap.Extensions; 19 | global using Bloxstrap.Models; 20 | global using Bloxstrap.Models.APIs.Config; 21 | global using Bloxstrap.Models.APIs.GitHub; 22 | global using Bloxstrap.Models.APIs.Roblox; 23 | global using Bloxstrap.Models.Attributes; 24 | global using Bloxstrap.Models.BloxstrapRPC; 25 | global using Bloxstrap.Models.Entities; 26 | global using Bloxstrap.Models.Manifest; 27 | global using Bloxstrap.Models.Persistable; 28 | global using Bloxstrap.Models.SettingTasks; 29 | global using Bloxstrap.Models.SettingTasks.Base; 30 | global using Bloxstrap.Resources; 31 | global using Bloxstrap.UI; 32 | global using Bloxstrap.Utility; -------------------------------------------------------------------------------- /Bloxstrap/HttpClientLoggingHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap 2 | { 3 | internal class HttpClientLoggingHandler : MessageProcessingHandler 4 | { 5 | public HttpClientLoggingHandler(HttpMessageHandler innerHandler) 6 | : base(innerHandler) 7 | { 8 | } 9 | 10 | protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken) 11 | { 12 | App.Logger.WriteLine("HttpClientLoggingHandler::ProcessRequest", $"{request.Method} {request.RequestUri}"); 13 | return request; 14 | } 15 | 16 | protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken) 17 | { 18 | App.Logger.WriteLine("HttpClientLoggingHandler::ProcessResponse", $"{(int)response.StatusCode} {response.ReasonPhrase} {response.RequestMessage!.RequestUri}"); 19 | return response; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Config/Supporter.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Config 2 | { 3 | public class Supporter 4 | { 5 | [JsonPropertyName("imageAsset")] 6 | public string ImageAsset { get; set; } = null!; 7 | 8 | [JsonPropertyName("name")] 9 | public string Name { get; set; } = null!; 10 | 11 | public string Image => $"https://raw.githubusercontent.com/bloxstraplabs/config/main/assets/{ImageAsset}"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Config/SupporterData.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Config 2 | { 3 | public class SupporterData 4 | { 5 | [JsonPropertyName("monthly")] 6 | public SupporterGroup Monthly { get; set; } = new(); 7 | 8 | [JsonPropertyName("oneoff")] 9 | public SupporterGroup OneOff { get; set; } = new(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Config/SupporterGroup.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Config 2 | { 3 | public class SupporterGroup 4 | { 5 | [JsonPropertyName("columns")] 6 | public int Columns { get; set; } = 0; 7 | 8 | [JsonPropertyName("supporters")] 9 | public List Supporters { get; set; } = Enumerable.Empty().ToList(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/GitHub/GitHubReleaseAsset.cs: -------------------------------------------------------------------------------- 1 | public class GithubReleaseAsset 2 | { 3 | [JsonPropertyName("browser_download_url")] 4 | public string BrowserDownloadUrl { get; set; } = null!; 5 | 6 | [JsonPropertyName("name")] 7 | public string Name { get; set; } = null!; 8 | } -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/GitHub/GithubRelease.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.GitHub 2 | { 3 | public class GithubRelease 4 | { 5 | [JsonPropertyName("tag_name")] 6 | public string TagName { get; set; } = null!; 7 | 8 | [JsonPropertyName("name")] 9 | public string Name { get; set; } = null!; 10 | 11 | [JsonPropertyName("body")] 12 | public string Body { get; set; } = null!; 13 | 14 | [JsonPropertyName("created_at")] 15 | public string CreatedAt { get; set; } = null!; 16 | 17 | [JsonPropertyName("assets")] 18 | public List? Assets { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/IPInfoResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs 2 | { 3 | public class IPInfoResponse 4 | { 5 | [JsonPropertyName("city")] 6 | public string City { get; set; } = null!; 7 | 8 | [JsonPropertyName("country")] 9 | public string Country { get; set; } = null!; 10 | 11 | [JsonPropertyName("region")] 12 | public string Region { get; set; } = null!; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ApiArrayResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | /// 4 | /// Roblox.Web.WebAPI.Models.ApiArrayResponse 5 | /// 6 | public class ApiArrayResponse 7 | { 8 | [JsonPropertyName("data")] 9 | public IEnumerable Data { get; set; } = null!; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ClientFlagSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | public class ClientFlagSettings 4 | { 5 | [JsonPropertyName("applicationSettings")] 6 | public Dictionary? ApplicationSettings { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ClientVersion.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | public class ClientVersion 4 | { 5 | [JsonPropertyName("version")] 6 | public string Version { get; set; } = null!; 7 | 8 | [JsonPropertyName("clientVersionUpload")] 9 | public string VersionGuid { get; set; } = null!; 10 | 11 | [JsonPropertyName("bootstrapperVersion")] 12 | public string BootstrapperVersion { get; set; } = null!; 13 | 14 | public DateTime? Timestamp { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/GameCreator.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | /// 4 | /// Roblox.Games.Api.Models.Response.GameCreator 5 | /// Response model for getting the game creator 6 | /// 7 | public class GameCreator 8 | { 9 | /// 10 | /// The game creator id 11 | /// 12 | [JsonPropertyName("id")] 13 | public long Id { get; set; } 14 | 15 | /// 16 | /// The game creator name 17 | /// 18 | [JsonPropertyName("name")] 19 | public string Name { get; set; } = null!; 20 | 21 | /// 22 | /// The game creator type 23 | /// 24 | [JsonPropertyName("type")] 25 | public string Type { get; set; } = null!; 26 | 27 | /// 28 | /// The game creator account is Luobu Real Name Verified 29 | /// 30 | [JsonPropertyName("isRNVAccount")] 31 | public bool IsRNVAccount { get; set; } 32 | 33 | /// 34 | /// Builder verified badge status. 35 | /// 36 | [JsonPropertyName("hasVerifiedBadge")] 37 | public bool HasVerifiedBadge { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/GetUserResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.RobloxApi 2 | { 3 | /// 4 | /// Roblox.Users.Api.GetUserResponse 5 | /// 6 | public class GetUserResponse 7 | { 8 | /// 9 | /// The user description. 10 | /// 11 | [JsonPropertyName("description")] 12 | public string Description { get; set; } = null!; 13 | 14 | /// 15 | /// When the user signed up. 16 | /// 17 | [JsonPropertyName("created")] 18 | public DateTime Created { get; set; } 19 | 20 | /// 21 | /// Whether the user is banned 22 | /// 23 | [JsonPropertyName("isBanned")] 24 | public bool IsBanned { get; set; } 25 | 26 | /// 27 | /// Unused, legacy attribute… rely on its existence. 28 | /// 29 | [JsonPropertyName("externalAppDisplayName")] 30 | public string ExternalAppDisplayName { get; set; } = null!; 31 | 32 | /// 33 | /// The user's verified badge status. 34 | /// 35 | [JsonPropertyName("hasVerifiedBadge")] 36 | public bool HasVerifiedBadge { get; set; } 37 | 38 | /// 39 | /// The user Id. 40 | /// 41 | [JsonPropertyName("id")] 42 | public long Id { get; set; } 43 | 44 | /// 45 | /// The user name. 46 | /// 47 | [JsonPropertyName("name")] 48 | public string Name { get; set; } = null!; 49 | 50 | /// 51 | /// The user DisplayName. 52 | /// 53 | [JsonPropertyName("displayName")] 54 | public string DisplayName { get; set; } = null!; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ThumbnailBatchResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | internal class ThumbnailBatchResponse 4 | { 5 | [JsonPropertyName("data")] 6 | public ThumbnailResponse[] Data { get; set; } = Array.Empty(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ThumbnailRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | internal class ThumbnailRequest 4 | { 5 | [JsonPropertyName("requestId")] 6 | public string? RequestId { get; set; } 7 | 8 | [JsonPropertyName("targetId")] 9 | public ulong TargetId { get; set; } 10 | 11 | /// 12 | /// TODO: make this an enum 13 | /// List of valid types can be found at https://thumbnails.roblox.com//docs/index.html 14 | /// 15 | [JsonPropertyName("type")] 16 | public string Type { get; set; } = "Avatar"; 17 | 18 | /// 19 | /// List of valid sizes can be found at https://thumbnails.roblox.com//docs/index.html 20 | /// 21 | [JsonPropertyName("size")] 22 | public string Size { get; set; } = "30x30"; 23 | 24 | /// 25 | /// TODO: make this an enum 26 | /// List of valid types can be found at https://thumbnails.roblox.com//docs/index.html 27 | /// 28 | [JsonPropertyName("format")] 29 | public string Format { get; set; } = "Png"; 30 | 31 | [JsonPropertyName("isCircular")] 32 | public bool IsCircular { get; set; } = true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/ThumbnailResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | /// 4 | /// Roblox.Web.Responses.Thumbnails.ThumbnailResponse 5 | /// 6 | public class ThumbnailResponse 7 | { 8 | [JsonPropertyName("requestId")] 9 | public string RequestId { get; set; } = null!; 10 | 11 | [JsonPropertyName("errorCode")] 12 | public int ErrorCode { get; set; } = 0; 13 | 14 | [JsonPropertyName("errorMessage")] 15 | public string? ErrorMessage { get; set; } = null; 16 | 17 | [JsonPropertyName("targetId")] 18 | public long TargetId { get; set; } 19 | 20 | /// 21 | /// Valid states: 22 | /// - Error 23 | /// - Completed 24 | /// - InReview 25 | /// - Pending 26 | /// - Blocked 27 | /// - TemporarilyUnavailable 28 | /// 29 | [JsonPropertyName("state")] 30 | public string State { get; set; } = null!; 31 | 32 | [JsonPropertyName("imageUrl")] 33 | public string? ImageUrl { get; set; } = null!; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Bloxstrap/Models/APIs/Roblox/UniverseIdResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.APIs.Roblox 2 | { 3 | // lmao its just one property 4 | public class UniverseIdResponse 5 | { 6 | [JsonPropertyName("universeId")] 7 | public long UniverseId { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Attributes/BuildMetadataAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Attributes 2 | { 3 | [AttributeUsage(AttributeTargets.Assembly)] 4 | public class BuildMetadataAttribute : Attribute 5 | { 6 | public DateTime Timestamp { get; set; } 7 | public string Machine { get; set; } 8 | public string CommitHash { get; set; } 9 | public string CommitRef { get; set; } 10 | 11 | public BuildMetadataAttribute(string timestamp, string machine, string commitHash, string commitRef) 12 | { 13 | Timestamp = DateTime.Parse(timestamp).ToLocalTime(); 14 | Machine = machine; 15 | CommitHash = commitHash; 16 | CommitRef = commitRef; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Attributes/EnumNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models.Attributes 8 | { 9 | class EnumNameAttribute : Attribute 10 | { 11 | public string? StaticName { get; set; } 12 | public string? FromTranslation { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Attributes/EnumSortAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models.Attributes 8 | { 9 | class EnumSortAttribute : Attribute 10 | { 11 | public int Order { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Models/BloxstrapRPC/Message.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.BloxstrapRPC; 2 | 3 | public class Message 4 | { 5 | [JsonPropertyName("command")] 6 | public string Command { get; set; } = null!; 7 | 8 | [JsonPropertyName("data")] 9 | public JsonElement Data { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Models/BloxstrapRPC/RichPresence.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.BloxstrapRPC 2 | { 3 | class RichPresence 4 | { 5 | [JsonPropertyName("details")] 6 | public string? Details { get; set; } 7 | 8 | [JsonPropertyName("state")] 9 | public string? State { get; set; } 10 | 11 | [JsonPropertyName("timeStart")] 12 | public ulong? TimestampStart { get; set; } 13 | 14 | [JsonPropertyName("timeEnd")] 15 | public ulong? TimestampEnd { get; set; } 16 | 17 | [JsonPropertyName("smallImage")] 18 | public RichPresenceImage? SmallImage { get; set; } 19 | 20 | [JsonPropertyName("largeImage")] 21 | public RichPresenceImage? LargeImage { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Bloxstrap/Models/BloxstrapRPC/RichPresenceImage.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.BloxstrapRPC 2 | { 3 | class RichPresenceImage 4 | { 5 | [JsonPropertyName("assetId")] 6 | public ulong? AssetId { get; set; } 7 | 8 | [JsonPropertyName("hoverText")] 9 | public string? HoverText { get; set; } 10 | 11 | [JsonPropertyName("clear")] 12 | public bool Clear { get; set; } = false; 13 | 14 | [JsonPropertyName("reset")] 15 | public bool Reset { get; set; } = false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Bloxstrap/Models/BootstrapperIconEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Media; 2 | 3 | namespace Bloxstrap.Models 4 | { 5 | public class BootstrapperIconEntry 6 | { 7 | public BootstrapperIcon IconType { get; set; } 8 | public ImageSource ImageSource => IconType.GetIcon().GetImageSource(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Models/CustomIntegration.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | public class CustomIntegration 4 | { 5 | public string Name { get; set; } = ""; 6 | public string Location { get; set; } = ""; 7 | public string LaunchArgs { get; set; } = ""; 8 | public bool AutoClose { get; set; } = true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Bloxstrap/Models/DeployInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | public class DeployInfo 4 | { 5 | public string Timestamp { get; set; } = null!; 6 | public string Version { get; set; } = null!; 7 | public string VersionGuid { get; set; } = null!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Entities/ModPresetFileData.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Windows.Markup; 3 | 4 | namespace Bloxstrap.Models.Entities 5 | { 6 | public class ModPresetFileData 7 | { 8 | public string FilePath { get; private set; } 9 | 10 | public string FullFilePath => Path.Combine(Paths.Modifications, FilePath); 11 | 12 | public FileStream FileStream => File.OpenRead(FullFilePath); 13 | 14 | public string ResourceIdentifier { get; private set; } 15 | 16 | public Stream ResourceStream => Resource.GetStream(ResourceIdentifier); 17 | 18 | public byte[] ResourceHash { get; private set; } 19 | 20 | public ModPresetFileData(string contentPath, string resource) 21 | { 22 | FilePath = contentPath; 23 | ResourceIdentifier = resource; 24 | 25 | using var stream = ResourceStream; 26 | ResourceHash = App.MD5Provider.ComputeHash(stream); 27 | } 28 | 29 | public bool HashMatches() 30 | { 31 | if (!File.Exists(FullFilePath)) 32 | return false; 33 | 34 | using var fileStream = FileStream; 35 | var fileHash = App.MD5Provider.ComputeHash(fileStream); 36 | 37 | return fileHash.SequenceEqual(ResourceHash); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Entities/UniverseDetails.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Entities 2 | { 3 | /// 4 | /// Explicit loading. Load from cache before and after a fetch. 5 | /// 6 | public class UniverseDetails 7 | { 8 | private static List _cache { get; set; } = new(); 9 | 10 | public GameDetailResponse Data { get; set; } = null!; 11 | 12 | /// 13 | /// Returns data for a 128x128 icon 14 | /// 15 | public ThumbnailResponse Thumbnail { get; set; } = null!; 16 | 17 | public static UniverseDetails? LoadFromCache(long id) 18 | { 19 | var cacheQuery = _cache.Where(x => x.Data?.Id == id); 20 | 21 | if (cacheQuery.Any()) 22 | return cacheQuery.First(); 23 | 24 | return null; 25 | } 26 | 27 | public static Task FetchSingle(long id) => FetchBulk(id.ToString()); 28 | 29 | public static async Task FetchBulk(string ids) 30 | { 31 | var gameDetailResponse = await Http.GetJson>($"https://games.roblox.com/v1/games?universeIds={ids}"); 32 | 33 | if (!gameDetailResponse.Data.Any()) 34 | throw new InvalidHTTPResponseException("Roblox API for Game Details returned invalid data"); 35 | 36 | var universeThumbnailResponse = await Http.GetJson>($"https://thumbnails.roblox.com/v1/games/icons?universeIds={ids}&returnPolicy=PlaceHolder&size=128x128&format=Png&isCircular=false"); 37 | 38 | if (!universeThumbnailResponse.Data.Any()) 39 | throw new InvalidHTTPResponseException("Roblox API for Game Thumbnails returned invalid data"); 40 | 41 | foreach (string strId in ids.Split(',')) 42 | { 43 | long id = long.Parse(strId); 44 | 45 | _cache.Add(new UniverseDetails 46 | { 47 | Data = gameDetailResponse.Data.Where(x => x.Id == id).First(), 48 | Thumbnail = universeThumbnailResponse.Data.Where(x => x.TargetId == id).First(), 49 | }); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Entities/UserDetails.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.Models.RobloxApi; 2 | 3 | namespace Bloxstrap.Models.Entities 4 | { 5 | public class UserDetails 6 | { 7 | private static List _cache { get; set; } = new(); 8 | 9 | public GetUserResponse Data { get; set; } = null!; 10 | 11 | public ThumbnailResponse Thumbnail { get; set; } = null!; 12 | 13 | public static async Task Fetch(long id) 14 | { 15 | var cacheQuery = _cache.Where(x => x.Data?.Id == id); 16 | 17 | if (cacheQuery.Any()) 18 | return cacheQuery.First(); 19 | 20 | var userResponse = await Http.GetJson($"https://users.roblox.com/v1/users/{id}"); 21 | 22 | if (userResponse is null) 23 | throw new InvalidHTTPResponseException("Roblox API for User Details returned invalid data"); 24 | 25 | // we can remove '-headshot' from the url if we want a full avatar picture 26 | var thumbnailResponse = await Http.GetJson>($"https://thumbnails.roblox.com/v1/users/avatar-headshot?userIds={id}&size=180x180&format=Png&isCircular=false"); 27 | 28 | if (thumbnailResponse is null || !thumbnailResponse.Data.Any()) 29 | throw new InvalidHTTPResponseException("Roblox API for Thumbnails returned invalid data"); 30 | 31 | var details = new UserDetails 32 | { 33 | Data = userResponse, 34 | Thumbnail = thumbnailResponse.Data.First() 35 | }; 36 | 37 | _cache.Add(details); 38 | 39 | return details; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Bloxstrap/Models/FastFlag.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | public class FastFlag 4 | { 5 | // public bool Enabled { get; set; } 6 | public string Name { get; set; } = null!; 7 | public string Value { get; set; } = null!; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Bloxstrap/Models/FontFace.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | public class FontFace 4 | { 5 | [JsonPropertyName("name")] 6 | public string Name { get; set; } = null!; 7 | 8 | [JsonPropertyName("weight")] 9 | public int Weight { get; set; } 10 | 11 | [JsonPropertyName("style")] 12 | public string Style { get; set; } = null!; 13 | 14 | [JsonPropertyName("assetId")] 15 | public string AssetId { get; set; } = null!; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Bloxstrap/Models/FontFamily.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | public class FontFamily 4 | { 5 | [JsonPropertyName("name")] 6 | public string Name { get; set; } = null!; 7 | 8 | [JsonPropertyName("faces")] 9 | public IEnumerable Faces { get; set; } = null!; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/LaunchFlag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models 8 | { 9 | public class LaunchFlag 10 | { 11 | public string Identifiers { get; private set; } 12 | 13 | public bool Active = false; 14 | 15 | public string? Data; 16 | 17 | public LaunchFlag(string identifiers) 18 | { 19 | Identifiers = identifiers; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Manifest/FileManifest.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.RobloxInterfaces; 2 | 3 | namespace Bloxstrap.Models.Manifest 4 | { 5 | public class FileManifest : List 6 | { 7 | private FileManifest(string data) 8 | { 9 | using StringReader reader = new StringReader(data); 10 | 11 | while (true) 12 | { 13 | string? fileName = reader.ReadLine(); 14 | string? signature = reader.ReadLine(); 15 | 16 | if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(signature)) 17 | break; 18 | 19 | Add(new ManifestFile 20 | { 21 | Name = fileName, 22 | Signature = signature 23 | }); 24 | } 25 | } 26 | 27 | public static async Task Get(string versionGuid) 28 | { 29 | string pkgManifestUrl = Deployment.GetLocation($"/{versionGuid}-rbxManifest.txt"); 30 | var pkgManifestData = await App.HttpClient.GetStringAsync(pkgManifestUrl); 31 | 32 | return new FileManifest(pkgManifestData); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Manifest/ManifestFile.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Manifest 2 | { 3 | public class ManifestFile 4 | { 5 | public string Name { get; set; } = ""; 6 | public string Signature { get; set; } = ""; 7 | 8 | public override string ToString() 9 | { 10 | return $"[{Signature}] {Name}"; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Manifest/Package.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Roblox Studio Mod Manager (ProjectSrc/Utility/Package.cs) 3 | * MIT License 4 | * Copyright (c) 2015-present MaximumADHD 5 | */ 6 | 7 | namespace Bloxstrap.Models.Manifest 8 | { 9 | public class Package 10 | { 11 | public string Name { get; set; } = ""; 12 | 13 | public string Signature { get; set; } = ""; 14 | 15 | public int PackedSize { get; set; } 16 | 17 | public int Size { get; set; } 18 | 19 | public string DownloadPath => Path.Combine(Paths.Downloads, Signature); 20 | 21 | public override string ToString() 22 | { 23 | return $"[{Signature}] {Name}"; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Manifest/PackageManifest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Roblox Studio Mod Manager (ProjectSrc/Utility/PackageManifest.cs) 3 | * MIT License 4 | * Copyright (c) 2015-present MaximumADHD 5 | */ 6 | 7 | namespace Bloxstrap.Models.Manifest 8 | { 9 | public class PackageManifest : List 10 | { 11 | public PackageManifest(string data) 12 | { 13 | using var reader = new StringReader(data); 14 | string? version = reader.ReadLine(); 15 | 16 | if (version != "v0") 17 | throw new NotSupportedException($"Unexpected package manifest version: {version} (expected v0!)"); 18 | 19 | while (true) 20 | { 21 | string? fileName = reader.ReadLine(); 22 | string? signature = reader.ReadLine(); 23 | 24 | string? rawPackedSize = reader.ReadLine(); 25 | string? rawSize = reader.ReadLine(); 26 | 27 | if (string.IsNullOrEmpty(fileName) || 28 | string.IsNullOrEmpty(signature) || 29 | string.IsNullOrEmpty(rawPackedSize) || 30 | string.IsNullOrEmpty(rawSize)) 31 | break; 32 | 33 | // ignore launcher 34 | if (fileName == "RobloxPlayerLauncher.exe") 35 | break; 36 | 37 | int packedSize = int.Parse(rawPackedSize); 38 | int size = int.Parse(rawSize); 39 | 40 | Add(new Package 41 | { 42 | Name = fileName, 43 | Signature = signature, 44 | PackedSize = packedSize, 45 | Size = size 46 | }); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Persistable/AppState.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Persistable 2 | { 3 | public class AppState 4 | { 5 | public string VersionGuid { get; set; } = string.Empty; 6 | 7 | public Dictionary PackageHashes { get; set; } = new(); 8 | 9 | public int Size { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Persistable/RobloxState.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Persistable 2 | { 3 | public class RobloxState 4 | { 5 | public AppState Player { get; set; } = new(); 6 | 7 | public AppState Studio { get; set; } = new(); 8 | 9 | public List ModManifest { get; set; } = new(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Persistable/Settings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace Bloxstrap.Models.Persistable 4 | { 5 | public class Settings 6 | { 7 | // bloxstrap configuration 8 | public BootstrapperStyle BootstrapperStyle { get; set; } = BootstrapperStyle.FluentDialog; 9 | public BootstrapperIcon BootstrapperIcon { get; set; } = BootstrapperIcon.IconBloxstrap; 10 | public string BootstrapperTitle { get; set; } = App.ProjectName; 11 | public string BootstrapperIconCustomLocation { get; set; } = ""; 12 | public Theme Theme { get; set; } = Theme.Default; 13 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 14 | public bool DeveloperMode { get; set; } = false; 15 | public bool CheckForUpdates { get; set; } = true; 16 | public bool MultiInstanceLaunching { get; set; } = false; 17 | public bool ConfirmLaunches { get; set; } = false; 18 | public string Locale { get; set; } = "nil"; 19 | public bool ForceRobloxLanguage { get; set; } = false; 20 | public bool UseFastFlagManager { get; set; } = true; 21 | public bool WPFSoftwareRender { get; set; } = false; 22 | public bool EnableAnalytics { get; set; } = true; 23 | public bool BackgroundUpdatesEnabled { get; set; } = false; 24 | public bool DebugDisableVersionPackageCleanup { get; set; } = false; 25 | public string? SelectedCustomTheme { get; set; } = null; 26 | public WebEnvironment WebEnvironment { get; set; } = WebEnvironment.Production; 27 | 28 | // integration configuration 29 | public bool EnableActivityTracking { get; set; } = true; 30 | public bool UseDiscordRichPresence { get; set; } = true; 31 | public bool HideRPCButtons { get; set; } = true; 32 | public bool ShowAccountOnRichPresence { get; set; } = false; 33 | public bool ShowServerDetails { get; set; } = false; 34 | public ObservableCollection CustomIntegrations { get; set; } = new(); 35 | 36 | // mod preset configuration 37 | public bool UseDisableAppPatch { get; set; } = false; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Persistable/State.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Persistable 2 | { 3 | public class State 4 | { 5 | public bool ShowFFlagEditorWarning { get; set; } = true; 6 | 7 | public bool PromptWebView2Install { get; set; } = true; 8 | 9 | public bool ForceReinstall { get; set; } = false; 10 | 11 | public WindowState SettingsWindow { get; set; } = new(); 12 | 13 | #region Deprecated properties 14 | /// 15 | /// Deprecated, use App.RobloxState.Player 16 | /// 17 | public AppState? Player { private get; set; } 18 | public AppState? GetDeprecatedPlayer() => Player; 19 | 20 | /// 21 | /// Deprecated, use App.RobloxState.Studio 22 | /// 23 | public AppState? Studio { private get; set; } 24 | public AppState? GetDeprecatedStudio() => Studio; 25 | 26 | /// 27 | /// Deprecated, use App.RobloxState.ModManifest 28 | /// 29 | public List? ModManifest { private get; set; } 30 | public List? GetDeprecatedModManifest() => ModManifest; 31 | #endregion 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Bloxstrap/Models/Persistable/WindowState.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.Persistable 2 | { 3 | public class WindowState 4 | { 5 | public double Width { get; set; } 6 | 7 | public double Height { get; set; } 8 | 9 | public double Left { get; set; } 10 | 11 | public double Top { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/Base/BaseTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models.SettingTasks.Base 8 | { 9 | public abstract class BaseTask 10 | { 11 | public string Name { get; private set; } 12 | 13 | public abstract bool Changed { get; } 14 | 15 | public BaseTask(string prefix, string name) : this($"{prefix}.{name}") { } 16 | 17 | public BaseTask(string name) => Name = name; 18 | 19 | public override string ToString() => Name; 20 | 21 | public abstract void Execute(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/Base/BoolBaseTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models.SettingTasks.Base 8 | { 9 | public abstract class BoolBaseTask : BaseTask 10 | { 11 | private bool _originalState; 12 | 13 | private bool _newState; 14 | 15 | public virtual bool OriginalState 16 | { 17 | get => _originalState; 18 | 19 | set 20 | { 21 | _originalState = value; 22 | _newState = value; 23 | } 24 | } 25 | 26 | public virtual bool NewState 27 | { 28 | get => _newState; 29 | 30 | set 31 | { 32 | _newState = value; 33 | 34 | if (Changed) 35 | App.PendingSettingTasks[Name] = this; 36 | else 37 | App.PendingSettingTasks.Remove(Name); 38 | } 39 | } 40 | 41 | public override bool Changed => _newState != OriginalState; 42 | 43 | public BoolBaseTask(string prefix, string name) : base(prefix, name) { } 44 | 45 | public BoolBaseTask(string name) : base(name) { } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/Base/EnumBaseTask.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.SettingTasks.Base 2 | { 3 | public abstract class EnumBaseTask : BaseTask where T : struct, Enum 4 | { 5 | private T _originalState = default!; 6 | 7 | private T _newState = default!; 8 | 9 | public virtual T OriginalState 10 | { 11 | get => _originalState; 12 | 13 | set 14 | { 15 | _originalState = value; 16 | _newState = value; 17 | } 18 | } 19 | 20 | public virtual T NewState 21 | { 22 | get => _newState; 23 | 24 | set 25 | { 26 | _newState = value; 27 | 28 | if (Changed) 29 | App.PendingSettingTasks[Name] = this; 30 | else 31 | App.PendingSettingTasks.Remove(Name); 32 | } 33 | } 34 | 35 | public override bool Changed => !_newState.Equals(OriginalState); 36 | 37 | public IEnumerable Selections { get; private set; } 38 | = Enum.GetValues(typeof(T)).Cast().OrderBy(x => 39 | { 40 | var attributes = x.GetType().GetMember(x.ToString())[0].GetCustomAttributes(typeof(EnumSortAttribute), false); 41 | 42 | if (attributes.Length > 0) 43 | { 44 | var attribute = (EnumSortAttribute)attributes[0]; 45 | return attribute.Order; 46 | } 47 | 48 | return 0; 49 | }); 50 | 51 | public EnumBaseTask(string prefix, string name) : base(prefix, name) { } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/Base/StringBaseTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Bloxstrap.Models.SettingTasks.Base 8 | { 9 | public abstract class StringBaseTask : BaseTask 10 | { 11 | private string _originalState = ""; 12 | 13 | private string _newState = ""; 14 | 15 | public virtual string OriginalState 16 | { 17 | get => _originalState; 18 | 19 | set 20 | { 21 | _originalState = value; 22 | _newState = value; 23 | } 24 | } 25 | 26 | public virtual string NewState 27 | { 28 | get => _newState; 29 | 30 | set 31 | { 32 | _newState = value; 33 | 34 | if (Changed) 35 | App.PendingSettingTasks[Name] = this; 36 | else 37 | App.PendingSettingTasks.Remove(Name); 38 | } 39 | } 40 | 41 | public override bool Changed => _newState != OriginalState; 42 | 43 | public StringBaseTask(string prefix, string name) : base(prefix, name) { } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/EmojiModPresetTask.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | using Bloxstrap.Models.SettingTasks.Base; 4 | 5 | namespace Bloxstrap.Models.SettingTasks 6 | { 7 | public class EmojiModPresetTask : EnumBaseTask 8 | { 9 | private string _filePath => Path.Combine(Paths.Modifications, @"content\fonts\TwemojiMozilla.ttf"); 10 | 11 | private IEnumerable>? QueryCurrentValue() 12 | { 13 | if (!File.Exists(_filePath)) 14 | return null; 15 | 16 | using var fileStream = File.OpenRead(_filePath); 17 | string hash = MD5Hash.Stringify(App.MD5Provider.ComputeHash(fileStream)); 18 | 19 | return EmojiTypeEx.Hashes.Where(x => x.Value == hash); 20 | } 21 | 22 | public EmojiModPresetTask() : base("ModPreset", "EmojiFont") 23 | { 24 | var query = QueryCurrentValue(); 25 | 26 | if (query is not null) 27 | OriginalState = query.FirstOrDefault().Key; 28 | } 29 | 30 | public override async void Execute() 31 | { 32 | const string LOG_IDENT = "EmojiModPresetTask::Execute"; 33 | 34 | var query = QueryCurrentValue(); 35 | 36 | if (NewState != EmojiType.Default && (query is null || query.FirstOrDefault().Key != NewState)) 37 | { 38 | try 39 | { 40 | var response = await App.HttpClient.GetAsync(NewState.GetUrl()); 41 | 42 | response.EnsureSuccessStatusCode(); 43 | 44 | Directory.CreateDirectory(Path.GetDirectoryName(_filePath)!); 45 | 46 | await using var fileStream = new FileStream(_filePath, FileMode.Create); 47 | await response.Content.CopyToAsync(fileStream); 48 | 49 | OriginalState = NewState; 50 | } 51 | catch (Exception ex) 52 | { 53 | App.Logger.WriteException(LOG_IDENT, ex); 54 | 55 | Frontend.ShowConnectivityDialog( 56 | String.Format(Strings.Dialog_Connectivity_UnableToConnect, "GitHub"), 57 | $"{Strings.Menu_Mods_Presets_EmojiType_Error}\n\n{Strings.Dialog_Connectivity_TryAgainLater}", 58 | MessageBoxImage.Warning, 59 | ex 60 | ); 61 | } 62 | } 63 | else if (query is not null && query.Any()) 64 | { 65 | Filesystem.AssertReadOnly(_filePath); 66 | File.Delete(_filePath); 67 | 68 | OriginalState = NewState; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/EnumModPresetTask.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.Models.Entities; 2 | using Bloxstrap.Models.SettingTasks.Base; 3 | 4 | namespace Bloxstrap.Models.SettingTasks 5 | { 6 | public class EnumModPresetTask : EnumBaseTask where T : struct, Enum 7 | { 8 | private readonly Dictionary> _fileDataMap = new(); 9 | 10 | private readonly Dictionary> _map; 11 | 12 | public EnumModPresetTask(string name, Dictionary> map) : base("ModPreset", name) 13 | { 14 | _map = map; 15 | 16 | foreach (var enumPair in _map) 17 | { 18 | var dataMap = new Dictionary(); 19 | 20 | foreach (var resourcePair in enumPair.Value) 21 | { 22 | var data = new ModPresetFileData(resourcePair.Key, resourcePair.Value); 23 | 24 | if (data.HashMatches() && OriginalState.Equals(default(T))) 25 | OriginalState = enumPair.Key; 26 | 27 | dataMap[resourcePair.Key] = data; 28 | } 29 | 30 | _fileDataMap[enumPair.Key] = dataMap; 31 | } 32 | } 33 | 34 | public override void Execute() 35 | { 36 | if (!NewState.Equals(default(T))) 37 | { 38 | var resourceMap = _fileDataMap[NewState]; 39 | 40 | foreach (var resourcePair in resourceMap) 41 | { 42 | var data = resourcePair.Value; 43 | 44 | if (!data.HashMatches()) 45 | { 46 | Directory.CreateDirectory(Path.GetDirectoryName(data.FullFilePath)!); 47 | 48 | using var resourceStream = data.ResourceStream; 49 | using var memoryStream = new MemoryStream(); 50 | data.ResourceStream.CopyTo(memoryStream); 51 | 52 | Filesystem.AssertReadOnly(data.FullFilePath); 53 | File.WriteAllBytes(data.FullFilePath, memoryStream.ToArray()); 54 | } 55 | } 56 | } 57 | else 58 | { 59 | foreach (var dataPair in _fileDataMap.First().Value) 60 | { 61 | Filesystem.AssertReadOnly(dataPair.Value.FullFilePath); 62 | File.Delete(dataPair.Value.FullFilePath); 63 | } 64 | } 65 | 66 | OriginalState = NewState; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/ExtractIconsTask.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Bloxstrap.Models.SettingTasks 4 | { 5 | public class ExtractIconsTask : BoolBaseTask 6 | { 7 | private string _path => Path.Combine(Paths.Base, Strings.Paths_Icons); 8 | 9 | public ExtractIconsTask() : base("ExtractIcons") 10 | { 11 | OriginalState = Directory.Exists(_path); 12 | } 13 | 14 | public override void Execute() 15 | { 16 | if (NewState) 17 | { 18 | Directory.CreateDirectory(_path); 19 | 20 | var assembly = Assembly.GetExecutingAssembly(); 21 | var resourceNames = assembly.GetManifestResourceNames().Where(x => x.EndsWith(".ico")); 22 | 23 | foreach (string name in resourceNames) 24 | { 25 | string path = Path.Combine(_path, name.Replace("Bloxstrap.Resources.", "")); 26 | var stream = assembly.GetManifestResourceStream(name)!; 27 | 28 | using var memoryStream = new MemoryStream(); 29 | stream.CopyTo(memoryStream); 30 | 31 | Filesystem.AssertReadOnly(path); 32 | File.WriteAllBytes(path, memoryStream.ToArray()); 33 | } 34 | } 35 | else if (Directory.Exists(_path)) 36 | { 37 | Directory.Delete(_path, true); 38 | } 39 | 40 | OriginalState = NewState; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/FontModPresetTask.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.Models.SettingTasks.Base; 2 | 3 | namespace Bloxstrap.Models.SettingTasks 4 | { 5 | public class FontModPresetTask : StringBaseTask 6 | { 7 | public string? GetFileHash() 8 | { 9 | if (!File.Exists(Paths.CustomFont)) 10 | return null; 11 | 12 | using var fileStream = File.OpenRead(Paths.CustomFont); 13 | return MD5Hash.Stringify(App.MD5Provider.ComputeHash(fileStream)); 14 | } 15 | 16 | public FontModPresetTask() : base("ModPreset", "TextFont") 17 | { 18 | if (File.Exists(Paths.CustomFont)) 19 | OriginalState = Paths.CustomFont; 20 | } 21 | 22 | public override void Execute() 23 | { 24 | if (!String.IsNullOrEmpty(NewState)) 25 | { 26 | if (String.Compare(NewState, Paths.CustomFont, StringComparison.InvariantCultureIgnoreCase) != 0 && File.Exists(NewState)) 27 | { 28 | Directory.CreateDirectory(Path.GetDirectoryName(Paths.CustomFont)!); 29 | 30 | Filesystem.AssertReadOnly(Paths.CustomFont); 31 | File.Copy(NewState, Paths.CustomFont, true); 32 | } 33 | } 34 | else if (File.Exists(Paths.CustomFont)) 35 | { 36 | Filesystem.AssertReadOnly(Paths.CustomFont); 37 | File.Delete(Paths.CustomFont); 38 | } 39 | 40 | OriginalState = NewState; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/ModPresetTask.cs: -------------------------------------------------------------------------------- 1 | using Bloxstrap.Models.Entities; 2 | using Bloxstrap.Models.SettingTasks.Base; 3 | 4 | namespace Bloxstrap.Models.SettingTasks 5 | { 6 | public class ModPresetTask : BoolBaseTask 7 | { 8 | private Dictionary _fileDataMap = new(); 9 | 10 | private Dictionary _pathMap; 11 | 12 | public ModPresetTask(string name, string path, string resource) : this(name, new() {{ path, resource }}) { } 13 | 14 | public ModPresetTask(string name, Dictionary pathMap) : base("ModPreset", name) 15 | { 16 | _pathMap = pathMap; 17 | 18 | foreach (var pair in _pathMap) 19 | { 20 | var data = new ModPresetFileData(pair.Key, pair.Value); 21 | 22 | if (data.HashMatches() && !OriginalState) 23 | OriginalState = true; 24 | 25 | _fileDataMap[pair.Key] = data; 26 | } 27 | } 28 | 29 | public override void Execute() 30 | { 31 | if (NewState == OriginalState) 32 | return; 33 | 34 | foreach (var pair in _fileDataMap) 35 | { 36 | var data = pair.Value; 37 | bool hashMatches = data.HashMatches(); 38 | 39 | if (NewState && !hashMatches) 40 | { 41 | Directory.CreateDirectory(Path.GetDirectoryName(data.FullFilePath)!); 42 | 43 | using var resourceStream = data.ResourceStream; 44 | using var memoryStream = new MemoryStream(); 45 | resourceStream.CopyTo(memoryStream); 46 | 47 | Filesystem.AssertReadOnly(data.FullFilePath); 48 | File.WriteAllBytes(data.FullFilePath, memoryStream.ToArray()); 49 | } 50 | else if (!NewState && hashMatches) 51 | { 52 | Filesystem.AssertReadOnly(data.FullFilePath); 53 | File.Delete(data.FullFilePath); 54 | } 55 | } 56 | 57 | OriginalState = NewState; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Bloxstrap/Models/SettingTasks/ShortcutTask.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models.SettingTasks 2 | { 3 | public class ShortcutTask : BoolBaseTask 4 | { 5 | private string _shortcutPath; 6 | 7 | private string _exeFlags; 8 | 9 | public ShortcutTask(string name, string lnkFolder, string lnkName, string exeFlags = "") : base("Shortcut", name) 10 | { 11 | _shortcutPath = Path.Combine(lnkFolder, lnkName); 12 | _exeFlags = exeFlags; 13 | 14 | OriginalState = File.Exists(_shortcutPath); 15 | } 16 | 17 | public override void Execute() 18 | { 19 | if (NewState) 20 | Shortcut.Create(Paths.Application, _exeFlags, _shortcutPath); 21 | else if (File.Exists(_shortcutPath)) 22 | File.Delete(_shortcutPath); 23 | 24 | OriginalState = NewState; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Bloxstrap/Models/ThumbnailCacheEntry.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | internal class ThumbnailCacheEntry 4 | { 5 | public ulong Id { get; set; } 6 | public string Url { get; set; } = string.Empty; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Bloxstrap/Models/WatcherData.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap.Models 2 | { 3 | internal class WatcherData 4 | { 5 | public int ProcessId { get; set; } 6 | 7 | public string? LogFile { get; set; } 8 | 9 | public List? AutoclosePids { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Bloxstrap/MultiInstanceWatcher.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap 2 | { 3 | internal static class MultiInstanceWatcher 4 | { 5 | private static int GetOpenProcessesCount() 6 | { 7 | const string LOG_IDENT = "MultiInstanceWatcher::GetOpenProcessesCount"; 8 | 9 | try 10 | { 11 | // prevent any possible race conditions by checking for bloxstrap processes too 12 | int count = Process.GetProcesses().Count(x => x.ProcessName is "RobloxPlayerBeta" or "Bloxstrap"); 13 | count -= 1; // ignore the current process 14 | return count; 15 | } 16 | catch (Exception ex) 17 | { 18 | // everything process related can error at any time 19 | App.Logger.WriteException(LOG_IDENT, ex); 20 | return -1; 21 | } 22 | } 23 | 24 | private static void FireInitialisedEvent() 25 | { 26 | using EventWaitHandle initEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, "Bloxstrap-MultiInstanceWatcherInitialisationFinished"); 27 | initEventHandle.Set(); 28 | } 29 | 30 | public static void Run() 31 | { 32 | const string LOG_IDENT = "MultiInstanceWatcher::Run"; 33 | 34 | // try to get the mutex 35 | bool acquiredMutex; 36 | using Mutex mutex = new Mutex(false, "ROBLOX_singletonMutex"); 37 | try 38 | { 39 | acquiredMutex = mutex.WaitOne(0); 40 | } 41 | catch (AbandonedMutexException) 42 | { 43 | acquiredMutex = true; 44 | } 45 | 46 | if (!acquiredMutex) 47 | { 48 | App.Logger.WriteLine(LOG_IDENT, "Client singleton mutex is already acquired"); 49 | FireInitialisedEvent(); 50 | return; 51 | } 52 | 53 | App.Logger.WriteLine(LOG_IDENT, "Acquired mutex!"); 54 | FireInitialisedEvent(); 55 | 56 | // watch for alive processes 57 | int count; 58 | do 59 | { 60 | Thread.Sleep(5000); 61 | count = GetOpenProcessesCount(); 62 | } 63 | while (count == -1 || count > 0); // redo if -1 (one of the Process apis failed) 64 | 65 | App.Logger.WriteLine(LOG_IDENT, "All Roblox related processes have closed, exiting!"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Bloxstrap/NativeMethods.txt: -------------------------------------------------------------------------------- 1 | SetForegroundWindow 2 | FlashWindow 3 | GetWindowLong 4 | SetWindowLong 5 | SHObjectProperties -------------------------------------------------------------------------------- /Bloxstrap/Paths.cs: -------------------------------------------------------------------------------- 1 | namespace Bloxstrap 2 | { 3 | static class Paths 4 | { 5 | // note that these are directories that aren't tethered to the basedirectory 6 | // so these can safely be called before initialization 7 | public static string Temp => Path.Combine(Path.GetTempPath(), App.ProjectName); 8 | public static string UserProfile => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); 9 | public static string LocalAppData => Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 10 | public static string Desktop => Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); 11 | public static string WindowsStartMenu => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Programs"); 12 | public static string System => Environment.GetFolderPath(Environment.SpecialFolder.System); 13 | 14 | public static string Process => Environment.ProcessPath!; 15 | 16 | public static string TempUpdates => Path.Combine(Temp, "Updates"); 17 | public static string TempLogs => Path.Combine(Temp, "Logs"); 18 | 19 | public static string Base { get; private set; } = ""; 20 | public static string Downloads { get; private set; } = ""; 21 | public static string Logs { get; private set; } = ""; 22 | public static string Integrations { get; private set; } = ""; 23 | public static string Versions { get; private set; } = ""; 24 | public static string Modifications { get; private set; } = ""; 25 | public static string CustomThemes { get; private set; } = ""; 26 | 27 | public static string Application { get; private set; } = ""; 28 | 29 | public static string CustomFont => Path.Combine(Modifications, "content\\fonts\\CustomFont.ttf"); 30 | 31 | public static bool Initialized => !String.IsNullOrEmpty(Base); 32 | 33 | public static void Initialize(string baseDirectory) 34 | { 35 | Base = baseDirectory; 36 | Downloads = Path.Combine(Base, "Downloads"); 37 | Logs = Path.Combine(Base, "Logs"); 38 | Integrations = Path.Combine(Base, "Integrations"); 39 | Versions = Path.Combine(Base, "Versions"); 40 | Modifications = Path.Combine(Base, "Modifications"); 41 | CustomThemes = Path.Combine(Base, "CustomThemes"); 42 | 43 | Application = Path.Combine(Base, $"{App.ProjectName}.exe"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Bloxstrap/Properties/PublishProfiles/Publish-x64.pubxml: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | Release 8 | Any CPU 9 | bin\Release\net6.0-windows\publish\win-x64\ 10 | FileSystem 11 | <_TargetId>Folder 12 | net6.0-windows 13 | win-x64 14 | false 15 | true 16 | false 17 | 18 | -------------------------------------------------------------------------------- /Bloxstrap/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Bloxstrap.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Bloxstrap/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Bloxstrap/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Bloxstrap (Launch)": { 4 | "commandName": "Project" 5 | }, 6 | "Bloxstrap (Quiet Launch)": { 7 | "commandName": "Project", 8 | "commandLineArgs": "-quiet" 9 | }, 10 | "Bloxstrap (Uninstall)": { 11 | "commandName": "Project", 12 | "commandLineArgs": "-uninstall" 13 | }, 14 | "Bloxstrap (Quiet Uninstall)": { 15 | "commandName": "Project", 16 | "commandLineArgs": "-uninstall -quiet" 17 | }, 18 | "Bloxstrap (Menu)": { 19 | "commandName": "Project", 20 | "commandLineArgs": "-menu" 21 | }, 22 | "Bloxstrap (Deeplink)": { 23 | "commandName": "Project", 24 | "commandLineArgs": "-player \"roblox://experiences/start?placeId=13700835620\"" 25 | }, 26 | "Bloxstrap (Studio Launch)": { 27 | "commandName": "Project", 28 | "commandLineArgs": "-studio" 29 | }, 30 | "Bloxstrap (Watcher)": { 31 | "commandName": "Project", 32 | "commandLineArgs": "-watcher" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Bloxstrap/Resource.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Bloxstrap 4 | { 5 | static class Resource 6 | { 7 | static readonly Assembly assembly = Assembly.GetExecutingAssembly(); 8 | static readonly string[] resourceNames = assembly.GetManifestResourceNames(); 9 | 10 | public static Stream GetStream(string name) 11 | { 12 | string path = resourceNames.Single(str => str.EndsWith(name)); 13 | return assembly.GetManifestResourceStream(path)!; 14 | } 15 | 16 | public static async Task Get(string name) 17 | { 18 | using var stream = GetStream(name); 19 | using var memoryStream = new MemoryStream(); 20 | 21 | await stream.CopyToAsync(memoryStream); 22 | return memoryStream.ToArray(); 23 | } 24 | 25 | public static async Task GetString(string name) 26 | { 27 | return Encoding.UTF8.GetString(await Get(name)); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/ByfronLogoDark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/ByfronLogoDark.jpg -------------------------------------------------------------------------------- /Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/ByfronLogoLight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/ByfronLogoLight.jpg -------------------------------------------------------------------------------- /Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/Matt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Resources/BootstrapperStyles/ByfronDialog/Matt.png -------------------------------------------------------------------------------- /Bloxstrap/Resources/CancelButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Resources/CancelButton.png -------------------------------------------------------------------------------- /Bloxstrap/Resources/CancelButtonHover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bloxstraplabs/bloxstrap/1f21e8ce0b073e2684ce36564a6cc2cca1b01b04/Bloxstrap/Resources/CancelButtonHover.png -------------------------------------------------------------------------------- /Bloxstrap/Resources/CustomBootstrapperTemplate_Blank.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Bloxstrap/Resources/CustomBootstrapperTemplate_Simple.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 |