diff --git a/.eslintrc.js b/.eslintrc.js index ac327b68..5b6d738c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -46,7 +46,12 @@ module.exports = { "react/prop-types": "off", "import/no-cycle": "off", "prefer-promise-reject-errors": "off", - "react/jsx-no-target-blank": "off" + "react/jsx-no-target-blank": "off", + "@typescript-eslint/ban-types": ["error", { + types: { + Function: false, + } + }] }, parserOptions: { ecmaVersion: 2020, diff --git a/.gitignore b/.gitignore index da134307..ad436745 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ npm-debug.log.* *.css.d.ts *.sass.d.ts *.scss.d.ts + +# externals +externals/bs-admin-start/target/* diff --git a/assets/jsons/patreons.json b/assets/jsons/patreons.json index 4a876ea3..a6eaed66 100644 --- a/assets/jsons/patreons.json +++ b/assets/jsons/patreons.json @@ -67,5 +67,11 @@ "username": "Lumberjack462", "type": "diamond", "link": "https://www.youtube.com/@lumberjack462" + }, + { + "username": "Xero" + }, + { + "username": "Minescence" } -] \ No newline at end of file +] diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index a29e3579..dbbe2329 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -723,6 +723,16 @@ "enter-oculus-token": "Ein Oculus-Token eingeben" }, "valid-btn": "Validieren" + }, + "launch-as-admin": { + "title": "Administratorberechtigungen", + "body": { + "info": "Es wurde festgestellt, dass Steam mit Administratorrechten ausgeführt wird. Um mit Steam zu kommunizieren, muss auch Beat Saber als Administrator gestartet werden. Andernfalls könnte Beat Saber Probleme haben und nach dem Starten schließen.", + "info-2": "Das Starten von Beat Saber im Administratormodus gibt auch den installierten Mods Administratorrechte. Daher wird empfohlen, Steam ohne Administratorrechte neu zu starten.", + "info-3": "Beachten Sie, dass es nicht ratsam ist, Steam Administratorrechte zu geben, da dies auch die installierten Spiele und Mods betrifft und ein Sicherheitsrisiko darstellt." + }, + "launch-as-admin": "Als Administrator starten", + "not-remind-me": "Nicht mehr erinnern" } }, "maps": { diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 0f25f568..5f669d62 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -723,6 +723,16 @@ "enter-oculus-token": "Enter an Oculus token" }, "valid-btn": "Validate" + }, + "launch-as-admin": { + "title": "Administrator Permissions", + "body": { + "info": "Steam has been detected as running with administrator permissions. To communicate with Steam, Beat Saber must also be launched as an administrator. Otherwise, Beat Saber might experience problems and close after launching.", + "info-2": "Launching Beat Saber in administrator mode will also give administrator rights to the installed mods. Therefore, it is recommended to restart Steam without administrator permissions.", + "info-3": "Note that it is advised against giving administrator rights to Steam, as this affects the installed games and mods, presenting a security risk." + }, + "launch-as-admin": "Launch as Administrator", + "not-remind-me": "Do not remind me" } }, "maps": { diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index 4e8ed44a..019d4020 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -723,6 +723,16 @@ "enter-oculus-token": "Introducir un token de Oculus" }, "valid-btn": "Validar" + }, + "launch-as-admin": { + "title": "Permisos Administrador", + "body": { + "info": "Se ha detectado que Steam está funcionando con permisos de administrador. Para comunicarse con Steam, Beat Saber también debe iniciarse como administrador. De lo contrario, Beat Saber podría experimentar problemas y cerrarse después de iniciar.", + "info-2": "Iniciar Beat Saber en modo administrador también otorgará derechos de administrador a los mods instalados. Por lo tanto, se recomienda reiniciar Steam sin permisos de administrador.", + "info-3": "Tenga en cuenta que se desaconseja otorgar derechos de administrador a Steam, ya que esto afecta a los juegos y mods instalados, presentando un riesgo de seguridad." + }, + "launch-as-admin": "Iniciar como Administrador", + "not-remind-me": "No volver a recordármelo" } }, "maps": { diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index 2daec164..76ea9252 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -723,6 +723,16 @@ "enter-oculus-token": "Entrer un token Oculus" }, "valid-btn": "Valider" + }, + "launch-as-admin": { + "title": "Permissions Administrateur", + "body": { + "info": "Steam a été détecté comme fonctionnant avec des permissions administrateur. Afin communiquer avec Steam, Beat Saber doit également être lancé en tant qu'administrateur. Dans le cas contraire, Beat Saber pourrait rencontrer des problèmes et se fermer après son lancement.", + "info-2": "Lancer Beat Saber en mode administrateur donnera également les droits administrateur aux mods installés. Il est donc plutôt recommandé de redémarrer Steam sans permissions administrateur.", + "info-3": "Notez qu'il est déconseillé de donner des droits d'administrateur à Steam, car cela affecte également les jeux et mods installés, présentant un risque pour la sécurité." + }, + "launch-as-admin": "Lancer en administateur", + "not-remind-me": "Ne plus me rappeler" } }, "maps": { diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index 2e6aa5d9..6df738b2 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -723,6 +723,16 @@ "enter-oculus-token": "オキュラストークンを入力する" }, "valid-btn": "確認" + }, + "launch-as-admin": { + "title": "管理者権限", + "body": { + "info": "Steamが管理者権限で動作していることが検出されました。Steamと通信するためには、Beat Saberも管理者として起動する必要があります。そうでない場合、Beat Saberは問題が発生し、起動後に終了する可能性があります。", + "info-2": "管理者モードでBeat Saberを起動すると、インストールされたMODにも管理者権限が与えられます。そのため、管理者権限なしでSteamを再起動することをお勧めします。", + "info-3": "Steamに管理者権限を与えることはお勧めできません。これはインストールされたゲームやMODにも影響を与え、セキュリティリスクをもたらすからです。" + }, + "launch-as-admin": "管理者として起動", + "not-remind-me": "Больше не напоминать" } }, "maps": { diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index a40d8c28..3bc6cf26 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -723,6 +723,15 @@ "enter-oculus-token": "Введите токен Oculus" }, "valid-btn": "Подтвердить" + }, + "launch-as-admin": { + "title": "Права Администратора", + "body": { + "info": "Обнаружено, что Steam работает с правами администратора. Чтобы связаться со Steam, Beat Saber также должен быть запущен от имени администратора. В противном случае Beat Saber может столкнуться с проблемами и закрыться после запуска.", + "info-2": "Запуск Beat Saber в режиме администратора также даст права администратора установленным модам. Поэтому рекомендуется перезапустить Steam без прав администратора.", + "info-3": "Обратите внимание, что не рекомендуется предоставлять права администратора Steam, так как это также влияет на установленные игры и моды, представляя угрозу безопасности." + }, + "launch-as-admin": "Запустить от имени администратора" } }, "maps": { diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index 93970058..96cde310 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -723,6 +723,16 @@ "enter-oculus-token": "輸入Oculus令牌" }, "valid-btn": "驗證" + }, + "launch-as-admin": { + "title": "管理員權限", + "body": { + "info": "檢測到Steam正在以管理員權限運行。為了與Steam通信,Beat Saber也必須以管理員身份啟動。否則,Beat Saber可能會遇到問題並在啟動後關閉。", + "info-2": "以管理員模式啟動Beat Saber還會將管理員權限賦予已安裝的模組。因此,建議重啟Steam,不使用管理員權限。", + "info-3": "請注意,不建議給Steam管理員權限,因為這也會影響已安裝的遊戲和模組,帶來安全風險。" + }, + "launch-as-admin": "以管理員身份啟動", + "not-remind-me": "不再提醒我" } }, "maps": { diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 192d9bed..243f29a5 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -723,6 +723,16 @@ "enter-oculus-token": "输入Oculus令牌" }, "valid-btn": "验证" + }, + "launch-as-admin": { + "title": "管理员权限", + "body": { + "info": "检测到Steam正在以管理员权限运行。为了与Steam通信,Beat Saber也必须以管理员身份启动。否则,Beat Saber可能会遇到问题并在启动后关闭。", + "info-2": "以管理员模式启动Beat Saber还会将管理员权限赋予已安装的模组。因此,建议重启Steam,不使用管理员权限。", + "info-3": "请注意,不建议给Steam管理员权限,因为这也会影响已安装的游戏和模组,带来安全风险。" + }, + "launch-as-admin": "以管理员身份启动", + "not-remind-me": "不再提醒我" } }, "maps": { diff --git a/assets/scripts/start_beat_saber_admin.exe b/assets/scripts/start_beat_saber_admin.exe new file mode 100644 index 00000000..c898e9d3 Binary files /dev/null and b/assets/scripts/start_beat_saber_admin.exe differ diff --git a/externals/bs-admin-start/.cargo/config.toml b/externals/bs-admin-start/.cargo/config.toml new file mode 100644 index 00000000..ac2b23f8 --- /dev/null +++ b/externals/bs-admin-start/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/externals/bs-admin-start/Cargo.lock b/externals/bs-admin-start/Cargo.lock new file mode 100644 index 00000000..f15d8279 --- /dev/null +++ b/externals/bs-admin-start/Cargo.lock @@ -0,0 +1,83 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +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 = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "start_beat_saber_admin" +version = "0.1.0" +dependencies = [ + "winres", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[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/bs-admin-start/Cargo.toml b/externals/bs-admin-start/Cargo.toml new file mode 100644 index 00000000..118f1b32 --- /dev/null +++ b/externals/bs-admin-start/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "start_beat_saber_admin" +version = "0.1.0" +edition = "2021" +build = "build.rs" + +[build-dependencies] +winres = "0.1.12" + +[profile.release] +codegen-units = 1 +lto = true +opt-level = "z" + +[package.metadata.winres] +FileDescription = "Start Beat Saber as Admin" +LegalCopyright = "Copyright © 2024 Zagrios" +CompanyName = "Zagrios" diff --git a/externals/bs-admin-start/build.rs b/externals/bs-admin-start/build.rs new file mode 100644 index 00000000..374f1355 --- /dev/null +++ b/externals/bs-admin-start/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/bs-admin-start/icon.ico b/externals/bs-admin-start/icon.ico new file mode 100644 index 00000000..660923e2 Binary files /dev/null and b/externals/bs-admin-start/icon.ico differ diff --git a/externals/bs-admin-start/src/main.rs b/externals/bs-admin-start/src/main.rs new file mode 100644 index 00000000..5964e94f --- /dev/null +++ b/externals/bs-admin-start/src/main.rs @@ -0,0 +1,47 @@ + +use std::{process::Command, path::Path}; + +const EXECUTABLE_NAME: &str = "Beat Saber.exe"; +const AUTORIZED_ARGS: [&str; 8] = [ + "fpfc", + "-vrmode", + "oculus", + "--verbose", + "--no-yeet", + "--disable-autoupdate", + "--combine-logs", + "editor" +]; + +fn main() { + let args: Vec = std::env::args().collect(); + + let executable_path = Path::new(&args[1]); + + if executable_path.file_name().unwrap() != EXECUTABLE_NAME { + eprintln!("Executable path is not Beat Saber.exe."); + return; + } + + if !executable_path.exists() || !executable_path.is_file() { + eprintln!("Executable path is not valid."); + return; + } + + let mut command = Command::new(executable_path); + + if let Some(parent_dir) = executable_path.parent() { + command.current_dir(parent_dir); + } + + for arg in args.iter().skip(2) { + if !AUTORIZED_ARGS.contains(&arg.as_str()) { + continue; + } + command.arg(arg); + } + + command.env("SteamAppId", "620980"); + + let _ = command.spawn(); +} diff --git a/package-lock.json b/package-lock.json index d6c579b2..f5ccb3a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "framer-motion": "^10.16.16", "fs-extra": "^11.2.0", "history": "^5.3.0", + "is-elevated": "^3.0.0", "jszip": "^3.10.1", "md5-file": "^5.0.0", "node-abi": "^3.47.0", @@ -7069,7 +7070,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "dependencies": { "once": "^1.4.0" } @@ -10313,6 +10313,125 @@ "node": ">= 10" } }, + "node_modules/is-admin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-admin/-/is-admin-3.0.0.tgz", + "integrity": "sha512-wOa3CXFJAu8BZ2BDtG9xYOOrsq6oiSvc2jFPy4X/HINx5bmJUcW8e+apItVbU2E7GIfBVaFVO7Zit4oAWtTJcw==", + "dependencies": { + "execa": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-admin/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/is-admin/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-admin/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-admin/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-admin/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-admin/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/is-admin/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/is-admin/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-admin/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-admin/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -10453,6 +10572,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-elevated": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-3.0.0.tgz", + "integrity": "sha512-wjcp6RkouU9jpg55zERl+BglvV5j4jx5c/EMvQ+d12j/+nIEenNWPu+qc0tCg3JkLodbKZMg1qhJzEwG4qjclg==", + "dependencies": { + "is-admin": "^3.0.0", + "is-root": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -10617,6 +10748,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, "node_modules/is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -10782,8 +10921,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -13594,6 +13732,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -14102,6 +14245,14 @@ "node": ">=8" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -15411,7 +15562,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -16651,8 +16801,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-swizzle": { "version": "0.2.2", @@ -17113,6 +17262,14 @@ "node": ">=4" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -24652,7 +24809,6 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -27020,6 +27176,94 @@ "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", "dev": true }, + "is-admin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-admin/-/is-admin-3.0.0.tgz", + "integrity": "sha512-wOa3CXFJAu8BZ2BDtG9xYOOrsq6oiSvc2jFPy4X/HINx5bmJUcW8e+apItVbU2E7GIfBVaFVO7Zit4oAWtTJcw==", + "requires": { + "execa": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -27115,6 +27359,15 @@ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true }, + "is-elevated": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-elevated/-/is-elevated-3.0.0.tgz", + "integrity": "sha512-wjcp6RkouU9jpg55zERl+BglvV5j4jx5c/EMvQ+d12j/+nIEenNWPu+qc0tCg3JkLodbKZMg1qhJzEwG4qjclg==", + "requires": { + "is-admin": "^3.0.0", + "is-root": "^2.1.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -27225,6 +27478,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, "is-set": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", @@ -27336,8 +27594,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isobject": { "version": "3.0.1", @@ -29625,6 +29882,11 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, "no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -29996,6 +30258,11 @@ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==" + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -30886,7 +31153,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -31829,8 +32095,7 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "simple-swizzle": { "version": "0.2.2", @@ -32194,6 +32459,11 @@ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==" + }, "strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", diff --git a/package.json b/package.json index ea2f56d3..32251796 100644 --- a/package.json +++ b/package.json @@ -252,6 +252,7 @@ "framer-motion": "^10.16.16", "fs-extra": "^11.2.0", "history": "^5.3.0", + "is-elevated": "^3.0.0", "jszip": "^3.10.1", "md5-file": "^5.0.0", "node-abi": "^3.47.0", diff --git a/release/app/package-lock.json b/release/app/package-lock.json index 76622776..24e99b05 100644 --- a/release/app/package-lock.json +++ b/release/app/package-lock.json @@ -1,17 +1,18 @@ { "name": "bs-manager", - "version": "1.4.3", + "version": "1.4.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bs-manager", - "version": "1.4.3", + "version": "1.4.4", "hasInstallScript": true, "license": "MIT", "dependencies": { "ps-list": "^7.2.0", - "regedit-rs": "^1.0.1", + "query-process": "^0.0.3", + "regedit-rs": "^1.0.2", "sharp": "^0.32.6" } }, @@ -344,6 +345,160 @@ "once": "^1.3.1" } }, + "node_modules/query-process": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process/-/query-process-0.0.3.tgz", + "integrity": "sha512-6WRue0P+c84abP0o8MkDvigoS/gkkJzsSBt10PsOoBkP7Yodo4DducFsn9XyS4S+LuVYPAE5Hr4+kVNR5Onykg==", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "query-process-android-arm-eabi": "0.0.3", + "query-process-android-arm64": "0.0.3", + "query-process-linux-arm-gnueabihf": "0.0.3", + "query-process-linux-arm64-gnu": "0.0.3", + "query-process-linux-arm64-musl": "0.0.3", + "query-process-linux-x64-gnu": "0.0.3", + "query-process-linux-x64-musl": "0.0.3", + "query-process-win32-arm64-msvc": "0.0.3", + "query-process-win32-x64-msvc": "0.0.3" + } + }, + "node_modules/query-process-android-arm-eabi": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-android-arm-eabi/-/query-process-android-arm-eabi-0.0.3.tgz", + "integrity": "sha512-NB+9T+/poBcygDiG+7d2lJSeioP5ZyLT0HwUBdnLgNWvNCpzbJxydup1SLpnt5G2NF6nlTgbUZlH4hvxY7t/bw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-android-arm64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-android-arm64/-/query-process-android-arm64-0.0.3.tgz", + "integrity": "sha512-TiAw2yO62vQzM0s02471LToHyUs/PRkLGGlA715z10BPIM5NckzIe245AI45CDyH6tE/3nVHUd0LX/dfjQtTcw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-linux-arm-gnueabihf": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm-gnueabihf/-/query-process-linux-arm-gnueabihf-0.0.3.tgz", + "integrity": "sha512-w7NLHBKt7qselCDoh/PVWyeuEb2NiiDDf1A9UB4K3Ns/H24hK3NbfMMBm8mCFxUnFUqoVX8O+VMs7dD/FNlTxA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-linux-arm64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm64-gnu/-/query-process-linux-arm64-gnu-0.0.3.tgz", + "integrity": "sha512-nrPHjnSqCBuuN7IZkZWOnBpiJjyKOjfpT0Xt2EjSGp+I7C0CHbnhCL/vG1hBzaWZn0mxJ5nf0c5N0Fja7krnpQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-linux-arm64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm64-musl/-/query-process-linux-arm64-musl-0.0.3.tgz", + "integrity": "sha512-vk8xMYTnS1PRKhwJgqLKyBAGIFP2CYZW2BsaKuNgQKUrDv7NnBaG7yukZT8niHNO1ycSrFpR/xqeJ8R23hO07A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-linux-x64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-x64-gnu/-/query-process-linux-x64-gnu-0.0.3.tgz", + "integrity": "sha512-j1tGcNnGyVCABJ1PHZD1gKuyuomNzsPow3NDuZzUGjPqvKu67RsmKJJJOhMyT1SvUkcqdLFdtS1KTD20I4GS5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-linux-x64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-x64-musl/-/query-process-linux-x64-musl-0.0.3.tgz", + "integrity": "sha512-AOGrrI/Qcb30iKFstiW60zNG/HQevCY0Q38eMHGd7LrBZ5sEN7NzZOjzKp43KeNHejuZBDMxf0gdGAqDznOAng==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-win32-arm64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-win32-arm64-msvc/-/query-process-win32-arm64-msvc-0.0.3.tgz", + "integrity": "sha512-LHvxbzMFwPPUXHe4bDCAqs1bYbp8j0bMW8XyJGPZnIYrnwHz6g8VE5WQEtXQxSznsbfVxAKJXUtn088EjDVazw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/query-process-win32-x64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-win32-x64-msvc/-/query-process-win32-x64-msvc-0.0.3.tgz", + "integrity": "sha512-LfHAKmfqyKCgl9K+sTz0KyNwMwB79VcXZUy/Et9iQKLuZt7PDYpHWcFdhrfHw2qqIdKWTED8KmnmsOLhUn/2OA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/queue-tick": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", @@ -377,22 +532,22 @@ } }, "node_modules/regedit-rs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs/-/regedit-rs-1.0.1.tgz", - "integrity": "sha512-wsvoGP5JDevIcx3q52aATW1c1dY86bQx7/tJ32gqV+L98OHRJykobKkjYqGzROtz9c2Pr+wGJZr0glFyc2dpvQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs/-/regedit-rs-1.0.2.tgz", + "integrity": "sha512-4vEgiZNO1FCG8z/Zx3v/6PU1+eZ+ELe6R0ca+VB96Vw+Mi3M0IVHAjtMFbl97lUSX11dJqpyousX/wY8QcI1lA==", "engines": { "node": ">= 10" }, "optionalDependencies": { - "regedit-rs-win32-arm64-msvc": "1.0.1", - "regedit-rs-win32-ia32-msvc": "1.0.1", - "regedit-rs-win32-x64-msvc": "1.0.1" + "regedit-rs-win32-arm64-msvc": "1.0.2", + "regedit-rs-win32-ia32-msvc": "1.0.2", + "regedit-rs-win32-x64-msvc": "1.0.2" } }, "node_modules/regedit-rs-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-arm64-msvc/-/regedit-rs-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-QA46d/wiUHlW2h0ZlHKrtDhnwoxMpVle+EbFziEbLYC7dimtAiWCeXu4zNymaFClDimYSifQkWgza5GBmD8etg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-arm64-msvc/-/regedit-rs-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-hM1sxazOJWKmiC9DM8QXW9Iqm50Mh/Y9G4/rRYQpWXjMzq7lTqjwVZRkAoBrHliFS6d1Lt4qkm5+Ybt6GkbDpw==", "cpu": [ "arm64" ], @@ -405,9 +560,9 @@ } }, "node_modules/regedit-rs-win32-ia32-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-ia32-msvc/-/regedit-rs-win32-ia32-msvc-1.0.1.tgz", - "integrity": "sha512-V2NisvicQqiozl0yMas9tjDYmBwHWf48M/dMJUWMQxxkJaDCNoHW8PljL59A/h8ew2NBFsJK5RYkYfxW1o+OyQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-ia32-msvc/-/regedit-rs-win32-ia32-msvc-1.0.2.tgz", + "integrity": "sha512-FLINrCJ30wm6NYw7skQUDET8NP1N46kH77dqesCiU+/FjWzzPE5luZYY+j4uf+hKjPY6/MCj2CB9l9VdPhaBVQ==", "cpu": [ "ia32" ], @@ -420,9 +575,9 @@ } }, "node_modules/regedit-rs-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-x64-msvc/-/regedit-rs-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-qhQawjYPzXGrz0xODJiviwrzw0eytGP9+quuQJUQAiLEw2P1tQ0LCyID3jOvNltLH+tqJChSV4JaQcPVleohZg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-x64-msvc/-/regedit-rs-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-ccCSyd5vWBKVWftBKLKzegqwwPMWcQtIW0ub66dCFFuv2s+x2EcZZWGdD9dVXX2Z6V9DU2JRPKgWUNjVPaj6Xg==", "cpu": [ "x64" ], @@ -851,6 +1006,76 @@ "once": "^1.3.1" } }, + "query-process": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process/-/query-process-0.0.3.tgz", + "integrity": "sha512-6WRue0P+c84abP0o8MkDvigoS/gkkJzsSBt10PsOoBkP7Yodo4DducFsn9XyS4S+LuVYPAE5Hr4+kVNR5Onykg==", + "requires": { + "query-process-android-arm-eabi": "0.0.3", + "query-process-android-arm64": "0.0.3", + "query-process-linux-arm-gnueabihf": "0.0.3", + "query-process-linux-arm64-gnu": "0.0.3", + "query-process-linux-arm64-musl": "0.0.3", + "query-process-linux-x64-gnu": "0.0.3", + "query-process-linux-x64-musl": "0.0.3", + "query-process-win32-arm64-msvc": "0.0.3", + "query-process-win32-x64-msvc": "0.0.3" + } + }, + "query-process-android-arm-eabi": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-android-arm-eabi/-/query-process-android-arm-eabi-0.0.3.tgz", + "integrity": "sha512-NB+9T+/poBcygDiG+7d2lJSeioP5ZyLT0HwUBdnLgNWvNCpzbJxydup1SLpnt5G2NF6nlTgbUZlH4hvxY7t/bw==", + "optional": true + }, + "query-process-android-arm64": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-android-arm64/-/query-process-android-arm64-0.0.3.tgz", + "integrity": "sha512-TiAw2yO62vQzM0s02471LToHyUs/PRkLGGlA715z10BPIM5NckzIe245AI45CDyH6tE/3nVHUd0LX/dfjQtTcw==", + "optional": true + }, + "query-process-linux-arm-gnueabihf": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm-gnueabihf/-/query-process-linux-arm-gnueabihf-0.0.3.tgz", + "integrity": "sha512-w7NLHBKt7qselCDoh/PVWyeuEb2NiiDDf1A9UB4K3Ns/H24hK3NbfMMBm8mCFxUnFUqoVX8O+VMs7dD/FNlTxA==", + "optional": true + }, + "query-process-linux-arm64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm64-gnu/-/query-process-linux-arm64-gnu-0.0.3.tgz", + "integrity": "sha512-nrPHjnSqCBuuN7IZkZWOnBpiJjyKOjfpT0Xt2EjSGp+I7C0CHbnhCL/vG1hBzaWZn0mxJ5nf0c5N0Fja7krnpQ==", + "optional": true + }, + "query-process-linux-arm64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-arm64-musl/-/query-process-linux-arm64-musl-0.0.3.tgz", + "integrity": "sha512-vk8xMYTnS1PRKhwJgqLKyBAGIFP2CYZW2BsaKuNgQKUrDv7NnBaG7yukZT8niHNO1ycSrFpR/xqeJ8R23hO07A==", + "optional": true + }, + "query-process-linux-x64-gnu": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-x64-gnu/-/query-process-linux-x64-gnu-0.0.3.tgz", + "integrity": "sha512-j1tGcNnGyVCABJ1PHZD1gKuyuomNzsPow3NDuZzUGjPqvKu67RsmKJJJOhMyT1SvUkcqdLFdtS1KTD20I4GS5Q==", + "optional": true + }, + "query-process-linux-x64-musl": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-linux-x64-musl/-/query-process-linux-x64-musl-0.0.3.tgz", + "integrity": "sha512-AOGrrI/Qcb30iKFstiW60zNG/HQevCY0Q38eMHGd7LrBZ5sEN7NzZOjzKp43KeNHejuZBDMxf0gdGAqDznOAng==", + "optional": true + }, + "query-process-win32-arm64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-win32-arm64-msvc/-/query-process-win32-arm64-msvc-0.0.3.tgz", + "integrity": "sha512-LHvxbzMFwPPUXHe4bDCAqs1bYbp8j0bMW8XyJGPZnIYrnwHz6g8VE5WQEtXQxSznsbfVxAKJXUtn088EjDVazw==", + "optional": true + }, + "query-process-win32-x64-msvc": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/query-process-win32-x64-msvc/-/query-process-win32-x64-msvc-0.0.3.tgz", + "integrity": "sha512-LfHAKmfqyKCgl9K+sTz0KyNwMwB79VcXZUy/Et9iQKLuZt7PDYpHWcFdhrfHw2qqIdKWTED8KmnmsOLhUn/2OA==", + "optional": true + }, "queue-tick": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", @@ -878,31 +1103,31 @@ } }, "regedit-rs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs/-/regedit-rs-1.0.1.tgz", - "integrity": "sha512-wsvoGP5JDevIcx3q52aATW1c1dY86bQx7/tJ32gqV+L98OHRJykobKkjYqGzROtz9c2Pr+wGJZr0glFyc2dpvQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs/-/regedit-rs-1.0.2.tgz", + "integrity": "sha512-4vEgiZNO1FCG8z/Zx3v/6PU1+eZ+ELe6R0ca+VB96Vw+Mi3M0IVHAjtMFbl97lUSX11dJqpyousX/wY8QcI1lA==", "requires": { - "regedit-rs-win32-arm64-msvc": "1.0.1", - "regedit-rs-win32-ia32-msvc": "1.0.1", - "regedit-rs-win32-x64-msvc": "1.0.1" + "regedit-rs-win32-arm64-msvc": "1.0.2", + "regedit-rs-win32-ia32-msvc": "1.0.2", + "regedit-rs-win32-x64-msvc": "1.0.2" } }, "regedit-rs-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-arm64-msvc/-/regedit-rs-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-QA46d/wiUHlW2h0ZlHKrtDhnwoxMpVle+EbFziEbLYC7dimtAiWCeXu4zNymaFClDimYSifQkWgza5GBmD8etg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-arm64-msvc/-/regedit-rs-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-hM1sxazOJWKmiC9DM8QXW9Iqm50Mh/Y9G4/rRYQpWXjMzq7lTqjwVZRkAoBrHliFS6d1Lt4qkm5+Ybt6GkbDpw==", "optional": true }, "regedit-rs-win32-ia32-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-ia32-msvc/-/regedit-rs-win32-ia32-msvc-1.0.1.tgz", - "integrity": "sha512-V2NisvicQqiozl0yMas9tjDYmBwHWf48M/dMJUWMQxxkJaDCNoHW8PljL59A/h8ew2NBFsJK5RYkYfxW1o+OyQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-ia32-msvc/-/regedit-rs-win32-ia32-msvc-1.0.2.tgz", + "integrity": "sha512-FLINrCJ30wm6NYw7skQUDET8NP1N46kH77dqesCiU+/FjWzzPE5luZYY+j4uf+hKjPY6/MCj2CB9l9VdPhaBVQ==", "optional": true }, "regedit-rs-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/regedit-rs-win32-x64-msvc/-/regedit-rs-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-qhQawjYPzXGrz0xODJiviwrzw0eytGP9+quuQJUQAiLEw2P1tQ0LCyID3jOvNltLH+tqJChSV4JaQcPVleohZg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regedit-rs-win32-x64-msvc/-/regedit-rs-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-ccCSyd5vWBKVWftBKLKzegqwwPMWcQtIW0ub66dCFFuv2s+x2EcZZWGdD9dVXX2Z6V9DU2JRPKgWUNjVPaj6Xg==", "optional": true }, "safe-buffer": { diff --git a/release/app/package.json b/release/app/package.json index 86a037ba..5b674c7d 100644 --- a/release/app/package.json +++ b/release/app/package.json @@ -1,6 +1,6 @@ { "name": "bs-manager", - "version": "1.4.3", + "version": "1.4.4", "description": "BSManager", "main": "./dist/main/main.js", "author": { @@ -15,7 +15,8 @@ }, "dependencies": { "ps-list": "^7.2.0", - "regedit-rs": "^1.0.1", + "query-process": "^0.0.3", + "regedit-rs": "^1.0.2", "sharp": "^0.32.6" }, "license": "MIT" diff --git a/src/main/decorators/log.decorator.ts b/src/main/decorators/log.decorator.ts new file mode 100644 index 00000000..9c87f932 --- /dev/null +++ b/src/main/decorators/log.decorator.ts @@ -0,0 +1,67 @@ +import log from "electron-log"; +import { isPromise } from "../../shared/helpers/promise.helpers"; +import { tryit } from "../../shared/helpers/error.helpers"; + +type LogOptions = { + logInput?: boolean; + logOutput?: boolean; + logArgs?: boolean; +}; + +const stringifyArgs = (args: unknown[]) => { + return args?.map(a => JSON.stringify(a)).join(', '); +}; + +const logInfo = (message: string, propertyKey: string, args: unknown[], logArgs: boolean, result: unknown) => { + log.info(`[${message}] ${propertyKey}(${logArgs ? stringifyArgs(args) : ''})`, result); +}; + +const logError = (propertyKey: string, args: unknown[], logArgs: boolean, error: unknown) => { + log.error(`[ERROR] ${propertyKey}(${logArgs ? stringifyArgs(args) : ''})`, error); + throw error; +}; + +export function Log(options?: LogOptions){ + + const logInput = options?.logInput ?? false; + const logOutput = options?.logOutput ?? true; + const logArgs = options?.logArgs ?? true; + + return (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => { + const originalMethod = descriptor.value; + + descriptor.value = function(...args: unknown[]) { + + if (logInput) { + logInfo('INPUT', propertyKey, args, logArgs, null); + } + + const outcome = tryit(() => originalMethod.apply(this, args)); + + if (isPromise(outcome)) { + return outcome.then(({ result, error }) => { + + if (error) { + logError(propertyKey, args, logArgs, error); + } + if (logOutput) { + logInfo('OUTPUT', propertyKey, args, logArgs, result); + } + return result; + }); + } + + const { result, error } = outcome; + + if (error) { + logError(propertyKey, args, logArgs, error); + } + + if (logOutput) { + logInfo('OUTPUT', propertyKey, args, logArgs, result); + } + + return result; + }; + }; +} diff --git a/src/main/helpers/os.helpers.ts b/src/main/helpers/os.helpers.ts index da6aed8d..e44a6c70 100644 --- a/src/main/helpers/os.helpers.ts +++ b/src/main/helpers/os.helpers.ts @@ -10,4 +10,16 @@ export async function taskRunning(task: string): Promise { log.error(error); return false; } -} \ No newline at end of file +} + +export async function getProcessPid(task: string): Promise { + try { + const processes = await psList(); + const process = processes.find(process => process.name?.includes(task) || process.cmd?.includes(task)); + return process?.pid; + } + catch(error){ + log.error(error); + return null; + } +} diff --git a/src/main/ipcs/bs-launcher-ipcs.ts b/src/main/ipcs/bs-launcher-ipcs.ts index c79cde6e..53614fb1 100644 --- a/src/main/ipcs/bs-launcher-ipcs.ts +++ b/src/main/ipcs/bs-launcher-ipcs.ts @@ -5,6 +5,9 @@ import { IpcService } from '../services/ipc.service'; import { from } from "rxjs"; import { SteamLauncherService } from "../services/bs-launcher/steam-launcher.service"; import { OculusLauncherService } from "../services/bs-launcher/oculus-launcher.service"; +import { SteamService } from "../services/steam.service"; +import log from "electron-log"; +import isElevated from "is-elevated"; const ipc = IpcService.getInstance(); @@ -13,6 +16,17 @@ ipc.on('bs-launch.launch', (req, reply) => { reply(bsLauncher.launch(req.args)); }); +ipc.on("bs-launch.need-start-as-admin", (_, reply) => { + const steam = SteamService.getInstance(); + reply(from(isElevated().then(elevated => { + if(elevated){ return false; } + return steam.isElevated().catch(e => { + log.error("Error while checking if Steam is running as admin", e); + return false; + }); + }))); +}); + ipc.on("create-launch-shortcut", (req, reply) => { const bsLauncher = BSLauncherService.getInstance(); reply(from(bsLauncher.createLaunchShortcut(req.args))); diff --git a/src/main/services/bs-launcher/oculus-launcher.service.ts b/src/main/services/bs-launcher/oculus-launcher.service.ts index e07232b9..b71cd2a9 100644 --- a/src/main/services/bs-launcher/oculus-launcher.service.ts +++ b/src/main/services/bs-launcher/oculus-launcher.service.ts @@ -61,13 +61,13 @@ export class OculusLauncherService extends AbstractLauncherService implements St 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); @@ -138,20 +138,24 @@ export class OculusLauncherService extends AbstractLauncherService implements St if(bsRunning){ 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()); - // Launch Beat Saber const exePath = path.join(bsPath, BS_EXECUTABLE); if(!(await pathExists(exePath))){ throw CustomError.fromError(new Error(`BS Path not exist ${bsPath}`), BSLaunchError.BS_NOT_FOUND); } + // Make sure Oculus is running + await this.oculus.startOculus().catch(err => log.error("Error while starting Oculus", err)); + obs.next({type: BSLaunchEvent.BS_LAUNCHING}); + + // Launch Beat Saber return this.launchBs(exePath, this.buildBsLaunchArgs(launchOptions)).catch(err => { throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR); }); @@ -170,4 +174,4 @@ export class OculusLauncherService extends AbstractLauncherService implements St }); } -} \ No newline at end of file +} diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index 7ebc0cb0..b0d18a63 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -8,6 +8,9 @@ import { BS_APP_ID, BS_EXECUTABLE, STEAMVR_APP_ID } from "../../constants"; import log from "electron-log"; import { AbstractLauncherService } from "./abstract-launcher.service"; import { CustomError } from "../../../shared/models/exceptions/custom-error.class"; +import isElevated from "is-elevated"; +import { UtilsService } from "../utils.service"; +import { exec } from "child_process"; export class SteamLauncherService extends AbstractLauncherService implements StoreLauncherInterface{ @@ -21,10 +24,12 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto } private readonly steam: SteamService; + private readonly util: UtilsService; private constructor(){ super(); this.steam = SteamService.getInstance(); + this.util = UtilsService.getInstance(); } private getSteamVRPath(): Promise { @@ -41,6 +46,17 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto }); } + private needStartBsAsAdmin(): Promise { + return isElevated().then(elevated => { + if(elevated){ return false; } + return this.steam.isElevated(); + }) + } + + private getStartBsAsAdminExePath(): string { + return path.join(this.util.getAssetsScriptsPath(), "start_beat_saber_admin.exe"); + } + public async restoreSteamVR(): Promise { const steamVrFolder = await this.getSteamVRPath(); const steamVrBackup = `${steamVrFolder}.bak`; @@ -53,7 +69,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto log.error("Error while restoring SteamVR", err); }); } - + public launch(launchOptions: LaunchOption): Observable{ return new Observable(obs => {(async () => { @@ -90,7 +106,24 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto obs.next({type: BSLaunchEvent.BS_LAUNCHING}); - await this.launchBs(exePath, launchArgs, { env: {...process.env, "SteamAppId": BS_APP_ID} }).then(exitCode => { + const launchPromise = !launchOptions.admin ? ( + this.launchBs(exePath, launchArgs, { env: {...process.env, "SteamAppId": BS_APP_ID} }) + ) : ( + new Promise(resolve => { + const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${exePath}" ${launchArgs.join(" ")}`, { env: {...process.env, "SteamAppId": BS_APP_ID} }); + adminProcess.on("error", err => { + log.error("Error while starting BS as Admin", err); + resolve(-1) + }); + + setTimeout(() => { + adminProcess.removeAllListeners("error"); + resolve(-1); + }, 35_000); + }) + ); + + await launchPromise.then(exitCode => { log.info("BS process exit code", exitCode); }).catch(err => { throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR); @@ -109,4 +142,4 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto })}); } -} \ No newline at end of file +} diff --git a/src/main/services/liv/liv.service.ts b/src/main/services/liv/liv.service.ts index ce9c649d..b94038ec 100644 --- a/src/main/services/liv/liv.service.ts +++ b/src/main/services/liv/liv.service.ts @@ -1,6 +1,8 @@ import { execOnOs } from "../../helpers/env.helpers"; -import { list, createKey, putValue, deleteKey, RegSzValue } from "regedit-rs"; import path from "path"; +import { Log } from "../../decorators/log.decorator"; + +const { list, createKey, putValue, deleteKey, RegSzValue } = (execOnOs({ win32: () => require("regedit-rs") }, true) ?? {}) as typeof import("regedit-rs"); export class LivService { @@ -20,6 +22,7 @@ export class LivService { } + @Log() public async isLivInstalled(): Promise { return execOnOs({ win32: async () => { @@ -29,6 +32,7 @@ export class LivService { }, true); } + @Log() public async createLivShortcut(entry: LivEntry): Promise { return execOnOs({ win32: async () => { @@ -46,6 +50,7 @@ export class LivService { }); } + @Log() public async deleteLivShortcuts(ids: string[]): Promise { return execOnOs({ win32: async () => { @@ -55,11 +60,13 @@ export class LivService { }) } + @Log() public getLivShortcuts(): Promise { + return execOnOs({ win32: async () => { const regRes = await list(this.livExternalAppsRegeditKey).then(res => res[this.livExternalAppsRegeditKey]); - + if(!regRes.exists){ return []; } diff --git a/src/main/services/oculus.service.ts b/src/main/services/oculus.service.ts index c0b0622b..2119a02f 100644 --- a/src/main/services/oculus.service.ts +++ b/src/main/services/oculus.service.ts @@ -1,9 +1,14 @@ -import { list } from "regedit-rs"; import path from "path"; import { pathExist, resolveGUIDPath } from "../helpers/fs.helpers"; import log from "electron-log"; import { lstat } from "fs-extra"; import { tryit } from "../../shared/helpers/error.helpers"; +import { shell } from "electron"; +import { taskRunning } from "../helpers/os.helpers"; +import { sToMs } from "../../shared/helpers/time.helpers"; +import { execOnOs } from "../helpers/env.helpers"; + +const { list } = (execOnOs({ win32: () => require("regedit-rs") }, true) ?? {}) as typeof import("regedit-rs"); export class OculusService { private static instance: OculusService; @@ -42,11 +47,11 @@ export class OculusService { const libsPath: OculusLibrary[] = ( await Promise.all(libsRegData.keys.map(async key => { const originalPath = await list([`${oculusLibsRegKey}\\${key}`]).then(res => res[`${oculusLibsRegKey}\\${key}`]); - + if (originalPath.values?.OriginalPath) { return { id: key, path: originalPath.values.OriginalPath.value, isDefault: defaultLibraryId === key } as OculusLibrary; - } - + } + if(originalPath.values?.Path) { const { result } = tryit(() => resolveGUIDPath(originalPath.values.Path.value as string)); return result ? { id: key, path: result, isDefault: defaultLibraryId === key } as OculusLibrary : null; @@ -82,7 +87,7 @@ export class OculusService { /** * Return the first game folder found in the list - * @param {string[]} gameFolders + * @param {string[]} gameFolders */ public async tryGetGameFolder(gameFolders: string[]): Promise { for(const gameFolder of gameFolders){ @@ -92,6 +97,29 @@ export class OculusService { return null; } + + public oculusRunning(): Promise { + return taskRunning("OculusClient"); + } + + public async startOculus(): Promise{ + if(await this.oculusRunning()){ return; } + + await shell.openPath("oculus://view/homepage"); + + return new Promise((resolve, reject) => { + const interval = setInterval(async () => { + if(!(await this.oculusRunning())){ return; } + clearInterval(interval); + resolve(); + }, sToMs(3)); + + setTimeout(() => { + clearInterval(interval); + reject(new Error("Unable to open Oculus")); + }, sToMs(30)); + }); + } } export interface OculusLibrary { diff --git a/src/main/services/steam.service.ts b/src/main/services/steam.service.ts index 9e8472a3..54b796f0 100644 --- a/src/main/services/steam.service.ts +++ b/src/main/services/steam.service.ts @@ -1,15 +1,22 @@ -import { list, RegDwordValue } from "regedit-rs" +import { RegDwordValue } from "regedit-rs" import path from "path"; import { parse } from "@node-steam/vdf"; import { readFile } from "fs/promises"; import { pathExist } from "../helpers/fs.helpers"; import log from "electron-log"; import { app, shell } from "electron"; -import { taskRunning } from "../helpers/os.helpers"; +import { getProcessPid, taskRunning } from "../helpers/os.helpers"; +import { isElevated } from "query-process"; +import { execOnOs } from "../helpers/env.helpers"; + +const { list } = (execOnOs({ win32: () => require("regedit-rs") }, true) ?? {}) as typeof import("regedit-rs"); export class SteamService { + private static readonly PROCESS_NAME = "steam"; + private static instance: SteamService; + private steamPath: string = ''; private constructor(){} @@ -29,12 +36,30 @@ export class SteamService { } public async steamRunning(): Promise{ - const steamProcessRunning = await taskRunning("steam"); + const steamProcessRunning = await taskRunning(SteamService.PROCESS_NAME); if(process.platform === "linux") { return steamProcessRunning; } const activeUser = await this.getActiveUser().catch(err => log.error(err)); return steamProcessRunning && !!activeUser; } + public async getSteamPid(): Promise{ + return getProcessPid(SteamService.PROCESS_NAME); + } + + /** + * Check if the Steam process is running as administrator + * @throws Can throw an error if the Steam process is running as admin + * @returns true if the Steam process is running as administrator + */ + public async isElevated(): Promise{ + if(process.platform === "linux"){ return true; } + const steamPid = await this.getSteamPid(); + + if(!steamPid){ return false; } + + return isElevated(steamPid); + } + public async getSteamPath(): Promise{ if(this.steamPath){ return this.steamPath; } @@ -68,7 +93,7 @@ export class SteamService { let libraryFolders: any = path.join(steamPath, "steamapps", "libraryfolders.vdf"); if (!(await pathExist(libraryFolders))) { return null; } - + libraryFolders = parse(await readFile(libraryFolders, { encoding: "utf-8" })); if (!libraryFolders.libraryfolders) { return null; } @@ -83,7 +108,7 @@ export class SteamService { } return null; - + } catch (e) { log.error(e); return null; @@ -91,7 +116,7 @@ export class SteamService { } public async openSteam(): Promise { - + await shell.openPath("steam://open/games"); return new Promise((resolve, reject) => { diff --git a/src/renderer/components/modal/modal-types/need-launch-admin-modal.component.tsx b/src/renderer/components/modal/modal-types/need-launch-admin-modal.component.tsx new file mode 100644 index 00000000..36b8b2d0 --- /dev/null +++ b/src/renderer/components/modal/modal-types/need-launch-admin-modal.component.tsx @@ -0,0 +1,34 @@ +import { BsmButton } from "renderer/components/shared/bsm-button.component" +import { BsmImage } from "renderer/components/shared/bsm-image.component" +import { ModalComponent, ModalExitCode } from "renderer/services/modale.service" +import BeatConflict from "../../../../../assets/images/apngs/beat-conflict.png"; +import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component"; +import { useState } from "react"; + +export const NeedLaunchAdminModal: ModalComponent = ({resolver}) => { + + const t = useTranslation(); + + const [dontShowAgain, setDontShowAgain] = useState(false); + + return ( + + {t("modals.launch-as-admin.title")} + + + {t("modals.launch-as-admin.body.info")} + {t("modals.launch-as-admin.body.info-2")} + {t("modals.launch-as-admin.body.info-3")} + + + + {t("modals.launch-as-admin.not-remind-me")} + + + resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" /> + resolver({ exitCode: ModalExitCode.COMPLETED, data: dontShowAgain })} withBar={false} text="modals.launch-as-admin.launch-as-admin" /> + + + ) +} diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index 8770f4ff..d47e1d18 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -10,6 +10,7 @@ import { ModalExitCode, ModalService } from "./modale.service"; import { OriginalOculusVersionBackupModal } from "renderer/components/modal/modal-types/original-oculus-version-backup.modal"; 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"; export class BSLauncherService { private static instance: BSLauncherService; @@ -21,7 +22,7 @@ export class BSLauncherService { private readonly modals: ModalService; public readonly versionRunning$: BehaviorSubject = new BehaviorSubject(null); - + public static getInstance(){ if(!BSLauncherService.instance){ BSLauncherService.instance = new BSLauncherService(); } return BSLauncherService.instance; @@ -70,7 +71,17 @@ export class BSLauncherService { } })) } - + + private async doMustStartAsAdmin(): Promise { + const needAdmin = await lastValueFrom(this.ipcService.sendV2("bs-launch.need-start-as-admin")); + if(!needAdmin){ return false; } + if(this.config.get("dont-remind-admin")){ return true; } + const modalRes = await this.modals.openModal(NeedLaunchAdminModal); + if(modalRes.exitCode !== ModalExitCode.COMPLETED){ throw new Error("Admin launch canceled"); } + this.config.set("dont-remind-admin", modalRes.data); + return true; + } + public doLaunch(launchOptions: LaunchOption): Observable{ return this.ipcService.sendV2("bs-launch.launch", {args: launchOptions}); } @@ -79,17 +90,21 @@ 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(launchOptions.version.metadata?.store !== BsStore.OCULUS){ + launchOptions.admin = await this.doMustStartAsAdmin(); + } + const launch$ = this.handleLaunchEvents(this.doLaunch(launchOptions)); await lastValueFrom(launch$); - + })().then(() => { obs.complete(); }).catch(err => { diff --git a/src/shared/helpers/error.helpers.ts b/src/shared/helpers/error.helpers.ts index ce67ffa0..33f71fce 100644 --- a/src/shared/helpers/error.helpers.ts +++ b/src/shared/helpers/error.helpers.ts @@ -1,7 +1,22 @@ -export function tryit(func: () => Return): {error: Error, result: Return} { +import { isPromise } from "./promise.helpers"; + +type TryitReturn = Return extends Promise ? Promise<{ error: Error | null, result: Awaited | null }> : { error: Error | null, result: Return | null }; + +export function tryit(func: () => Return): TryitReturn { try { - return { error: null, result: func() }; + const result = func(); + + if(isPromise(result)){ + return result + .then((value) => ({ error: null, result: value })) + .catch((err) => ({ error: err instanceof Error ? err : new Error(`${err}`), result: null })) as Return extends Promise + ? Promise<{error: Error, result: undefined} | {error: undefined, result: Awaited}> + : {error: Error, result: undefined} | {error: undefined, result: Return}; + } + + return { error: undefined, result } as TryitReturn; + } catch (err) { - return { error: err instanceof Error ? err : new Error(`${err}`), result: null } + return { error: err, result: undefined } as TryitReturn; } -} \ No newline at end of file +} diff --git a/src/shared/helpers/function.helpers.ts b/src/shared/helpers/function.helpers.ts new file mode 100644 index 00000000..6f17657d --- /dev/null +++ b/src/shared/helpers/function.helpers.ts @@ -0,0 +1,3 @@ +export function isFunction(value: any): value is Function { + return !!(value && value.constructor && value.call && value.apply) +} diff --git a/src/shared/helpers/promise.helpers.ts b/src/shared/helpers/promise.helpers.ts index d9ed9995..3bcbd069 100644 --- a/src/shared/helpers/promise.helpers.ts +++ b/src/shared/helpers/promise.helpers.ts @@ -1,3 +1,5 @@ +import { isFunction } from "./function.helpers"; + export type AllSettledHelperOptions = { keepStructure?: boolean; removeFalsy?: boolean; @@ -19,3 +21,10 @@ export async function allSettled(promises: Promise[], options?: AllSettled return acc; }, []); } + +export function isPromise(value: any): value is Promise { + if(!value) { return false; } + if(!value.then) { return false; } + if(!isFunction(value.then)) { return false; } + return true; +} diff --git a/src/shared/models/bs-launch/launch-option.interface.ts b/src/shared/models/bs-launch/launch-option.interface.ts index 6f4b04ea..21056c19 100644 --- a/src/shared/models/bs-launch/launch-option.interface.ts +++ b/src/shared/models/bs-launch/launch-option.interface.ts @@ -5,5 +5,6 @@ export interface LaunchOption { oculus?: boolean, desktop?: boolean, debug?: boolean, - additionalArgs?: string[] + additionalArgs?: string[], + admin?: boolean, } diff --git a/tsconfig.json b/tsconfig.json index 13c86d2f..3f432026 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ "resolveJsonModule": true, "allowJs": true, "outDir": "release/app/dist", - "strictNullChecks": false + "strictNullChecks": false, + "experimentalDecorators": true }, "exclude": ["test", "release/build", "release/app/dist", ".erb/dll"] }
{t("modals.launch-as-admin.body.info")}
{t("modals.launch-as-admin.body.info-2")}
{t("modals.launch-as-admin.body.info-3")}