Compare commits

...

25 Commits

Author SHA1 Message Date
MathieuG-P 59c624d58d Merge pull request #432 from Zagrios/bugfix/oculus-versions-being-deleted-by-oculus-auto-updates
[bugfix] Added a Beat Saber ends watcher to delete created symlink to avoid Oculus version getting wiped by Oculus auto updates
2024-03-05 13:54:32 +01:00
MathieuG-P 2d67876ee1 [bugfix] Added a Beat Saber ends watcher to delete created symlink to avoid Oculus version getting wiped by Oculus auto updates 2024-03-05 13:33:04 +01:00
MathieuG-P 6b30d74628 Merge pull request #431 from Zagrios/bugfix/handle-unable-to-get-documents-path
[bugfix] handle unable to get documents path error
2024-03-04 13:35:26 +01:00
MathieuG-P eb76bb5df0 [bugfix] handle unable to get documents path error 2024-03-04 13:29:23 +01:00
MathieuG-P 590daf3be2 add patreon 2024-02-27 18:26:27 +01:00
MathieuG-P ecf18b9967 Update translations and disable "exclude installed" filters in list of installed maps 2024-02-26 13:44:04 +01:00
MathieuG-P bc26f329a5 Merge pull request #426 from Liborsaf/feature/exclude-installed
[feature] Implement exclude of installed maps as filter
2024-02-26 13:05:01 +01:00
Libor Šafář 030df21619 Implement exclude of installed maps as filter 2024-02-24 22:49:25 +01:00
MathieuG-P d61588c09f add patreon 2024-02-18 22:22:41 +01:00
MathieuG-P 7ed2b5c820 add OculusBinaryId 2024-02-16 16:07:20 +01:00
Iluhadesu 239a68821e Add 1.34.6 2024-02-16 15:53:59 +01:00
MathieuG-P fc512091e4 add patreon 2024-02-14 10:27:42 +01:00
MathieuG-P f71c29da58 bumb to 1.4.5 2024-02-12 16:35:04 +01:00
Iluhadesu 60c656b45b Add 1.34.5 2024-02-12 16:04:28 +01:00
MathieuG-P bec8e1f571 Merge pull request #417 from slinkstr/bugfix/fpfc-restore/416
[bugfix-416] Wait for SteamVR folder rename before exit
2024-02-12 15:22:57 +01:00
MathieuG-P 20d0cd7ee6 Merge pull request #421 from Zagrios/bugfix/unable-to-launch-oculus-version-when-backup-already-exists
[bugfix] Fixed unable to launch downgraded oculus version when backup already exist
2024-02-12 14:30:55 +01:00
MathieuG-P 3f37c2ee34 [bugfix] Fixed unable to launch downgraded oculus version when backup already exists 2024-02-12 14:19:59 +01:00
slinky 0b24a5ceb2 [bugfix-416] Wait for SteamVR folder rename before exit 2024-02-08 11:55:57 -08:00
MathieuG-P 318a6b5afa add patreon 2024-02-06 22:30:08 +01:00
MathieuG-P c80685065e Merge pull request #415 from Zagrios/bugfix/cannot-launch-bs-steam-is-elevated-error
[bugfix] handle errors when cheking if steam is running as admin
2024-02-02 20:23:19 +01:00
MathieuG-P cdc589f602 [bugfix] handle error when cheking if steam is running as admin 2024-02-02 20:18:33 +01:00
MathieuG-P ee6acb7980 bumb to 1.4.4 2024-02-01 10:48:28 +01:00
MathieuG-P 515ef06589 add patreon 2024-01-27 20:12:10 +01:00
MathieuG-P 344cce9461 Merge pull request #412 from Zagrios/bugfix/regedit-rs-broke-linux-support/411
[bugfix-411] dynamically import regedit-rs depending on the os
2024-01-27 13:53:05 +01:00
MathieuG-P 536690dc55 [bugfix-411] dynamically import regedit-rs depending on the os 2024-01-27 13:46:44 +01:00
33 changed files with 604 additions and 67 deletions
+1 -1
View File
@@ -29,4 +29,4 @@ npm-debug.log.*
*.scss.d.ts
# externals
externals/bs-admin-start/target/*
externals/**/target/*
+18
View File
@@ -681,5 +681,23 @@
"ReleaseImg": "https://clan.akamai.steamstatic.com/images/32055887/ae11d195bbe41a0f244d7b4d055a0ab89f88e5bb.png",
"ReleaseDate": "1702309893",
"year": "2023"
},
{
"BSVersion": "1.34.5",
"BSManifest": "7413405154512822201",
"OculusBinaryId": "6639053626194360",
"ReleaseURL": "https://steamcommunity.com/games/620980/announcements/detail/4036983137766828694",
"ReleaseImg": "https://clan.cloudflare.steamstatic.com/images/32055887/925e60f14c6815f7617fde11a894ed20f54a0846.png",
"ReleaseDate": "1707745958",
"year": "2024"
},
{
"BSVersion": "1.34.6",
"BSManifest": "4398761375819224126",
"OculusBinaryId": "6682206641879058",
"ReleaseURL": "https://store.steampowered.com/news/app/620980/view/7595953477644376720",
"ReleaseImg": "https://clan.cloudflare.steamstatic.com/images/32055887/e7c75bdfc76d00713adc3fbdf221c9ce8ab3620d.png",
"ReleaseDate": "1708091473",
"year": "2024"
}
]
+20 -1
View File
@@ -68,7 +68,26 @@
"type": "diamond",
"link": "https://www.youtube.com/@lumberjack462"
},
{
{
"username": "Xero"
},
{
"username": "Minescence"
},
{
"username": "Finlay"
},
{
"username": "mereknom"
},
{
"username": "Jascha"
},
{
"username": "Celldweller",
"type": "gold"
},
{
"username": "rhythmshade"
}
]
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "Sie müssen Beat Saber mindestens einmal aus dem Oculus Store installiert haben, sonst könnte sich Beat Saber nach dem Start automatisch schließen.",
"need-backup": "Um diese Version zu starten, wird der Installationsordner von Beat Saber in Ihrer Oculus-Bibliothek umbenannt.",
"can-restore-later": "Bei Bedarf können Sie ihn vom BSManager aus wiederherstellen, indem Sie zu den Optionen der originalen Oculus-Version gehen und auf 'Ordner wiederherstellen' klicken",
"tips-launch-oculus": "Tipp: Sie können diese Version direkt über Oculus starten, solange die Originalversion nicht wiederhergestellt ist."
"can-restore-later": "Bei Bedarf können Sie ihn vom BSManager aus wiederherstellen, indem Sie zu den Optionen der originalen Oculus-Version gehen und auf 'Ordner wiederherstellen' klicken"
},
"not-remind-me": "Nicht mehr erinnern",
"understood": "Verstanden"
@@ -737,7 +736,8 @@
"nps" : "Noten Pro Sekunde",
"tags": "Stichworte",
"specificities": "Allgemein",
"requirements": "Anforderungen"
"requirements": "Anforderungen",
"exclude": "ausschließen"
},
"map-types": {
"accuracy": "Präzision",
@@ -793,6 +793,9 @@
"verified": "verifiziert",
"fullSpread": "full spread"
},
"map-excludes": {
"installed": "installiert"
},
"difficulties": {
"Easy": "einfach",
"Normal": "normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "You must have installed Beat Saber at least once from the Oculus Store, otherwise Beat Saber might automatically close after launching.",
"need-backup": "To launch this version, the Beat Saber installation folder located in your Oculus library will be renamed.",
"can-restore-later": "If needed, you can restore it from BSManager by going to the options of the original Oculus version and clicking on 'Restore the folder'",
"tips-launch-oculus": "Tip: You can launch this version directly from Oculus, as long as the original version is not restored."
"can-restore-later": "If needed, you can restore it from BSManager by going to the options of the original Oculus version and clicking on 'Restore the folder'"
},
"not-remind-me": "Do not remind me",
"understood": "Understood"
@@ -737,7 +736,8 @@
"nps" : "Notes Per Second",
"tags": "tags",
"specificities": "general",
"requirements": "requirements"
"requirements": "requirements",
"exclude": "exclude"
},
"map-types": {
"accuracy": "accuracy",
@@ -793,6 +793,9 @@
"verified": "verified",
"fullSpread": "full spread"
},
"map-excludes": {
"installed": "installed"
},
"difficulties": {
"Easy": "easy",
"Normal": "normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "Debes haber instalado Beat Saber al menos una vez desde la Oculus Store, de lo contrario, Beat Saber podría cerrarse automáticamente después de iniciar.",
"need-backup": "Para iniciar esta versión, la carpeta de instalación de Beat Saber que se encuentra en tu biblioteca de Oculus será renombrada.",
"can-restore-later": "Si es necesario, podrás restaurarla desde el BSManager accediendo a las opciones de la versión original de Oculus y haciendo clic en 'Restaurar la carpeta'",
"tips-launch-oculus": "Consejo: Podrás iniciar esta versión directamente desde Oculus, siempre que la versión original no haya sido restaurada."
"can-restore-later": "Si es necesario, podrás restaurarla desde el BSManager accediendo a las opciones de la versión original de Oculus y haciendo clic en 'Restaurar la carpeta'"
},
"not-remind-me": "No volver a recordármelo",
"understood": "Entendido"
@@ -737,7 +736,8 @@
"nps" : "Notas Por Segundo",
"tags": "tags",
"specificities": "general",
"requirements": "requisitos"
"requirements": "requisitos",
"exclude": "excluir"
},
"map-types": {
"accuracy": "precisión",
@@ -793,6 +793,9 @@
"verified": "verificado",
"fullSpread": "panel completo"
},
"map-excludes": {
"installed": "instalado"
},
"difficulties": {
"Easy": "fácil",
"Normal": "normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "Vous devez avoir installé au moins une fois Beat Saber depuis l'Oculus Store, sinon Beat Saber pourrait se fermer automatiquement après son lancement.",
"need-backup": "Pour lancer cette version, le dossier d'installation de Beat Saber se trouvant dans votre librairie Oculus va être renommé.",
"can-restore-later": "Au besoin, vous pourrez le restaurer depuis BSManager en vous rendant dans les options de la version originale Oculus et en cliquant sur \"Restaurer le dossier\"",
"tips-launch-oculus": "Astuce : Vous pourrez lancer cette version directement depuis Oculus, tant que la version originale n'est pas restaurée."
"can-restore-later": "Au besoin, vous pourrez le restaurer depuis BSManager en vous rendant dans les options de la version originale Oculus et en cliquant sur \"Restaurer le dossier\""
},
"not-remind-me": "Ne plus me rappeler",
"understood": "J'ai compris"
@@ -737,7 +736,8 @@
"nps" : "Notes Par Seconde",
"tags": "tags",
"specificities": "général",
"requirements": "requis"
"requirements": "requis",
"exclude": "exclure"
},
"map-types": {
"accuracy": "précision",
@@ -793,6 +793,9 @@
"verified": "vérifiée",
"fullSpread": "panel complet"
},
"map-excludes": {
"installed": "installé"
},
"difficulties": {
"Easy": "facile",
"Normal": "normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "少なくとも一度はOculus StoreからBeat Saberをインストールしている必要があります。そうしないとBeat Saberは起動後に自動的に終了する可能性があります。",
"need-backup": "このバージョンを起動するには、OculusライブラリにあるBeat Saberインストールフォルダの名前を変更します。",
"can-restore-later": "必要であれば、BSManagerから元のOculusバージョンのオプションに行き、'フォルダを復元'をクリックして復元することができます。",
"tips-launch-oculus": "ヒント: 元のバージョンが復元されない限り、Oculusから直接このバージョンを起動することができます。"
"can-restore-later": "必要であれば、BSManagerから元のOculusバージョンのオプションに行き、'フォルダを復元'をクリックして復元することができます。"
},
"not-remind-me": "二度と表示しないでください",
"understood": "分かった"
@@ -737,7 +736,8 @@
"nps" : "秒間ノート数",
"tags": "タグ",
"specificities": "一般",
"requirements": "要Mod"
"requirements": "要Mod",
"exclude": "除外する"
},
"map-types": {
"accuracy": "正確",
@@ -793,6 +793,9 @@
"verified": "認証済み",
"fullSpread": "フルスプレッド"
},
"map-excludes": {
"installed": "インストール済み"
},
"difficulties": {
"Easy": "easy",
"Normal": "normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "Вы должны были установить Beat Saber хотя бы один раз из Oculus Store, иначе Beat Saber может автоматически закрыться после запуска.",
"need-backup": "Для запуска этой версии папка установки Beat Saber в вашей библиотеке Oculus будет переименована.",
"can-restore-later": "При необходимости вы можете восстановить её через BSManager, перейдя в опции оригинальной версии Oculus и нажав на 'Восстановить папку'",
"tips-launch-oculus": "Совет: Вы можете запустить эту версию напрямую через Oculus, пока оригинальная версия не восстановлена."
"can-restore-later": "При необходимости вы можете восстановить её через BSManager, перейдя в опции оригинальной версии Oculus и нажав на 'Восстановить папку'"
},
"not-remind-me": "Больше не напоминать",
"understood": "Понял"
@@ -736,7 +735,8 @@
"nps" : "Нот в Секунду",
"tags": "тэги",
"specificities": "основное",
"requirements": "требуемые моды"
"requirements": "требуемые моды",
"exclude": "исключить"
},
"map-types": {
"accuracy": "точность",
@@ -792,6 +792,9 @@
"verified": "проверенные авторы",
"fullSpread": "полный набор"
},
"map-excludes": {
"installed": "установленный"
},
"difficulties": {
"Easy": "легко",
"Normal": "нормально",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "你必須至少從 Oculus 商店安裝過一次 BeatSaber,否則 BeatSaber 可能會在啟動後自動關閉。",
"need-backup": "要啟動該版本,位於你的 Oculus 庫中的 BeatSaber 安裝文件夾將被重命名。",
"can-restore-later": "如果需要,你可以通過前往 BSManager 的原始 Oculus 版本選項並點擊'恢復文件夾'來恢復它。",
"tips-launch-oculus": "提示:只要原始版本沒有被恢復,你就可以直接從 Oculus 啟動該版本。"
"can-restore-later": "如果需要,你可以通過前往 BSManager 的原始 Oculus 版本選項並點擊'恢復文件夾'來恢復它。"
},
"not-remind-me": "不再提醒我",
"understood": "明白了"
@@ -737,7 +736,8 @@
"nps" : "每秒音符數",
"tags": "標籤",
"specificities": "general",
"requirements": "要求"
"requirements": "要求",
"exclude": "排除"
},
"map-types": {
"accuracy": "精確度",
@@ -793,6 +793,9 @@
"verified": "Verified",
"fullSpread": "Full spread"
},
"map-excludes": {
"installed": "已安裝"
},
"difficulties": {
"Easy": "Easy",
"Normal": "Normal",
+6 -3
View File
@@ -696,8 +696,7 @@
"body": {
"must-be-installed-once": "你必须至少从 Oculus 商店安装过一次 BeatSaber,否则 BeatSaber 可能会在启动后自动关闭。",
"need-backup": "要启动该版本,位于你的 Oculus 库中的 BeatSaber 安装文件夹将被重命名。",
"can-restore-later": "如果需要,你可以通过前往 BSManager 的原始 Oculus 版本选项并点击'恢复文件夹'来恢复它。",
"tips-launch-oculus": "提示:只要原始版本没有被恢复,你就可以直接从 Oculus 启动该版本。"
"can-restore-later": "如果需要,你可以通过前往 BSManager 的原始 Oculus 版本选项并点击'恢复文件夹'来恢复它。"
},
"not-remind-me": "不再提醒我",
"understood": "明白了"
@@ -737,7 +736,8 @@
"nps" : "每秒音符数",
"tags": "标签",
"specificities": "general",
"requirements": "要求"
"requirements": "要求",
"exclude": "排除"
},
"map-types": {
"accuracy": "精确度",
@@ -793,6 +793,9 @@
"verified": "verified",
"fullSpread": "full spread"
},
"map-excludes": {
"installed": "已安装"
},
"difficulties": {
"Easy": "easy",
"Normal": "normal",
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
+281
View File
@@ -0,0 +1,281 @@
# 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",
]
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "oculus_symlink_cleaner"
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"
[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"
+9
View File
@@ -0,0 +1,9 @@
extern crate winres;
fn main() {
if cfg!(target_os = "windows") {
let mut res = winres::WindowsResource::new();
res.set_icon("./icon.ico");
res.compile().unwrap();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

+60
View File
@@ -0,0 +1,60 @@
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<String> = env::args().collect();
if args.len() != 3 {
eprintln!("Usage: {} <pid> <path_to_directory>", 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),
Err(e) => eprintln!("Failed to delete directory {:?}: {}", directory_path, e),
}
break;
}
}
}
}
fn delete_dir_if_is_symlink(path: &Path) -> Result<(), Box<dyn std::error::Error>>{
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())
}
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"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": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "bs-manager",
"version": "1.4.4-alpha.2",
"version": "1.4.5",
"description": "BSManager",
"main": "./dist/main/main.js",
"author": {
+6 -2
View File
@@ -6,6 +6,7 @@ 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();
@@ -19,9 +20,12 @@ ipc.on<boolean>("bs-launch.need-start-as-admin", (_, reply) => {
const steam = SteamService.getInstance();
reply(from(isElevated().then(elevated => {
if(elevated){ return false; }
return steam.isElevated();
return steam.isElevated().catch(e => {
log.error("Error while checking if Steam is running as admin", e);
return false;
});
})));
})
});
ipc.on<LaunchOption>("create-launch-shortcut", (req, reply) => {
const bsLauncher = BSLauncherService.getInstance();
@@ -5,9 +5,9 @@ import path from "path";
import log from "electron-log";
export abstract class AbstractLauncherService {
protected readonly localVersions = BSLocalVersionService.getInstance();
constructor(){
this.localVersions = BSLocalVersionService.getInstance();
}
@@ -46,18 +46,20 @@ export abstract class AbstractLauncherService {
}
protected launchBs(bsExePath: string, args: string[], options?: SpawnOptionsWithoutStdio): Promise<number> {
return new Promise<number>((resolve, reject) => {
const bsProcess = this.launchBSProcess(bsExePath, args, options);
protected launchBs(bsExePath: string, args: string[], options?: SpawnOptionsWithoutStdio): {process: ChildProcessWithoutNullStreams, exit: Promise<number>} {
const process = this.launchBSProcess(bsExePath, args, options);
bsProcess.on("error", reject);
bsProcess.on("exit", resolve);
setTimeout(() => {
bsProcess.removeAllListeners("error");
bsProcess.removeAllListeners("exit");
resolve(-1);
}, 30_000);
const exit = new Promise<number>((resolve, reject) => {
process.on("error", (err) => {
log.error(`Error while launching BS`, err);
reject(err);
});
process.on("exit", (code) => {
log.info(`BS process exit with code ${code}`);
resolve(code);
});
});
return { process, exit };
}
}
}
@@ -11,6 +11,10 @@ import { AbstractLauncherService } from "./abstract-launcher.service";
import { taskRunning } 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 {
@@ -25,6 +29,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St
private readonly oculus: OculusService;
private readonly pathsService: InstallationLocationService;
private readonly util: UtilsService;
private readonly oculusLib$ = new ReplaySubject<string>();
@@ -32,6 +37,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St
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){
@@ -86,7 +92,7 @@ export class OculusLauncherService extends AbstractLauncherService implements St
private async backupOriginalBeatSaber(): Promise<void>{
const bsFolder = await this.oculus.getGameFolder(OCULUS_BS_DIR);
if(!bsFolder){ return; }
const backupPath = path.join(bsFolder, "..", OCULUS_BS_BACKUP_DIR);
const backupPath = await ensurePathNotAlreadyExist(path.join(bsFolder, "..", OCULUS_BS_BACKUP_DIR));
log.info("Backing up original Beat Saber", bsFolder, backupPath);
return rename(bsFolder, backupPath);
}
@@ -99,6 +105,15 @@ export class OculusLauncherService extends AbstractLauncherService implements St
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<BSLaunchEventData> {
const prepareOriginalVersion: () => Promise<string> = async () => {
@@ -156,7 +171,16 @@ export class OculusLauncherService extends AbstractLauncherService implements St
obs.next({type: BSLaunchEvent.BS_LAUNCHING});
// Launch Beat Saber
return this.launchBs(exePath, this.buildBsLaunchArgs(launchOptions)).catch(err => {
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);
});
@@ -107,7 +107,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
obs.next({type: BSLaunchEvent.BS_LAUNCHING});
const launchPromise = !launchOptions.admin ? (
this.launchBs(exePath, launchArgs, { env: {...process.env, "SteamAppId": BS_APP_ID} })
this.launchBs(exePath, launchArgs, { env: {...process.env, "SteamAppId": BS_APP_ID} }).exit
) : (
new Promise<number>(resolve => {
const adminProcess = exec(`"${this.getStartBsAsAdminExePath()}" "${exePath}" ${launchArgs.join(" ")}`, { env: {...process.env, "SteamAppId": BS_APP_ID} });
@@ -123,13 +123,16 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
})
);
await launchPromise.then(exitCode => {
try {
const exitCode = await launchPromise;
log.info("BS process exit code", exitCode);
}).catch(err => {
}
catch(err: any) {
throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR);
}).finally(() => {
this.restoreSteamVR().catch(log.error);
});
}
finally {
await this.restoreSteamVR().catch(log.error);
}
})().then(() => {
obs.complete();
@@ -2,6 +2,8 @@ import path from "path";
import { app } from "electron";
import ElectronStore from "electron-store";
import { copyDirectoryWithJunctions, deleteFolder, ensureFolderExist, pathExist } from "../helpers/fs.helpers";
import { tryit } from "../../shared/helpers/error.helpers";
import { pathExistsSync } from "fs-extra";
export class InstallationLocationService {
private static instance: InstallationLocationService;
@@ -67,8 +69,8 @@ export class InstallationLocationService {
return this.installPathConfig.get(this.STORE_INSTALLATION_PATH_KEY) as string;
}
const oldPath = path.join(app.getPath("documents"), this.INSTALLATION_FOLDER);
if(await pathExist(oldPath)){
const { result: oldPath } = tryit(() => path.join(app.getPath("documents"), this.INSTALLATION_FOLDER));
if(oldPath && pathExistsSync(oldPath)){
return app.getPath("documents");
}
+2 -1
View File
@@ -1,8 +1,9 @@
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 {
private static instance: LivService;
+3 -1
View File
@@ -1,4 +1,3 @@
import { list } from "regedit-rs";
import path from "path";
import { pathExist, resolveGUIDPath } from "../helpers/fs.helpers";
import log from "electron-log";
@@ -7,6 +6,9 @@ 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;
+9 -1
View File
@@ -1,4 +1,4 @@
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";
@@ -7,6 +7,9 @@ import log from "electron-log";
import { app, shell } from "electron";
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 {
@@ -43,6 +46,11 @@ export class SteamService {
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<boolean>{
if(process.platform === "linux"){ return true; }
const steamPid = await this.getSteamPid();
@@ -11,6 +11,7 @@ import { useTranslation } from "renderer/hooks/use-translation.hook";
import { MAP_SPECIFICITIES } from "renderer/partials/maps/map-general/map-specificity";
import { MAP_REQUIREMENTS } from "renderer/partials/maps/map-requirements/map-requirements";
import { MAP_DIFFICULTIES_COLORS } from "renderer/partials/maps/map-difficulties/map-difficulties-colors";
import { MapExclude, MAP_EXCLUDES } from "renderer/partials/maps/map-excludes/map-excludes";
import { BsmButton } from "../shared/bsm-button.component";
import equal from "fast-deep-equal/es6";
import clone from "rfdc";
@@ -21,12 +22,13 @@ export type Props = {
ref?: MutableRefObject<undefined>;
playlist?: boolean;
filter: MapFilter;
localData?: boolean;
onChange?: (filter: MapFilter) => void;
onApply?: (filter: MapFilter) => void;
onClose?: (filter: MapFilter) => void;
};
export function FilterPanel({ className, ref, playlist = false, filter, onChange, onApply, onClose }: Props) {
export function FilterPanel({ className, ref, playlist = false, filter, localData = true, onChange, onApply, onClose }: Props) {
const t = useTranslation();
const [haveChanged, setHaveChanged] = useState(false);
@@ -147,6 +149,10 @@ export function FilterPanel({ className, ref, playlist = false, filter, onChange
return t(`maps.map-specificities.${specificity}`);
};
const translateMapExclude = (exclude: MapExclude): string => {
return t(`maps.map-excludes.${exclude}`);
};
type BooleanKeys<T> = { [k in keyof T]: T[k] extends boolean ? k : never }[keyof T];
const handleCheckbox = (key: BooleanKeys<MapFilter>) => {
@@ -189,6 +195,17 @@ export function FilterPanel({ className, ref, playlist = false, filter, onChange
<span className="grow capitalize">{requirement}</span>
</div>
))}
{ !localData && (
<>
<h2 className="my-1 uppercase text-sm">{t("maps.map-filter-panel.exclude")}</h2>
{MAP_EXCLUDES.map(exclude => (
<div key={exclude} className="flex justify-start items-center h-[22px] z-20 relative py-0.5 cursor-pointer" onClick={() => handleCheckbox(exclude)}>
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" checked={filter?.[exclude]} onChange={() => handleCheckbox(exclude)} />
<span className="grow capitalize">{translateMapExclude(exclude)}</span>
</div>
))}
</>
)}
</section>
<section className="grow capitalize">
<h2 className="uppercase text-sm mb-1">{t("maps.map-filter-panel.tags")}</h2>
@@ -76,12 +76,48 @@ export const DownloadMapsModal: ModalComponent<void, { version: BSVersion; owned
};
}, []);
const loadMaps = (params: SearchParams) => {
const applyInstalledFilter = (maps: BsvMapDetail[]): BsvMapDetail[] => {
return maps.filter(map => {
if (!filter.installed) {
return true;
}
return !map.versions.some(version => ownedMapHashs.includes(version.hash));
});
}
const loadMaps = (params: SearchParams, tryToLoad = 5) => {
setLoading(() => true);
beatSaver
.searchMaps(params)
.then(maps => setMaps(prev => [...prev, ...maps]))
.finally(() => setLoading(() => false));
const searchResult = beatSaver.searchMaps(params);
if (!filter.installed) {
searchResult
.then(maps => setMaps(prev => [...prev, ...maps]))
.finally(() => setLoading(() => false));
return;
}
searchResult
.then(maps => {
if (!maps.length) {
setLoading(() => false);
return;
}
maps = applyInstalledFilter(maps);
setMaps(prev => [...prev, ...maps])
if (maps.length < tryToLoad) {
handleLoadMore();
return;
}
setLoading(() => false);
})
};
const extractMapDiffs = (map: BsvMapDetail): Map<BsvMapCharacteristic, ParsedMapDiff[]> => {
@@ -146,7 +182,7 @@ export const DownloadMapsModal: ModalComponent<void, { version: BSVersion; owned
>
<div className="flex h-9 gap-2 shrink-0">
<BsmDropdownButton ref={filterContainerRef} className="shrink-0 h-full relative z-[1] flex justify-start" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1 !bg-light-main-color-1 dark:!bg-main-color-1" icon="filter" text="pages.version-viewer.maps.search-bar.filters-btn" withBar={false}>
<FilterPanel className="absolute top-[calc(100%+3px)] origin-top w-[450px] h-fit p-2 rounded-md shadow-md shadow-black" filter={filter} onChange={setFilter} onApply={handleSearch} onClose={() => filterContainerRef.current.close()} />
<FilterPanel className="absolute top-[calc(100%+3px)] origin-top w-[450px] h-fit p-2 rounded-md shadow-md shadow-black" localData={false} filter={filter} onChange={setFilter} onApply={handleSearch} onClose={() => filterContainerRef.current.close()} />
</BsmDropdownButton>
<input className="h-full bg-light-main-color-1 dark:bg-main-color-1 rounded-full px-2 grow pb-0.5" type="text" name="" id="" placeholder={t("pages.version-viewer.maps.search-bar.search-placeholder")} value={query} onChange={e => setQuery(e.target.value)} />
<BsmButton
@@ -19,11 +19,10 @@ export const OriginalOculusVersionBackupModal: ModalComponent<boolean, void> = (
<form className="max-w-[450px] text-gray-800 dark:text-gray-200">
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.original-version-backup-oculus.title")}</h1>
<BsmImage className="mx-auto h-20" image={BeatConflict} />
<p className="text-sm italic mb-4 font-bold">{t("modals.original-version-backup-oculus.body.must-be-installed-once")}</p>
<p className="mb-4">{t("modals.original-version-backup-oculus.body.need-backup")}</p>
<p className="mb-4">{t("modals.original-version-backup-oculus.body.can-restore-later")}</p>
<p className="text-sm italic mb-4">{t("modals.original-version-backup-oculus.body.tips-launch-oculus")}</p>
<div className="flex flex-row justify-start items-center gap-1.5 my-4" >
<BsmCheckbox className="relative z-[1] w-6 aspect-square" checked={dontShowAgain} onChange={setDontShowAgain} />
@@ -0,0 +1,3 @@
export type MapExclude = "installed";
export const MAP_EXCLUDES: MapExclude[] = ["installed"];
@@ -162,6 +162,7 @@ export interface MapFilter {
to?: number;
fullSpread?: boolean;
ranked?: boolean;
installed?: boolean;
minDuration?: number;
maxDuration?: number;
minNps?: number;