From ff23e4c0df7f823272967bacac7d3075a05dad9e Mon Sep 17 00:00:00 2001 From: Boris Yumankulov Date: Mon, 8 Jun 2026 09:54:08 +0500 Subject: [PATCH] feat: add nrg disc image launch support Signed-off-by: Boris Yumankulov --- CHANGELOG.md | 1 + README.md | 2 +- .../applications/ru.linux_gaming.PortProtonQt.desktop | 2 +- portprotonqt/cli.py | 4 ++-- portprotonqt/config/portproton.py | 2 +- portprotonqt/dialogs/base.py | 2 +- portprotonqt/dialogs/file_explorer.py | 2 +- portprotonqt/disc_image_utils.py | 8 ++++++-- portprotonqt/main_window.py | 2 +- 9 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cd77713..f10d9bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Добавлено обновление `steam_scripts` в миграции ярлыков - Добавлена вкладка избранных параметров в диалог настроек exe - Добавлена вкладка настроек vkBasalt в диалог настроек exe +- Добавлена поддержка запуска `.nrg` ### Changed - Действия управления питанием во вкладке `System` переведены на использование `loginctl` diff --git a/README.md b/README.md index 8bffa271..5d7693bb 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ PortProtonQt использует код и зависимости от след - [Icoextract](https://github.com/jlu5/icoextract) — библиотека для извлечения иконок, лицензия [MIT](https://github.com/jlu5/icoextract/blob/master/LICENSE). - [HowLongToBeat Python API](https://github.com/ScrappyCocco/HowLongToBeat-PythonAPI) — библиотека для взаимодействия с HowLongToBeat, лицензия [MIT](https://github.com/ScrappyCocco/HowLongToBeat-PythonAPI/blob/master/LICENSE.md). -- [iat](https://sourceforge.net/projects/iat.berlios) — библиотека для конвертации mdf в iso, лицензия GPLv3 +- [iat](https://sourceforge.net/projects/iat.berlios) — библиотека для конвертации mdf и nrg в iso, лицензия GPLv3 - [pyte](https://github.com/selectel/pyte) — разбор ANSI escape-кодов, лицензия [LGPLv3](https://github.com/selectel/pyte?tab=LGPL-3.0-1-ov-file) Полный текст лицензий см. в файле [LICENSE](LICENSE). diff --git a/build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop b/build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop index 628bf779..2beef46f 100644 --- a/build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop +++ b/build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop @@ -11,4 +11,4 @@ Terminal=false Icon=ru.linux_gaming.PortProtonQt StartupWMClass=ru.linux_gaming.PortProtonQt Categories=Game;Utility; -MimeType=x-scheme-handler/portproton;application/x-portproton-prefix-backup;application/x-ms-dos-exec;application/x-ms-dos-batch;application/x-bat;application/x-msi;text/x-ms-regedit;application/x-executable;application/x-dosexec;application/x-iso9660-image;application/x-cd-image;application/x-raw-disk-image;application/x-mdf;application/x-mds-disk; +MimeType=x-scheme-handler/portproton;application/x-portproton-prefix-backup;application/x-ms-dos-exec;application/x-ms-dos-batch;application/x-bat;application/x-msi;text/x-ms-regedit;application/x-executable;application/x-dosexec;application/x-iso9660-image;application/x-cd-image;application/x-raw-disk-image;application/x-mdf;application/x-mds-disk;application/x-nrg; diff --git a/portprotonqt/cli.py b/portprotonqt/cli.py index 5a81b0f6..9190b15a 100644 --- a/portprotonqt/cli.py +++ b/portprotonqt/cli.py @@ -7,7 +7,7 @@ from pathlib import Path from portprotonqt.steam_api import get_steam_home -LAUNCH_FILE_EXTENSIONS = ('.exe', '.bat', '.cmd', '.msi', '.reg', '.iso', '.mdf') +LAUNCH_FILE_EXTENSIONS = ('.exe', '.bat', '.cmd', '.msi', '.reg', '.iso', '.mdf', '.nrg') PREFIX_BACKUP_EXTENSION = '.ppack' @@ -83,7 +83,7 @@ def parse_args(): parser.add_argument( 'file_or_url', nargs='?', - help="Launch file path (.exe/.bat/.cmd/.msi/.reg/.iso/.mdf) or portproton:// URL" + help="Launch file path (.exe/.bat/.cmd/.msi/.reg/.iso/.mdf/.nrg) or portproton:// URL" ) if os.environ.get("STEAM_COMPAT") == "1" and "--" in sys.argv[1:]: separator_index = sys.argv.index("--") diff --git a/portprotonqt/config/portproton.py b/portprotonqt/config/portproton.py index d5b1a4cf..1c5f8b00 100644 --- a/portprotonqt/config/portproton.py +++ b/portprotonqt/config/portproton.py @@ -434,7 +434,7 @@ def parse_desktop_entry(file_path: str) -> configparser.SectionProxy | None: WINDOWS_LAUNCH_EXTENSIONS = (".exe", ".bat", ".cmd", ".msi", ".reg") -DISC_IMAGE_EXTENSIONS = (".iso", ".mdf") +DISC_IMAGE_EXTENSIONS = (".iso", ".mdf", ".nrg") LAUNCH_FILE_EXTENSIONS = WINDOWS_LAUNCH_EXTENSIONS + DISC_IMAGE_EXTENSIONS THEMED_LAUNCH_ICON_NAMES = { ".bat": "bat", diff --git a/portprotonqt/dialogs/base.py b/portprotonqt/dialogs/base.py index 226a3d6f..e9763bcb 100644 --- a/portprotonqt/dialogs/base.py +++ b/portprotonqt/dialogs/base.py @@ -31,7 +31,7 @@ from portprotonqt.icon_extractor import generate_thumbnail logger = get_logger(__name__) theme_manager = ThemeManager() -DISC_IMAGE_EXTENSIONS = (".iso", ".mdf") +DISC_IMAGE_EXTENSIONS = (".iso", ".mdf", ".nrg") class FileSelectedSignal(QObject): diff --git a/portprotonqt/dialogs/file_explorer.py b/portprotonqt/dialogs/file_explorer.py index 3e07ced4..c9b7aac4 100644 --- a/portprotonqt/dialogs/file_explorer.py +++ b/portprotonqt/dialogs/file_explorer.py @@ -28,7 +28,7 @@ from portprotonqt.scripts_utils.mount_points import list_mounted_drive_paths logger = get_logger(__name__) theme_manager = ThemeManager() -DISC_IMAGE_EXTENSIONS = (".iso", ".mdf") +DISC_IMAGE_EXTENSIONS = (".iso", ".mdf", ".nrg") THUMBNAIL_PRELOAD_ROWS = 5 THUMBNAIL_QUEUE_LIMIT = 24 THUMBNAIL_SCROLL_DELAY_MS = 80 diff --git a/portprotonqt/disc_image_utils.py b/portprotonqt/disc_image_utils.py index 8d5f4667..31a9d197 100644 --- a/portprotonqt/disc_image_utils.py +++ b/portprotonqt/disc_image_utils.py @@ -21,6 +21,7 @@ SVCD_SUB_HEADERS = ( b"\x00\x00\x89\x00\x00\x00\x89\x00", ) UDF_SIGNATURES = (b"BEA01", b"BOOT2", b"CD001", b"CDW02", b"NSR02", b"NSR03", b"TEA01") +CONVERTIBLE_DISC_EXTENSIONS = (".mdf", ".nrg") class DiscImageManager: @@ -300,7 +301,10 @@ class DiscImageManager: logger.error("7zz or 7z is required to extract MDF set %s", mdf_path) return None - mdf_sources = self._get_mdf_sources(normalized_mdf) + if normalized_mdf.lower().endswith(".mdf"): + mdf_sources = self._get_mdf_sources(normalized_mdf) + else: + mdf_sources = [normalized_mdf] source_stamp = self._get_disc_source_stamp(mdf_sources) if not source_stamp: return None @@ -338,7 +342,7 @@ class DiscImageManager: def sync_iso_to_rw(self, iso_path: str) -> str | None: """Extract disc image content to writable runtime directory if source changed.""" normalized_iso = os.path.abspath(os.path.expanduser(iso_path)) - if normalized_iso.lower().endswith(".mdf"): + if normalized_iso.lower().endswith(CONVERTIBLE_DISC_EXTENSIONS): return self._sync_mdf_set_to_rw(normalized_iso) rw_root = self._iso_rw_paths.get(normalized_iso, self._get_iso_rw_root(normalized_iso)) os.makedirs(rw_root, exist_ok=True) diff --git a/portprotonqt/main_window.py b/portprotonqt/main_window.py index cfbfc39e..16445c6f 100644 --- a/portprotonqt/main_window.py +++ b/portprotonqt/main_window.py @@ -84,7 +84,7 @@ from concurrent.futures import ThreadPoolExecutor from datetime import datetime logger = get_logger(__name__) -DISC_IMAGE_EXTENSIONS = (".iso", ".mdf") +DISC_IMAGE_EXTENSIONS = (".iso", ".mdf", ".nrg") class MainWindow(MainWindowControlHintsMixin, MainWindowSystemTabMixin, MainWindowWorkersMixin, QMainWindow): games_loaded = Signal(list) -- 2.54.0