diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index c8cda9ea..aba8cb27 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -15,7 +15,9 @@ "apply": "Übernehmen", "copy": "Kopieren", "copied": "Kopiert!", - "confirm": "Bestätigen" + "confirm": "Bestätigen", + "choose-folder": "Ordner Wählen", + "unknown": "Ein unbekannter Fehler ist aufgetreten ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "Version hinzufügen", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "Installations-Ordner", - "description": "Ändern Sie den Ordner, der alle von BSManager heruntergeladenen Inhalte enthalten wird.", - "choose-folder": "Ordner wählen" + "description": "Ändern Sie den Ordner, der alle von BSManager heruntergeladenen Inhalte enthalten wird." + }, + "proton-folder": { + "title": "Proton-Ordner", + "description": "Ändern Sie den Ordner zum Proton-Pfad. (z. B. Proton - Experimental)", + "errors": { + "title": "Das Einstellen des Proton-Ordners ist fehlgeschlagen", + "invalid-folder": "Ungültiger Proton-Ordnerpfad" + } }, "additional-content": { "title": "Zusätzlicher Inhalt", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus läuft nicht", "BS_ALREADY_RUNNING": "Beat Saber läuft bereits", "EXE_NOT_FINDED": "Dateien fehlen", + "PROTON_NOT_SET": "Proton-Ordner nicht festgelegt", + "PROTON_NOT_FOUND": "Proton-Binärdatei nicht gefunden", "EXIT": "Abrupter Stop", "OCULUS_LIB_NOT_FOUND": "Oculus-Bibliothek nicht gefunden" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "Schließe Beat Saber, bevor du es erneut startest.", "EXE_NOT_FINDED": "Einige Dateien scheinen zu fehlen. Versuche, die Dateien zu überprüfen.", "EXIT": "Beat Saber stoppte abrupt. Versuche, die Dateien zu überprüfen.", - "OCULUS_LIB_NOT_FOUND": "Überprüfe, ob die Oculus-Anwendung korrekt installiert ist und dass die Bibliotheken in Oculus richtig definiert sind." + "OCULUS_LIB_NOT_FOUND": "Überprüfe, ob die Oculus-Anwendung korrekt installiert ist und dass die Bibliotheken in Oculus richtig definiert sind.", + "PROTON_NOT_SET": "Stellen Sie den Proton-Pfad in den Einstellungen ein.", + "PROTON_NOT_FOUND": "Stellen Sie den Proton-Pfad in den Einstellungen ein." }, "actions": { "STEAM_NOT_RUNNING": "Starte Steam" @@ -821,9 +834,14 @@ "ask-install-path": { "title": "Installations Ordner", "choose-folder-description": "Wählen Sie den Ordner aus, der alle von BSManager heruntergeladenen Inhalte enthalten soll. (Versionen, Mods, Karten, Playlists, etc.)", - "choose-folder": "Ordner Wählen", "default": "Standard", "default-tooltip": "Standardmäßig in Ihrem persönlichen Ordner" + }, + "choose-proton-folder": { + "title": "Proton-Ordner", + "proton-folder-description": "Unter Linux benötigt BSManager Proton, um zu funktionieren. Wählen Sie den Installationsordner von Proton, um fortzufahren.", + "proton-folder-placeholder": "Proton-Installationsordner", + "where-is-proton-installed": "Wo ist Proton installiert?" } }, "maps": { diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 40d3218f..97d3c90a 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -15,7 +15,9 @@ "apply": "Apply", "copy": "Copy", "copied": "Copied!", - "confirm": "Confirm" + "confirm": "Confirm", + "choose-folder": "Choose folder", + "unknown": "An unknown error occurred ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "Add a version", @@ -157,13 +159,15 @@ }, "installation-folder": { "title": "Installation folder", - "description": "Change the folder that will contain all the content downloaded by BSManager.", - "choose-folder": "Choose folder" + "description": "Change the folder that will contain all the content downloaded by BSManager." }, - "proton-path": { - "title": "Proton path", - "description": "Change the path to the Proton binary", - "choose-file": "Choose file" + "proton-folder": { + "title": "Proton folder", + "description": "Change the folder to the Proton path. (eg. Proton - Experimental)", + "errors": { + "title": "Setting the Proton folder failed", + "invalid-folder": "Invalid Proton folder path" + } }, "additional-content": { "title": "Additional content", @@ -439,7 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus is not running", "BS_ALREADY_RUNNING": "Beat Saber already running", "EXE_NOT_FINDED": "Missing files", - "PROTON_NOT_SET": "Proton path not set", + "PROTON_NOT_SET": "Proton folder not set", + "PROTON_NOT_FOUND": "Proton binary not found", "EXIT": "Abrupt stop", "OCULUS_LIB_NOT_FOUND": "Oculus library not found" }, @@ -451,7 +456,8 @@ "EXE_NOT_FINDED": "Some files seem to be missing, try to verify the files.", "EXIT": "BeatSaber stopped abruptly, tries to check the files.", "OCULUS_LIB_NOT_FOUND": "Check that the Oculus application is properly installed, and that the libraries are correctly defined in Oculus.", - "PROTON_NOT_SET": "Set the Proton path in settings" + "PROTON_NOT_SET": "Set the Proton path in settings.", + "PROTON_NOT_FOUND": "Set the Proton path in settings." }, "actions": { "STEAM_NOT_RUNNING": "Launch Steam" @@ -828,9 +834,14 @@ "ask-install-path": { "title": "Installation folder", "choose-folder-description": "Choose the folder that will contain all the content downloaded by BSManager. (versions, mods, maps, playlists, etc.)", - "choose-folder": "Choose folder", "default": "Default", "default-tooltip": "Defaults to your home folder" + }, + "choose-proton-folder": { + "title": "Proton Folder", + "proton-folder-description": "On Linux, BSManager requires Proton to work. Choose the Proton installation folder to continue.", + "proton-folder-placeholder": "Proton installation folder", + "where-is-proton-installed": "Where is Proton installed?" } }, "maps": { diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index b068189d..9d00c590 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -15,7 +15,9 @@ "apply": "Aplicar", "copy": "Copiar", "copied": "¡Copiado!", - "confirm": "Confirmar" + "confirm": "Confirmar", + "choose-folder": "Elige la carpeta", + "unknown": "Se ha producido un error desconocido ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "Agregar una versión", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "Carpeta de instalación", - "description": "Cambiar la carpeta que contendrá todo el contenido descargado por BSManager.", - "choose-folder": "Elige la carpeta" + "description": "Cambiar la carpeta que contendrá todo el contenido descargado por BSManager." + }, + "proton-folder": { + "title": "Carpeta de Proton", + "description": "Cambia la carpeta a la ruta de Proton. (por ejemplo, Proton - Experimental)", + "errors": { + "title": "Falló al configurar la carpeta de Proton", + "invalid-folder": "Ruta de carpeta de Proton no válida" + } }, "additional-content": { "title": "Contenido adicional", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus no funciona", "BS_ALREADY_RUNNING": "Beat Saber ya está en marcha", "EXE_NOT_FINDED": "Archivos no encontrados", + "PROTON_NOT_SET": "Carpeta de Proton no establecida", + "PROTON_NOT_FOUND": "Binario de Proton no encontrado", "EXIT": "Parada abrupta", "OCULUS_LIB_NOT_FOUND": "Librería Oculus no encontrada" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "Cierra Beat Saber antes de volver a iniciarlo.", "EXE_NOT_FINDED": "Parece que faltan algunos archivos, intenta verificar los archivos.", "EXIT": "Beat Saber se detuvo abruptamente, trata de verificar los archivos.", - "OCULUS_LIB_NOT_FOUND": "Verifica que la aplicación Oculus esté correctamente instalada y que las librerías estén bien definidas en Oculus." + "OCULUS_LIB_NOT_FOUND": "Verifica que la aplicación Oculus esté correctamente instalada y que las librerías estén bien definidas en Oculus.", + "PROTON_NOT_SET": "Establece la ruta de Proton en la configuración.", + "PROTON_NOT_FOUND": "Establece la ruta de Proton en la configuración." }, "actions": { "STEAM_NOT_RUNNING": "Iniciar Steam" @@ -821,9 +834,14 @@ "ask-install-path": { "title": "Carpeta de instalación", "choose-folder-description": "Elija la carpeta que contendrá todo el contenido descargado por BSManager. (versiones, mods, mapas, listas de reproducción, etc.)", - "choose-folder": "Elige la carpeta", "default": "Predeterminado", "default-tooltip": "Por defecto, en su carpeta personal" + }, + "choose-proton-folder": { + "title": "Carpeta de Proton", + "proton-folder-description": "En Linux, BSManager necesita Proton para funcionar. Elija la carpeta de instalación de Proton para continuar.", + "proton-folder-placeholder": "Carpeta de instalación de Proton", + "where-is-proton-installed": "¿Dónde está instalado Proton?" } }, "maps": { diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index f926eae7..47921231 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -15,7 +15,9 @@ "apply": "Appliquer", "copy": "Copier", "copied": "Copié !", - "confirm": "Confirmer" + "confirm": "Confirmer", + "choose-folder": "Choisir un dossier", + "unknown": "Une erreur inconnue s'est produite ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "Ajouter une version", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "Dossier d'installation", - "description": "Changer le dossier qui contiendra tout le contenu téléchargé par BSManager.", - "choose-folder": "Choisir un dossier" + "description": "Changer le dossier qui contiendra tout le contenu téléchargé par BSManager." + }, + "proton-folder": { + "title": "Dossier Proton", + "description": "Changez le dossier vers le chemin de Proton. (par exemple, Proton - Experimental)", + "errors": { + "title": "Le paramétrage du dossier Proton a échoué", + "invalid-folder": "Chemin du dossier Proton invalide" + } }, "additional-content": { "title": "Contenus additionnels", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus n'est pas lancé", "BS_ALREADY_RUNNING": "Beat Saber est déjà lancé", "EXE_NOT_FINDED": "Fichiers manquants", + "PROTON_NOT_SET": "Dossier Proton non défini", + "PROTON_NOT_FOUND": "Binaire Proton non trouvé", "EXIT": "Arrêt brutal", "OCULUS_LIB_NOT_FOUND": "Librairie Oculus introuvable" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "Ferme Beat Saber avant de le lancer à nouveau.", "EXE_NOT_FINDED": "Quelques fichiers semblent manquants. Essaye de vérifier les fichiers.", "EXIT": "Beat Saber s'est arrêté brusquement, essaye de vérifier les fichiers.", - "OCULUS_LIB_NOT_FOUND": "Vérifie que l'application Oculus est correctement installée, et que les librairies sont bien définies dans Oculus." + "OCULUS_LIB_NOT_FOUND": "Vérifie que l'application Oculus est correctement installée, et que les librairies sont bien définies dans Oculus.", + "PROTON_NOT_SET": "Définissez le chemin de Proton dans les paramètres.", + "PROTON_NOT_FOUND": "Définissez le chemin de Proton dans les paramètres." }, "actions": { "STEAM_NOT_RUNNING": "Lancer Steam" @@ -822,9 +835,14 @@ "ask-install-path": { "title": "Dossier d'installation", "choose-folder-description": "Choisissez le dossier qui contiendra tout le contenu téléchargé par BSManager. (versions, mods, cartes, playlists, etc.)", - "choose-folder": "Choisir un dossier", "default": "Par défaut", "default-tooltip": "Par défaut, dans votre dossier personnel" + }, + "choose-proton-folder": { + "title": "Dossier Proton", + "proton-folder-description": "Sous Linux, BSManager a besoin de Proton pour fonctionner. Choisissez le dossier d'installation de Proton pour continuer.", + "proton-folder-placeholder": "Dossier d'installation de Proton", + "where-is-proton-installed": "Où est installé Proton ?" } }, "maps": { diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index d77e9af3..7c4ce289 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -15,7 +15,9 @@ "apply": "適用", "copy": "コピー", "copied": "コピー済み!", - "confirm": "確認" + "confirm": "確認", + "choose-folder": "フォルダーを選択", + "unknown": "不明なエラーが発生しました (´・ω・`)" }, "nav-bar": { "add-version": "バージョンを追加", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "インストールフォルダー", - "description": "BSManager によってダウンロードされたすべてのコンテンツを含むフォルダを変更します。", - "choose-folder": "フォルダーを選択" + "description": "BSManager によってダウンロードされたすべてのコンテンツを含むフォルダを変更します。" + }, + "proton-folder": { + "title": "Protonフォルダー", + "description": "フォルダーをProtonのパスに変更してください。(例:Proton - Experimental)", + "errors": { + "title": "Protonフォルダーの設定に失敗しました", + "invalid-folder": "無効なProtonフォルダーのパスです" + } }, "additional-content": { "title": "追加コンテンツ", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculusが起動していません", "BS_ALREADY_RUNNING": "Beat Saberはすでに起動しています", "EXE_NOT_FINDED": "ファイルが見つかりません", + "PROTON_NOT_SET": "Protonフォルダーが設定されていません", + "PROTON_NOT_FOUND": "Protonのバイナリが見つかりません", "EXIT": "緊急停止", "OCULUS_LIB_NOT_FOUND": "Oculusライブラリが見つかりません" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "再度起動する前にBeatSaberを閉じてください。", "EXE_NOT_FINDED": "いくつかのファイルが見つからないようです。", "EXIT": "BeatSaberが緊急停止したため、ファイルをチェックします。", - "OCULUS_LIB_NOT_FOUND": "Oculusアプリケーションが正しくインストールしていることと、Oculusライブラリが正しく定義されていることを確認してください。" + "OCULUS_LIB_NOT_FOUND": "Oculusアプリケーションが正しくインストールしていることと、Oculusライブラリが正しく定義されていることを確認してください。", + "PROTON_NOT_SET": "設定でProtonのパスを設定してください。", + "PROTON_NOT_FOUND": "設定でProtonのパスを設定してください。" }, "actions": { "STEAM_NOT_RUNNING": "Steamを起動" @@ -821,9 +834,14 @@ "ask-install-path": { "title": "インストールフォルダー", "choose-folder-description": "BSManager によってダウンロードされたすべてのコンテンツ (バージョン、MOD、マップ、プレイリストなど) を含むフォルダを選択してください。", - "choose-folder": "フォルダーを選択", "default": "デフォルト", "default-tooltip": "デフォルトでは、ホームフォルダに設定されます" + }, + "choose-proton-folder": { + "title": "Protonフォルダ", + "proton-folder-description": "Linuxでは、BSManagerを動作させるためにProtonが必要です。続行するには、Protonのインストールフォルダを選択してください。", + "proton-folder-placeholder": "Protonのインストールフォルダ", + "where-is-proton-installed": "Protonはどこにインストールされていますか?" } }, "maps": { diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index 2049abc0..d4fdd86a 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -15,7 +15,9 @@ "apply": "Применить", "copy": "Скопировать", "copied": "Скопировано!", - "confirm": "Подтвердить" + "confirm": "Подтвердить", + "choose-folder": "Изменить папку", + "unknown": "Неизвестная ошибка ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "Добавить версию игры", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "Папка установок", - "description": "Изменить папку, которая будет содержать весь контент, загруженный BSManager.", - "choose-folder": "Изменить папку" + "description": "Изменить папку, которая будет содержать весь контент, загруженный BSManager." + }, + "proton-folder": { + "title": "Папка Proton", + "description": "Измените папку на путь к Proton. (например, Proton - Experimental)", + "errors": { + "title": "Не удалось установить папку Proton", + "invalid-folder": "Недействительный путь к папке Proton" + } }, "additional-content": { "title": "Дополнительный контент", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus не запущен", "BS_ALREADY_RUNNING": "Beat Saber уде запущен", "EXE_NOT_FINDED": "Отсутствуют файлы", + "PROTON_NOT_SET": "Папка Proton не установлена", + "PROTON_NOT_FOUND": "Бинарный файл Proton не найде", "EXIT": "Вылет", "OCULUS_LIB_NOT_FOUND": "Библиотека Oculus не найдена" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "Закройте Beat Saber, чтобы запустить его.", "EXE_NOT_FINDED": "Некоторые файлы отсутствуют, пробуем их проверить.", "EXIT": "Beat Saber вылетел, пытаемся проверить файлы.", - "OCULUS_LIB_NOT_FOUND": "Проверьте, правильно ли установлено приложение Oculus, и правильно ли определены библиотеки в Oculus." + "OCULUS_LIB_NOT_FOUND": "Проверьте, правильно ли установлено приложение Oculus, и правильно ли определены библиотеки в Oculus.", + "PROTON_NOT_SET": "Установите путь к Proton в настройках.", + "PROTON_NOT_FOUND": "Установите путь к Proton в настройках." }, "actions": { "STEAM_NOT_RUNNING": "Запустить Steam" @@ -820,9 +833,14 @@ "ask-install-path": { "title": "Папка установок", "choose-folder-description": "Выберите папку, которая будет содержать весь контент, загруженный BSManager. (версии, моды, карты, плейлисты и т.д.)", - "choose-folder": "Изменить папку", "default": "По умолчанию", "default-tooltip": "По умолчанию в вашей домашней папке" + }, + "choose-proton-folder": { + "title": "Папка Proton", + "proton-folder-description": "В Linux BSManager требует Proton для работы. Выберите папку установки Proton, чтобы продолжить.", + "proton-folder-placeholder": "Папка установки Proton", + "where-is-proton-installed": "Где установлен Proton?" } }, "maps": { diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index 79c75ffe..a83783cd 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -15,7 +15,9 @@ "apply": "應用", "copy": "複製", "copied": "已複製!", - "confirm": "確認" + "confirm": "確認", + "choose-folder": "選擇文件夾", + "unknown": "發生了一個錯誤 ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "新增版本", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "安裝文件夾", - "description": "更改將包含 BSManager 下載的所有內容的文件夾。", - "choose-folder": "選擇文件夾" + "description": "更改將包含 BSManager 下載的所有內容的文件夾。" + }, + "proton-folder": { + "title": "Proton資料夾", + "description": "將資料夾更改為Proton路徑。(例如:Proton - Experimental)", + "errors": { + "title": "設置Proton資料夾失敗", + "invalid-folder": "無效的Proton資料夾路徑" + } }, "additional-content": { "title": "附加內容", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus 未運行", "BS_ALREADY_RUNNING": "Beat Saber 已在運行", "EXE_NOT_FINDED": "缺少文件", + "PROTON_NOT_SET": "Proton資料夾未設置", + "PROTON_NOT_FOUND": "找不到Proton二進位檔", "EXIT": "突然停止", "OCULUS_LIB_NOT_FOUND": "未找到 Oculus 庫" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "再次啟動前請關閉 BeatSaber。", "EXE_NOT_FINDED": "某些文件似乎遺失了,嘗試驗證文件。", "EXIT": "BeatSaber 突然停止,請檢查文件。", - "OCULUS_LIB_NOT_FOUND": "檢查 Oculus 應用是否正確安裝,以及庫是否在 Oculus 中正確定義。" + "OCULUS_LIB_NOT_FOUND": "檢查 Oculus 應用是否正確安裝,以及庫是否在 Oculus 中正確定義。", + "PROTON_NOT_SET": "在設定中設置Proton路徑。", + "PROTON_NOT_FOUND": "在設定中設置Proton路徑。" }, "actions": { "STEAM_NOT_RUNNING": "啟動 Steam" @@ -821,9 +834,14 @@ "ask-install-path": { "title": "安裝文件夾", "choose-folder-description": "選擇將包含 BSManager 下載的所有內容的文件夾。(版本、mod、地圖、播放列表等)", - "choose-folder": "選擇文件夾", "default": "預設", "default-tooltip": "預設為您的主資料夾" + }, + "choose-proton-folder": { + "title": "Proton資料夾", + "proton-folder-description": "在Linux上,BSManager需要Proton才能運行。請選擇Proton的安裝資料夾以繼續。", + "proton-folder-placeholder": "Proton安裝資料夾", + "where-is-proton-installed": "Proton安裝在哪裡?" } }, "maps": { diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 57359296..58ea8f57 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -15,7 +15,9 @@ "apply": "应用", "copy": "复制", "copied": "已复制!", - "confirm": "确认" + "confirm": "确认", + "choose-folder": "选择文件夹", + "Unknown": "发生了一个错误 ¯\\_(ツ)_/¯" }, "nav-bar": { "add-version": "添加版本", @@ -157,8 +159,15 @@ }, "installation-folder": { "title": "安装文件夹", - "description": "更改将包含 BSManager 下载的所有内容的文件夹。", - "choose-folder": "选择文件夹" + "description": "更改将包含 BSManager 下载的所有内容的文件夹。" + }, + "proton-folder": { + "title": "Proton文件夹", + "description": "将文件夹更改为Proton路径。(例如:Proton - Experimental)", + "errors": { + "title": "设置Proton文件夹失败", + "invalid-folder": "无效的Proton文件夹路径" + } }, "additional-content": { "title": "附加内容", @@ -434,6 +443,8 @@ "OCULUS_NOT_RUNNING": "Oculus 未运行", "BS_ALREADY_RUNNING": "Beat Saber 已在运行", "EXE_NOT_FINDED": "缺少文件", + "PROTON_NOT_SET": "Proton文件夹未设置", + "PROTON_NOT_FOUND": "未找到Proton二进制文件", "EXIT": "突然停止", "OCULUS_LIB_NOT_FOUND": "未找到 Oculus 库" }, @@ -444,7 +455,9 @@ "BS_ALREADY_RUNNING": "再次启动前请关闭 BeatSaber。", "EXE_NOT_FINDED": "某些文件似乎丢失了,尝试验证文件。", "EXIT": "BeatSaber 突然停止,请检查文件。", - "OCULUS_LIB_NOT_FOUND": "检查 Oculus 应用是否正确安装,以及库是否在 Oculus 中正确定义。" + "OCULUS_LIB_NOT_FOUND": "检查 Oculus 应用是否正确安装,以及库是否在 Oculus 中正确定义。", + "PROTON_NOT_SET": "在设置中设置Proton路径。", + "PROTON_NOT_FOUND": "在设置中设置Proton路径。" }, "actions": { "STEAM_NOT_RUNNING": "启动 Steam" @@ -821,9 +834,14 @@ "ask-install-path": { "title": "安装文件夹", "choose-folder-description": "选择将包含 BSManager 下载的所有内容的文件夹。(版本、mod、地图、播放列表等)", - "choose-folder": "选择文件夹", "default": "默认", "default-tooltip": "默认为您的主文件夹" + }, + "choose-proton-folder": { + "title": "Proton文件夹", + "proton-folder-description": "在Linux上,BSManager需要Proton才能运行。请选择Proton的安装文件夹以继续。", + "proton-folder-placeholder": "Proton安装文件夹", + "where-is-proton-installed": "Proton安装在哪里?" } }, "maps": { diff --git a/src/main/constants.ts b/src/main/constants.ts index 3a3ba13f..9a77872e 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -16,3 +16,9 @@ export const CACHE_PATH = path.join(app.getPath("userData"), "CachedData"); export const IMAGE_CACHE_PATH = path.join(CACHE_PATH, "imagescache"); export const HTTP_STATUS_CODES = constants; + +// Linux related stuff + +export const PROTON_BINARY_PREFIX = "proton"; +export const WINE_BINARY_PREFIX = path.join("files", "bin", "wine64"); + diff --git a/src/main/ipcs/index.ts b/src/main/ipcs/index.ts index f80cf9f8..b8d20b46 100644 --- a/src/main/ipcs/index.ts +++ b/src/main/ipcs/index.ts @@ -14,3 +14,4 @@ import "./model-saber.ipcs"; import "./bs-model-ipcs"; import "./bs-version-download/bs-download-ipcs"; import "./static-configuration.ipcs"; +import "./linux.ipcs.ts"; diff --git a/src/main/ipcs/linux.ipcs.ts b/src/main/ipcs/linux.ipcs.ts new file mode 100644 index 00000000..d40ad9fa --- /dev/null +++ b/src/main/ipcs/linux.ipcs.ts @@ -0,0 +1,11 @@ +import { LinuxService } from "main/services/linux.service"; +import { IpcService } from "../services/ipc.service"; +import { of } from "rxjs"; + +const ipc = IpcService.getInstance(); + +ipc.on("linux.verify-proton-folder", (_, reply) => { + const linuxService = LinuxService.getInstance(); + reply(of(linuxService.verifyProtonPath())); +}); + diff --git a/src/main/ipcs/os-controls-ipcs.ts b/src/main/ipcs/os-controls-ipcs.ts index 2ca355a3..5eaa7954 100644 --- a/src/main/ipcs/os-controls-ipcs.ts +++ b/src/main/ipcs/os-controls-ipcs.ts @@ -1,9 +1,10 @@ -import { shell, dialog, app, BrowserWindow } from "electron"; +import { shell, dialog, app, BrowserWindow, OpenDialogOptions } from "electron"; import { NotificationService } from "../services/notification.service"; import { IpcService } from "../services/ipc.service"; import { from, of } from "rxjs"; import { readFileSync } from "fs-extra"; import log from "electron-log"; +import path from "path"; // TODO IMPROVE WINDOW CONTROL BY USING WINDOW SERVICE @@ -19,7 +20,20 @@ ipc.on("open-dialog", (args, reply) => { }) ipc.on("choose-folder", (args, reply) => { - reply(from(dialog.showOpenDialog({ properties: ["openDirectory"], defaultPath: args ?? "" }))); + const options: OpenDialogOptions = { + properties: ["openDirectory"], + defaultPath: args?.defaultPath ?? "" + }; + + if (args?.showHidden) { + options.properties.push("showHiddenFiles"); + } + + if (args?.parent) { + options.defaultPath = path.join(app.getPath(args.parent), options.defaultPath); + } + + reply(from(dialog.showOpenDialog(options))); }); ipc.on("choose-file", async (args, reply) => { diff --git a/src/main/ipcs/static-configuration.ipcs.ts b/src/main/ipcs/static-configuration.ipcs.ts index 586b04e5..d3dc7b93 100644 --- a/src/main/ipcs/static-configuration.ipcs.ts +++ b/src/main/ipcs/static-configuration.ipcs.ts @@ -1,4 +1,4 @@ -import { of } from "rxjs"; +import { from, of } from "rxjs"; import { IpcService } from "../services/ipc.service"; import { StaticConfigurationService } from "../services/static-configuration.service"; @@ -10,5 +10,5 @@ ipc.on("static-configuration.get", (args, reply) => { }); ipc.on("static-configuration.set", (args, reply) => { - reply(of(staticConfig.set(args.key, args.value))); + reply(from(staticConfig.set(args.key, args.value))); }); diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index 0e414087..5154d2e2 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -1,16 +1,17 @@ import { Observable } from "rxjs"; import { BSLaunchError, BSLaunchEvent, BSLaunchEventData, BSLaunchWarning, LaunchOption } from "../../../shared/models/bs-launch"; import { StoreLauncherInterface } from "./store-launcher.interface"; -import { pathExists, rename } from "fs-extra"; +import { pathExists, pathExistsSync, rename } from "fs-extra"; import { SteamService } from "../steam.service"; import path from "path"; -import { BS_APP_ID, BS_EXECUTABLE, STEAMVR_APP_ID } from "../../constants"; +import { BS_APP_ID, BS_EXECUTABLE, PROTON_BINARY_PREFIX, 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 { UtilsService } from "../utils.service"; import { exec } from "child_process"; import fs from 'fs'; +import { StaticConfigurationService } from "../static-configuration.service"; export class SteamLauncherService extends AbstractLauncherService implements StoreLauncherInterface{ @@ -23,11 +24,13 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto return SteamLauncherService.instance; } + private readonly staticConfig: StaticConfigurationService; private readonly steam: SteamService; private readonly util: UtilsService; private constructor(){ super(); + this.staticConfig = StaticConfigurationService.getInstance(); this.steam = SteamService.getInstance(); this.util = UtilsService.getInstance(); } @@ -128,9 +131,16 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto `${exePath}`, ...launchArgs, ]; - exePath = launchOptions.protonPath; - if (!exePath) { - throw CustomError.fromError(new Error("Proton path not set"), BSLaunchError.PROTON_NOT_SET); + + if (!this.staticConfig.has("proton-folder")) { + throw CustomError.fromError(new Error("Proton folder not set"), BSLaunchError.PROTON_NOT_SET); + } + exePath = path.join(this.staticConfig.get("proton-folder"), PROTON_BINARY_PREFIX); + if (!pathExistsSync(exePath)) { + throw CustomError.fromError( + new Error("Could not locate proton binary"), + BSLaunchError.PROTON_NOT_FOUND + ); } // Setup Proton environment variables diff --git a/src/main/services/linux.service.ts b/src/main/services/linux.service.ts new file mode 100644 index 00000000..2336229a --- /dev/null +++ b/src/main/services/linux.service.ts @@ -0,0 +1,51 @@ +import path from "path"; +import { pathExistsSync } from "fs-extra"; +import { PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants"; +import { StaticConfigurationService } from "./static-configuration.service"; + +export class LinuxService { + private static instance: LinuxService; + + public static getInstance(): LinuxService { + if (!LinuxService.instance) { + LinuxService.instance = new LinuxService(); + } + return LinuxService.instance; + } + + private readonly staticConfig: StaticConfigurationService; + + private constructor() { + this.staticConfig = StaticConfigurationService.getInstance(); + } + + public verifyProtonPath(protonFolder: string = ""): boolean { + if (protonFolder === "") { + if (!this.staticConfig.has("proton-folder")) { + return false; + } + + protonFolder = this.staticConfig.get("proton-folder"); + } + + const protonPath = path.join(protonFolder, PROTON_BINARY_PREFIX); + const winePath = path.join(protonFolder, WINE_BINARY_PREFIX); + return pathExistsSync(protonPath) && pathExistsSync(winePath); + } + + public getWinePath(): string { + if (!this.staticConfig.has("proton-folder")) { + throw new Error("proton-folder variable not set"); + } + + const winePath = path.join( + this.staticConfig.get("proton-folder"), + WINE_BINARY_PREFIX + ); + if (!pathExistsSync(winePath)) { + throw new Error(`"${winePath}" binary file not found`); + } + + return winePath; + } +} diff --git a/src/main/services/mods/bs-mods-manager.service.ts b/src/main/services/mods/bs-mods-manager.service.ts index dd574c67..91a849e4 100644 --- a/src/main/services/mods/bs-mods-manager.service.ts +++ b/src/main/services/mods/bs-mods-manager.service.ts @@ -17,12 +17,15 @@ import { sToMs } from "../../../shared/helpers/time.helpers"; import { ensureDir, pathExistsSync } from "fs-extra"; import { CustomError } from "shared/models/exceptions/custom-error.class"; import { popElement } from "shared/helpers/array.helpers"; +import { LinuxService } from "../linux.service"; +import { tryit } from "shared/helpers/error.helpers"; export class BsModsManagerService { private static instance: BsModsManagerService; private readonly beatModsApi: BeatModsApiService; private readonly bsLocalService: BSLocalVersionService; + private readonly linuxService: LinuxService; private readonly requestService: RequestService; private manifestMatches: Mod[]; @@ -37,6 +40,7 @@ export class BsModsManagerService { private constructor() { this.beatModsApi = BeatModsApiService.getInstance(); this.bsLocalService = BSLocalVersionService.getInstance(); + this.linuxService = LinuxService.getInstance(); this.requestService = RequestService.getInstance(); } @@ -140,25 +144,34 @@ export class BsModsManagerService { return false; } - return new Promise(resolve => { - const cmd = process.platform === 'linux' - ? `screen -dmS "BSIPA" dotnet "${ipaPath}" ${args.join(" ")}` // Must run through screen, otherwise BSIPA tries to move console cursor and crashes. - : `"${ipaPath}" ${args.join(" ")}`; + let cmd = `"${ipaPath}" "${bsExePath}" ${args.join(" ")}`; + if (process.platform === "linux") { + const { error, result: winePath } = tryit(() => this.linuxService.getWinePath()); + if (error) { + log.error(error); + return false; + } + cmd = `"${winePath}" ${cmd}`; + } + return new Promise(resolve => { log.info("START IPA PROCESS", cmd); const processIPA = spawn(cmd, { cwd: versionPath, detached: true, shell: true }); - const timemout = setTimeout(() => { + const timeout = setTimeout(() => { log.info("Ipa process timeout"); resolve(false) }, sToMs(30)); + processIPA.stdout.on("data", data => { + log.info("IPA process stdout", data.toString()); + }); processIPA.stderr.on("data", data => { log.error("IPA process stderr", data.toString()); }) processIPA.once("exit", code => { - clearTimeout(timemout); + clearTimeout(timeout); if (code === 0) { log.info("Ipa process exist with code 0"); return resolve(true); @@ -195,7 +208,7 @@ export class BsModsManagerService { } const crypto = require("crypto"); - const files = await zip.files; + const { files } = zip; const checkedEntries = ( await Promise.all( diff --git a/src/main/services/static-configuration.service.ts b/src/main/services/static-configuration.service.ts index 87f6f2f1..508df1ea 100644 --- a/src/main/services/static-configuration.service.ts +++ b/src/main/services/static-configuration.service.ts @@ -1,6 +1,10 @@ import ElectronStore from "electron-store"; +import { pathExistsSync } from "fs-extra"; +import path from "path"; +import { PROTON_BINARY_PREFIX, WINE_BINARY_PREFIX } from "main/constants"; import { Observable, Subject } from "rxjs"; import { BSVersion } from "shared/bs-version.interface"; +import { CustomError } from "shared/models/exceptions/custom-error.class"; export class StaticConfigurationService { private static instance: StaticConfigurationService; @@ -34,7 +38,17 @@ export class StaticConfigurationService { cb(this.get(key)); } - public set(key: K, value: StaticConfigKeyValues[K]): void { + public async set(key: K, value: StaticConfigKeyValues[K]): Promise { + // Validate the setters + switch (key) { + case "proton-folder": + this.validateProtonFolder(value as string); + break; + + default: + break; + } + this.store.set(key, value); if (this.watchers[key]) { @@ -42,6 +56,16 @@ export class StaticConfigurationService { } } + // Setters with validation + + private validateProtonFolder(protonFolder: string): void { + const protonPath = path.join(protonFolder, PROTON_BINARY_PREFIX); + const winePath = path.join(protonFolder, WINE_BINARY_PREFIX); + if (!pathExistsSync(protonPath) || !pathExistsSync(winePath)) { + throw new CustomError("Invalid proton folder path", "invalid-folder"); + } + } + public delete(key: K): void { this.store.delete(key); } @@ -60,12 +84,15 @@ export class StaticConfigurationService { } export interface StaticConfigKeyValues { - "versions": BSVersion[]; "installation-folder": string; "song-details-cache-etag": string; "disable-hadware-acceleration": boolean; "use-symlinks": boolean; -} + + // Linux Specific static configs + "versions": BSVersion[]; + "proton-folder": string; +}; export type StaticConfigKeys = keyof StaticConfigKeyValues; @@ -77,5 +104,5 @@ export type StaticConfigGetIpcRequestResponse = { export type StaticConfigSetIpcRequest = { request: { key: K, value: StaticConfigKeyValues[K] }; response: void; -} +}; diff --git a/src/renderer/components/modal/modal-types/ask-install-path.component.tsx b/src/renderer/components/modal/modal-types/setup/ask-install-path.component.tsx similarity index 98% rename from src/renderer/components/modal/modal-types/ask-install-path.component.tsx rename to src/renderer/components/modal/modal-types/setup/ask-install-path.component.tsx index 4df0691d..7e4ff0ab 100644 --- a/src/renderer/components/modal/modal-types/ask-install-path.component.tsx +++ b/src/renderer/components/modal/modal-types/setup/ask-install-path.component.tsx @@ -75,7 +75,7 @@ export const AskInstallPathModal: ModalComponent<{ installPath: string }, {}> = diff --git a/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx b/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx new file mode 100644 index 00000000..c0af536f --- /dev/null +++ b/src/renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component.tsx @@ -0,0 +1,97 @@ +import { lastValueFrom } from "rxjs"; +import { useState } from "react"; +import { useTranslation } from "renderer/hooks/use-translation.hook"; +import { useService } from "renderer/hooks/use-service.hook"; + +import { IpcService } from "renderer/services/ipc.service"; +import { ModalComponent, ModalExitCode } from "renderer/services/modale.service"; +import { NotificationService } from "renderer/services/notification.service"; +import { StaticConfigurationService } from "renderer/services/static-configuration.service"; + +import { BsmButton } from "renderer/components/shared/bsm-button.component"; + +export const ChooseProtonFolderModal: ModalComponent<{}, {}> = ({ resolver }) => { + + const t = useTranslation(); + const ipcService = useService(IpcService); + const notificationService = useService(NotificationService); + const staticConfigService = useService(StaticConfigurationService); + + const [protonFolder, setProtonFolder] = useState(null); + + const selectProtonPath = async () => { + const response = await lastValueFrom(ipcService.sendV2("choose-folder", { + parent: "home", + defaultPath: ".local/share/Steam/steamapps/common", + showHidden: true, + })); + + if (response.canceled || !response.filePaths?.length) { + return; + } + + const path = response.filePaths[0]; + + await staticConfigService.set("proton-folder", path).then(() => { + setProtonFolder(path); + }).catch(err => { + notificationService.notifyError({ + title: "pages.settings.proton-folder.errors.title", + desc: ["invalid-folder"].includes(err?.code) + ? `pages.settings.proton-folder.errors.${err.code}` + : "misc.unknown" + }); + }); + } + + const onConfirmButtonPressed = async () => { + resolver({ + exitCode: ModalExitCode.COMPLETED + }); + } + + return ( +
{ + event.preventDefault(); + onConfirmButtonPressed(); + }}> + +

{t("modals.choose-proton-folder.title")}

+ +

{t("modals.choose-proton-folder.proton-folder-description")}

+ + {t("modals.choose-proton-folder.where-is-proton-installed")} +
+ {protonFolder ? ( + + {protonFolder} + + ) : ( + {t("modals.choose-proton-folder.proton-folder-placeholder")} + )} + +
+ +
+ +
+
+ ) +} diff --git a/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx b/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx index 3538179e..b685a3fe 100644 --- a/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/share-folders-modal.component.tsx @@ -47,7 +47,9 @@ export const ShareFoldersModal: ModalComponent = ({ options: {d const addFolder = async () => { const versionPath = await lastValueFrom(versionManager.getVersionPath(data)); - const folder = await lastValueFrom(ipc.sendV2("choose-folder", versionPath)); + const folder = await lastValueFrom(ipc.sendV2("choose-folder", { + defaultPath: versionPath + })); if (!folder || folder.canceled || !folder.filePaths?.length) { return; diff --git a/src/renderer/components/shared/bsm-button.component.tsx b/src/renderer/components/shared/bsm-button.component.tsx index 5723d1ef..7883a869 100644 --- a/src/renderer/components/shared/bsm-button.component.tsx +++ b/src/renderer/components/shared/bsm-button.component.tsx @@ -90,7 +90,7 @@ export const BsmButton = forwardRef(({ className, style, iconSty {icon && } {text && (type === "submit" ? ( - ) : ( diff --git a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx index 46418feb..3f64b672 100644 --- a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx +++ b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx @@ -66,7 +66,6 @@ export function LaunchSlide({ version }: Props) { desktop: desktopMode, debug: debugMode, additionalArgs: advancedLaunch ? additionalArgs : [], - protonPath: bsLauncherService.getProtonPath(), }); return lastValueFrom(launch$).catch(() => {}); diff --git a/src/renderer/pages/settings-page.component.tsx b/src/renderer/pages/settings-page.component.tsx index 18e88002..4637c61b 100644 --- a/src/renderer/pages/settings-page.component.tsx +++ b/src/renderer/pages/settings-page.component.tsx @@ -43,7 +43,6 @@ import { AutoUpdaterService } from "renderer/services/auto-updater.service"; import BeatWaitingImg from "../../../assets/images/apngs/beat-waiting.png"; import BeatConflict from "../../../assets/images/apngs/beat-conflict.png"; import { logRenderError } from "renderer"; -import { BSLauncherService } from "renderer/services/bs-launcher.service"; import { SettingToogleSwitchGrid } from "renderer/components/settings/setting-toogle-switch-grid.component"; import { BasicModal } from "renderer/components/modal/basic-modal.component"; import { StaticConfigurationService } from "renderer/services/static-configuration.service"; @@ -57,7 +56,6 @@ export function SettingsPage() { const ipcService = useService(IpcService); const modalService = useService(ModalService); const bsDownloader = useService(BsDownloaderService); - const bsLauncher = useService(BSLauncherService); const steamDownloader = useService(SteamDownloaderService); const progressBarService = useService(ProgressBarService); const notificationService = useService(NotificationService); @@ -94,7 +92,7 @@ export function SettingsPage() { const downloadStore = useObservable(() => bsDownloader.defaultStore$); const [installationFolder, setInstallationFolder] = useState(null); - const [protonPath, setProtonPath] = useState(bsLauncher.getProtonPath()); + const [protonFolder, setProtonFolder] = useState(""); const [showSupporters, setShowSupporters] = useState(false); const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false); const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false); @@ -116,6 +114,7 @@ export function SettingsPage() { staticConfig.get("disable-hadware-acceleration").then(disabled =>setHardwareAccelerationEnabled(() => disabled !== true)); staticConfig.get("use-symlinks").then(useSymlinks => setUseSymlink(() => useSymlinks)); + staticConfig.get("proton-folder").then(setProtonFolder); }, []); const allDeepLinkEnabled = mapDeepLinksEnabled && playlistsDeepLinkEnabled && modelsDeepLinkEnabled; @@ -180,18 +179,36 @@ export function SettingsPage() { clearTimeout(timeoutId); }; - const setDefaultProtonPath = () => { + const setDefaultProtonFolder = async () => { if (!progressBarService.require()) { return; } - lastValueFrom(ipcService.sendV2("choose-file")).then(res => { - if (!res.canceled && res.filePaths?.length) { - const protonPath = res.filePaths[0]; - setProtonPath(protonPath); - bsLauncher.setProtonPath(protonPath); + try { + const pathResponse = await lastValueFrom(ipcService.sendV2("choose-folder", { + parent: "home", + defaultPath: ".local/share/Steam/steamapps/common", + showHidden: true, + })); + if ( + pathResponse.canceled + || !pathResponse.filePaths + || pathResponse.filePaths.length === 0 + ) { + return; } - }); + + const folder = pathResponse.filePaths[0]; + await staticConfig.set("proton-folder", folder); + setProtonFolder(folder); + } catch (error: any) { + notificationService.notifyError({ + title: "pages.settings.proton-folder.errors.title", + desc: ["invalid-folder"].includes(error?.code) + ? `pages.settings.proton-folder.errors.${error.code}` + : "misc.unknown", + }); + } }; const setDefaultInstallationFolder = () => { @@ -390,16 +407,16 @@ export function SettingsPage() { {installationFolder} - + - +
- - {protonPath} + + {protonFolder} - +
diff --git a/src/renderer/services/bs-launcher.service.ts b/src/renderer/services/bs-launcher.service.ts index 248870b9..8bc89ec8 100644 --- a/src/renderer/services/bs-launcher.service.ts +++ b/src/renderer/services/bs-launcher.service.ts @@ -23,8 +23,6 @@ export class BSLauncherService { public readonly versionRunning$: BehaviorSubject = new BehaviorSubject(null); - private readonly PROTON_PATH_KEY = "protonPath"; - public static getInstance(){ if(!BSLauncherService.instance){ BSLauncherService.instance = new BSLauncherService(); } return BSLauncherService.instance; @@ -38,14 +36,6 @@ export class BSLauncherService { this.modals = ModalService.getInstance(); } - public setProtonPath(protonPath: string|undefined): void { - this.config.set(this.PROTON_PATH_KEY, protonPath); - } - - public getProtonPath(): string|undefined { - return this.config.get(this.PROTON_PATH_KEY); - } - private notRewindBackupOculus(): boolean{ return this.config.get("not-rewind-backup-oculus"); } @@ -93,7 +83,6 @@ export class BSLauncherService { } public doLaunch(launchOptions: LaunchOption): Observable{ - launchOptions.protonPath = this.getProtonPath(); return this.ipcService.sendV2("bs-launch.launch", launchOptions); } diff --git a/src/renderer/services/setup.service.ts b/src/renderer/services/setup.service.ts index 5bb38f18..474cf38b 100644 --- a/src/renderer/services/setup.service.ts +++ b/src/renderer/services/setup.service.ts @@ -6,7 +6,8 @@ import { InstallationLocationService } from "./installation-location.service"; import { IpcService } from "./ipc.service"; import { ModalService } from "./modale.service"; -import { AskInstallPathModal } from "renderer/components/modal/modal-types/ask-install-path.component"; +import { AskInstallPathModal } from "renderer/components/modal/modal-types/setup/ask-install-path.component"; +import { ChooseProtonFolderModal } from "renderer/components/modal/modal-types/setup/choose-proton-folder-modal.component"; // Handle setup modals/prompts, ordering of the modals/prompts may be done here export class SetupService { @@ -35,6 +36,10 @@ export class SetupService { try { // NOTE: for modal sequencing await this.checkInstallationPath(); + + if (window.electron.platform === "linux") { + await this.checkProtonFolder(); + } } catch (error) { logRenderError(error); } @@ -61,4 +66,20 @@ export class SetupService { } } + private async checkProtonFolder(): Promise { + try { + const valid = await lastValueFrom(this.ipcService.sendV2("linux.verify-proton-folder")); + if (valid) { + return; + } + + await this.modalService.openModal( + ChooseProtonFolderModal, + { closable: false } + ); + } catch (error) { + logRenderError(error); + } + } + } diff --git a/src/shared/models/bs-launch/launch-event.model.ts b/src/shared/models/bs-launch/launch-event.model.ts index 29663132..61eb80a4 100644 --- a/src/shared/models/bs-launch/launch-event.model.ts +++ b/src/shared/models/bs-launch/launch-event.model.ts @@ -15,6 +15,7 @@ export enum BSLaunchError{ BS_EXIT_ERROR = "EXIT", OCULUS_LIB_NOT_FOUND = "OCULUS_LIB_NOT_FOUND", PROTON_NOT_SET = "PROTON_NOT_SET", + PROTON_NOT_FOUND = "PROTON_NOT_FOUND", UNKNOWN_ERROR = "UNKNOWN_ERROR", } diff --git a/src/shared/models/bs-launch/launch-option.interface.ts b/src/shared/models/bs-launch/launch-option.interface.ts index 3731143b..21056c19 100644 --- a/src/shared/models/bs-launch/launch-option.interface.ts +++ b/src/shared/models/bs-launch/launch-option.interface.ts @@ -7,5 +7,4 @@ export interface LaunchOption { debug?: boolean, additionalArgs?: string[], admin?: boolean, - protonPath?: string, } diff --git a/src/shared/models/ipc/ipc-routes.ts b/src/shared/models/ipc/ipc-routes.ts index ef7a71f7..8ab6e082 100644 --- a/src/shared/models/ipc/ipc-routes.ts +++ b/src/shared/models/ipc/ipc-routes.ts @@ -124,7 +124,7 @@ export interface IpcChannelMapping { /* ** os-controls-ipcs ** */ "new-window": { request: string, response: void }; "open-dialog": { request: OpenDialogOptions, response: OpenDialogReturnValue }; - "choose-folder": { request: string, response: OpenDialogReturnValue }; + "choose-folder": { request: { defaultPath?: string, parent?: "home", showHidden?: boolean }, response: OpenDialogReturnValue }; "choose-file": { request: string, response: OpenDialogReturnValue } "choose-image": { request: { multiple?: boolean, base64?: boolean }, response: string[] } "window.progression": { request: number, response: void }; @@ -150,6 +150,9 @@ export interface IpcChannelMapping { "static-configuration.get": StaticConfigGetIpcRequestResponse; "static-configuration.set": StaticConfigSetIpcRequest; + /* ** linux.ipcs ** */ + "linux.verify-proton-folder": { request: void, response: boolean }; + /* ** OTHERS (if your IPC channel is not in a "-ipcs" file, put it here) ** */ "shortcut-launch-options": { request: void, response: LaunchOption }; }