diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index 4d7809669..7303844cf 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -854,13 +854,12 @@ "stay": "Angemeldet bleiben", "connect-to-meta": "Mit Meta verbinden" }, - "original-version-backup-oculus": { - "title": "Achtung", - "body": { - "must-be-installed-once": "Sie müssen Beat Saber aus dem Oculus Store auf diesem Gerät installiert haben, andernfalls wird Beat Saber nach dem Start automatisch geschlossen.", - "will-backup": "Um diese Version zu starten, wird der ursprüngliche Installationsordner von Beat Saber in deiner Oculus-Bibliothek umbenannt und beim Schließen von Beat Saber automatisch wiederhergestellt." - }, - "not-remind-me": "Nicht mehr erinnern", + "enable-oculus-sideloaded-apps": { + "title": "Sideloading aktivieren", + "info-1": "Um Beat Saber starten zu können, muss die Möglichkeit aktiviert werden, Sideloading-Apps auszuführen. BSManager wird Administratorrechte anfordern, um diese Funktion automatisch zu aktivieren.", + "info-2": "Die Sideloading-Funktion ermöglicht das Starten von Spielen, die sich außerhalb Ihres Oculus-Bibliotheksordners befinden.", + "info-3": "Nachdem das Sideloading aktiviert wurde, bleibt die Funktion aktiv, und Sie werden nicht mehr aufgefordert, sie zu aktivieren.", + "i-want-to-do-it-myself": "Ich möchte es selbst machen", "understood": "Verstanden" }, "enter-meta-token": { diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 7356483f6..f1cca4128 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -849,13 +849,12 @@ "stay": "Remember me", "connect-to-meta": "Connect to Meta" }, - "original-version-backup-oculus": { - "title": "Warning", - "body": { - "must-be-installed-once": "You must have Beat Saber installed from the Oculus Store on this device, otherwise Beat Saber might automatically close after launching.", - "will-backup": "To launch this version, the original installation folder of Beat Saber located in your Oculus library will be renamed and will be automatically restored when Beat Saber is closed." - }, - "not-remind-me": "Do not remind me", + "enable-oculus-sideloaded-apps": { + "title": "Enable Sideloading", + "info-1": "In order to launch Beat Saber, the ability to run sideloaded apps must be enabled. BSManager will request administrator rights to enable this feature automatically.", + "info-2": "The sideloaded apps feature allows launching games located outside your Oculus library folder.", + "info-3": "After sideloading is activated, the feature will remain active, and you will no longer be prompted to enable it.", + "i-want-to-do-it-myself": "I want to do it myself", "understood": "Understood" }, "enter-meta-token": { diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index 2790779da..6679f994a 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -854,13 +854,12 @@ "stay": "Recuérdame", "connect-to-meta": "Conectarse a Meta" }, - "original-version-backup-oculus": { - "title": "Atención", - "body": { - "must-be-installed-once": "Debes tener Beat Saber instalado desde la tienda de Oculus en este dispositivo, de lo contrario, Beat Saber se cerrará automáticamente después de iniciarse.", - "will-backup": "Para lanzar esta versión, la carpeta de instalación original de Beat Saber ubicada en tu biblioteca de Oculus será renombrada y se restaurará automáticamente al cerrar Beat Saber." - }, - "not-remind-me": "No volver a recordármelo", + "enable-oculus-sideloaded-apps": { + "title": "Habilitar Sideloading", + "info-1": "Para poder iniciar Beat Saber, se debe habilitar la capacidad de ejecutar aplicaciones en sideloading. BSManager solicitará permisos de administrador para habilitar esta función automáticamente.", + "info-2": "La función de sideloading permite iniciar juegos ubicados fuera de la carpeta de la biblioteca de Oculus.", + "info-3": "Una vez que se active el sideloading, la función permanecerá activa y ya no se le pedirá que la habilite.", + "i-want-to-do-it-myself": "Quiero hacerlo yo mismo", "understood": "Entendido" }, "enter-meta-token": { diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index dd98d7911..36a68f7cd 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -855,13 +855,12 @@ "stay": "Se souvenir de moi", "connect-to-meta": "Se connecter à Meta" }, - "original-version-backup-oculus": { - "title": "Attention", - "body": { - "must-be-installed-once": "Vous devez avoir Beat Saber installé depuis le Oculus Store sur cet appareil, sinon Beat Saber se fermera automatiquement après le lancement.", - "will-backup": "Afin de lancer cette version, le dossier d'installation original de Beat Saber se trouvant dans votre bibliothèque Oculus va être renommé et sera automatiquement restauré à l'arrêt de Beat Saber." - }, - "not-remind-me": "Ne plus me rappeler", + "enable-oculus-sideloaded-apps": { + "title": "Activer le sideloading", + "info-1": "Pour lancer Beat Saber, la possibilité d'exécuter des applications en sideloading doit être activée. BSManager demandera les droits administrateur pour activer cette fonctionnalité automatiquement.", + "info-2": "La fonctionnalité de sideloading permet de lancer des jeux situés en dehors de votre dossier de bibliothèque Oculus.", + "info-3": "Une fois le sideloading activé, la fonctionnalité restera active et vous ne serez plus invité à l’activer.", + "i-want-to-do-it-myself": "Je veux le faire moi-même", "understood": "J'ai compris" }, "enter-meta-token": { diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index bf7860391..823fbd305 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -854,14 +854,13 @@ "stay": "記憶する", "connect-to-meta": "Metaに接続する" }, - "original-version-backup-oculus": { - "title": "注意", - "body": { - "must-be-installed-once": "このデバイスにOculusストアからBeat Saberをインストールしておく必要があります。そうしないと、Beat Saberは起動後に自動的に閉じます。", - "will-backup": "このバージョンを起動するために、OculusライブラリにあるBeat Saberの元のインストールフォルダは名前が変更され、Beat Saberの終了時に自動的に復元されます。" - }, - "not-remind-me": "二度と表示しないでください", - "understood": "分かった" + "enable-oculus-sideloaded-apps": { + "title": "サイドローディングを有効化", + "info-1": "Beat Saberを起動するには、サイドローディングアプリを実行する機能を有効にする必要があります。BSManagerは、この機能を自動的に有効にするために管理者権限を要求します。", + "info-2": "サイドローディング機能により、Oculusライブラリフォルダ外にあるゲームを起動できます。", + "info-3": "サイドローディングを有効化すると、この機能はアクティブなままとなり、再度有効化を求められることはありません。", + "i-want-to-do-it-myself": "自分でやりたい", + "understood": "了解しました" }, "enter-meta-token": { "title": "Oculusトークン", diff --git a/assets/jsons/translations/ko.json b/assets/jsons/translations/ko.json index 97f05591e..0e0fe2693 100644 --- a/assets/jsons/translations/ko.json +++ b/assets/jsons/translations/ko.json @@ -854,13 +854,12 @@ "stay": "기억하기", "connect-to-meta": "Meta에 연결하기" }, - "original-version-backup-oculus": { - "title": "주의", - "body": { - "must-be-installed-once": "이 장치에 Oculus 스토어에서 Beat Saber를 설치해야 합니다. 그렇지 않으면 Beat Saber가 실행 후 자동으로 종료됩니다..", - "will-backup": "이 버전을 실행하기 위해 Oculus 라이브러리에 있는 Beat Saber의 원래 설치 폴더의 이름이 변경되며, Beat Saber가 종료되면 자동으로 복원됩니다." - }, - "not-remind-me": "다시 표시하지 않기", + "enable-oculus-sideloaded-apps": { + "title": "사이드로딩 활성화", + "info-1": "Beat Saber를 실행하려면 사이드로딩 앱을 실행할 수 있는 기능을 활성화해야 합니다. BSManager는 이 기능을 자동으로 활성화하기 위해 관리자 권한을 요청할 것입니다.", + "info-2": "사이드로딩 기능을 사용하면 Oculus 라이브러리 폴더 외부에 있는 게임을 실행할 수 있습니다.", + "info-3": "사이드로딩(sideloading)이 활성화되면 이 기능은 계속 활성 상태를 유지하며, 더 이상 활성화를 요청받지 않습니다.", + "i-want-to-do-it-myself": "스스로 하고 싶습니다", "understood": "알겠습니다" }, "enter-meta-token": { diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index 518d87584..1c0593693 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -854,14 +854,13 @@ "stay": "Запомнить меня", "connect-to-meta": "Подключиться к Meta" }, - "original-version-backup-oculus": { - "title": "Внимание", - "body": { - "must-be-installed-once": "Вы должны установить Beat Saber из магазина Oculus на этом устройстве, в противном случае Beat Saber автоматически закроется после запуска.", - "will-backup": "Для запуска этой версии исходная папка установки Beat Saber, находящаяся в вашей библиотеке Oculus, будет переименована и автоматически восстановлена при выходе из Beat Saber." - }, - "not-remind-me": "Больше не напоминать", - "understood": "Понял" + "enable-oculus-sideloaded-apps": { + "title": "Включить Sideloading", + "info-1": "Чтобы запустить Beat Saber, необходимо включить возможность запуска приложений через sideloading. BSManager запросит права администратора для автоматического включения этой функции.", + "info-2": "Функция sideloading позволяет запускать игры, расположенные за пределами папки библиотеки Oculus.", + "info-3": "После активации сайдлоадинга функция останется активной, и вам больше не будет предложено её включить.", + "i-want-to-do-it-myself": "Я хочу сделать это сам", + "understood": "Понято" }, "enter-meta-token": { "title": "Токен Oculus", diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index 72bc3c5df..52f14a42f 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -854,14 +854,13 @@ "stay": "記住我", "connect-to-meta": "連接到 Meta" }, - "original-version-backup-oculus": { - "title": "警告", - "body": { - "must-be-installed-once": "您必須在此設備上從Oculus商店安裝Beat Saber,否則Beat Saber將在啟動後自動關閉。", - "will-backup": "為了啟動這個版本,位於您的Oculus庫中的Beat Saber的原始安裝文件夾將被重新命名,並且在Beat Saber關閉時會自動恢復。" - }, - "not-remind-me": "不再提醒我", - "understood": "明白了" + "enable-oculus-sideloaded-apps": { + "title": "啟用旁載", + "info-1": "為了啟動 Beat Saber,必須啟用執行旁載應用程式的功能。BSManager 將請求管理員權限以自動啟用此功能。", + "info-2": "旁載功能允許啟動位於 Oculus 資料庫資料夾之外的遊戲。", + "info-3": "啟用 sideloading 後,此功能將保持啟用狀態,您將不再被提示啟用它。", + "i-want-to-do-it-myself": "我想自己完成", + "understood": "了解" }, "enter-meta-token": { "title": "Oculus令牌", diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 06e1e3d5a..0ff820e35 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -854,13 +854,12 @@ "stay": "记住我", "connect-to-meta": "连接到 Meta" }, - "original-version-backup-oculus": { - "title": "警告", - "body": { - "must-be-installed-once": "您必须在此设备上从Oculus商店安装Beat Saber,否则Beat Saber将在启动后自动关闭。", - "will-backup": "为了启动这个版本,位于您的Oculus库中的Beat Saber的原始安装文件夹将被重命名,并且在Beat Saber关闭时会自动恢复。" - }, - "not-remind-me": "不再提醒我", + "enable-oculus-sideloaded-apps": { + "title": "启用旁加载", + "info-1": "为了启动 Beat Saber,必须启用运行旁加载应用程序的功能。BSManager 将请求管理员权限以自动启用此功能。", + "info-2": "旁加载功能允许启动位于 Oculus 库文件夹之外的游戏。", + "info-3": "激活 sideloading 后,该功能将保持激活状态,您将不再被提示启用它。", + "i-want-to-do-it-myself": "我想自己完成", "understood": "明白了" }, "enter-meta-token": { diff --git a/assets/scripts/oculus-allow-dev-sideloaded.exe b/assets/scripts/oculus-allow-dev-sideloaded.exe new file mode 100644 index 000000000..90c684bee Binary files /dev/null and b/assets/scripts/oculus-allow-dev-sideloaded.exe differ diff --git a/assets/scripts/oculus_symlink_cleaner.exe b/assets/scripts/oculus_symlink_cleaner.exe deleted file mode 100644 index 05659aeb7..000000000 Binary files a/assets/scripts/oculus_symlink_cleaner.exe and /dev/null differ diff --git a/docs/assets/enable-oculus-sideloading.png b/docs/assets/enable-oculus-sideloading.png new file mode 100644 index 000000000..e416283eb Binary files /dev/null and b/docs/assets/enable-oculus-sideloading.png differ diff --git a/docs/wiki/Activate-Oculus-sideloading.md b/docs/wiki/Activate-Oculus-sideloading.md new file mode 100644 index 000000000..7a5c35f53 --- /dev/null +++ b/docs/wiki/Activate-Oculus-sideloading.md @@ -0,0 +1,11 @@ +Enabling Oculus sideloading allows games located outside if your Oculus library to be played on your Oculus Quest. + +- **Step 1:** Start the regedit application by pressing `Win + R` and typing `regedit` in the dialog box. +- **Step 2:** Navigate to `HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Oculus VR, LLC\Oculus`. +- **Step 3:** Right-click on the right panel and select `New > DWORD (32-bit) Value`. +- **Step 4:** Name the new value `AllowDevSideloaded` and set the value to `1`. + +You should end up with something like this: +![image](https://raw.githubusercontent.com/Zagrios/bs-manager/refs/heads/master/docs/assets/enable-oculus-sideloading.png) + +After completing these steps, you should be able to start Beat Saber from BSManager and play it on your Oculus Quest. If you are still having issues, please join our [Discord](https://discord.gg/uSqbHVpKdV) server for further assistance. diff --git a/externals/oculus-allow-dev-sideloaded/.cargo/config.toml b/externals/oculus-allow-dev-sideloaded/.cargo/config.toml new file mode 100644 index 000000000..ac2b23f81 --- /dev/null +++ b/externals/oculus-allow-dev-sideloaded/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/externals/oculus-allow-dev-sideloaded/Cargo.lock b/externals/oculus-allow-dev-sideloaded/Cargo.lock new file mode 100644 index 000000000..0a369b4d6 --- /dev/null +++ b/externals/oculus-allow-dev-sideloaded/Cargo.lock @@ -0,0 +1,166 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "oculus-allow-dev-sideloaded" +version = "0.1.0" +dependencies = [ + "winreg", + "winres", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml", +] diff --git a/externals/oculus-symlink-cleaner/Cargo.toml b/externals/oculus-allow-dev-sideloaded/Cargo.toml similarity index 55% rename from externals/oculus-symlink-cleaner/Cargo.toml rename to externals/oculus-allow-dev-sideloaded/Cargo.toml index f7dcb7e04..d79af150b 100644 --- a/externals/oculus-symlink-cleaner/Cargo.toml +++ b/externals/oculus-allow-dev-sideloaded/Cargo.toml @@ -1,22 +1,20 @@ [package] -name = "oculus_symlink_cleaner" +name = "oculus-allow-dev-sideloaded" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [build-dependencies] winres = "0.1.12" [dependencies] -sysinfo = "0.30.6" +winreg = "0.52.0" + +[package.metadata.winres] +FileDescription = "Enable Sideloaded Apps" +LegalCopyright = "Copyright © 2024 Zagrios" +CompanyName = "Zagrios" [profile.release] codegen-units = 1 lto = true opt-level = "z" - -[package.metadata.winres] -FileDescription = "Clean Oculus Symlink after Beat Saber ends" -LegalCopyright = "Copyright © 2024 Zagrios" -CompanyName = "Zagrios" diff --git a/externals/oculus-allow-dev-sideloaded/build.rs b/externals/oculus-allow-dev-sideloaded/build.rs new file mode 100644 index 000000000..374f1355f --- /dev/null +++ b/externals/oculus-allow-dev-sideloaded/build.rs @@ -0,0 +1,20 @@ +extern crate winres; + +fn main() { + if cfg!(target_os = "windows") { + let mut res = winres::WindowsResource::new(); + res.set_manifest(r#" + + + + + + + + + + "#); + res.set_icon("./icon.ico"); + res.compile().unwrap(); + } +} diff --git a/externals/oculus-symlink-cleaner/icon.ico b/externals/oculus-allow-dev-sideloaded/icon.ico similarity index 100% rename from externals/oculus-symlink-cleaner/icon.ico rename to externals/oculus-allow-dev-sideloaded/icon.ico diff --git a/externals/oculus-allow-dev-sideloaded/src/main.rs b/externals/oculus-allow-dev-sideloaded/src/main.rs new file mode 100644 index 000000000..d2155d624 --- /dev/null +++ b/externals/oculus-allow-dev-sideloaded/src/main.rs @@ -0,0 +1,22 @@ +use winreg::enums::HKEY_LOCAL_MACHINE; +use winreg::RegKey; + +const PATH: &str = "SOFTWARE\\Wow6432Node\\Oculus VR, LLC\\Oculus"; + +fn main() { + let hklm = RegKey::predef(HKEY_LOCAL_MACHINE); + + // Create (or open if it already exists) the subkey + let (key, _) = match hklm.create_subkey(PATH) { + Ok(res) => res, + Err(err) => return println!("{}", err.to_string()), + }; + + let res = key.set_value("AllowDevSideloaded", &1u32); + + if let Err(err) = res { + return println!("{}", err.to_string()); + } + + println!("AllowDevSideloaded = 1 has been successfully set in {PATH}"); +} diff --git a/externals/oculus-symlink-cleaner/.cargo/config.toml b/externals/oculus-symlink-cleaner/.cargo/config.toml deleted file mode 100644 index 0c17df095..000000000 --- a/externals/oculus-symlink-cleaner/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.x86_64-pc-windows-msvc] -rustflags = ["-C", "target-feature=+crt-static"] \ No newline at end of file diff --git a/externals/oculus-symlink-cleaner/Cargo.lock b/externals/oculus-symlink-cleaner/Cargo.lock deleted file mode 100644 index 233feb343..000000000 --- a/externals/oculus-symlink-cleaner/Cargo.lock +++ /dev/null @@ -1,281 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "oculus_symlink_cleaner" -version = "0.1.0" -dependencies = [ - "sysinfo", - "winres", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "serde" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.197" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "syn" -version = "2.0.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sysinfo" -version = "0.30.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6746919caf9f2a85bff759535664c060109f21975c5ac2e8652e60102bd4d196" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "windows", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winres" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" -dependencies = [ - "toml", -] diff --git a/externals/oculus-symlink-cleaner/build.rs b/externals/oculus-symlink-cleaner/build.rs deleted file mode 100644 index ac40e20cc..000000000 --- a/externals/oculus-symlink-cleaner/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -extern crate winres; - -fn main() { - if cfg!(target_os = "windows") { - let mut res = winres::WindowsResource::new(); - res.set_icon("./icon.ico"); - res.compile().unwrap(); - } -} diff --git a/externals/oculus-symlink-cleaner/src/main.rs b/externals/oculus-symlink-cleaner/src/main.rs deleted file mode 100644 index 5d3a39ed8..000000000 --- a/externals/oculus-symlink-cleaner/src/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -use sysinfo::{Pid, ProcessRefreshKind, System}; -use std::{env, thread, time::Duration, fs}; -use std::ffi::OsStr; -use std::path::Path; -use std::str::FromStr; - -const BEAT_SABER_OCULUS_FOLDER_NAME: &str = "hyperbolic-magnetism-beat-saber"; - -fn main() { - let args: Vec = env::args().collect(); - if args.len() != 3 { - eprintln!("Usage: {} ", args[0]); - std::process::exit(1); - } - - let pid: Pid = Pid::from_str(&args[1]).expect("Invalid pid"); - let directory_path = Path::new(&args[2]); - - if directory_path.file_name() != Some(OsStr::new(BEAT_SABER_OCULUS_FOLDER_NAME)) { - eprintln!("Directory name is not {}", BEAT_SABER_OCULUS_FOLDER_NAME); - std::process::exit(1); - } - - // we want to monitor only processes - let mut system = System::new_with_specifics(sysinfo::RefreshKind::new().with_processes(ProcessRefreshKind::everything())); - - loop { - system.refresh_processes(); - let process = system.process(pid); - - match process { - Some(_) => { - println!("Process {} is still running.", pid); - thread::sleep(Duration::from_secs(2)); - }, - None => { - println!("Process {} has stopped, deleting directory {:?}", pid, directory_path); - match delete_dir_if_is_symlink(directory_path) { - Ok(_) => { - println!("Directory {:?} deleted successfully.", directory_path); - if let Err(e) = rename_specific_backup_directory(directory_path.parent().unwrap()) { - eprintln!("Failed to rename backup directory: {}", e); - } - }, - Err(e) => eprintln!("Failed to delete directory {:?}: {}", directory_path, e), - } - break; - } - } - } -} - -fn delete_dir_if_is_symlink(path: &Path) -> Result<(), Box>{ - match path.symlink_metadata()?.file_type().is_symlink() { - true => { - fs::remove_dir_all(path)?; - Ok(()) - }, - false => { - eprintln!("Path {:?} is not a symlink.", path); - Err("Path is not a symlink.".into()) - } - } -} - -fn rename_specific_backup_directory(parent_path: &Path) -> Result<(), Box> { - let backup_dir_name = format!("{}.bsmbak", BEAT_SABER_OCULUS_FOLDER_NAME); - let backup_path = parent_path.join(&backup_dir_name); - if backup_path.exists() && backup_path.is_dir() { - let new_path = parent_path.join(BEAT_SABER_OCULUS_FOLDER_NAME); - fs::rename(&backup_path, &new_path)?; - println!("Renamed {:?} to {:?}", backup_path, new_path); - Ok(()) - } else { - Err(format!("Backup directory {:?} does not exist or is not a directory.", backup_path).into()) - } -} - diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index b8d20b46f..aa4f6318f 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -15,3 +15,4 @@ import "./bs-model-ipcs"; import "./bs-version-download/bs-download-ipcs"; import "./static-configuration.ipcs"; import "./linux.ipcs.ts"; +import "./oculus.ipcs"; diff --git a/src/main/ipcs/oculus.ipcs.ts b/src/main/ipcs/oculus.ipcs.ts new file mode 100644 index 000000000..ba55fd240 --- /dev/null +++ b/src/main/ipcs/oculus.ipcs.ts @@ -0,0 +1,15 @@ +import { OculusService } from "../services/oculus.service"; +import { IpcService } from "../services/ipc.service"; +import { from } from "rxjs"; + +const ipc = IpcService.getInstance(); + +ipc.on("is-oculus-sideloaded-apps-enabled", (_, reply) => { + const oculusService = OculusService.getInstance(); + reply(from(oculusService.isSideLoadedAppsEnabled())); +}); + +ipc.on("enable-oculus-sideloaded-apps", (_, reply) => { + const oculusService = OculusService.getInstance(); + reply(from(oculusService.enableSideloadedApps())); +}); diff --git a/src/main/services/bs-launcher/oculus-launcher.service.ts b/src/main/services/bs-launcher/oculus-launcher.service.ts index ac9457bbf..d60ffe3a1 100644 --- a/src/main/services/bs-launcher/oculus-launcher.service.ts +++ b/src/main/services/bs-launcher/oculus-launcher.service.ts @@ -1,20 +1,15 @@ -import { Observable, ReplaySubject, catchError, lastValueFrom, of, take, timeout } from "rxjs"; +import { Observable, ReplaySubject } from "rxjs"; import { StoreLauncherInterface } from "./store-launcher.interface"; import { BSLaunchError, BSLaunchEvent, BSLaunchEventData, LaunchOption } from "../../../shared/models/bs-launch"; import { OculusService } from "../oculus.service"; -import { BS_EXECUTABLE, OCULUS_BS_BACKUP_DIR, OCULUS_BS_DIR } from "../../constants"; +import { BS_EXECUTABLE } from "../../constants"; import path from "path"; import log from "electron-log"; -import { sToMs } from "../../../shared/helpers/time.helpers"; -import { lstat, pathExists, readdir, readlink, rename, symlink, unlink } from "fs-extra"; +import { pathExists } from "fs-extra"; import { AbstractLauncherService } from "./abstract-launcher.service"; import { isProcessRunning } from "../../helpers/os.helpers"; import { CustomError } from "../../../shared/models/exceptions/custom-error.class"; -import { InstallationLocationService } from "../installation-location.service"; -import { ensurePathNotAlreadyExist } from "../../helpers/fs.helpers"; import { UtilsService } from "../utils.service"; -import { spawn } from "child_process"; -import { tryit } from "../../../shared/helpers/error.helpers"; export class OculusLauncherService extends AbstractLauncherService implements StoreLauncherInterface { @@ -28,7 +23,6 @@ export class OculusLauncherService extends AbstractLauncherService implements St } private readonly oculus: OculusService; - private readonly pathsService: InstallationLocationService; private readonly util: UtilsService; private readonly oculusLib$ = new ReplaySubject(); @@ -36,120 +30,11 @@ export class OculusLauncherService extends AbstractLauncherService implements St private constructor() { super(); this.oculus = OculusService.getInstance(); - this.pathsService = InstallationLocationService.getInstance(); this.util = UtilsService.getInstance(); - - this.oculus.tryGetGameFolder([OCULUS_BS_DIR, OCULUS_BS_BACKUP_DIR]).then(async dirPath => { - if(dirPath){ - return this.oculusLib$.next( path.join(dirPath, "..") ); - } - const defaultLib = ((await this.oculus.getOculusLibs()) || []).find(lib => lib.isDefault); - if(defaultLib?.path){ return this.oculusLib$.next(path.join(defaultLib.path, "Software")); } - this.oculusLib$.next(null); - }).catch(err => { - log.error("Error while getting Oculus libs", err); - this.oculusLib$.next(null); - }); - } - - public async deleteBsSymlinks(): Promise { - const oculusLibPath = await lastValueFrom(this.oculusLib$.pipe(take(1), timeout(sToMs(30)), catchError(() => of(null)))); - - if(!oculusLibPath || !(await pathExists(oculusLibPath))){ - throw new Error("Oculus library not found, deleteBsSymlinks"); - } - - const libContents = await readdir(oculusLibPath); - const symlinks = (await Promise.all(libContents.map(async dir => { - return (await lstat(path.join(oculusLibPath, dir))).isSymbolicLink() ? dir : null; - }))).filter(Boolean); - - log.info("Symlinks found in Oculus library", symlinks); - - const bsSymlinks = symlinks.filter(dirent => dirent.startsWith(OCULUS_BS_DIR)); - - const bsmSymlinks = (await Promise.all(bsSymlinks.map(async symlink => { - const symlinkPath = path.join(oculusLibPath, symlink); - const targetPath = await readlink(symlinkPath).catch(err => log.error(err)); - - log.info("Oculus Symlink", symlink, "target", targetPath); - - if(!targetPath){ return null; } - - const bsmVersionsDir = path.join(this.pathsService.INSTALLATION_FOLDER, this.pathsService.VERSIONS_FOLDER); - - if(!targetPath.includes(bsmVersionsDir)){ return null; } - - return symlink; - }))).filter(Boolean); - - await Promise.all(bsmSymlinks.map(symlink => { - log.info("Delete symlink", symlink); - return unlink(path.join(oculusLibPath, symlink)); - })); - } - - private async backupOriginalBeatSaber(): Promise{ - const bsFolder = await this.oculus.getGameFolder(OCULUS_BS_DIR); - if(!bsFolder){ return; } - const backupPath = await ensurePathNotAlreadyExist(path.join(bsFolder, "..", OCULUS_BS_BACKUP_DIR)); - log.info("Backing up original Beat Saber", bsFolder, backupPath); - return rename(bsFolder, backupPath); - } - - public async restoreOriginalBeatSaber(): Promise{ - const bsFolderBackupPath = await this.oculus.getGameFolder(OCULUS_BS_BACKUP_DIR); - if(!(await pathExists(bsFolderBackupPath))){ return; } - const originalPath = path.join(bsFolderBackupPath, "..", OCULUS_BS_DIR); - log.info("Restoring original Beat Saber", bsFolderBackupPath, originalPath); - return rename(bsFolderBackupPath, originalPath); - } - - private launchSymlinkCleaner(pid: number, symlinkPath: string){ - - log.info("Launch Symlink Cleaner", pid, symlinkPath); - - const exeName = "oculus_symlink_cleaner.exe"; - const scriptPath = this.util.getAssetsScriptsPath(); - spawn(path.join(scriptPath, exeName), [`${pid}`, symlinkPath], { detached: true, stdio: "ignore" }); } public launch(launchOptions: LaunchOption): Observable { - const prepareOriginalVersion: () => Promise = async () => { - await this.restoreOriginalBeatSaber(); - const bsPath = await this.oculus.getGameFolder(OCULUS_BS_DIR); - if(!bsPath){ - throw new Error("Oculus Beat Saber path not found"); - } - return bsPath; - } - - const prepareDowngradedVersion: () => Promise = async () => { - - const originalVersionPath = await prepareOriginalVersion().catch(() => null); - if (!originalVersionPath) { - throw CustomError.fromError(new Error("Original Oculus Beat Saber not installed"), BSLaunchError.ORIGINAL_OCULUS_NOT_INSTALLED); - } - - const oculusLib = await lastValueFrom(this.oculusLib$.pipe(take(1), timeout(sToMs(30)), catchError(() => of(null)))); - - if(!oculusLib){ - throw CustomError.fromError(new Error("No Oculus library found"), BSLaunchError.OCULUS_LIB_NOT_FOUND); - } - - // Backup original Beat Saber folder - await this.backupOriginalBeatSaber(); - - // Create symlink in the oculus library from the BSM BS version - const symlinkTarget = await this.localVersions.getInstalledVersionPath(launchOptions.version); - const symlinkPath = path.join(oculusLib, OCULUS_BS_DIR); - log.info("Creating symlink", symlinkTarget, symlinkPath); - await symlink(symlinkTarget, symlinkPath, "junction"); - - return symlinkPath; - } - return new Observable(obs => { (async () => { @@ -159,11 +44,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St throw CustomError.fromError(new Error("Cannot start two instance of Beat Saber for Oculus"), BSLaunchError.BS_ALREADY_RUNNING); } - // Remove previously symlinks created by BSM - await this.deleteBsSymlinks().catch(err => log.error("Error while deleting BSM symlinks", err)); - - const bsPath = await (launchOptions.version.oculus ? prepareOriginalVersion() : prepareDowngradedVersion()); - + const bsPath = await this.localVersions.getInstalledVersionPath(launchOptions.version); const exePath = path.join(bsPath, BS_EXECUTABLE); if(!(await pathExists(exePath))){ @@ -178,13 +59,6 @@ export class OculusLauncherService extends AbstractLauncherService implements St // Launch Beat Saber const process = this.launchBs(exePath, this.buildBsLaunchArgs(launchOptions)); - if(launchOptions.version.oculus !== true && process?.process?.pid){ - const { error } = tryit(() => this.launchSymlinkCleaner(process.process.pid, bsPath)); - if(error){ - log.error("Error while launching symlink cleaner", error); - } - } - return process.exit.catch(err => { throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR); }); diff --git a/src/main/services/oculus.service.ts b/src/main/services/oculus.service.ts index 8647ef3cb..05dc5ad50 100644 --- a/src/main/services/oculus.service.ts +++ b/src/main/services/oculus.service.ts @@ -7,14 +7,14 @@ import { shell } from "electron"; import { isProcessRunning } from "../helpers/os.helpers"; import { sToMs } from "../../shared/helpers/time.helpers"; import { execOnOs } from "../helpers/env.helpers"; +import { UtilsService } from "./utils.service"; +import { exec } from "child_process"; const { list } = (execOnOs({ win32: () => require("regedit-rs") }, true) ?? {}) as typeof import("regedit-rs"); export class OculusService { private static instance: OculusService; - private oculusLibraries: OculusLibrary[]; - public static getInstance(): OculusService { if (!OculusService.instance) { OculusService.instance = new OculusService(); @@ -22,7 +22,14 @@ export class OculusService { return OculusService.instance; } - private constructor() {} + + private readonly utils: UtilsService; + + private oculusLibraries: OculusLibrary[]; + + private constructor() { + this.utils = UtilsService.getInstance(); + } public async getOculusLibs(): Promise { if (process.platform !== "win32") { @@ -120,6 +127,67 @@ export class OculusService { }, sToMs(30)); }); } + + public async isSideLoadedAppsEnabled(): Promise { + if(process.platform !== "win32"){ + log.info("Cannot check sideloaded apps on non-windows platforms"); + throw new Error("Cannot check sideloaded apps on non-windows platforms"); + } + + const regPath = "HKLM\\SOFTWARE\\Wow6432Node\\Oculus VR, LLC\\Oculus"; + const res = await list(regPath).then(res => res[regPath]); + + if(!res.exists){ + log.info("Registry key not found", regPath); + return false; + } + + const value = res.values?.AllowDevSideloaded; + + if(!value){ + log.info("Registry value not found", "AllowDevSideloaded"); + return false; + } + + return value.value === 1; + } + + public async enableSideloadedApps(): Promise { + if(process.platform !== "win32"){ + log.info("Cannot enable sideloaded apps on non-windows platforms"); + return; + } + + const enabled = await this.isSideLoadedAppsEnabled(); + + if(enabled){ + log.info("Sideloaded apps already enabled"); + return; + } + + const exePath = path.join(this.utils.getAssetsScriptsPath(), "oculus-allow-dev-sideloaded.exe"); + + return new Promise((resolve, reject) => { + log.info("Enabling sideloaded apps"); + const process = exec(exePath); + process.on("exit", code => { + if(code === 0){ + resolve(); + } else { + reject(new Error(`Failed to enable sideloaded apps, exit code: ${code}`)); + } + }); + + process.on("error", err => { + log.error("Error while enabling sideloaded apps", err); + reject(err); + }); + + process.stdout.on("data", data => { + log.info(data.toString?.() ?? data); + }); + }) + } } export interface OculusLibrary { diff --git a/src/renderer/components/modal/modal-types/original-oculus-version-backup.modal.tsx b/src/renderer/components/modal/modal-types/enable-oculus-sideloaded-apps.tsx similarity index 50% rename from src/renderer/components/modal/modal-types/original-oculus-version-backup.modal.tsx rename to src/renderer/components/modal/modal-types/enable-oculus-sideloaded-apps.tsx index 81bc37651..48b785adb 100644 --- a/src/renderer/components/modal/modal-types/original-oculus-version-backup.modal.tsx +++ b/src/renderer/components/modal/modal-types/enable-oculus-sideloaded-apps.tsx @@ -2,31 +2,26 @@ import { ModalComponent, ModalExitCode } from "renderer/services/modale.service" import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"; import { BsmButton } from "renderer/components/shared/bsm-button.component"; import { BsmImage } from "renderer/components/shared/bsm-image.component"; -import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component"; -import { useState } from "react"; -import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { useTranslationV2 } from "renderer/hooks/use-translation.hook"; -export const OriginalOculusVersionBackupModal: ModalComponent = ({ resolver }) => { +export const EnableOculusSideloadedApps: ModalComponent = ({ resolver }) => { - const t = useTranslation(); - const [dontShowAgain, setDontShowAgain] = useState(false); + const { text: t } = useTranslationV2(); const submit = () => { - resolver({ exitCode: ModalExitCode.COMPLETED, data: dontShowAgain }); + resolver({ exitCode: ModalExitCode.COMPLETED }); } return (
-

{t("modals.original-version-backup-oculus.title")}

- +

{t("modals.enable-oculus-sideloaded-apps.title")}

+ -

{t("modals.original-version-backup-oculus.body.must-be-installed-once")}

-

{t("modals.original-version-backup-oculus.body.will-backup")}

+

{t("modals.enable-oculus-sideloaded-apps.info-1")}

+

{t("modals.enable-oculus-sideloaded-apps.info-2")}

+

{t("modals.enable-oculus-sideloaded-apps.info-3")}

-
- - {t("modals.original-version-backup-oculus.not-remind-me")} -
+ {t("modals.enable-oculus-sideloaded-apps.i-want-to-do-it-myself")}
= ( className="rounded-md transition-all h-10 flex items-center justify-center" onClick={submit} withBar={false} - text="modals.original-version-backup-oculus.understood" + text="modals.enable-oculus-sideloaded-apps.understood" />
diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index 3c78ff16e..00766b272 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -7,7 +7,7 @@ import { ConfigurationService } from "./configuration.service"; import { ThemeService } from "./theme.service"; import { BsStore } from "shared/models/bs-store.enum"; import { ModalExitCode, ModalService } from "./modale.service"; -import { OriginalOculusVersionBackupModal } from "renderer/components/modal/modal-types/original-oculus-version-backup.modal"; +import { EnableOculusSideloadedApps } from "renderer/components/modal/modal-types/enable-oculus-sideloaded-apps"; import { CustomError } from "shared/models/exceptions/custom-error.class"; import { sToMs } from "shared/helpers/time.helpers"; import { NeedLaunchAdminModal } from "renderer/components/modal/modal-types/need-launch-admin-modal.component"; @@ -80,6 +80,20 @@ export class BSLauncherService { return true; } + private async enableSideloadedAppsIfNeeded(): Promise { + if(window.electron.platform !== "win32"){ return; } + const isSideloadedAppsEnabled = await lastValueFrom(this.ipcService.sendV2("is-oculus-sideloaded-apps-enabled")); + if(isSideloadedAppsEnabled){ return; } + + const modalRes = await this.modals.openModal(EnableOculusSideloadedApps); + + if(modalRes.exitCode !== ModalExitCode.COMPLETED){ + throw new Error("Enable sideloaded apps canceled"); + } + + await lastValueFrom(this.ipcService.sendV2("enable-oculus-sideloaded-apps")); + } + public doLaunch(launchOptions: LaunchOption): Observable{ return this.ipcService.sendV2("bs-launch.launch", launchOptions); } @@ -89,10 +103,9 @@ export class BSLauncherService { return new Observable(obs => { (async () => { - if(launchOptions.version.metadata?.store === BsStore.OCULUS && !this.notRewindBackupOculus()){ - const { exitCode, data: notRewind } = await this.modals.openModal(OriginalOculusVersionBackupModal); - if(exitCode !== ModalExitCode.COMPLETED){ return; } - this.setNotRewindBackupOculus(notRewind); + // If downgraded from oculus and its not the official version + if(launchOptions.version.metadata?.store === BsStore.OCULUS && !launchOptions.version.oculus){ + await this.enableSideloadedAppsIfNeeded(); } if(launchOptions.version.metadata?.store !== BsStore.OCULUS){ diff --git a/src/shared/models/ipc/ipc-routes.ts b/src/shared/models/ipc/ipc-routes.ts index ce87b62d8..bf19b76a4 100644 --- a/src/shared/models/ipc/ipc-routes.ts +++ b/src/shared/models/ipc/ipc-routes.ts @@ -161,6 +161,10 @@ export interface IpcChannelMapping { /* ** linux.ipcs ** */ "linux.verify-proton-folder": { request: void, response: boolean }; + /* ** oculus.ipcs ** */ + "is-oculus-sideloaded-apps-enabled": { request: void, response: boolean }; + "enable-oculus-sideloaded-apps": { request: void, response: void }; + /* ** OTHERS (if your IPC channel is not in a "-ipcs" file, put it here) ** */ "shortcut-launch-options": { request: void, response: LaunchOption }; }