From f4386423e88ae2c01b4f7afb1ac46deb50d39332 Mon Sep 17 00:00:00 2001 From: crueter Date: Mon, 15 Sep 2025 17:21:18 +0200 Subject: [PATCH] [qt] refactor: qt_common lib (#94) This is part of a series of PRs made in preparation for the QML rewrite. this PR specifically moves a bunch of utility functions from main.cpp into qt_common, with the biggest benefit being that QML can reuse the exact same code through ctx passthrough. Also, QtCommon::Frontend is an abstraction layer over several previously Widgets-specific stuff like QMessageBox that gets used everywhere. The idea is that once QML is implemented, these functions can have a Quick version implemented for systems that don't work well with Widgets (sun) or for those on Plasma 6+ (reduces memory usage w/o Widgets linkage) although Quick from C++ is actually anal, but whatever. Other than that this should also just kinda reduce the size of main.cpp which is a 6000-line behemoth rn, and clangd straight up gives up with it for me (likely caused by the massive amount of headers, which this DOES reduce). In the future, I probably want to create a common strings lookup table that both Qt and QML can reference--though I'm not sure how much linguist likes that--which should give us a way to keep language consistent (use frozen-map). TODO: Docs for Qt stuff Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/94 Reviewed-by: MaranBr Reviewed-by: Shinmegumi --- .ci/license-header.sh | 11 +- docs/CPM.md | 2 +- src/CMakeLists.txt | 2 + .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 19 - .../features/settings/model/BooleanSetting.kt | 1 - .../features/settings/model/Settings.kt | 1 - .../settings/model/view/SettingsItem.kt | 7 - .../settings/ui/SettingsFragmentPresenter.kt | 1 - .../app/src/main/res/values-ar/strings.xml | 2 - .../app/src/main/res/values-ckb/strings.xml | 2 - .../app/src/main/res/values-cs/strings.xml | 2 - .../app/src/main/res/values-es/strings.xml | 2 - .../app/src/main/res/values-fa/strings.xml | 2 - .../app/src/main/res/values-fr/strings.xml | 2 - .../app/src/main/res/values-he/strings.xml | 2 - .../app/src/main/res/values-hu/strings.xml | 2 - .../app/src/main/res/values-id/strings.xml | 2 - .../app/src/main/res/values-it/strings.xml | 2 - .../app/src/main/res/values-ja/strings.xml | 2 - .../app/src/main/res/values-ko/strings.xml | 2 - .../app/src/main/res/values-nb/strings.xml | 2 - .../app/src/main/res/values-pl/strings.xml | 2 - .../src/main/res/values-pt-rBR/strings.xml | 2 - .../src/main/res/values-pt-rPT/strings.xml | 2 - .../app/src/main/res/values-ru/strings.xml | 2 - .../app/src/main/res/values-sr/strings.xml | 2 - .../app/src/main/res/values-uk/strings.xml | 2 - .../app/src/main/res/values-vi/strings.xml | 2 - .../src/main/res/values-zh-rCN/strings.xml | 2 - .../src/main/res/values-zh-rTW/strings.xml | 2 - .../app/src/main/res/values/strings.xml | 5 - src/common/settings.h | 4 - .../fssystem/fssystem_bucket_tree.cpp | 4 +- .../fssystem_nca_file_system_driver.cpp | 132 +- src/core/file_sys/savedata_factory.cpp | 7 + src/frontend_common/firmware_manager.h | 5 +- src/qt_common/CMakeLists.txt | 47 + .../externals/CMakeLists.txt | 4 + .../externals/cpmfile.json | 7 + src/qt_common/qt_applet_util.cpp | 4 + src/qt_common/qt_applet_util.h | 11 + src/{yuzu => qt_common}/qt_common.cpp | 78 +- src/qt_common/qt_common.h | 44 + .../configuration => qt_common}/qt_config.cpp | 3 + .../configuration => qt_common}/qt_config.h | 3 + src/qt_common/qt_content_util.cpp | 313 ++++ src/qt_common/qt_content_util.h | 49 + src/qt_common/qt_frontend_util.cpp | 35 + src/qt_common/qt_frontend_util.h | 148 ++ src/qt_common/qt_game_util.cpp | 577 ++++++++ src/qt_common/qt_game_util.h | 85 ++ src/qt_common/qt_meta.cpp | 75 + src/qt_common/qt_meta.h | 15 + src/qt_common/qt_path_util.cpp | 28 + src/qt_common/qt_path_util.h | 12 + src/qt_common/qt_progress_dialog.cpp | 4 + src/qt_common/qt_progress_dialog.h | 47 + src/qt_common/qt_rom_util.cpp | 78 + src/qt_common/qt_rom_util.h | 20 + .../shared_translation.cpp | 120 +- .../shared_translation.h | 9 +- src/{yuzu => qt_common}/uisettings.cpp | 2 +- src/{yuzu => qt_common}/uisettings.h | 2 +- src/yuzu/CMakeLists.txt | 17 +- src/yuzu/bootmanager.cpp | 9 +- src/yuzu/configuration/configure_audio.cpp | 7 +- src/yuzu/configuration/configure_cpu.h | 5 +- src/yuzu/configuration/configure_debug.cpp | 2 +- src/yuzu/configuration/configure_dialog.cpp | 5 +- src/yuzu/configuration/configure_dialog.h | 5 +- .../configuration/configure_filesystem.cpp | 24 +- src/yuzu/configuration/configure_general.cpp | 5 +- src/yuzu/configuration/configure_graphics.cpp | 7 +- src/yuzu/configuration/configure_graphics.h | 5 +- .../configure_graphics_advanced.cpp | 5 +- .../configure_graphics_extensions.cpp | 5 +- src/yuzu/configuration/configure_hotkeys.cpp | 5 +- .../configuration/configure_input_per_game.h | 5 +- .../configuration/configure_input_player.cpp | 2 +- src/yuzu/configuration/configure_per_game.cpp | 5 +- src/yuzu/configuration/configure_per_game.h | 7 +- .../configure_per_game_addons.cpp | 5 +- src/yuzu/configuration/configure_ringcon.cpp | 5 +- src/yuzu/configuration/configure_tas.cpp | 5 +- src/yuzu/configuration/configure_ui.cpp | 2 +- src/yuzu/configuration/configure_web.cpp | 2 +- src/yuzu/configuration/input_profiles.h | 5 +- src/yuzu/configuration/shared_widget.cpp | 5 +- src/yuzu/configuration/shared_widget.h | 5 +- src/yuzu/debugger/console.cpp | 5 +- src/yuzu/debugger/wait_tree.cpp | 5 +- src/yuzu/game_list.cpp | 42 +- src/yuzu/game_list.h | 31 +- src/yuzu/game_list_p.h | 5 +- src/yuzu/game_list_worker.cpp | 33 +- src/yuzu/game_list_worker.h | 10 +- src/yuzu/hotkeys.cpp | 5 +- src/yuzu/install_dialog.cpp | 5 +- src/yuzu/main.cpp | 1313 +++-------------- src/yuzu/main.h | 54 +- src/yuzu/multiplayer/direct_connect.cpp | 2 +- src/yuzu/multiplayer/host_room.cpp | 2 +- src/yuzu/multiplayer/lobby.cpp | 2 +- src/yuzu/multiplayer/state.cpp | 5 +- src/yuzu/play_time_manager.cpp | 4 +- src/yuzu/play_time_manager.h | 6 +- src/yuzu/qt_common.h | 15 - src/yuzu/user_data_migration.h | 1 + src/yuzu/vk_device_info.cpp | 5 +- tools/cpm-fetch-all.sh | 2 +- tools/cpm-fetch.sh | 3 +- 111 files changed, 2235 insertions(+), 1544 deletions(-) create mode 100644 src/qt_common/CMakeLists.txt rename src/{yuzu => qt_common}/externals/CMakeLists.txt (86%) rename src/{yuzu => qt_common}/externals/cpmfile.json (55%) create mode 100644 src/qt_common/qt_applet_util.cpp create mode 100644 src/qt_common/qt_applet_util.h rename src/{yuzu => qt_common}/qt_common.cpp (55%) create mode 100644 src/qt_common/qt_common.h rename src/{yuzu/configuration => qt_common}/qt_config.cpp (99%) rename src/{yuzu/configuration => qt_common}/qt_config.h (94%) create mode 100644 src/qt_common/qt_content_util.cpp create mode 100644 src/qt_common/qt_content_util.h create mode 100644 src/qt_common/qt_frontend_util.cpp create mode 100644 src/qt_common/qt_frontend_util.h create mode 100644 src/qt_common/qt_game_util.cpp create mode 100644 src/qt_common/qt_game_util.h create mode 100644 src/qt_common/qt_meta.cpp create mode 100644 src/qt_common/qt_meta.h create mode 100644 src/qt_common/qt_path_util.cpp create mode 100644 src/qt_common/qt_path_util.h create mode 100644 src/qt_common/qt_progress_dialog.cpp create mode 100644 src/qt_common/qt_progress_dialog.h create mode 100644 src/qt_common/qt_rom_util.cpp create mode 100644 src/qt_common/qt_rom_util.h rename src/{yuzu/configuration => qt_common}/shared_translation.cpp (91%) rename src/{yuzu/configuration => qt_common}/shared_translation.h (94%) rename src/{yuzu => qt_common}/uisettings.cpp (99%) rename src/{yuzu => qt_common}/uisettings.h (99%) delete mode 100644 src/yuzu/qt_common.h diff --git a/.ci/license-header.sh b/.ci/license-header.sh index fecffaa7d3..3d4929d1c1 100755 --- a/.ci/license-header.sh +++ b/.ci/license-header.sh @@ -5,10 +5,13 @@ HEADER_HASH="$(cat "$PWD/.ci/license/header-hash.txt")" echo "Getting branch changes" -BRANCH=`git rev-parse --abbrev-ref HEAD` -COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"` -RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}" -FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r` +# BRANCH=`git rev-parse --abbrev-ref HEAD` +# COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"` +# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}" +# FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r` + +BASE=`git merge-base master HEAD` +FILES=`git diff --name-only $BASE` #FILES=$(git diff --name-only master) diff --git a/docs/CPM.md b/docs/CPM.md index 2afcdaf164..bce224da40 100644 --- a/docs/CPM.md +++ b/docs/CPM.md @@ -245,6 +245,6 @@ include(CPMUtil) Currently, `cpm-fetch.sh` defines the following directories for cpmfiles (max depth of 2, so subdirs are caught as well): -`externals src/yuzu src/dynarmic .` +`externals src/qt_common src/dynarmic .` Whenever you add a new cpmfile, update the script accordingly \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb66e55964..184b049d06 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -234,6 +234,8 @@ if (YUZU_ROOM_STANDALONE) endif() if (ENABLE_QT) + add_definitions(-DYUZU_QT_WIDGETS) + add_subdirectory(qt_common) add_subdirectory(yuzu) endif() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index 0a2cb41745..9ea2a9ee17 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -272,8 +272,6 @@ class GameAdapter(private val activity: AppCompatActivity) : binding.root.findNavController().navigate(action) } - val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) { MaterialAlertDialogBuilder(activity) .setTitle(R.string.loader_requires_firmware) @@ -288,23 +286,6 @@ class GameAdapter(private val activity: AppCompatActivity) : } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() - } else if (BooleanSetting.DISABLE_NCA_VERIFICATION.getBoolean(false) && !preferences.getBoolean( - Settings.PREF_HIDE_NCA_POPUP, false)) { - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.nca_verification_disabled) - .setMessage(activity.getString(R.string.nca_verification_disabled_description)) - .setPositiveButton(android.R.string.ok) { _, _ -> - launch() - } - .setNeutralButton(R.string.dont_show_again) { _, _ -> - preferences.edit { - putBoolean(Settings.PREF_HIDE_NCA_POPUP, true) - } - - launch() - } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() } else { launch() } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index 6d4bfd97ac..3c5b9003de 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -35,7 +35,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { RENDERER_SAMPLE_SHADING("sample_shading"), PICTURE_IN_PICTURE("picture_in_picture"), USE_CUSTOM_RTC("custom_rtc_enabled"), - DISABLE_NCA_VERIFICATION("disable_nca_verification"), BLACK_BACKGROUNDS("black_backgrounds"), JOYSTICK_REL_CENTER("joystick_rel_center"), DPAD_SLIDE("dpad_slide"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt index 2564849ef4..a52f582031 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt @@ -37,7 +37,6 @@ object Settings { const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning" const val PREF_SHOULD_SHOW_EDENS_VEIL_DIALOG = "ShouldShowEdensVeilDialog" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" - const val PREF_HIDE_NCA_POPUP = "HideNCAVerificationPopup" const val SECTION_STATS_OVERLAY = "Stats Overlay" // Deprecated input overlay preference keys diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 883d8efaef..1f2ba81a73 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -297,13 +297,6 @@ abstract class SettingsItem( descriptionId = R.string.use_custom_rtc_description ) ) - put( - SwitchSetting( - BooleanSetting.DISABLE_NCA_VERIFICATION, - titleId = R.string.disable_nca_verification, - descriptionId = R.string.disable_nca_verification_description - ) - ) put( StringInputSetting( StringSetting.WEB_TOKEN, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 630bcb0d74..14d62ceec3 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -210,7 +210,6 @@ class SettingsFragmentPresenter( add(IntSetting.LANGUAGE_INDEX.key) add(BooleanSetting.USE_CUSTOM_RTC.key) add(LongSetting.CUSTOM_RTC.key) - add(BooleanSetting.DISABLE_NCA_VERIFICATION.key) add(HeaderSetting(R.string.network)) add(StringSetting.WEB_TOKEN.key) diff --git a/src/android/app/src/main/res/values-ar/strings.xml b/src/android/app/src/main/res/values-ar/strings.xml index c705ae08f1..ed3fc76f3b 100644 --- a/src/android/app/src/main/res/values-ar/strings.xml +++ b/src/android/app/src/main/res/values-ar/strings.xml @@ -498,8 +498,6 @@ ساعة مخصصة في الوقت الحقيقي يسمح لك بتعيين ساعة مخصصة في الوقت الفعلي منفصلة عن وقت النظام الحالي لديك تعيين ساعة مخصصة في الوقت الحقيقي - تعطيل التحقق من NCA - يعطل التحقق من سلامة أرشيفات محتوى NCA. قد يحسن هذا من سرعة التحميل لكنه يخاطر بتلف البيانات أو تمرير ملفات غير صالحة دون اكتشاف. ضروري لجعل الألعاب والتحديثات التي تتطلب نظامًا أساسيًا 20+ تعمل. توليد diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml index af0eeeaa45..34b1ae6252 100644 --- a/src/android/app/src/main/res/values-ckb/strings.xml +++ b/src/android/app/src/main/res/values-ckb/strings.xml @@ -482,8 +482,6 @@ RTCی تایبەتمەند ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت. دانانی RTCی تایبەتمەند - ناچالاککردنی پشکنینی NCA - پشکنینی پێکهاتەی ئارشیڤەکانی ناوەڕۆکی NCA ناچالاک دەکات. ئەمە لەوانەیە خێرایی بارکردن به‌ره‌وپێش ببات، بەڵام مەترسی لەناوچوونی داتا یان ئەوەی فایلە نادروستەکان بەبێ ئەوەی دۆزرایەوە تێپەڕبن زیاتر دەکات. بۆ ئەوەی یاری و نوێکردنەوەکان کار بکەن کە پێویستی بە فریموێری 20+ هەیە زۆر پێویستە. بەرهەم هێنان diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml index 8d42b8303f..293524271e 100644 --- a/src/android/app/src/main/res/values-cs/strings.xml +++ b/src/android/app/src/main/res/values-cs/strings.xml @@ -458,8 +458,6 @@ Vlastní RTC Vlastní nastavení času Nastavit vlastní RTC - Zakázat ověřování NCA - Zakáže ověřování integrity archivů obsahu NCA. To může zlepšit rychlost načítání, ale hrozí poškození dat nebo neodhalení neplatných souborů. Je nutné, aby fungovaly hry a aktualizace vyžadující firmware 20+. Generovat diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index 12b7183cae..8712f455de 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml @@ -506,8 +506,6 @@ RTC personalizado Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema. Configurar RTC personalizado - Desactivar verificación NCA - Desactiva la verificación de integridad de los archivos de contenido NCA. Esto puede mejorar la velocidad de carga, pero arriesga corrupción de datos o que archivos inválidos pasen desapercibidos. Es necesario para que funcionen juegos y actualizaciones que requieren firmware 20+. Generar diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml index d7ac1b770a..07ff8ff4e0 100644 --- a/src/android/app/src/main/res/values-fa/strings.xml +++ b/src/android/app/src/main/res/values-fa/strings.xml @@ -504,8 +504,6 @@ زمان سفارشی به شما امکان می‌دهد یک ساعت سفارشی جدا از زمان فعلی سیستم خود تنظیم کنید. تنظیم زمان سفارشی - غیرفعال کردن تأیید اعتبار NCA - بررسی صحت آرشیوهای محتوای NCA را غیرفعال می‌کند. این ممکن است سرعت بارگذاری را بهبود بخشد اما خطر خرابی داده یا تشخیص داده نشدن فایل‌های نامعتبر را به همراه دارد. برای کار کردن بازی‌ها و به‌روزرسانی‌هایی که به فرمور ۲۰+ نیاز دارند، ضروری است. تولید diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index 62e67a6fee..2e06ac98e1 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml @@ -506,8 +506,6 @@ RTC personnalisé Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système. Définir l\'horloge RTC personnalisée - Désactiver la vérification NCA - Désactive la vérification d\'intégrité des archives de contenu NCA. Cela peut améliorer la vitesse de chargement mais risque une corruption des données ou que des fichiers invalides ne soient pas détectés. Est nécessaire pour faire fonctionner les jeux et mises à jour nécessitant un firmware 20+. Générer diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml index ec5b526ce0..c0c835d633 100644 --- a/src/android/app/src/main/res/values-he/strings.xml +++ b/src/android/app/src/main/res/values-he/strings.xml @@ -505,8 +505,6 @@ RTC מותאם אישית מאפשר לך לקבוע שעון זמן אמת נפרד משעון המערכת שלך. קבע RTC מותאם אישית - השבת אימות NCA - משבית את אימות השלמות של ארכיוני התוכן של NCA. זה עשוי לשפר את מהירות הטעינה אך מסתכן בשחיקת נתונים או שמא קבצים לא חוקיים יעברו ללא זיהוי. זה הכרחי כדי לגרום למשחקים ועדכונים הדורשים firmware 20+ לעבוד. יצירה diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml index b45692f147..46a5ac7cce 100644 --- a/src/android/app/src/main/res/values-hu/strings.xml +++ b/src/android/app/src/main/res/values-hu/strings.xml @@ -501,8 +501,6 @@ Egyéni RTC Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától. Egyéni RTC beállítása - NCA ellenőrzés letiltása - Letiltja az NCA tartalomarchívumok integritás-ellenőrzését. Ez javíthatja a betöltési sebességet, de az adatsérülés vagy az érvénytelen fájlok észrevétlen maradásának kockázatával jár. Elengedhetetlen a 20+ firmware-et igénylő játékok és frissítések működtetéséhez. Generálás diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml index 1817fc654a..cffb526ad5 100644 --- a/src/android/app/src/main/res/values-id/strings.xml +++ b/src/android/app/src/main/res/values-id/strings.xml @@ -502,8 +502,6 @@ RTC Kustom Memungkinkan Anda untuk mengatur jam waktu nyata kustom yang terpisah dari waktu sistem saat ini Anda. Setel RTC Kustom - Nonaktifkan Verifikasi NCA - Menonaktifkan verifikasi integritas arsip konten NCA. Ini dapat meningkatkan kecepatan pemuatan tetapi berisiko kerusakan data atau file yang tidak valid tidak terdeteksi. Diperlukan untuk membuat game dan pembaruan yang membutuhkan firmware 20+ bekerja. Hasilkan diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index ff3706dd40..cb234cf61e 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml @@ -505,8 +505,6 @@ RTC Personalizzato Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema. Imposta un orologio in tempo reale personalizzato - Disabilita verifica NCA - Disabilita la verifica dell\'integrità degli archivi di contenuto NCA. Può migliorare la velocità di caricamento ma rischia il danneggiamento dei dati o che file non validi passino inosservati. Necessario per far funzionare giochi e aggiornamenti che richiedono il firmware 20+. Genera diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index a6c1ebf8ab..abedb1e0bc 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml @@ -491,8 +491,6 @@ カスタム RTC 現在のシステム時間とは別に、任意のリアルタイムクロックを設定できます。 カスタムRTCを設定 - NCA検証を無効化 - NCAコンテンツアーカイブの整合性検証を無効にします。読み込み速度が向上する可能性がありますが、データ破損や不正なファイルが検出されないリスクがあります。ファームウェア20以上が必要なゲームや更新を動作させるために必要です。 生成 diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 01e2c5f4c0..c6d9457744 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml @@ -501,8 +501,6 @@ 사용자 지정 RTC 현재 시스템 시간과 별도로 사용자 지정 RTC를 설정할 수 있습니다. 사용자 지정 RTC 설정 - NCA 검증 비활성화 - NCA 콘텐츠 아카이브의 무결성 검증을 비활성화합니다. 로딩 속도를 향상시킬 수 있지만 데이터 손상이나 유효하지 않은 파일이 미검증될 위험이 있습니다. 펌웨어 20+가 필요한 게임 및 업데이트를 실행하려면 필요합니다. 생성 diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index 90c0dbf05e..3cc4c6d12c 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml @@ -482,8 +482,6 @@ Tilpasset Sannhetstidsklokke Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden. Angi tilpasset RTC - Deaktiver NCA-verifisering - Deaktiverer integritetsverifisering av NCA-innholdsarkiv. Dette kan forbedre lastehastigheten, men medfører risiko for datakorrupsjon eller at ugyldige filer ikke oppdages. Er nødvendig for å få spill og oppdateringer som trenger firmware 20+ til å fungere. Generer diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 080064edaf..b9858838e8 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml @@ -482,8 +482,6 @@ Niestandardowy RTC Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android. Ustaw niestandardowy czas RTC - Wyłącz weryfikację NCA - Wyłącza weryfikację integralności archiwów zawartości NCA. Może to poprawić szybkość ładowania, ale niesie ryzyko uszkodzenia danych lub niezauważenia nieprawidłowych plików. Konieczne, aby działały gry i aktualizacje wymagające firmware\'u 20+. Generuj diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 3dc1f83e6e..1296fad889 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml @@ -506,8 +506,6 @@ Data e hora personalizadas Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo. Definir um relógio em tempo real personalizado - Desativar verificação NCA - Desativa a verificação de integridade de arquivos de conteúdo NCA. Pode melhorar a velocidade de carregamento, mas arrisica corromper dados ou que arquivos inválidos passem despercebidos. É necessário para fazer jogos e atualizações que exigem firmware 20+ funcionarem. Gerar diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index feb4950fcb..a166907877 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml @@ -506,8 +506,6 @@ RTC personalizado Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo. Defina um relógio em tempo real personalizado - Desativar verificação NCA - Desativa a verificação de integridade dos arquivos de conteúdo NCA. Pode melhorar a velocidade de carregamento, mas arrisca a corrupção de dados ou que ficheiros inválidos passem despercebidos. É necessário para que jogos e atualizações que necessitam de firmware 20+ funcionem. Gerar diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index d56c94f10b..dc68c7b817 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml @@ -508,8 +508,6 @@ Пользовательский RTC Позволяет установить пользовательские часы реального времени отдельно от текущего системного времени. Установить пользовательский RTC - Отключить проверку NCA - Отключает проверку целостности архивов содержимого NCA. Может улучшить скорость загрузки, но есть риск повреждения данных или того, что недействительные файлы останутся незамеченными. Необходимо для работы игр и обновлений, требующих прошивку 20+. Создать diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml index 5b444f6187..c547b3f761 100644 --- a/src/android/app/src/main/res/values-sr/strings.xml +++ b/src/android/app/src/main/res/values-sr/strings.xml @@ -457,8 +457,6 @@ Цустом РТЦ Омогућава вам да поставите прилагођени сат у реалном времену одвојено од тренутног времена система. Подесите прилагођени РТЦ - Искључи верификацију НЦА - Искључује верификацију интегритета НЦА архива садржаја. Ово може побољшати брзину учитавања, али ризикује оштећење података или да неважећи фајлови прођу незапажено. Неопходно је да би игре и ажурирања која захтевају firmware 20+ радили. Генериши diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 4ae61340dc..b48a8a4a58 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml @@ -495,8 +495,6 @@ Свій RTC Дозволяє встановити власний час (Real-time clock, або RTC), відмінний від системного. Встановити RTC - Вимкнути перевірку NCA - Вимкає перевірку цілісності архівів вмісту NCA. Може покращити швидкість завантаження, але ризикує пошкодженням даних або тим, що недійсні файли залишаться непоміченими. Необхідно для роботи ігор та оновлень, які вимагають прошивки 20+. Створити diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml index dd64fbca55..b19d437ceb 100644 --- a/src/android/app/src/main/res/values-vi/strings.xml +++ b/src/android/app/src/main/res/values-vi/strings.xml @@ -482,8 +482,6 @@ RTC tuỳ chỉnh Cho phép bạn thiết lập một đồng hồ thời gian thực tùy chỉnh riêng biệt so với thời gian hệ thống hiện tại. Thiết lập RTC tùy chỉnh - Tắt xác minh NCA - Tắt xác minh tính toàn vẹn của kho lưu trữ nội dung NCA. Có thể cải thiện tốc độ tải nhưng có nguy cơ hỏng dữ liệu hoặc các tệp không hợp lệ không bị phát hiện. Cần thiết để các trò chơi và bản cập nhật yêu cầu firmware 20+ hoạt động. Tạo diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index a12c00063f..95ab14abd0 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml @@ -500,8 +500,6 @@ 自定义系统时间 此选项允许您设置与目前系统时间相独立的自定义系统时钟。 设置自定义系统时间 -禁用NCA验证 -禁用NCA内容存档的完整性验证。可能会提高加载速度,但存在数据损坏或无效文件未被检测到的风险。对于需要固件20+的游戏和更新是必需的。 生成 diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index a125553102..8640875f2c 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml @@ -505,8 +505,6 @@ 自訂 RTC 允許您設定與您的目前系統時間相互獨立的自訂即時時鐘。 設定自訂 RTC - 停用NCA驗證 - 停用NCA內容存檔的完整性驗證。可能會提高載入速度,但存在資料損毀或無效檔案未被偵測到的風險。對於需要韌體20+的遊戲和更新是必需的。 生成 diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 20c6e85b6c..d99776b440 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -475,8 +475,6 @@ Custom RTC Allows you to set a custom real-time clock separate from your current system time. Set custom RTC - Disable NCA Verification - Disables integrity verification of NCA content archives. This may improve loading speed but risks data corruption or invalid files going undetected. Some games that require firmware versions 20+ may need this as well. Generate @@ -785,9 +783,6 @@ Game Requires Firmware dump and install firmware, or press "OK" to launch anyways.]]> - NCA Verification Disabled - This is required to run new games and updates, but may cause instability or crashes if NCA files are corrupt, modified, or tampered with. If unsure, re-enable verification in Advanced Settings -> System, and use firmware versions of 19.0.1 or below. - Searching for game... Game not found for Title ID: %1$s diff --git a/src/common/settings.h b/src/common/settings.h index 9d448a2b38..fafd765804 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -626,10 +626,6 @@ struct Values { true, true, &rng_seed_enabled}; Setting device_name{ linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true}; - SwitchableSetting disable_nca_verification{linkage, true, "disable_nca_verification", - Category::System, Specialization::Default}; - Setting hide_nca_verification_popup{ - linkage, false, "hide_nca_verification_popup", Category::System, Specialization::Default}; Setting current_user{linkage, 0, "current_user", Category::System}; diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp index 615a624f4f..71ba458cef 100644 --- a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp +++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp @@ -238,9 +238,7 @@ void BucketTree::Initialize(size_t node_size, s64 end_offset) { ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax); ASSERT(Common::IsPowerOfTwo(node_size)); - if (!Settings::values.disable_nca_verification.GetValue()) { - ASSERT(end_offset > 0); - } + ASSERT(end_offset > 0); ASSERT(!this->IsInitialized()); m_node_size = node_size; diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp index 4cfa5c58f8..37fb71e9e3 100644 --- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp +++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -1296,91 +1296,65 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl( ASSERT(base_storage != nullptr); ASSERT(layer_info_offset >= 0); - if (!Settings::values.disable_nca_verification.GetValue()) { - // Define storage types. - using VerificationStorage = HierarchicalIntegrityVerificationStorage; - using StorageInfo = VerificationStorage::HierarchicalStorageInformation; + // Define storage types. + using VerificationStorage = HierarchicalIntegrityVerificationStorage; + using StorageInfo = VerificationStorage::HierarchicalStorageInformation; - // Validate the meta info. - HierarchicalIntegrityVerificationInformation level_hash_info; - std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info), - sizeof(level_hash_info)); + // Validate the meta info. + HierarchicalIntegrityVerificationInformation level_hash_info; + std::memcpy(std::addressof(level_hash_info), + std::addressof(meta_info.level_hash_info), + sizeof(level_hash_info)); - R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers, - ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); - R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount, - ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); + R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers, + ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); + R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount, + ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); - // Get the base storage size. - s64 base_storage_size = base_storage->GetSize(); + // Get the base storage size. + s64 base_storage_size = base_storage->GetSize(); - // Create storage info. - StorageInfo storage_info; - for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) { - const auto& layer_info = level_hash_info.info[i]; - R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size, - ResultNcaBaseStorageOutOfRangeD); - - storage_info[i + 1] = std::make_shared( - base_storage, layer_info.size, layer_info_offset + layer_info.offset); - } - - // Set the last layer info. - const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2]; - const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get(); - R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size, + // Create storage info. + StorageInfo storage_info; + for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) { + const auto& layer_info = level_hash_info.info[i]; + R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size, ResultNcaBaseStorageOutOfRangeD); - if (layer_info_offset > 0) { - R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset, - ResultRomNcaInvalidIntegrityLayerInfoOffset); - } - storage_info[level_hash_info.max_layers - 1] = std::make_shared( - std::move(base_storage), layer_info.size, last_layer_info_offset); - // Make the integrity romfs storage. - auto integrity_storage = std::make_shared(); - R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared); - - // Initialize the integrity storage. - R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info, - max_data_cache_entries, max_hash_cache_entries, - buffer_level)); - - // Set the output. - *out = std::move(integrity_storage); - R_SUCCEED(); - } else { - // Read IVFC layout - HierarchicalIntegrityVerificationInformation lhi{}; - std::memcpy(std::addressof(lhi), std::addressof(meta_info.level_hash_info), sizeof(lhi)); - - R_UNLESS(IntegrityMinLayerCount <= lhi.max_layers, - ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); - R_UNLESS(lhi.max_layers <= IntegrityMaxLayerCount, - ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount); - - const auto& data_li = lhi.info[lhi.max_layers - 2]; - - const s64 base_size = base_storage->GetSize(); - - // Compute the data layer window - const s64 data_off = (layer_info_offset > 0) ? 0LL : data_li.offset.Get(); - R_UNLESS(data_off + data_li.size <= base_size, ResultNcaBaseStorageOutOfRangeD); - if (layer_info_offset > 0) { - R_UNLESS(data_off + data_li.size <= layer_info_offset, - ResultRomNcaInvalidIntegrityLayerInfoOffset); - } - - // TODO: Passthrough (temporary compatibility: integrity disabled) - auto data_view = std::make_shared(base_storage, data_li.size, data_off); - R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared); - - auto passthrough = std::make_shared(std::move(data_view)); - R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared); - - *out = std::move(passthrough); - R_SUCCEED(); + storage_info[i + 1] = std::make_shared(base_storage, + layer_info.size, + layer_info_offset + layer_info.offset); } + + // Set the last layer info. + const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2]; + const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get(); + R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size, + ResultNcaBaseStorageOutOfRangeD); + if (layer_info_offset > 0) { + R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset, + ResultRomNcaInvalidIntegrityLayerInfoOffset); + } + storage_info[level_hash_info.max_layers - 1] + = std::make_shared(std::move(base_storage), + layer_info.size, + last_layer_info_offset); + + // Make the integrity romfs storage. + auto integrity_storage = std::make_shared(); + R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared); + + // Initialize the integrity storage. + R_TRY(integrity_storage->Initialize(level_hash_info, + meta_info.master_hash, + storage_info, + max_data_cache_entries, + max_hash_cache_entries, + buffer_level)); + + // Set the output. + *out = std::move(integrity_storage); + R_SUCCEED(); } Result NcaFileSystemDriver::CreateRegionSwitchStorage(VirtualFile* out, diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 106922e04f..edf51e74de 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -126,6 +129,10 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir, std::string out = GetSaveDataSpaceIdPath(space); + LOG_INFO(Common_Filesystem, "Save ID: {:016X}", save_id); + LOG_INFO(Common_Filesystem, "User ID[1]: {:016X}", user_id[1]); + LOG_INFO(Common_Filesystem, "User ID[0]: {:016X}", user_id[0]); + switch (type) { case SaveDataType::System: return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h index 20f3b41478..23fce59eb3 100644 --- a/src/frontend_common/firmware_manager.h +++ b/src/frontend_common/firmware_manager.h @@ -7,6 +7,7 @@ #include "common/common_types.h" #include "core/core.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/content_archive.h" #include "core/file_sys/registered_cache.h" #include "core/hle/service/filesystem/filesystem.h" #include @@ -143,6 +144,8 @@ inline std::pair GetFirmwareVersion return {firmware_data, result}; } + +// TODO(crueter): GET AS STRING } -#endif // FIRMWARE_MANAGER_H +#endif diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt new file mode 100644 index 0000000000..9d292da401 --- /dev/null +++ b/src/qt_common/CMakeLists.txt @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +find_package(Qt6 REQUIRED COMPONENTS Core) +find_package(Qt6 REQUIRED COMPONENTS Core) + +add_library(qt_common STATIC + qt_common.h + qt_common.cpp + + uisettings.cpp + uisettings.h + + qt_config.cpp + qt_config.h + + shared_translation.cpp + shared_translation.h + qt_path_util.h qt_path_util.cpp + qt_game_util.h qt_game_util.cpp + qt_frontend_util.h qt_frontend_util.cpp + qt_meta.h qt_meta.cpp + qt_content_util.h qt_content_util.cpp + qt_rom_util.h qt_rom_util.cpp + qt_applet_util.h qt_applet_util.cpp + qt_progress_dialog.h qt_progress_dialog.cpp + +) + +create_target_directory_groups(qt_common) + +# TODO(crueter) +if (ENABLE_QT) + target_link_libraries(qt_common PRIVATE Qt6::Widgets) +endif() + +add_subdirectory(externals) + +target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip frozen::frozen) +target_link_libraries(qt_common PRIVATE Qt6::Core) + +if (NOT WIN32) + target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) +endif() diff --git a/src/yuzu/externals/CMakeLists.txt b/src/qt_common/externals/CMakeLists.txt similarity index 86% rename from src/yuzu/externals/CMakeLists.txt rename to src/qt_common/externals/CMakeLists.txt index 50594a741f..189a52c0a6 100644 --- a/src/yuzu/externals/CMakeLists.txt +++ b/src/qt_common/externals/CMakeLists.txt @@ -14,3 +14,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON) # QuaZip AddJsonPackage(quazip) + +# frozen +# TODO(crueter): Qt String Lookup +AddJsonPackage(frozen) diff --git a/src/yuzu/externals/cpmfile.json b/src/qt_common/externals/cpmfile.json similarity index 55% rename from src/yuzu/externals/cpmfile.json rename to src/qt_common/externals/cpmfile.json index e3590d0f7f..0b464b95b2 100644 --- a/src/yuzu/externals/cpmfile.json +++ b/src/qt_common/externals/cpmfile.json @@ -8,5 +8,12 @@ "options": [ "QUAZIP_INSTALL OFF" ] + }, + "frozen": { + "package": "frozen", + "repo": "serge-sans-paille/frozen", + "sha": "61dce5ae18", + "hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d", + "bundled": true } } diff --git a/src/qt_common/qt_applet_util.cpp b/src/qt_common/qt_applet_util.cpp new file mode 100644 index 0000000000..1b0189392e --- /dev/null +++ b/src/qt_common/qt_applet_util.cpp @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_applet_util.h" diff --git a/src/qt_common/qt_applet_util.h b/src/qt_common/qt_applet_util.h new file mode 100644 index 0000000000..2b48d16698 --- /dev/null +++ b/src/qt_common/qt_applet_util.h @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_APPLET_UTIL_H +#define QT_APPLET_UTIL_H + +// TODO +namespace QtCommon::Applets { + +} +#endif // QT_APPLET_UTIL_H diff --git a/src/yuzu/qt_common.cpp b/src/qt_common/qt_common.cpp similarity index 55% rename from src/yuzu/qt_common.cpp rename to src/qt_common/qt_common.cpp index 413402165c..6be241c740 100644 --- a/src/yuzu/qt_common.cpp +++ b/src/qt_common/qt_common.cpp @@ -1,12 +1,19 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_common.h" +#include "common/fs/fs.h" #include #include -#include #include "common/logging/log.h" #include "core/frontend/emu_window.h" -#include "yuzu/qt_common.h" + +#include + +#include + +#include #if !defined(WIN32) && !defined(__APPLE__) #include @@ -15,7 +22,19 @@ #endif namespace QtCommon { -Core::Frontend::WindowSystemType GetWindowSystemType() { + +#ifdef YUZU_QT_WIDGETS +QWidget* rootObject = nullptr; +#else +QObject* rootObject = nullptr; +#endif + +std::unique_ptr system = nullptr; +std::shared_ptr vfs = nullptr; +std::unique_ptr provider = nullptr; + +Core::Frontend::WindowSystemType GetWindowSystemType() +{ // Determine WSI type based on Qt platform. QString platform_name = QGuiApplication::platformName(); if (platform_name == QStringLiteral("windows")) @@ -35,7 +54,8 @@ Core::Frontend::WindowSystemType GetWindowSystemType() { return Core::Frontend::WindowSystemType::Windows; } // namespace Core::Frontend::WindowSystemType -Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { +Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) +{ Core::Frontend::EmuWindow::WindowSystemInfo wsi; wsi.type = GetWindowSystemType(); @@ -43,8 +63,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) // Our Win32 Qt external doesn't have the private API. wsi.render_surface = reinterpret_cast(window->winId()); #elif defined(__APPLE__) - wsi.render_surface = reinterpret_cast(objc_msgSend)( - reinterpret_cast(window->winId()), sel_registerName("layer")); + wsi.render_surface = reinterpret_cast( + objc_msgSend)(reinterpret_cast(window->winId()), sel_registerName("layer")); #else QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); wsi.display_connection = pni->nativeResourceForWindow("display", window); @@ -57,4 +77,46 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) return wsi; } + +const QString tr(const char* str) +{ + return QGuiApplication::tr(str); +} + +const QString tr(const std::string& str) +{ + return QGuiApplication::tr(str.c_str()); +} + +#ifdef YUZU_QT_WIDGETS +void Init(QWidget* root) +#else +void Init(QObject* root) +#endif +{ + system = std::make_unique(); + rootObject = root; + vfs = std::make_unique(); + provider = std::make_unique(); +} + +std::filesystem::path GetEdenCommand() { + std::filesystem::path command; + + QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE")); + if (!appimage.isEmpty()) { + command = std::filesystem::path{appimage.toStdString()}; + } else { + const QStringList args = QGuiApplication::arguments(); + command = args[0].toStdString(); + } + + // If relative path, make it an absolute path + if (command.c_str()[0] == '.') { + command = Common::FS::GetCurrentDir() / command; + } + + return command; +} + } // namespace QtCommon diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h new file mode 100644 index 0000000000..a2700427ab --- /dev/null +++ b/src/qt_common/qt_common.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_COMMON_H +#define QT_COMMON_H + +#include +#include "core/core.h" +#include "core/file_sys/registered_cache.h" +#include +#include + +#include + +namespace QtCommon { + +#ifdef YUZU_QT_WIDGETS +extern QWidget *rootObject; +#else +extern QObject *rootObject; +#endif + +extern std::unique_ptr system; +extern std::shared_ptr vfs; +extern std::unique_ptr provider; + +typedef std::function QtProgressCallback; + +Core::Frontend::WindowSystemType GetWindowSystemType(); + +Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window); + +#ifdef YUZU_QT_WIDGETS +void Init(QWidget *root); +#else +void Init(QObject *root); +#endif + +const QString tr(const char *str); +const QString tr(const std::string &str); + +std::filesystem::path GetEdenCommand(); +} // namespace QtCommon +#endif diff --git a/src/yuzu/configuration/qt_config.cpp b/src/qt_common/qt_config.cpp similarity index 99% rename from src/yuzu/configuration/qt_config.cpp rename to src/qt_common/qt_config.cpp index ae5b330e23..f787873cf6 100644 --- a/src/yuzu/configuration/qt_config.cpp +++ b/src/qt_common/qt_config.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/yuzu/configuration/qt_config.h b/src/qt_common/qt_config.h similarity index 94% rename from src/yuzu/configuration/qt_config.h rename to src/qt_common/qt_config.h index dc2dceb4d7..a8c80dd273 100644 --- a/src/yuzu/configuration/qt_config.h +++ b/src/qt_common/qt_config.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp new file mode 100644 index 0000000000..e4625aa423 --- /dev/null +++ b/src/qt_common/qt_content_util.cpp @@ -0,0 +1,313 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_content_util.h" +#include "common/fs/fs.h" +#include "frontend_common/content_manager.h" +#include "frontend_common/firmware_manager.h" +#include "qt_common/qt_common.h" +#include "qt_common/qt_progress_dialog.h" +#include "qt_frontend_util.h" + +#include + +namespace QtCommon::Content { + +bool CheckGameFirmware(u64 program_id, QObject* parent) +{ + if (FirmwareManager::GameRequiresFirmware(program_id) + && !FirmwareManager::CheckFirmwarePresence(*system)) { + auto result = QtCommon::Frontend::ShowMessage( + QMessageBox::Warning, + "Game Requires Firmware", + "The game you are trying to launch requires firmware to boot or to get past the " + "opening menu. Please " + "dump and install firmware, or press \"OK\" to launch anyways.", + QMessageBox::Ok | QMessageBox::Cancel, + parent); + + return result == QMessageBox::Ok; + } + + return true; +} + +void InstallFirmware(const QString& location, bool recursive) +{ + QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."), + tr("Cancel"), + 0, + 100, + rootObject); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + progress.show(); + + // Declare progress callback. + auto callback = [&](size_t total_size, size_t processed_size) { + progress.setValue(static_cast((processed_size * 100) / total_size)); + return progress.wasCanceled(); + }; + + static constexpr const char* failedTitle = "Firmware Install Failed"; + static constexpr const char* successTitle = "Firmware Install Succeeded"; + QMessageBox::Icon icon; + FirmwareInstallResult result; + + const auto ShowMessage = [&]() { + QtCommon::Frontend::ShowMessage(icon, + failedTitle, + GetFirmwareInstallResultString(result)); + }; + + LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); + + // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in + // there.) + std::filesystem::path firmware_source_path = location.toStdString(); + if (!Common::FS::IsDir(firmware_source_path)) { + return; + } + + std::vector out; + const Common::FS::DirEntryCallable dir_callback = + [&out](const std::filesystem::directory_entry& entry) { + if (entry.path().has_extension() && entry.path().extension() == ".nca") { + out.emplace_back(entry.path()); + } + + return true; + }; + + callback(100, 10); + + if (recursive) { + Common::FS::IterateDirEntriesRecursively(firmware_source_path, + dir_callback, + Common::FS::DirEntryFilter::File); + } else { + Common::FS::IterateDirEntries(firmware_source_path, + dir_callback, + Common::FS::DirEntryFilter::File); + } + + if (out.size() <= 0) { + result = FirmwareInstallResult::NoNCAs; + icon = QMessageBox::Warning; + ShowMessage(); + return; + } + + // Locate and erase the content of nand/system/Content/registered/*.nca, if any. + auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); + if (sysnand_content_vdir->IsWritable() + && !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { + result = FirmwareInstallResult::FailedDelete; + icon = QMessageBox::Critical; + ShowMessage(); + return; + } + + LOG_INFO(Frontend, + "Cleaned nand/system/Content/registered folder in preparation for new firmware."); + + callback(100, 20); + + auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered"); + + bool success = true; + int i = 0; + for (const auto& firmware_src_path : out) { + i++; + auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(), + FileSys::OpenMode::Read); + auto firmware_dst_vfile = firmware_vdir + ->CreateFileRelative(firmware_src_path.filename().string()); + + if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) { + LOG_ERROR(Frontend, + "Failed to copy firmware file {} to {} in registered folder!", + firmware_src_path.generic_string(), + firmware_src_path.filename().string()); + success = false; + } + + if (callback(100, 20 + static_cast(((i) / static_cast(out.size())) * 70.0))) { + result = FirmwareInstallResult::FailedCorrupted; + icon = QMessageBox::Warning; + ShowMessage(); + return; + } + } + + if (!success) { + result = FirmwareInstallResult::FailedCopy; + icon = QMessageBox::Critical; + ShowMessage(); + return; + } + + // Re-scan VFS for the newly placed firmware files. + system->GetFileSystemController().CreateFactories(*vfs); + + auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) { + progress.setValue(90 + static_cast((processed_size * 10) / total_size)); + return progress.wasCanceled(); + }; + + auto results = ContentManager::VerifyInstalledContents(*QtCommon::system, + *QtCommon::provider, + VerifyFirmwareCallback, + true); + + if (results.size() > 0) { + const auto failed_names = QString::fromStdString( + fmt::format("{}", fmt::join(results, "\n"))); + progress.close(); + QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"), + tr("Verification failed for the following files:\n\n%1") + .arg(failed_names)); + return; + } + + progress.close(); + + const auto pair = FirmwareManager::GetFirmwareVersion(*system); + const auto firmware_data = pair.first; + const std::string display_version(firmware_data.display_version.data()); + + result = FirmwareInstallResult::Success; + QtCommon::Frontend::Information(rootObject, + tr(successTitle), + tr(GetFirmwareInstallResultString(result)) + .arg(QString::fromStdString(display_version))); +} + +QString UnzipFirmwareToTmp(const QString& location) +{ + namespace fs = std::filesystem; + fs::path tmp{fs::temp_directory_path()}; + + if (!fs::create_directories(tmp / "eden" / "firmware")) { + return ""; + } + + tmp /= "eden"; + tmp /= "firmware"; + + QString qCacheDir = QString::fromStdString(tmp.string()); + + QFile zip(location); + + QStringList result = JlCompress::extractDir(&zip, qCacheDir); + if (result.isEmpty()) { + return ""; + } + + return qCacheDir; +} + +// Content // +void VerifyGameContents(const std::string& game_path) +{ + QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), + tr("Cancel"), + 0, + 100, + rootObject); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + const auto callback = [&](size_t total_size, size_t processed_size) { + progress.setValue(static_cast((processed_size * 100) / total_size)); + return progress.wasCanceled(); + }; + + const auto result = ContentManager::VerifyGameContents(*system, game_path, callback); + + switch (result) { + case ContentManager::GameVerificationResult::Success: + QtCommon::Frontend::Information(rootObject, + tr("Integrity verification succeeded!"), + tr("The operation completed successfully.")); + break; + case ContentManager::GameVerificationResult::Failed: + QtCommon::Frontend::Critical(rootObject, + tr("Integrity verification failed!"), + tr("File contents may be corrupt or missing.")); + break; + case ContentManager::GameVerificationResult::NotImplemented: + QtCommon::Frontend::Warning( + rootObject, + tr("Integrity verification couldn't be performed"), + tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. " + "File contents could not be checked for validity.")); + } +} + +void InstallKeys() +{ + const QString key_source_location + = QtCommon::Frontend::GetOpenFileName(tr("Select Dumped Keys Location"), + {}, + QStringLiteral("Decryption Keys (*.keys)"), + {}, + QtCommon::Frontend::Option::ReadOnly); + + if (key_source_location.isEmpty()) { + return; + } + + FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location + .toStdString(), + "keys"); + + system->GetFileSystemController().CreateFactories(*QtCommon::vfs); + + switch (result) { + case FirmwareManager::KeyInstallResult::Success: + QtCommon::Frontend::Information(tr("Decryption Keys install succeeded"), + tr("Decryption Keys were successfully installed")); + break; + default: + QtCommon::Frontend::Critical(tr("Decryption Keys install failed"), + tr(FirmwareManager::GetKeyInstallResultString(result))); + break; + } +} + +void VerifyInstalledContents() { + // Initialize a progress dialog. + QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject); + progress.setWindowModality(Qt::WindowModal); + progress.setMinimumDuration(100); + progress.setAutoClose(false); + progress.setAutoReset(false); + + // Declare progress callback. + auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { + progress.setValue(static_cast((processed_size * 100) / total_size)); + return progress.wasCanceled(); + }; + + const std::vector result = + ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback); + progress.close(); + + if (result.empty()) { + QtCommon::Frontend::Information(tr("Integrity verification succeeded!"), + tr("The operation completed successfully.")); + } else { + const auto failed_names = + QString::fromStdString(fmt::format("{}", fmt::join(result, "\n"))); + QtCommon::Frontend::Critical( + tr("Integrity verification failed!"), + tr("Verification failed for the following files:\n\n%1").arg(failed_names)); + } +} + +} // namespace QtCommon::Content diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h new file mode 100644 index 0000000000..b572c1c4a3 --- /dev/null +++ b/src/qt_common/qt_content_util.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_CONTENT_UTIL_H +#define QT_CONTENT_UTIL_H + +#include +#include "common/common_types.h" + +namespace QtCommon::Content { + +// +bool CheckGameFirmware(u64 program_id, QObject *parent); + +static constexpr std::array FIRMWARE_RESULTS + = {"Successfully installed firmware version %1", + "", + "Unable to locate potential firmware NCA files", + "Failed to delete one or more firmware files.", + "One or more firmware files failed to copy into NAND.", + "Firmware installation cancelled, firmware may be in a bad state or corrupted." + "Restart Eden or re-install firmware."}; + +enum class FirmwareInstallResult { + Success, + NoOp, + NoNCAs, + FailedDelete, + FailedCopy, + FailedCorrupted, +}; + +inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result) +{ + return FIRMWARE_RESULTS.at(static_cast(result)); +} + +void InstallFirmware(const QString &location, bool recursive); + +QString UnzipFirmwareToTmp(const QString &location); + +// Keys // +void InstallKeys(); + +// Content // +void VerifyGameContents(const std::string &game_path); +void VerifyInstalledContents(); +} +#endif // QT_CONTENT_UTIL_H diff --git a/src/qt_common/qt_frontend_util.cpp b/src/qt_common/qt_frontend_util.cpp new file mode 100644 index 0000000000..d519669ad5 --- /dev/null +++ b/src/qt_common/qt_frontend_util.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_frontend_util.h" +#include "qt_common/qt_common.h" + +#ifdef YUZU_QT_WIDGETS +#include +#endif + +namespace QtCommon::Frontend { + +StandardButton ShowMessage( + Icon icon, const QString &title, const QString &text, StandardButtons buttons, QObject *parent) +{ +#ifdef YUZU_QT_WIDGETS + QMessageBox *box = new QMessageBox(icon, title, text, buttons, (QWidget *) parent); + return static_cast(box->exec()); +#endif + // TODO(crueter): If Qt Widgets is disabled... + // need a way to reference icon/buttons too +} + +const QString GetOpenFileName(const QString &title, + const QString &dir, + const QString &filter, + QString *selectedFilter, + Options options) +{ +#ifdef YUZU_QT_WIDGETS + return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options); +#endif +} + +} // namespace QtCommon::Frontend diff --git a/src/qt_common/qt_frontend_util.h b/src/qt_common/qt_frontend_util.h new file mode 100644 index 0000000000..f86b9e1357 --- /dev/null +++ b/src/qt_common/qt_frontend_util.h @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_FRONTEND_UTIL_H +#define QT_FRONTEND_UTIL_H + +#include +#include "qt_common/qt_common.h" + +#ifdef YUZU_QT_WIDGETS +#include +#include +#include +#endif + +/** + * manages common functionality e.g. message boxes and such for Qt/QML + */ +namespace QtCommon::Frontend { + +Q_NAMESPACE + +#ifdef YUZU_QT_WIDGETS +using Options = QFileDialog::Options; +using Option = QFileDialog::Option; + +using StandardButton = QMessageBox::StandardButton; +using StandardButtons = QMessageBox::StandardButtons; + +using Icon = QMessageBox::Icon; +#else +enum Option { + ShowDirsOnly = 0x00000001, + DontResolveSymlinks = 0x00000002, + DontConfirmOverwrite = 0x00000004, + DontUseNativeDialog = 0x00000008, + ReadOnly = 0x00000010, + HideNameFilterDetails = 0x00000020, + DontUseCustomDirectoryIcons = 0x00000040 +}; +Q_ENUM_NS(Option) +Q_DECLARE_FLAGS(Options, Option) +Q_FLAG_NS(Options) + +enum StandardButton { + // keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton + NoButton = 0x00000000, + Ok = 0x00000400, + Save = 0x00000800, + SaveAll = 0x00001000, + Open = 0x00002000, + Yes = 0x00004000, + YesToAll = 0x00008000, + No = 0x00010000, + NoToAll = 0x00020000, + Abort = 0x00040000, + Retry = 0x00080000, + Ignore = 0x00100000, + Close = 0x00200000, + Cancel = 0x00400000, + Discard = 0x00800000, + Help = 0x01000000, + Apply = 0x02000000, + Reset = 0x04000000, + RestoreDefaults = 0x08000000, + + FirstButton = Ok, // internal + LastButton = RestoreDefaults, // internal + + YesAll = YesToAll, // obsolete + NoAll = NoToAll, // obsolete + + Default = 0x00000100, // obsolete + Escape = 0x00000200, // obsolete + FlagMask = 0x00000300, // obsolete + ButtonMask = ~FlagMask // obsolete +}; +Q_ENUM_NS(StandardButton) + +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) +typedef StandardButton Button; +#endif +Q_DECLARE_FLAGS(StandardButtons, StandardButton) +Q_FLAG_NS(StandardButtons) + +enum Icon { + // keep this in sync with QMessageDialogOptions::StandardIcon + NoIcon = 0, + Information = 1, + Warning = 2, + Critical = 3, + Question = 4 +}; +Q_ENUM_NS(Icon) + +#endif + +// TODO(crueter) widgets-less impl, choices et al. +StandardButton ShowMessage(Icon icon, + const QString &title, + const QString &text, + StandardButtons buttons = StandardButton::NoButton, + QObject *parent = nullptr); + +#define UTIL_OVERRIDES(level) \ + inline StandardButton level(QObject *parent, \ + const QString &title, \ + const QString &text, \ + StandardButtons buttons = StandardButton::Ok) \ + { \ + return ShowMessage(Icon::level, title, text, buttons, parent); \ + } \ + inline StandardButton level(QObject *parent, \ + const char *title, \ + const char *text, \ + StandardButtons buttons \ + = StandardButton::Ok) \ + { \ + return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \ + } \ + inline StandardButton level(const char *title, \ + const char *text, \ + StandardButtons buttons \ + = StandardButton::Ok) \ + { \ + return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \ + } \ + inline StandardButton level(const QString title, \ + const QString &text, \ + StandardButtons buttons \ + = StandardButton::Ok) \ + { \ + return ShowMessage(Icon::level, title, text, buttons, rootObject); \ + } + +UTIL_OVERRIDES(Information) +UTIL_OVERRIDES(Warning) +UTIL_OVERRIDES(Critical) +UTIL_OVERRIDES(Question) + +const QString GetOpenFileName(const QString &title, + const QString &dir, + const QString &filter, + QString *selectedFilter = nullptr, + Options options = Options()); + +} // namespace QtCommon::Frontend +#endif // QT_FRONTEND_UTIL_H diff --git a/src/qt_common/qt_game_util.cpp b/src/qt_common/qt_game_util.cpp new file mode 100644 index 0000000000..5d0b4d8ae7 --- /dev/null +++ b/src/qt_common/qt_game_util.cpp @@ -0,0 +1,577 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_game_util.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/service/am/am_types.h" +#include "frontend_common/content_manager.h" +#include "qt_common.h" +#include "qt_common/uisettings.h" +#include "qt_frontend_util.h" +#include "yuzu/util/util.h" + +#include +#include +#include + +#ifdef _WIN32 +#include "common/scope_exit.h" +#include "common/string_util.h" +#include +#include +#else +#include "fmt/ostream.h" +#include +#endif + +namespace QtCommon::Game { + +bool CreateShortcutLink(const std::filesystem::path& shortcut_path, + const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, + const std::string& arguments, + const std::string& categories, + const std::string& keywords, + const std::string& name) +try { +#ifdef _WIN32 // Windows + HRESULT hr = CoInitialize(nullptr); + if (FAILED(hr)) { + LOG_ERROR(Frontend, "CoInitialize failed"); + return false; + } + SCOPE_EXIT + { + CoUninitialize(); + }; + IShellLinkW* ps1 = nullptr; + IPersistFile* persist_file = nullptr; + SCOPE_EXIT + { + if (persist_file != nullptr) { + persist_file->Release(); + } + if (ps1 != nullptr) { + ps1->Release(); + } + }; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, + nullptr, + CLSCTX_INPROC_SERVER, + IID_IShellLinkW, + reinterpret_cast(&ps1)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); + return false; + } + hres = ps1->SetPath(command.c_str()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set path"); + return false; + } + if (!arguments.empty()) { + hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set arguments"); + return false; + } + } + if (!comment.empty()) { + hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set description"); + return false; + } + } + if (std::filesystem::is_regular_file(icon_path)) { + hres = ps1->SetIconLocation(icon_path.c_str(), 0); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set icon location"); + return false; + } + } + hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast(&persist_file)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to get IPersistFile interface"); + return false; + } + hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to save shortcut"); + return false; + } + return true; +#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX + std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); + std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); + if (!shortcut_stream.is_open()) { + LOG_ERROR(Frontend, "Failed to create shortcut"); + return false; + } + // TODO: Migrate fmt::print to std::print in futures STD C++ 23. + fmt::print(shortcut_stream, "[Desktop Entry]\n"); + fmt::print(shortcut_stream, "Type=Application\n"); + fmt::print(shortcut_stream, "Version=1.0\n"); + fmt::print(shortcut_stream, "Name={}\n", name); + if (!comment.empty()) { + fmt::print(shortcut_stream, "Comment={}\n", comment); + } + if (std::filesystem::is_regular_file(icon_path)) { + fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); + } + fmt::print(shortcut_stream, "TryExec={}\n", command.string()); + fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); + if (!categories.empty()) { + fmt::print(shortcut_stream, "Categories={}\n", categories); + } + if (!keywords.empty()) { + fmt::print(shortcut_stream, "Keywords={}\n", keywords); + } + return true; +#else // Unsupported platform + return false; +#endif +} catch (const std::exception& e) { + LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); + return false; +} + +bool MakeShortcutIcoPath(const u64 program_id, + const std::string_view game_file_name, + std::filesystem::path& out_icon_path) +{ + // Get path to Yuzu icons directory & icon extension + std::string ico_extension = "png"; +#if defined(_WIN32) + out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir); + ico_extension = "ico"; +#elif defined(__linux__) || defined(__FreeBSD__) + out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; +#endif + // Create icons directory if it doesn't exist + if (!Common::FS::CreateDirs(out_icon_path)) { + out_icon_path.clear(); + return false; + } + + // Create icon file path + out_icon_path /= (program_id == 0 ? fmt::format("eden-{}.{}", game_file_name, ico_extension) + : fmt::format("eden-{:016X}.{}", program_id, ico_extension)); + return true; +} + +void OpenEdenFolder(const Common::FS::EdenPath& path) +{ + QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path)))); +} + +void OpenRootDataFolder() +{ + OpenEdenFolder(Common::FS::EdenPath::EdenDir); +} + +void OpenNANDFolder() +{ + OpenEdenFolder(Common::FS::EdenPath::NANDDir); +} + +void OpenSDMCFolder() +{ + OpenEdenFolder(Common::FS::EdenPath::SDMCDir); +} + +void OpenModFolder() +{ + OpenEdenFolder(Common::FS::EdenPath::LoadDir); +} + +void OpenLogFolder() +{ + OpenEdenFolder(Common::FS::EdenPath::LogDir); +} + +static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type) +{ + switch (type) { + case QtCommon::Game::InstalledEntryType::Game: + return tr("Error Removing Contents"); + case QtCommon::Game::InstalledEntryType::Update: + return tr("Error Removing Update"); + case QtCommon::Game::InstalledEntryType::AddOnContent: + return tr("Error Removing DLC"); + default: + return QStringLiteral("Error Removing "); + } +} + +// Game Content // +void RemoveBaseContent(u64 program_id, InstalledEntryType type) +{ + const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(), + program_id); + if (res) { + QtCommon::Frontend::Information(rootObject, + "Successfully Removed", + "Successfully removed the installed base game."); + } else { + QtCommon::Frontend::Warning( + rootObject, + GetGameListErrorRemoving(type), + tr("The base game is not installed in the NAND and cannot be removed.")); + } +} + +void RemoveUpdateContent(u64 program_id, InstalledEntryType type) +{ + const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id); + if (res) { + QtCommon::Frontend::Information(rootObject, + "Successfully Removed", + "Successfully removed the installed update."); + } else { + QtCommon::Frontend::Warning(rootObject, + GetGameListErrorRemoving(type), + tr("There is no update installed for this title.")); + } +} + +void RemoveAddOnContent(u64 program_id, InstalledEntryType type) +{ + const size_t count = ContentManager::RemoveAllDLC(*system, program_id); + if (count == 0) { + QtCommon::Frontend::Warning(rootObject, + GetGameListErrorRemoving(type), + tr("There are no DLCs installed for this title.")); + return; + } + + QtCommon::Frontend::Information(rootObject, + tr("Successfully Removed"), + tr("Successfully removed %1 installed DLC.").arg(count)); +} + +// Global Content // + +void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) +{ + const auto target_file_name = [target] { + switch (target) { + case GameListRemoveTarget::GlShaderCache: + return "opengl.bin"; + case GameListRemoveTarget::VkShaderCache: + return "vulkan.bin"; + default: + return ""; + } + }(); + const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); + const auto target_file = shader_cache_folder_path / target_file_name; + + if (!Common::FS::Exists(target_file)) { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Transferable Shader Cache"), + tr("A shader cache for this title does not exist.")); + return; + } + if (Common::FS::RemoveFile(target_file)) { + QtCommon::Frontend::Information(rootObject, + tr("Successfully Removed"), + tr("Successfully removed the transferable shader cache.")); + } else { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Transferable Shader Cache"), + tr("Failed to remove the transferable shader cache.")); + } +} + +void RemoveVulkanDriverPipelineCache(u64 program_id) +{ + static constexpr std::string_view target_file_name = "vulkan_pipelines.bin"; + + const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); + const auto target_file = shader_cache_folder_path / target_file_name; + + if (!Common::FS::Exists(target_file)) { + return; + } + if (!Common::FS::RemoveFile(target_file)) { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Vulkan Driver Pipeline Cache"), + tr("Failed to remove the driver pipeline cache.")); + } +} + +void RemoveAllTransferableShaderCaches(u64 program_id) +{ + const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); + + if (!Common::FS::Exists(program_shader_cache_dir)) { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Transferable Shader Caches"), + tr("A shader cache for this title does not exist.")); + return; + } + if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) { + QtCommon::Frontend::Information(rootObject, + tr("Successfully Removed"), + tr("Successfully removed the transferable shader caches.")); + } else { + QtCommon::Frontend::Warning( + rootObject, + tr("Error Removing Transferable Shader Caches"), + tr("Failed to remove the transferable shader cache directory.")); + } +} + +void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) +{ + const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); + const auto config_file_name = program_id == 0 + ? Common::FS::PathToUTF8String(file_path.filename()) + .append(".ini") + : fmt::format("{:016X}.ini", program_id); + const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) + / "custom" / config_file_name; + + if (!Common::FS::Exists(custom_config_file_path)) { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Custom Configuration"), + tr("A custom configuration for this title does not exist.")); + return; + } + + if (Common::FS::RemoveFile(custom_config_file_path)) { + QtCommon::Frontend::Information(rootObject, + tr("Successfully Removed"), + tr("Successfully removed the custom game configuration.")); + } else { + QtCommon::Frontend::Warning(rootObject, + tr("Error Removing Custom Configuration"), + tr("Failed to remove the custom game configuration.")); + } +} + +void RemoveCacheStorage(u64 program_id) +{ + const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir); + auto vfs_nand_dir = vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), + FileSys::OpenMode::Read); + + const auto cache_storage_path + = FileSys::SaveDataFactory::GetFullPath({}, + vfs_nand_dir, + FileSys::SaveDataSpaceId::User, + FileSys::SaveDataType::Cache, + 0 /* program_id */, + {}, + 0); + + const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); + + // Not an error if it wasn't cleared. + Common::FS::RemoveDirRecursively(path); +} + +// Metadata // +void ResetMetadata() +{ + const QString title = tr("Reset Metadata Cache"); + + if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) + / "game_list/")) { + QtCommon::Frontend::Warning(rootObject, title, tr("The metadata cache is already empty.")); + } else if (Common::FS::RemoveDirRecursively( + Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) { + QtCommon::Frontend::Information(rootObject, + title, + tr("The operation completed successfully.")); + UISettings::values.is_game_list_reload_pending.exchange(true); + } else { + QtCommon::Frontend::Warning( + rootObject, + title, + tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); + } +} + +// Uhhh // + +// Messages in pre-defined message boxes for less code spaghetti +inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title) +{ + int result = 0; + QMessageBox::StandardButtons buttons; + switch (imsg) { + case ShortcutMessages::Fullscreen: + buttons = QMessageBox::Yes | QMessageBox::No; + result + = QtCommon::Frontend::Information(tr("Create Shortcut"), + tr("Do you want to launch the game in fullscreen?"), + buttons); + return result == QMessageBox::Yes; + case ShortcutMessages::Success: + QtCommon::Frontend::Information(tr("Shortcut Created"), + tr("Successfully created a shortcut to %1").arg(game_title)); + return false; + case ShortcutMessages::Volatile: + buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; + result = QtCommon::Frontend::Warning( + tr("Shortcut may be Volatile!"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + buttons); + return result == QMessageBox::Ok; + default: + buttons = QMessageBox::Ok; + QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"), + tr("Failed to create a shortcut to %1").arg(game_title), + buttons); + return false; + } +} + +void CreateShortcut(const std::string& game_path, + const u64 program_id, + const std::string& game_title_, + const ShortcutTarget &target, + std::string arguments_, + const bool needs_title) +{ + // Get path to Eden executable + std::filesystem::path command = GetEdenCommand(); + + // Shortcut path + std::filesystem::path shortcut_path = GetShortcutPath(target); + + if (!std::filesystem::exists(shortcut_path)) { + CreateShortcutMessagesGUI(ShortcutMessages::Failed, + QString::fromStdString(shortcut_path.generic_string())); + LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string()); + return; + } + + const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(), + QtCommon::system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = + Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read)); + + std::string game_title{game_title_}; + + // Delete illegal characters from title + if (needs_title) { + game_title = fmt::format("{:016X}", program_id); + if (control.first != nullptr) { + game_title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(game_title); + } + } + + const std::string illegal_chars = "<>:\"/\\|?*."; + for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { + if (illegal_chars.find(*it) != std::string::npos) { + game_title.erase(it.base() - 1); + } + } + + const QString qgame_title = QString::fromStdString(game_title); + + // Get icon from game file + std::vector icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + + QImage icon_data = + QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); + std::filesystem::path out_icon_path; + if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { + if (!SaveIconToFile(out_icon_path, icon_data)) { + LOG_ERROR(Frontend, "Could not write icon to file"); + } + } else { + QtCommon::Frontend::Critical( + tr("Create Icon"), + tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") + .arg(QString::fromStdString(out_icon_path.string()))); + } + +#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) + // Special case for AppImages + // Warn once if we are making a shortcut to a volatile AppImage + if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) { + if (!CreateShortcutMessagesGUI(ShortcutMessages::Volatile, qgame_title)) { + return; + } + UISettings::values.shortcut_already_warned = true; + } +#endif + + // Create shortcut + std::string arguments{arguments_}; + if (CreateShortcutMessagesGUI(ShortcutMessages::Fullscreen, qgame_title)) { + arguments = "-f " + arguments; + } + const std::string comment = fmt::format("Start {:s} with the Eden Emulator", game_title); + const std::string categories = "Game;Emulator;Qt;"; + const std::string keywords = "Switch;Nintendo;"; + + if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command, + arguments, categories, keywords, game_title)) { + CreateShortcutMessagesGUI(ShortcutMessages::Success, + qgame_title); + return; + } + CreateShortcutMessagesGUI(ShortcutMessages::Failed, + qgame_title); +} + +constexpr std::string GetShortcutPath(ShortcutTarget target) { + { + std::string shortcut_path{}; + if (target == ShortcutTarget::Desktop) { + shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + .toStdString(); + } else if (target == ShortcutTarget::Applications) { + shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + .toStdString(); + } + + return shortcut_path; + } +} + +void CreateHomeMenuShortcut(ShortcutTarget target) { + constexpr u64 QLaunchId = static_cast(Service::AM::AppletProgramId::QLaunch); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + QtCommon::Frontend::Warning(tr("No firmware available"), + tr("Please install firmware to use the home menu.")); + return; + } + + auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); + if (!qlaunch_nca) { + QtCommon::Frontend::Warning(tr("Home Menu Applet"), + tr("Home Menu is not available. Please reinstall firmware.")); + return; + } + + auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); + const auto game_path = qlaunch_applet_nca->GetFullPath(); + + // TODO(crueter): Make this use the Eden icon + CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false); +} + + +} // namespace QtCommon::Game diff --git a/src/qt_common/qt_game_util.h b/src/qt_common/qt_game_util.h new file mode 100644 index 0000000000..0a21208659 --- /dev/null +++ b/src/qt_common/qt_game_util.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_GAME_UTIL_H +#define QT_GAME_UTIL_H + +#include +#include +#include "common/fs/path_util.h" + +namespace QtCommon::Game { + +enum class InstalledEntryType { + Game, + Update, + AddOnContent, +}; + +enum class GameListRemoveTarget { + GlShaderCache, + VkShaderCache, + AllShaderCache, + CustomConfiguration, + CacheStorage, +}; + +enum class ShortcutTarget { + Desktop, + Applications, +}; + +enum class ShortcutMessages{ + Fullscreen = 0, + Success = 1, + Volatile = 2, + Failed = 3 +}; + +bool CreateShortcutLink(const std::filesystem::path& shortcut_path, + const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, + const std::string& arguments, + const std::string& categories, + const std::string& keywords, + const std::string& name); + +bool MakeShortcutIcoPath(const u64 program_id, + const std::string_view game_file_name, + std::filesystem::path& out_icon_path); + +void OpenEdenFolder(const Common::FS::EdenPath &path); +void OpenRootDataFolder(); +void OpenNANDFolder(); +void OpenSDMCFolder(); +void OpenModFolder(); +void OpenLogFolder(); + +void RemoveBaseContent(u64 program_id, InstalledEntryType type); +void RemoveUpdateContent(u64 program_id, InstalledEntryType type); +void RemoveAddOnContent(u64 program_id, InstalledEntryType type); + +void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); +void RemoveVulkanDriverPipelineCache(u64 program_id); +void RemoveAllTransferableShaderCaches(u64 program_id); +void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); +void RemoveCacheStorage(u64 program_id); + +// Metadata // +void ResetMetadata(); + +// Shortcuts // +void CreateShortcut(const std::string& game_path, + const u64 program_id, + const std::string& game_title_, + const ShortcutTarget& target, + std::string arguments_, + const bool needs_title); + +constexpr std::string GetShortcutPath(ShortcutTarget target); +void CreateHomeMenuShortcut(ShortcutTarget target); + +} + +#endif // QT_GAME_UTIL_H diff --git a/src/qt_common/qt_meta.cpp b/src/qt_common/qt_meta.cpp new file mode 100644 index 0000000000..67ae659771 --- /dev/null +++ b/src/qt_common/qt_meta.cpp @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_meta.h" +#include "common/common_types.h" +#include "core/core.h" +#include "core/frontend/applets/cabinet.h" +#include "core/frontend/applets/controller.h" +#include "core/frontend/applets/profile_select.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/frontend/applet_web_browser_types.h" + +namespace QtCommon::Meta { + +void RegisterMetaTypes() +{ + // Register integral and floating point types + qRegisterMetaType("u8"); + qRegisterMetaType("u16"); + qRegisterMetaType("u32"); + qRegisterMetaType("u64"); + qRegisterMetaType("u128"); + qRegisterMetaType("s8"); + qRegisterMetaType("s16"); + qRegisterMetaType("s32"); + qRegisterMetaType("s64"); + qRegisterMetaType("f32"); + qRegisterMetaType("f64"); + + // Register string types + qRegisterMetaType("std::string"); + qRegisterMetaType("std::wstring"); + qRegisterMetaType("std::u8string"); + qRegisterMetaType("std::u16string"); + qRegisterMetaType("std::u32string"); + qRegisterMetaType("std::string_view"); + qRegisterMetaType("std::wstring_view"); + qRegisterMetaType("std::u8string_view"); + qRegisterMetaType("std::u16string_view"); + qRegisterMetaType("std::u32string_view"); + + // Register applet types + + // Cabinet Applet + qRegisterMetaType("Core::Frontend::CabinetParameters"); + qRegisterMetaType>( + "std::shared_ptr"); + + // Controller Applet + qRegisterMetaType("Core::Frontend::ControllerParameters"); + + // Profile Select Applet + qRegisterMetaType( + "Core::Frontend::ProfileSelectParameters"); + + // Software Keyboard Applet + qRegisterMetaType( + "Core::Frontend::KeyboardInitializeParameters"); + qRegisterMetaType( + "Core::Frontend::InlineAppearParameters"); + qRegisterMetaType("Core::Frontend::InlineTextParameters"); + qRegisterMetaType("Service::AM::Frontend::SwkbdResult"); + qRegisterMetaType( + "Service::AM::Frontend::SwkbdTextCheckResult"); + qRegisterMetaType( + "Service::AM::Frontend::SwkbdReplyType"); + + // Web Browser Applet + qRegisterMetaType("Service::AM::Frontend::WebExitReason"); + + // Register loader types + qRegisterMetaType("Core::SystemResultStatus"); +} + +} diff --git a/src/qt_common/qt_meta.h b/src/qt_common/qt_meta.h new file mode 100644 index 0000000000..c0a37db983 --- /dev/null +++ b/src/qt_common/qt_meta.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_META_H +#define QT_META_H + +#include + +namespace QtCommon::Meta { + +// +void RegisterMetaTypes(); + +} +#endif // QT_META_H diff --git a/src/qt_common/qt_path_util.cpp b/src/qt_common/qt_path_util.cpp new file mode 100644 index 0000000000..761e6e8405 --- /dev/null +++ b/src/qt_common/qt_path_util.cpp @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_path_util.h" +#include +#include +#include +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "qt_common/qt_frontend_util.h" +#include + +namespace QtCommon::Path { + +bool OpenShaderCache(u64 program_id, QObject *parent) +{ + const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); + const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)}; + if (!Common::FS::CreateDirs(shader_cache_folder_path)) { + QtCommon::Frontend::ShowMessage(QMessageBox::Warning, "Error Opening Shader Cache", "Failed to create or open shader cache for this title, ensure your app data directory has write permissions.", QMessageBox::Ok, parent); + } + + const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)}; + const auto qt_shader_cache_path = QString::fromStdString(shader_path_string); + return QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); +} + +} diff --git a/src/qt_common/qt_path_util.h b/src/qt_common/qt_path_util.h new file mode 100644 index 0000000000..855b06caa9 --- /dev/null +++ b/src/qt_common/qt_path_util.h @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_PATH_UTIL_H +#define QT_PATH_UTIL_H + +#include "common/common_types.h" +#include + +namespace QtCommon::Path { bool OpenShaderCache(u64 program_id, QObject *parent); } + +#endif // QT_PATH_UTIL_H diff --git a/src/qt_common/qt_progress_dialog.cpp b/src/qt_common/qt_progress_dialog.cpp new file mode 100644 index 0000000000..b4bf74c8bd --- /dev/null +++ b/src/qt_common/qt_progress_dialog.cpp @@ -0,0 +1,4 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_progress_dialog.h" diff --git a/src/qt_common/qt_progress_dialog.h b/src/qt_common/qt_progress_dialog.h new file mode 100644 index 0000000000..17f6817ffa --- /dev/null +++ b/src/qt_common/qt_progress_dialog.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_PROGRESS_DIALOG_H +#define QT_PROGRESS_DIALOG_H + +#include + +#ifdef YUZU_QT_WIDGETS +#include +#endif + +namespace QtCommon::Frontend { +#ifdef YUZU_QT_WIDGETS + +using QtProgressDialog = QProgressDialog; + +// TODO(crueter): QML impl +#else +class QtProgressDialog +{ +public: + QtProgressDialog(const QString &labelText, + const QString &cancelButtonText, + int minimum, + int maximum, + QObject *parent = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + + bool wasCanceled() const; + void setWindowModality(Qt::WindowModality modality); + void setMinimumDuration(int durationMs); + void setAutoClose(bool autoClose); + void setAutoReset(bool autoReset); + +public slots: + void setLabelText(QString &text); + void setRange(int min, int max); + void setValue(int progress); + bool close(); + + void show(); +}; +#endif // YUZU_QT_WIDGETS + +} +#endif // QT_PROGRESS_DIALOG_H diff --git a/src/qt_common/qt_rom_util.cpp b/src/qt_common/qt_rom_util.cpp new file mode 100644 index 0000000000..08ccb05a97 --- /dev/null +++ b/src/qt_common/qt_rom_util.cpp @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "qt_rom_util.h" + +#include + +namespace QtCommon::ROM { + +bool RomFSRawCopy(size_t total_size, + size_t& read_size, + QtProgressCallback callback, + const FileSys::VirtualDir& src, + const FileSys::VirtualDir& dest, + bool full) +{ + // TODO(crueter) + // if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) + // return false; + // if (dialog.wasCanceled()) + // return false; + + // std::vector buffer(CopyBufferSize); + // auto last_timestamp = std::chrono::steady_clock::now(); + + // const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file, + // const FileSys::VirtualFile& dest_file) { + // if (src_file == nullptr || dest_file == nullptr) { + // return false; + // } + // if (!dest_file->Resize(src_file->GetSize())) { + // return false; + // } + + // for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) { + // if (dialog.wasCanceled()) { + // dest_file->Resize(0); + // return false; + // } + + // using namespace std::literals::chrono_literals; + // const auto new_timestamp = std::chrono::steady_clock::now(); + + // if ((new_timestamp - last_timestamp) > 33ms) { + // last_timestamp = new_timestamp; + // dialog.setValue( + // static_cast(std::min(read_size, total_size) * 100 / total_size)); + // QCoreApplication::processEvents(); + // } + + // const auto read = src_file->Read(buffer.data(), buffer.size(), i); + // dest_file->Write(buffer.data(), read, i); + + // read_size += read; + // } + + // return true; + // }; + + // if (full) { + // for (const auto& file : src->GetFiles()) { + // const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); + // if (!QtRawCopy(file, out)) + // return false; + // } + // } + + // for (const auto& dir : src->GetSubdirectories()) { + // const auto out = dest->CreateSubdirectory(dir->GetName()); + // if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full)) + // return false; + // } + + // return true; + return true; +} + +} diff --git a/src/qt_common/qt_rom_util.h b/src/qt_common/qt_rom_util.h new file mode 100644 index 0000000000..f76b09753d --- /dev/null +++ b/src/qt_common/qt_rom_util.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef QT_ROM_UTIL_H +#define QT_ROM_UTIL_H + +#include "qt_common/qt_common.h" +#include + +namespace QtCommon::ROM { + +bool RomFSRawCopy(size_t total_size, + size_t& read_size, + QtProgressCallback callback, + const FileSys::VirtualDir& src, + const FileSys::VirtualDir& dest, + bool full); + +} +#endif // QT_ROM_UTIL_H diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/qt_common/shared_translation.cpp similarity index 91% rename from src/yuzu/configuration/shared_translation.cpp rename to src/qt_common/shared_translation.cpp index 1137145659..8f31e07154 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/qt_common/shared_translation.cpp @@ -7,23 +7,21 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "yuzu/configuration/shared_translation.h" +#include "shared_translation.h" #include -#include #include "common/settings.h" #include "common/settings_enums.h" #include "common/settings_setting.h" #include "common/time_zone.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include #include -#include #include namespace ConfigurationShared { -std::unique_ptr InitializeTranslations(QWidget* parent) +std::unique_ptr InitializeTranslations(QObject* parent) { std::unique_ptr translations = std::make_unique(); const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; @@ -409,12 +407,6 @@ std::unique_ptr InitializeTranslations(QWidget* parent) "their resolution, details and supported controllers and depending on this setting.\n" "Setting to Handheld can help improve performance for low end systems.")); INSERT(Settings, current_user, QString(), QString()); - INSERT(Settings, disable_nca_verification, tr("Disable NCA Verification"), - tr("Disables integrity verification of NCA content archives." - "\nThis may improve loading speed but risks data corruption or invalid files going " - "undetected.\n" - "Is necessary to make games and updates work that needs firmware 20+.")); - INSERT(Settings, hide_nca_verification_popup, QString(), QString()); // Controls @@ -473,7 +465,7 @@ std::unique_ptr InitializeTranslations(QWidget* parent) return translations; } -std::unique_ptr ComboboxEnumeration(QWidget* parent) +std::unique_ptr ComboboxEnumeration(QObject* parent) { std::unique_ptr translations = std::make_unique(); const auto& tr = [&](const char* text, const char* context = "") { @@ -650,58 +642,58 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) translations->insert( {Settings::EnumMetadata::Index(), { - {static_cast(Settings::TimeZone::Auto), - tr("Auto (%1)", "Auto select time zone") - .arg(QString::fromStdString( - Settings::GetTimeZoneString(Settings::TimeZone::Auto)))}, - {static_cast(Settings::TimeZone::Default), - tr("Default (%1)", "Default time zone") - .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, - PAIR(TimeZone, Cet, tr("CET")), - PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), - PAIR(TimeZone, Cuba, tr("Cuba")), - PAIR(TimeZone, Eet, tr("EET")), - PAIR(TimeZone, Egypt, tr("Egypt")), - PAIR(TimeZone, Eire, tr("Eire")), - PAIR(TimeZone, Est, tr("EST")), - PAIR(TimeZone, Est5Edt, tr("EST5EDT")), - PAIR(TimeZone, Gb, tr("GB")), - PAIR(TimeZone, GbEire, tr("GB-Eire")), - PAIR(TimeZone, Gmt, tr("GMT")), - PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), - PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), - PAIR(TimeZone, GmtZero, tr("GMT0")), - PAIR(TimeZone, Greenwich, tr("Greenwich")), - PAIR(TimeZone, Hongkong, tr("Hongkong")), - PAIR(TimeZone, Hst, tr("HST")), - PAIR(TimeZone, Iceland, tr("Iceland")), - PAIR(TimeZone, Iran, tr("Iran")), - PAIR(TimeZone, Israel, tr("Israel")), - PAIR(TimeZone, Jamaica, tr("Jamaica")), - PAIR(TimeZone, Japan, tr("Japan")), - PAIR(TimeZone, Kwajalein, tr("Kwajalein")), - PAIR(TimeZone, Libya, tr("Libya")), - PAIR(TimeZone, Met, tr("MET")), - PAIR(TimeZone, Mst, tr("MST")), - PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), - PAIR(TimeZone, Navajo, tr("Navajo")), - PAIR(TimeZone, Nz, tr("NZ")), - PAIR(TimeZone, NzChat, tr("NZ-CHAT")), - PAIR(TimeZone, Poland, tr("Poland")), - PAIR(TimeZone, Portugal, tr("Portugal")), - PAIR(TimeZone, Prc, tr("PRC")), - PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), - PAIR(TimeZone, Roc, tr("ROC")), - PAIR(TimeZone, Rok, tr("ROK")), - PAIR(TimeZone, Singapore, tr("Singapore")), - PAIR(TimeZone, Turkey, tr("Turkey")), - PAIR(TimeZone, Uct, tr("UCT")), - PAIR(TimeZone, Universal, tr("Universal")), - PAIR(TimeZone, Utc, tr("UTC")), - PAIR(TimeZone, WSu, tr("W-SU")), - PAIR(TimeZone, Wet, tr("WET")), - PAIR(TimeZone, Zulu, tr("Zulu")), - }}); + {static_cast(Settings::TimeZone::Auto), + tr("Auto (%1)", "Auto select time zone") + .arg(QString::fromStdString( + Settings::GetTimeZoneString(Settings::TimeZone::Auto)))}, + {static_cast(Settings::TimeZone::Default), + tr("Default (%1)", "Default time zone") + .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, + PAIR(TimeZone, Cet, tr("CET")), + PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), + PAIR(TimeZone, Cuba, tr("Cuba")), + PAIR(TimeZone, Eet, tr("EET")), + PAIR(TimeZone, Egypt, tr("Egypt")), + PAIR(TimeZone, Eire, tr("Eire")), + PAIR(TimeZone, Est, tr("EST")), + PAIR(TimeZone, Est5Edt, tr("EST5EDT")), + PAIR(TimeZone, Gb, tr("GB")), + PAIR(TimeZone, GbEire, tr("GB-Eire")), + PAIR(TimeZone, Gmt, tr("GMT")), + PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), + PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), + PAIR(TimeZone, GmtZero, tr("GMT0")), + PAIR(TimeZone, Greenwich, tr("Greenwich")), + PAIR(TimeZone, Hongkong, tr("Hongkong")), + PAIR(TimeZone, Hst, tr("HST")), + PAIR(TimeZone, Iceland, tr("Iceland")), + PAIR(TimeZone, Iran, tr("Iran")), + PAIR(TimeZone, Israel, tr("Israel")), + PAIR(TimeZone, Jamaica, tr("Jamaica")), + PAIR(TimeZone, Japan, tr("Japan")), + PAIR(TimeZone, Kwajalein, tr("Kwajalein")), + PAIR(TimeZone, Libya, tr("Libya")), + PAIR(TimeZone, Met, tr("MET")), + PAIR(TimeZone, Mst, tr("MST")), + PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), + PAIR(TimeZone, Navajo, tr("Navajo")), + PAIR(TimeZone, Nz, tr("NZ")), + PAIR(TimeZone, NzChat, tr("NZ-CHAT")), + PAIR(TimeZone, Poland, tr("Poland")), + PAIR(TimeZone, Portugal, tr("Portugal")), + PAIR(TimeZone, Prc, tr("PRC")), + PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), + PAIR(TimeZone, Roc, tr("ROC")), + PAIR(TimeZone, Rok, tr("ROK")), + PAIR(TimeZone, Singapore, tr("Singapore")), + PAIR(TimeZone, Turkey, tr("Turkey")), + PAIR(TimeZone, Uct, tr("UCT")), + PAIR(TimeZone, Universal, tr("Universal")), + PAIR(TimeZone, Utc, tr("UTC")), + PAIR(TimeZone, WSu, tr("W-SU")), + PAIR(TimeZone, Wet, tr("WET")), + PAIR(TimeZone, Zulu, tr("Zulu")), + }}); translations->insert({Settings::EnumMetadata::Index(), { PAIR(AudioMode, Mono, tr("Mono")), diff --git a/src/yuzu/configuration/shared_translation.h b/src/qt_common/shared_translation.h similarity index 94% rename from src/yuzu/configuration/shared_translation.h rename to src/qt_common/shared_translation.h index 574b1e2c78..48a2cb5205 100644 --- a/src/yuzu/configuration/shared_translation.h +++ b/src/qt_common/shared_translation.h @@ -11,23 +11,20 @@ #include #include -#include #include #include #include #include "common/common_types.h" -#include "common/settings.h" - -class QWidget; +#include "common/settings_enums.h" namespace ConfigurationShared { using TranslationMap = std::map>; using ComboboxTranslations = std::vector>; using ComboboxTranslationMap = std::map; -std::unique_ptr InitializeTranslations(QWidget* parent); +std::unique_ptr InitializeTranslations(QObject *parent); -std::unique_ptr ComboboxEnumeration(QWidget* parent); +std::unique_ptr ComboboxEnumeration(QObject* parent); static const std::map anti_aliasing_texts_map = { {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, diff --git a/src/yuzu/uisettings.cpp b/src/qt_common/uisettings.cpp similarity index 99% rename from src/yuzu/uisettings.cpp rename to src/qt_common/uisettings.cpp index 6da5424775..a17f3c0a4a 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/qt_common/uisettings.cpp @@ -7,7 +7,7 @@ #include #include "common/fs/fs.h" #include "common/fs/path_util.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #ifndef CANNOT_EXPLICITLY_INSTANTIATE namespace Settings { diff --git a/src/yuzu/uisettings.h b/src/qt_common/uisettings.h similarity index 99% rename from src/yuzu/uisettings.h rename to src/qt_common/uisettings.h index 3322b31ca3..4981d98dbf 100644 --- a/src/yuzu/uisettings.h +++ b/src/qt_common/uisettings.h @@ -17,7 +17,7 @@ #include "common/common_types.h" #include "common/settings.h" #include "common/settings_enums.h" -#include "configuration/qt_config.h" +#include "qt_common/qt_config.h" using Settings::Category; using Settings::ConfirmStop; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index d663f6c282..8a1d760c50 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -150,12 +150,8 @@ add_executable(yuzu configuration/configure_web.ui configuration/input_profiles.cpp configuration/input_profiles.h - configuration/shared_translation.cpp - configuration/shared_translation.h configuration/shared_widget.cpp configuration/shared_widget.h - configuration/qt_config.cpp - configuration/qt_config.h debugger/console.cpp debugger/console.h debugger/controller.cpp @@ -205,12 +201,8 @@ add_executable(yuzu play_time_manager.cpp play_time_manager.h precompiled_headers.h - qt_common.cpp - qt_common.h startup_checks.cpp startup_checks.h - uisettings.cpp - uisettings.h util/clickable_label.cpp util/clickable_label.h util/controller_navigation.cpp @@ -396,14 +388,17 @@ elseif(WIN32) endif() endif() -target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core) target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common) target_link_libraries(yuzu PRIVATE Boost::headers glad Qt6::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) if (NOT WIN32) target_include_directories(yuzu PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) endif() + +target_link_libraries(yuzu PRIVATE Vulkan::Headers) + if (UNIX AND NOT APPLE) target_link_libraries(yuzu PRIVATE Qt6::DBus) @@ -501,8 +496,4 @@ if (YUZU_ROOM) target_link_libraries(yuzu PRIVATE yuzu-room) endif() -# Extra deps -add_subdirectory(externals) -target_link_libraries(yuzu PRIVATE QuaZip::QuaZip) - create_target_directory_groups(yuzu) diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 63e7a74003..7b5f2a314f 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -12,7 +12,7 @@ #include #include "common/settings_enums.h" -#include "uisettings.h" +#include "qt_common/uisettings.h" #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA #include #include @@ -58,7 +58,7 @@ #include "video_core/renderer_base.h" #include "yuzu/bootmanager.h" #include "yuzu/main.h" -#include "yuzu/qt_common.h" +#include "qt_common/qt_common.h" class QObject; class QPaintEngine; @@ -234,7 +234,7 @@ class DummyContext : public Core::Frontend::GraphicsContext {}; class RenderWidget : public QWidget { public: - explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { + explicit RenderWidget(GRenderWindow* parent) : QWidget(parent) { setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_PaintOnScreen); if (QtCommon::GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) { @@ -247,9 +247,6 @@ public: QPaintEngine* paintEngine() const override { return nullptr; } - -private: - GRenderWindow* render_window; }; struct OpenGLRenderWidget : public RenderWidget { diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index c235b0fca4..a7ebae91f8 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -16,9 +19,9 @@ #include "ui_configure_audio.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" #include "yuzu/configuration/shared_widget.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureAudio::ConfigureAudio(const Core::System& system_, std::shared_ptr> group_, diff --git a/src/yuzu/configuration/configure_cpu.h b/src/yuzu/configuration/configure_cpu.h index 098e0e397b..bbc6096f9b 100644 --- a/src/yuzu/configuration/configure_cpu.h +++ b/src/yuzu/configuration/configure_cpu.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,7 +10,7 @@ #include #include #include "yuzu/configuration/configuration_shared.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" class QComboBox; diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 733c419c4b..18f629f639 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -15,7 +15,7 @@ #include "ui_configure_debug.h" #include "yuzu/configuration/configure_debug.h" #include "yuzu/debugger/console.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent) : QScrollArea(parent), ui{std::make_unique()}, system{system_} { diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 987b1462db..0816782282 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -27,7 +30,7 @@ #include "yuzu/configuration/configure_ui.h" #include "yuzu/configuration/configure_web.h" #include "yuzu/hotkeys.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_, InputCommon::InputSubsystem* input_subsystem, diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h index 0b6b285678..c1e148fc8e 100644 --- a/src/yuzu/configuration/configure_dialog.h +++ b/src/yuzu/configuration/configure_dialog.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ #include #include "configuration/shared_widget.h" #include "yuzu/configuration/configuration_shared.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" #include "yuzu/vk_device_info.h" namespace Core { diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index f25f14708b..d461bd2cc9 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp @@ -1,14 +1,19 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "yuzu/configuration/configure_filesystem.h" #include #include #include "common/fs/fs.h" #include "common/fs/path_util.h" #include "common/settings.h" +#include "qt_common/qt_common.h" +#include "qt_common/qt_game_util.h" +#include "qt_common/uisettings.h" #include "ui_configure_filesystem.h" -#include "yuzu/configuration/configure_filesystem.h" -#include "yuzu/uisettings.h" ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { @@ -126,20 +131,7 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) } void ConfigureFilesystem::ResetMetadata() { - if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / - "game_list/")) { - QMessageBox::information(this, tr("Reset Metadata Cache"), - tr("The metadata cache is already empty.")); - } else if (Common::FS::RemoveDirRecursively( - Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) { - QMessageBox::information(this, tr("Reset Metadata Cache"), - tr("The operation completed successfully.")); - UISettings::values.is_game_list_reload_pending.exchange(true); - } else { - QMessageBox::warning( - this, tr("Reset Metadata Cache"), - tr("The metadata cache couldn't be deleted. It might be in use or non-existent.")); - } + QtCommon::Game::ResetMetadata(); } void ConfigureFilesystem::UpdateEnabledControls() { diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 918fa15d4d..18c2c9cb77 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +14,7 @@ #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_general.h" #include "yuzu/configuration/shared_widget.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureGeneral::ConfigureGeneral(const Core::System& system_, std::shared_ptr> group_, diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 16846878f9..cd806e5ef8 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -40,8 +43,8 @@ #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" #include "yuzu/configuration/shared_widget.h" -#include "yuzu/qt_common.h" -#include "yuzu/uisettings.h" +#include "qt_common/qt_common.h" +#include "qt_common/uisettings.h" #include "yuzu/vk_device_info.h" static const std::vector default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR, diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index b92b4496ba..38d97aae80 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,7 @@ #include #include "common/common_types.h" #include "common/settings_enums.h" -#include "configuration/shared_translation.h" +#include "qt_common/shared_translation.h" #include "vk_device_info.h" #include "yuzu/configuration/configuration_shared.h" diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 4db18673d4..e07ad8c466 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +12,7 @@ #include "ui_configure_graphics_advanced.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics_advanced.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" #include "yuzu/configuration/shared_widget.h" ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced( diff --git a/src/yuzu/configuration/configure_graphics_extensions.cpp b/src/yuzu/configuration/configure_graphics_extensions.cpp index 322fa9ea08..f165e703d9 100644 --- a/src/yuzu/configuration/configure_graphics_extensions.cpp +++ b/src/yuzu/configuration/configure_graphics_extensions.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +14,7 @@ #include "ui_configure_graphics_extensions.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics_extensions.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" #include "yuzu/configuration/shared_widget.h" ConfigureGraphicsExtensions::ConfigureGraphicsExtensions( diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 3f68de12d8..a5c1ee009a 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2017 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -13,7 +16,7 @@ #include "ui_configure_hotkeys.h" #include "yuzu/configuration/configure_hotkeys.h" #include "yuzu/hotkeys.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/util/sequence_dialog/sequence_dialog.h" constexpr int name_column = 0; diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h index 4420e856cb..dcd6dd841b 100644 --- a/src/yuzu/configuration/configure_input_per_game.h +++ b/src/yuzu/configuration/configure_input_per_game.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +12,7 @@ #include "ui_configure_input_per_game.h" #include "yuzu/configuration/input_profiles.h" -#include "yuzu/configuration/qt_config.h" +#include "qt_common/qt_config.h" class QComboBox; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index d0dc0ff44c..6afa8320a2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -14,7 +14,7 @@ #include #include "common/assert.h" #include "common/param_package.h" -#include "configuration/qt_config.h" +#include "qt_common/qt_config.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "hid_core/hid_types.h" diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 031a8d4c2d..b51ede0de8 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -38,7 +41,7 @@ #include "yuzu/configuration/configure_per_game.h" #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/configuration/configure_system.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/util/util.h" #include "yuzu/vk_device_info.h" diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index e0f4e5cd67..83afc27f3d 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,8 +18,8 @@ #include "frontend_common/config.h" #include "vk_device_info.h" #include "yuzu/configuration/configuration_shared.h" -#include "yuzu/configuration/qt_config.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/qt_config.h" +#include "qt_common/shared_translation.h" namespace Core { class System; diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 078f2e8288..ed4cbc1147 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -21,7 +24,7 @@ #include "ui_configure_per_game_addons.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_per_game_addons.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigurePerGameAddons::ConfigurePerGameAddons(Core::System& system_, QWidget* parent) : QWidget(parent), ui{std::make_unique()}, system{system_} { diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index f7249be97e..795ad1a85e 100644 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ #include #include -#include "configuration/qt_config.h" +#include "qt_common/qt_config.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" #include "input_common/drivers/keyboard.h" diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index 773658bf22..898a1a3e59 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ #include "common/settings.h" #include "ui_configure_tas.h" #include "yuzu/configuration/configure_tas.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureTasDialog::ConfigureTasDialog(QWidget* parent) : QDialog(parent), ui(std::make_unique()) { diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 8dafee628b..ac6d6e34aa 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -27,7 +27,7 @@ #include "core/core.h" #include "core/frontend/framebuffer_layout.h" #include "ui_configure_ui.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" namespace { constexpr std::array default_game_icon_sizes{ diff --git a/src/yuzu/configuration/configure_web.cpp b/src/yuzu/configuration/configure_web.cpp index d62b5b0853..15a0029901 100644 --- a/src/yuzu/configuration/configure_web.cpp +++ b/src/yuzu/configuration/configure_web.cpp @@ -17,7 +17,7 @@ #include #include "common/settings.h" #include "ui_configure_web.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" ConfigureWeb::ConfigureWeb(QWidget* parent) : QWidget(parent) diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h index 023ec74a63..dba5ce1318 100644 --- a/src/yuzu/configuration/input_profiles.h +++ b/src/yuzu/configuration/input_profiles.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -6,7 +9,7 @@ #include #include -#include "configuration/qt_config.h" +#include "qt_common/qt_config.h" namespace Core { class System; diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index e23d86dc69..e30ecc4848 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -42,7 +45,7 @@ #include "common/logging/log.h" #include "common/settings.h" #include "common/settings_common.h" -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" namespace ConfigurationShared { diff --git a/src/yuzu/configuration/shared_widget.h b/src/yuzu/configuration/shared_widget.h index 5c67d83542..9e718098a3 100644 --- a/src/yuzu/configuration/shared_widget.h +++ b/src/yuzu/configuration/shared_widget.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,7 +14,7 @@ #include #include #include -#include "yuzu/configuration/shared_translation.h" +#include "qt_common/shared_translation.h" class QCheckBox; class QComboBox; diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index 1c1342ff18..8fb22db192 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,7 +12,7 @@ #include "common/logging/backend.h" #include "yuzu/debugger/console.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" namespace Debugger { void ToggleConsole() { diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 9f9e21bc28..feb814b25e 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,7 +8,7 @@ #include #include "yuzu/debugger/wait_tree.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "core/arm/debug.h" #include "core/core.h" diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 1ecef4af92..fa61cdfb1f 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include +#include "yuzu/game_list.h" #include #include #include @@ -13,19 +13,20 @@ #include #include #include -#include #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "qt_common/qt_game_util.h" +#include "qt_common/uisettings.h" #include "yuzu/compatibility_list.h" -#include "yuzu/game_list.h" #include "yuzu/game_list_p.h" #include "yuzu/game_list_worker.h" #include "yuzu/main.h" -#include "yuzu/uisettings.h" #include "yuzu/util/controller_navigation.h" +#include +#include GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent) : QObject(parent), gamelist{gamelist_} {} @@ -608,30 +609,30 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(open_transferable_shader_cache, &QAction::triggered, [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); connect(remove_all_content, &QAction::triggered, [this, program_id]() { - emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game); + emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Game); }); connect(remove_update, &QAction::triggered, [this, program_id]() { - emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update); + emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::Update); }); connect(remove_dlc, &QAction::triggered, [this, program_id]() { - emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); + emit RemoveInstalledEntryRequested(program_id, QtCommon::Game::InstalledEntryType::AddOnContent); }); connect(remove_gl_shader_cache, &QAction::triggered, [this, program_id, path]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::GlShaderCache, path); + emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::GlShaderCache, path); }); connect(remove_vk_shader_cache, &QAction::triggered, [this, program_id, path]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::VkShaderCache, path); + emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::VkShaderCache, path); }); connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::AllShaderCache, path); + emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::AllShaderCache, path); }); connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); + emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CustomConfiguration, path); }); connect(remove_play_time_data, &QAction::triggered, [this, program_id]() { emit RemovePlayTimeRequested(program_id); }); connect(remove_cache_storage, &QAction::triggered, [this, program_id, path] { - emit RemoveFileRequested(program_id, GameListRemoveTarget::CacheStorage, path); + emit RemoveFileRequested(program_id, QtCommon::Game::GameListRemoveTarget::CacheStorage, path); }); connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { emit DumpRomFSRequested(program_id, path, DumpRomFSTarget::Normal); @@ -649,10 +650,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri // TODO: Implement shortcut creation for macOS #if !defined(__APPLE__) connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { - emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); + emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Desktop); }); connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { - emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); + emit CreateShortcut(program_id, path, QtCommon::Game::ShortcutTarget::Applications); }); #endif connect(properties, &QAction::triggered, @@ -820,7 +821,7 @@ QStandardItemModel* GameList::GetModel() const { return item_model; } -void GameList::PopulateAsync(QVector& game_dirs, const bool cached) +void GameList::PopulateAsync(QVector& game_dirs) { tree_view->setEnabled(false); @@ -843,8 +844,7 @@ void GameList::PopulateAsync(QVector& game_dirs, const bool game_dirs, compatibility_list, play_time_manager, - system, - cached); + system); // Get events from the worker as data becomes available connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent, @@ -873,14 +873,6 @@ const QStringList GameList::supported_file_extensions = { QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; -void GameList::ForceRefreshGameDirectory() -{ - if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { - LOG_INFO(Frontend, "Force-reloading game list per user request."); - PopulateAsync(UISettings::values.game_dirs, false); - } -} - void GameList::RefreshGameDirectory() { if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 7c492bc19f..94e7b2dc42 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -20,7 +20,8 @@ #include "common/common_types.h" #include "core/core.h" -#include "uisettings.h" +#include "qt_common/uisettings.h" +#include "qt_common/qt_game_util.h" #include "yuzu/compatibility_list.h" #include "yuzu/play_time_manager.h" @@ -46,30 +47,11 @@ enum class GameListOpenTarget { ModData, }; -enum class GameListRemoveTarget { - GlShaderCache, - VkShaderCache, - AllShaderCache, - CustomConfiguration, - CacheStorage, -}; - enum class DumpRomFSTarget { Normal, SDMC, }; -enum class GameListShortcutTarget { - Desktop, - Applications, -}; - -enum class InstalledEntryType { - Game, - Update, - AddOnContent, -}; - class GameList : public QWidget { Q_OBJECT @@ -97,7 +79,7 @@ public: bool IsEmpty() const; void LoadCompatibilityList(); - void PopulateAsync(QVector& game_dirs, const bool cached = true); + void PopulateAsync(QVector& game_dirs); void SaveInterfaceLayout(); void LoadInterfaceLayout(); @@ -110,7 +92,6 @@ public: static const QStringList supported_file_extensions; public slots: - void ForceRefreshGameDirectory(); void RefreshGameDirectory(); signals: @@ -119,15 +100,15 @@ signals: void OpenFolderRequested(u64 program_id, GameListOpenTarget target, const std::string& game_path); void OpenTransferableShaderCacheRequested(u64 program_id); - void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); - void RemoveFileRequested(u64 program_id, GameListRemoveTarget target, + void RemoveInstalledEntryRequested(u64 program_id, QtCommon::Game::InstalledEntryType type); + void RemoveFileRequested(u64 program_id, QtCommon::Game::GameListRemoveTarget target, const std::string& game_path); void RemovePlayTimeRequested(u64 program_id); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void VerifyIntegrityRequested(const std::string& game_path); void CopyTIDRequested(u64 program_id); void CreateShortcut(u64 program_id, const std::string& game_path, - GameListShortcutTarget target); + const QtCommon::Game::ShortcutTarget target); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index c330b574f9..5a3b5829f5 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2015 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,7 +22,7 @@ #include "common/logging/log.h" #include "common/string_util.h" #include "yuzu/play_time_manager.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/util/util.h" enum class GameListItemType { diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 60109769bf..538c7ab822 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -27,7 +30,7 @@ #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" #include "yuzu/game_list_worker.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" namespace { @@ -199,8 +202,7 @@ QList MakeGameListEntry(const std::string& path, u64 program_id, const CompatibilityList& compatibility_list, const PlayTime::PlayTimeManager& play_time_manager, - const FileSys::PatchManager& patch, - const bool cached) + const FileSys::PatchManager& patch) { const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); @@ -224,14 +226,10 @@ QList MakeGameListEntry(const std::string& path, QString patch_versions; - if (cached) { - patch_versions = GetGameListCachedObject( - fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { - return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); - }); - } else { - patch_versions = FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); - } + patch_versions = GetGameListCachedObject( + fmt::format("{:016X}", patch.GetTitleID()), "pv.txt", [&patch, &loader] { + return FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable()); + }); list.insert(2, new GameListItem(patch_versions)); @@ -244,15 +242,13 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_, QVector& game_dirs_, const CompatibilityList& compatibility_list_, const PlayTime::PlayTimeManager& play_time_manager_, - Core::System& system_, - const bool cached_) + Core::System& system_) : vfs{std::move(vfs_)} , provider{provider_} , game_dirs{game_dirs_} , compatibility_list{compatibility_list_} , play_time_manager{play_time_manager_} , system{system_} - , cached{cached_} { // We want the game list to manage our lifetime. setAutoDelete(false); @@ -355,8 +351,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { program_id, compatibility_list, play_time_manager, - patch, - cached); + patch); RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); } } @@ -439,8 +434,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa id, compatibility_list, play_time_manager, - patch, - cached); + patch); RecordEvent( [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); @@ -463,8 +457,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa program_id, compatibility_list, play_time_manager, - patch, - cached); + patch); RecordEvent( [=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); }); diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 0afd7c7849..f5d5f6341b 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,7 @@ #include "common/thread.h" #include "core/file_sys/registered_cache.h" -#include "uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/compatibility_list.h" #include "yuzu/play_time_manager.h" @@ -45,8 +48,7 @@ public: QVector& game_dirs_, const CompatibilityList& compatibility_list_, const PlayTime::PlayTimeManager& play_time_manager_, - Core::System& system_, - const bool cached = true); + Core::System& system_); ~GameListWorker() override; /// Starts the processing of directory tree information. @@ -95,6 +97,4 @@ private: Common::Event processing_completed; Core::System& system; - - const bool cached; }; diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index 1931dcd1f6..31932e6f43 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ #include "hid_core/frontend/emulated_controller.h" #include "yuzu/hotkeys.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" HotkeyRegistry::HotkeyRegistry() = default; HotkeyRegistry::~HotkeyRegistry() = default; diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp index 673bbaa836..e6f1392ce0 100644 --- a/src/yuzu/install_dialog.cpp +++ b/src/yuzu/install_dialog.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,7 +11,7 @@ #include #include #include "yuzu/install_dialog.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) { file_list = new QListWidget(this); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e23e9a6a48..d7d4e94ab7 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1,18 +1,19 @@ // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later -#include -#include -#include -#include -#include -#include #include "core/hle/service/am/applet_manager.h" #include "core/loader/nca.h" #include "core/tools/renderdoc.h" #include "frontend_common/firmware_manager.h" - -#include +#include "qt_common/qt_common.h" +#include "qt_common/qt_content_util.h" +#include "qt_common/qt_game_util.h" +#include "qt_common/qt_meta.h" +#include "qt_common/qt_path_util.h" +#include +#include +#include +#include #ifdef __APPLE__ #include // for chdir @@ -39,21 +40,16 @@ #include "configuration/configure_input.h" #include "configuration/configure_per_game.h" #include "configuration/configure_tas.h" -#include "core/core_timing.h" #include "core/file_sys/romfs_factory.h" -#include "core/file_sys/vfs/vfs.h" -#include "core/file_sys/vfs/vfs_real.h" #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/general.h" #include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/software_keyboard.h" -#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/frontend/applets.h" -#include "core/hle/service/set/system_settings_server.h" #include "frontend_common/content_manager.h" -#include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" +#include "hid_core/frontend/emulated_controller.h" #include "yuzu/multiplayer/state.h" #include "yuzu/util/controller_navigation.h" @@ -67,9 +63,8 @@ // These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows // defines. -static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( - const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::OpenMode mode) { - return vfs->CreateDirectory(path, mode); +static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(const std::string& path, FileSys::OpenMode mode) { + return QtCommon::vfs->CreateDirectory(path, mode); } static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir, @@ -118,7 +113,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "common/scm_rev.h" #include "common/scope_exit.h" #ifdef _WIN32 -#include +#include "core/core_timing.h" #include "common/windows/timer_resolution.h" #endif #ifdef ARCHITECTURE_x86_64 @@ -137,6 +132,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/file_sys/savedata_factory.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/sm/sm.h" @@ -156,7 +152,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/compatibility_list.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_input_per_game.h" -#include "yuzu/configuration/qt_config.h" +#include "qt_common/qt_config.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/wait_tree.h" @@ -164,13 +160,12 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/discord.h" #include "yuzu/game_list.h" #include "yuzu/game_list_p.h" -#include "yuzu/hotkeys.h" #include "yuzu/install_dialog.h" #include "yuzu/loading_screen.h" #include "yuzu/main.h" #include "yuzu/play_time_manager.h" #include "yuzu/startup_checks.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/util/clickable_label.h" #include "yuzu/vk_device_info.h" @@ -299,14 +294,6 @@ enum class CalloutFlag : uint32_t { DRDDeprecation = 0x2, }; -/** - * Some games perform worse or straight-up don't work with updates, - * so this tracks which games are bad in this regard. - */ -constexpr std::array bad_update_games{ - 0x0100F2C0115B6000 // Tears of the Kingdom -}; - const int GMainWindow::max_recent_files_item; static void RemoveCachedContents() { @@ -389,6 +376,7 @@ static void OverrideWindowsFont() { #endif #ifndef _WIN32 +// TODO(crueter): carboxyl does this, is it needed in qml? inline static bool isDarkMode() { #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) const auto scheme = QGuiApplication::styleHints()->colorScheme(); @@ -403,10 +391,10 @@ inline static bool isDarkMode() { #endif // _WIN32 GMainWindow::GMainWindow(bool has_broken_vulkan) - : ui{std::make_unique()}, system{std::make_unique()}, - input_subsystem{std::make_shared()}, user_data_migrator{this}, - vfs{std::make_shared()}, - provider{std::make_unique()} { + : ui{std::make_unique()}, + input_subsystem{std::make_shared()}, user_data_migrator{this} { + QtCommon::Init(this); + Common::FS::CreateEdenPaths(); this->config = std::make_unique(); @@ -437,7 +425,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) UISettings::RestoreWindowState(config); - system->Initialize(); + QtCommon::system->Initialize(); Common::Log::Initialize(); Common::Log::Start(); @@ -459,11 +447,11 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); discord_rpc->Update(); - play_time_manager = std::make_unique(system->GetProfileManager()); + play_time_manager = std::make_unique(QtCommon::system->GetProfileManager()); Network::Init(); - RegisterMetaTypes(); + QtCommon::Meta::RegisterMetaTypes(); InitializeWidgets(); InitializeDebugWidgets(); @@ -476,7 +464,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) ConnectMenuEvents(); ConnectWidgetEvents(); - system->HIDCore().ReloadInputDevices(); + QtCommon::system->HIDCore().ReloadInputDevices(); controller_dialog->refreshConfiguration(); const auto branch_name = std::string(Common::g_scm_branch); @@ -520,7 +508,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) std::chrono::duration_cast>( Common::Windows::SetCurrentTimerResolutionToMaximum()) .count()); - system->CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution()); + QtCommon::system->CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution()); #endif UpdateWindowTitle(); @@ -546,10 +534,10 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) } #endif - system->SetContentProvider(std::make_unique()); - system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, - provider.get()); - system->GetFileSystemController().CreateFactories(*vfs); + QtCommon::system->SetContentProvider(std::make_unique()); + QtCommon::system->RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + QtCommon::provider.get()); + QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs); // Remove cached contents generated during the previous session RemoveCachedContents(); @@ -559,7 +547,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) game_list->LoadCompatibilityList(); // force reload on first load to ensure add-ons get updated - game_list->PopulateAsync(UISettings::values.game_dirs, false); + game_list->PopulateAsync(UISettings::values.game_dirs); // make sure menubar has the arrow cursor instead of inheriting from this ui->menubar->setCursor(QCursor()); @@ -663,7 +651,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) if (!argument_ok) { // try to look it up by username, only finds the first username that matches. const std::string user_arg_str = args[user_arg_idx].toStdString(); - const auto user_idx = system->GetProfileManager().GetUserIndex(user_arg_str); + const auto user_idx = QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str); if (user_idx == std::nullopt) { LOG_ERROR(Frontend, "Invalid user argument"); @@ -673,7 +661,7 @@ GMainWindow::GMainWindow(bool has_broken_vulkan) selected_user = user_idx.value(); } - if (!system->GetProfileManager().UserExistsIndex(selected_user)) { + if (!QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) { LOG_ERROR(Frontend, "Selected user doesn't exist"); continue; } @@ -734,65 +722,6 @@ GMainWindow::~GMainWindow() { #endif } -void GMainWindow::RegisterMetaTypes() { - // Register integral and floating point types - qRegisterMetaType("u8"); - qRegisterMetaType("u16"); - qRegisterMetaType("u32"); - qRegisterMetaType("u64"); - qRegisterMetaType("u128"); - qRegisterMetaType("s8"); - qRegisterMetaType("s16"); - qRegisterMetaType("s32"); - qRegisterMetaType("s64"); - qRegisterMetaType("f32"); - qRegisterMetaType("f64"); - - // Register string types - qRegisterMetaType("std::string"); - qRegisterMetaType("std::wstring"); - qRegisterMetaType("std::u8string"); - qRegisterMetaType("std::u16string"); - qRegisterMetaType("std::u32string"); - qRegisterMetaType("std::string_view"); - qRegisterMetaType("std::wstring_view"); - qRegisterMetaType("std::u8string_view"); - qRegisterMetaType("std::u16string_view"); - qRegisterMetaType("std::u32string_view"); - - // Register applet types - - // Cabinet Applet - qRegisterMetaType("Core::Frontend::CabinetParameters"); - qRegisterMetaType>( - "std::shared_ptr"); - - // Controller Applet - qRegisterMetaType("Core::Frontend::ControllerParameters"); - - // Profile Select Applet - qRegisterMetaType( - "Core::Frontend::ProfileSelectParameters"); - - // Software Keyboard Applet - qRegisterMetaType( - "Core::Frontend::KeyboardInitializeParameters"); - qRegisterMetaType( - "Core::Frontend::InlineAppearParameters"); - qRegisterMetaType("Core::Frontend::InlineTextParameters"); - qRegisterMetaType("Service::AM::Frontend::SwkbdResult"); - qRegisterMetaType( - "Service::AM::Frontend::SwkbdTextCheckResult"); - qRegisterMetaType( - "Service::AM::Frontend::SwkbdReplyType"); - - // Web Browser Applet - qRegisterMetaType("Service::AM::Frontend::WebExitReason"); - - // Register loader types - qRegisterMetaType("Core::SystemResultStatus"); -} - void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, std::shared_ptr nfp_device) { cabinet_applet = @@ -823,7 +752,7 @@ void GMainWindow::AmiiboSettingsRequestExit() { void GMainWindow::ControllerSelectorReconfigureControllers( const Core::Frontend::ControllerParameters& parameters) { controller_applet = - new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *system); + new QtControllerSelectorDialog(this, parameters, input_subsystem.get(), *QtCommon::system); SCOPE_EXIT { controller_applet->deleteLater(); controller_applet = nullptr; @@ -836,8 +765,8 @@ void GMainWindow::ControllerSelectorReconfigureControllers( bool is_success = controller_applet->exec() != QDialog::Rejected; // Don't forget to apply settings. - system->HIDCore().DisableAllControllerConfiguration(); - system->ApplySettings(); + QtCommon::system->HIDCore().DisableAllControllerConfiguration(); + QtCommon::system->ApplySettings(); config->SaveAllValues(); UpdateStatusButtons(); @@ -853,7 +782,7 @@ void GMainWindow::ControllerSelectorRequestExit() { void GMainWindow::ProfileSelectorSelectProfile( const Core::Frontend::ProfileSelectParameters& parameters) { - profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters); + profile_select_applet = new QtProfileSelectionDialog(*QtCommon::system, this, parameters); SCOPE_EXIT { profile_select_applet->deleteLater(); profile_select_applet = nullptr; @@ -868,7 +797,7 @@ void GMainWindow::ProfileSelectorSelectProfile( return; } - const auto uuid = system->GetProfileManager().GetUser( + const auto uuid = QtCommon::system->GetProfileManager().GetUser( static_cast(profile_select_applet->GetIndex())); if (!uuid.has_value()) { emit ProfileSelectorFinishedSelection(std::nullopt); @@ -891,7 +820,7 @@ void GMainWindow::SoftwareKeyboardInitialize( return; } - software_keyboard = new QtSoftwareKeyboardDialog(render_window, *system, is_inline, + software_keyboard = new QtSoftwareKeyboardDialog(render_window, *QtCommon::system, is_inline, std::move(initialize_parameters)); if (is_inline) { @@ -1008,7 +937,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, return; } - web_applet = new QtNXWebEngineView(this, *system, input_subsystem.get()); + web_applet = new QtNXWebEngineView(this, *QtCommon::system, input_subsystem.get()); ui->action_Pause->setEnabled(false); ui->action_Restart->setEnabled(false); @@ -1152,10 +1081,10 @@ void GMainWindow::InitializeWidgets() { #ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING ui->action_Report_Compatibility->setVisible(true); #endif - render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *system); + render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem, *QtCommon::system); render_window->hide(); - game_list = new GameList(vfs, provider.get(), *play_time_manager, *system, this); + game_list = new GameList(QtCommon::vfs, QtCommon::provider.get(), *play_time_manager, *QtCommon::system, this); ui->horizontalLayout->addWidget(game_list); game_list_placeholder = new GameListPlaceholder(this); @@ -1165,7 +1094,7 @@ void GMainWindow::InitializeWidgets() { loading_screen = new LoadingScreen(this); loading_screen->hide(); ui->horizontalLayout->addWidget(loading_screen); - connect(loading_screen, &LoadingScreen::Hidden, [&] { + connect(loading_screen, &LoadingScreen::Hidden, this, [&] { loading_screen->Clear(); if (emulation_running) { render_window->show(); @@ -1174,7 +1103,7 @@ void GMainWindow::InitializeWidgets() { }); multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, - ui->action_Show_Room, *system); + ui->action_Show_Room, *QtCommon::system); multiplayer_state->setVisible(false); // Create status bar @@ -1428,12 +1357,12 @@ void GMainWindow::InitializeWidgets() { void GMainWindow::InitializeDebugWidgets() { QMenu* debug_menu = ui->menu_View_Debugging; - waitTreeWidget = new WaitTreeWidget(*system, this); + waitTreeWidget = new WaitTreeWidget(*QtCommon::system, this); addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget); waitTreeWidget->hide(); debug_menu->addAction(waitTreeWidget->toggleViewAction()); - controller_dialog = new ControllerDialog(system->HIDCore(), input_subsystem, this); + controller_dialog = new ControllerDialog(QtCommon::system->HIDCore(), input_subsystem, this); controller_dialog->hide(); debug_menu->addAction(controller_dialog->toggleViewAction()); @@ -1473,7 +1402,7 @@ void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name this->addAction(action); - auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* controller = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller); connect( @@ -1518,7 +1447,7 @@ void GMainWindow::InitializeHotkeys() { const auto connect_shortcut = [&](const QString& action_name, const Fn& function) { const auto* hotkey = hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this); - auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* controller = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); const auto* controller_hotkey = hotkey_registry.GetControllerHotkey( main_window.toStdString(), action_name.toStdString(), controller); connect(hotkey, &QShortcut::activated, this, function); @@ -1542,9 +1471,9 @@ void GMainWindow::InitializeHotkeys() { connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); }); - connect_shortcut(QStringLiteral("Toggle Renderdoc Capture"), [this] { + connect_shortcut(QStringLiteral("Toggle Renderdoc Capture"), [] { if (Settings::values.enable_renderdoc_hotkey) { - system->GetRenderdocAPI().ToggleCapture(); + QtCommon::system->GetRenderdocAPI().ToggleCapture(); } }); connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] { @@ -1634,8 +1563,9 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); - connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, - &GMainWindow::OnTransferableShaderCacheOpenFile); + connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, [this](u64 program_id) { + QtCommon::Path::OpenShaderCache(program_id, this); + }); connect(game_list, &GameList::RemoveInstalledEntryRequested, this, &GMainWindow::OnGameListRemoveInstalledEntry); connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); @@ -1949,13 +1879,13 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa return false; } - system->SetFilesystem(vfs); + QtCommon::system->SetFilesystem(QtCommon::vfs); if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) { - system->GetUserChannel().clear(); + QtCommon::system->GetUserChannel().clear(); } - system->SetFrontendAppletSet({ + QtCommon::system->SetFrontendAppletSet({ std::make_unique(*this), // Amiibo Settings (UISettings::values.controller_applet_disabled.GetValue() == true) ? nullptr @@ -1970,89 +1900,21 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa nullptr, // Net Connect }); - /** Game Updates check */ + /** firmware check */ - // yuzu's configuration doesn't actually support lists so this is a bit hacky - QSettings settings; - QStringList currentIgnored = settings.value("ignoredBadUpdates", {}).toStringList(); - - if (std::find(bad_update_games.begin(), bad_update_games.end(), params.program_id) != - bad_update_games.end() && - !currentIgnored.contains(QString::number(params.program_id))) { - QMessageBox* msg = new QMessageBox(this); - msg->setWindowTitle(tr("Game Updates Warning")); - msg->setIcon(QMessageBox::Warning); - msg->setText( - tr("The game you are trying to launch is known to have performance or booting " - "issues when updates are applied. Please try increasing the memory layout to " - "6GB or 8GB if any issues occur.

Press \"OK\" to continue launching, or " - "\"Cancel\" to cancel the launch.")); - - msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - - QCheckBox* dontShowAgain = new QCheckBox(msg); - dontShowAgain->setText(tr("Don't show again for this game")); - msg->setCheckBox(dontShowAgain); - - int result = msg->exec(); - - // wtf - QMessageBox::ButtonRole role = - msg->buttonRole(msg->button((QMessageBox::StandardButton)result)); - - switch (role) { - case QMessageBox::RejectRole: - return false; - case QMessageBox::AcceptRole: - default: - if (dontShowAgain->isChecked()) { - currentIgnored << QString::number(params.program_id); - settings.setValue("ignoredBadUpdates", currentIgnored); - settings.sync(); - } - break; - } - } - - if (FirmwareManager::GameRequiresFirmware(params.program_id) && - !FirmwareManager::CheckFirmwarePresence(*system)) { - QMessageBox* msg = new QMessageBox(this); - msg->setWindowTitle(tr("Game Requires Firmware")); - msg->setIcon(QMessageBox::Warning); - msg->setText( - tr("The game you are trying to launch requires firmware to boot or to get past the " - "opening menu. Please " - "dump and install firmware, or press \"OK\" to launch anyways.")); - - msg->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - - int exec_result = msg->exec(); - - QMessageBox::ButtonRole role = - msg->buttonRole(msg->button((QMessageBox::StandardButton)exec_result)); - - switch (role) { - case QMessageBox::RejectRole: - return false; - case QMessageBox::AcceptRole: - default: - break; - } - } - - if (!OnCheckNcaVerification()) { + if (!QtCommon::Content::CheckGameFirmware(params.program_id, this)) { return false; } /** Exec */ const Core::SystemResultStatus result{ - system->Load(*render_window, filename.toStdString(), params)}; + QtCommon::system->Load(*render_window, filename.toStdString(), params)}; const auto drd_callout = (UISettings::values.callout_flags.GetValue() & static_cast(CalloutFlag::DRDDeprecation)) == 0; if (result == Core::SystemResultStatus::Success && - system->GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && + QtCommon::system->GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && drd_callout) { UISettings::values.callout_flags = UISettings::values.callout_flags.GetValue() | static_cast(CalloutFlag::DRDDeprecation); @@ -2117,7 +1979,7 @@ bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletPa bool GMainWindow::SelectAndSetCurrentUser( const Core::Frontend::ProfileSelectParameters& parameters) { - QtProfileSelectionDialog dialog(*system, this, parameters); + QtProfileSelectionDialog dialog(*QtCommon::system, this, parameters); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); @@ -2132,12 +1994,12 @@ bool GMainWindow::SelectAndSetCurrentUser( void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { // Ensure all NCAs are registered before launching the game - const auto file = vfs->OpenFile(filepath, FileSys::OpenMode::Read); + const auto file = QtCommon::vfs->OpenFile(filepath, FileSys::OpenMode::Read); if (!file) { return; } - auto loader = Loader::GetLoader(*system, file); + auto loader = Loader::GetLoader(*QtCommon::system, file); if (!loader) { return; } @@ -2150,7 +2012,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { u64 program_id = 0; const auto res2 = loader->ReadProgramId(program_id); if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { - provider->AddEntry(FileSys::TitleType::Application, + QtCommon::provider->AddEntry(FileSys::TitleType::Application, FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), program_id, file); } else if (res2 == Loader::ResultStatus::Success && @@ -2160,7 +2022,7 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { : FileSys::XCI{file}.GetSecurePartitionNSP(); for (const auto& title : nsp->GetNCAs()) { for (const auto& entry : title.second) { - provider->AddEntry(entry.first.first, entry.first.second, title.first, + QtCommon::provider->AddEntry(entry.first.first, entry.first.second, title.first, entry.second->GetBaseFile()); } } @@ -2186,8 +2048,8 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP last_filename_booted = filename; ConfigureFilesystemProvider(filename.toStdString()); - const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); - const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index); + const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, filename.toUtf8().constData()); + const auto loader = Loader::GetLoader(*QtCommon::system, v_file, params.program_id, params.program_index); if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && type == StartGameType::Normal) { @@ -2198,8 +2060,8 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig); - system->HIDCore().ReloadInputDevices(); - system->ApplySettings(); + QtCommon::system->HIDCore().ReloadInputDevices(); + QtCommon::system->ApplySettings(); } Settings::LogSettings(); @@ -2225,19 +2087,19 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP return; } - system->SetShuttingDown(false); + QtCommon::system->SetShuttingDown(false); game_list->setDisabled(true); // Create and start the emulation thread - emu_thread = std::make_unique(*system); + emu_thread = std::make_unique(*QtCommon::system); emit EmulationStarting(emu_thread.get()); emu_thread->start(); // Register an ExecuteProgram callback such that Core can execute a sub-program - system->RegisterExecuteProgramCallback( + QtCommon::system->RegisterExecuteProgramCallback( [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); - system->RegisterExitCallback([this] { + QtCommon::system->RegisterExitCallback([this] { emu_thread->ForceStop(); render_window->Exit(); }); @@ -2277,11 +2139,11 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP std::string title_name; std::string title_version; - const auto res = system->GetGameName(title_name); + const auto res = QtCommon::system->GetGameName(title_name); - const auto metadata = [this, title_id] { - const FileSys::PatchManager pm(title_id, system->GetFileSystemController(), - system->GetContentProvider()); + const auto metadata = [title_id] { + const FileSys::PatchManager pm(title_id, QtCommon::system->GetFileSystemController(), + QtCommon::system->GetContentProvider()); return pm.GetControlMetadata(); }(); if (metadata.first != nullptr) { @@ -2293,16 +2155,16 @@ void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletP std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())} .filename()); } - const bool is_64bit = system->Kernel().ApplicationProcess()->Is64Bit(); + const bool is_64bit = QtCommon::system->Kernel().ApplicationProcess()->Is64Bit(); const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); title_name = tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") .arg(QString::fromStdString(title_name), instruction_set_suffix) .toStdString(); LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); - const auto gpu_vendor = system->GPU().Renderer().GetDeviceVendor(); + const auto gpu_vendor = QtCommon::system->GPU().Renderer().GetDeviceVendor(); UpdateWindowTitle(title_name, title_version, gpu_vendor); - loading_screen->Prepare(system->GetAppLoader()); + loading_screen->Prepare(QtCommon::system->GetAppLoader()); loading_screen->show(); emulation_running = true; @@ -2330,11 +2192,11 @@ bool GMainWindow::OnShutdownBegin() { // Disable unlimited frame rate Settings::values.use_speed_limit.SetValue(true); - if (system->IsShuttingDown()) { + if (QtCommon::system->IsShuttingDown()) { return false; } - system->SetShuttingDown(true); + QtCommon::system->SetShuttingDown(true); discord_rpc->Pause(); RequestGameExit(); @@ -2345,9 +2207,9 @@ bool GMainWindow::OnShutdownBegin() { int shutdown_time = 1000; - if (system->DebuggerEnabled()) { + if (QtCommon::system->DebuggerEnabled()) { shutdown_time = 0; - } else if (system->GetExitLocked()) { + } else if (QtCommon::system->GetExitLocked()) { shutdown_time = 5000; } @@ -2365,7 +2227,7 @@ bool GMainWindow::OnShutdownBegin() { } void GMainWindow::OnShutdownBeginDialog() { - shutdown_dialog = new OverlayDialog(this, *system, QString{}, tr("Closing software..."), + shutdown_dialog = new OverlayDialog(this, *QtCommon::system, QString{}, tr("Closing software..."), QString{}, QString{}, Qt::AlignHCenter | Qt::AlignVCenter); shutdown_dialog->open(); } @@ -2417,10 +2279,10 @@ void GMainWindow::OnEmulationStopped() { OnTasStateChanged(); render_window->FinalizeCamera(); - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::None); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::None); // Enable all controllers - system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); + QtCommon::system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); render_window->removeEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, false); @@ -2449,8 +2311,8 @@ void GMainWindow::OnEmulationStopped() { // Enable game list game_list->setEnabled(true); - Settings::RestoreGlobalState(system->IsPoweredOn()); - system->HIDCore().ReloadInputDevices(); + Settings::RestoreGlobalState(QtCommon::system->IsPoweredOn()); + QtCommon::system->HIDCore().ReloadInputDevices(); UpdateStatusButtons(); } @@ -2459,6 +2321,7 @@ void GMainWindow::ShutdownGame() { return; } + // TODO(crueter): make this common as well (frontend_common?) play_time_manager->Stop(); OnShutdownBegin(); OnEmulationStopTimeExpired(); @@ -2503,21 +2366,22 @@ void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) { BootGame(game_path, params); } +// TODO(crueter): Common profile selector void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, const std::string& game_path) { std::filesystem::path path; QString open_target; - const auto [user_save_size, device_save_size] = [this, &game_path, &program_id] { - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), - system->GetContentProvider()}; + const auto [user_save_size, device_save_size] = [&game_path, &program_id] { + const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(), + QtCommon::system->GetContentProvider()}; const auto control = pm.GetControlMetadata().first; if (control != nullptr) { return std::make_pair(control->GetDefaultNormalSaveSize(), control->GetDeviceSaveDataSize()); } else { - const auto file = Core::GetGameFileFromPath(vfs, game_path); - const auto loader = Loader::GetLoader(*system, file); + const auto file = Core::GetGameFileFromPath(QtCommon::vfs, game_path); + const auto loader = Loader::GetLoader(*QtCommon::system, file); FileSys::NACP nacp{}; loader->ReadControlData(nacp); @@ -2536,7 +2400,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target open_target = tr("Save Data"); const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir); auto vfs_nand_dir = - vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); + QtCommon::vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); if (has_user_save) { // User save data @@ -2547,7 +2411,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target .display_options = {}, .purpose = Service::AM::Frontend::UserSelectionPurpose::General, }; - QtProfileSelectionDialog dialog(*system, this, parameters); + QtProfileSelectionDialog dialog(*QtCommon::system, this, parameters); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); dialog.setWindowModality(Qt::WindowModal); @@ -2565,7 +2429,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target } const auto user_id = - system->GetProfileManager().GetUser(static_cast(index)); + QtCommon::system->GetProfileManager().GetUser(static_cast(index)); ASSERT(user_id); const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( @@ -2611,19 +2475,6 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); } -void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { - const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); - const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)}; - if (!Common::FS::CreateDirs(shader_cache_folder_path)) { - QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), - tr("Failed to create the shader cache directory for this title.")); - return; - } - const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)}; - const auto qt_shader_cache_path = QString::fromStdString(shader_path_string); - QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); -} - static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& dialog, const FileSys::VirtualDir& src, const FileSys::VirtualDir& dest, bool full) { @@ -2686,26 +2537,17 @@ static bool RomFSRawCopy(size_t total_size, size_t& read_size, QProgressDialog& return true; } -QString GMainWindow::GetGameListErrorRemoving(InstalledEntryType type) const { - switch (type) { - case InstalledEntryType::Game: - return tr("Error Removing Contents"); - case InstalledEntryType::Update: - return tr("Error Removing Update"); - case InstalledEntryType::AddOnContent: - return tr("Error Removing DLC"); - default: - return QStringLiteral("Error Removing "); - } -} -void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { +// TODO(crueter): All this can be transfered to qt_common +// Aldoe I need to decide re: message boxes for QML +// translations_common? strings_common? qt_strings? who knows +void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, QtCommon::Game::InstalledEntryType type) { const QString entry_question = [type] { switch (type) { - case InstalledEntryType::Game: + case QtCommon::Game::InstalledEntryType::Game: return tr("Remove Installed Game Contents?"); - case InstalledEntryType::Update: + case QtCommon::Game::InstalledEntryType::Update: return tr("Remove Installed Game Update?"); - case InstalledEntryType::AddOnContent: + case QtCommon::Game::InstalledEntryType::AddOnContent: return tr("Remove Installed Game DLC?"); default: return QStringLiteral("Remove Installed Game ?"); @@ -2717,18 +2559,19 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT return; } + // TODO(crueter): move this to QtCommon (populate async?) switch (type) { - case InstalledEntryType::Game: - RemoveBaseContent(program_id, type); + case QtCommon::Game::InstalledEntryType::Game: + QtCommon::Game::RemoveBaseContent(program_id, type); [[fallthrough]]; - case InstalledEntryType::Update: - RemoveUpdateContent(program_id, type); - if (type != InstalledEntryType::Game) { + case QtCommon::Game::InstalledEntryType::Update: + QtCommon::Game::RemoveUpdateContent(program_id, type); + if (type != QtCommon::Game::InstalledEntryType::Game) { break; } [[fallthrough]]; - case InstalledEntryType::AddOnContent: - RemoveAddOnContent(program_id, type); + case QtCommon::Game::InstalledEntryType::AddOnContent: + QtCommon::Game::RemoveAddOnContent(program_id, type); break; } Common::FS::RemoveDirRecursively(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / @@ -2736,55 +2579,19 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT game_list->PopulateAsync(UISettings::values.game_dirs); } -void GMainWindow::RemoveBaseContent(u64 program_id, InstalledEntryType type) { - const auto res = - ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id); - if (res) { - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed the installed base game.")); - } else { - QMessageBox::warning( - this, GetGameListErrorRemoving(type), - tr("The base game is not installed in the NAND and cannot be removed.")); - } -} - -void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { - const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id); - if (res) { - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed the installed update.")); - } else { - QMessageBox::warning(this, GetGameListErrorRemoving(type), - tr("There is no update installed for this title.")); - } -} - -void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { - const size_t count = ContentManager::RemoveAllDLC(*system, program_id); - if (count == 0) { - QMessageBox::warning(this, GetGameListErrorRemoving(type), - tr("There are no DLC installed for this title.")); - return; - } - - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed %1 installed DLC.").arg(count)); -} - -void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, +void GMainWindow::OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListRemoveTarget target, const std::string& game_path) { const QString question = [target] { switch (target) { - case GameListRemoveTarget::GlShaderCache: + case QtCommon::Game::GameListRemoveTarget::GlShaderCache: return tr("Delete OpenGL Transferable Shader Cache?"); - case GameListRemoveTarget::VkShaderCache: + case QtCommon::Game::GameListRemoveTarget::VkShaderCache: return tr("Delete Vulkan Transferable Shader Cache?"); - case GameListRemoveTarget::AllShaderCache: + case QtCommon::Game::GameListRemoveTarget::AllShaderCache: return tr("Delete All Transferable Shader Caches?"); - case GameListRemoveTarget::CustomConfiguration: + case QtCommon::Game::GameListRemoveTarget::CustomConfiguration: return tr("Remove Custom Game Configuration?"); - case GameListRemoveTarget::CacheStorage: + case QtCommon::Game::GameListRemoveTarget::CacheStorage: return tr("Remove Cache Storage?"); default: return QString{}; @@ -2797,20 +2604,20 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ } switch (target) { - case GameListRemoveTarget::VkShaderCache: - RemoveVulkanDriverPipelineCache(program_id); + case QtCommon::Game::GameListRemoveTarget::VkShaderCache: + QtCommon::Game::RemoveVulkanDriverPipelineCache(program_id); [[fallthrough]]; - case GameListRemoveTarget::GlShaderCache: - RemoveTransferableShaderCache(program_id, target); + case QtCommon::Game::GameListRemoveTarget::GlShaderCache: + QtCommon::Game::RemoveTransferableShaderCache(program_id, target); break; - case GameListRemoveTarget::AllShaderCache: - RemoveAllTransferableShaderCaches(program_id); + case QtCommon::Game::GameListRemoveTarget::AllShaderCache: + QtCommon::Game::RemoveAllTransferableShaderCaches(program_id); break; - case GameListRemoveTarget::CustomConfiguration: - RemoveCustomConfiguration(program_id, game_path); + case QtCommon::Game::GameListRemoveTarget::CustomConfiguration: + QtCommon::Game::RemoveCustomConfiguration(program_id, game_path); break; - case GameListRemoveTarget::CacheStorage: - RemoveCacheStorage(program_id); + case QtCommon::Game::GameListRemoveTarget::CacheStorage: + QtCommon::Game::RemoveCacheStorage(program_id); break; } } @@ -2826,107 +2633,6 @@ void GMainWindow::OnGameListRemovePlayTimeData(u64 program_id) { game_list->PopulateAsync(UISettings::values.game_dirs); } -void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) { - const auto target_file_name = [target] { - switch (target) { - case GameListRemoveTarget::GlShaderCache: - return "opengl.bin"; - case GameListRemoveTarget::VkShaderCache: - return "vulkan.bin"; - default: - return ""; - } - }(); - const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); - const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); - const auto target_file = shader_cache_folder_path / target_file_name; - - if (!Common::FS::Exists(target_file)) { - QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), - tr("A shader cache for this title does not exist.")); - return; - } - if (Common::FS::RemoveFile(target_file)) { - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed the transferable shader cache.")); - } else { - QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), - tr("Failed to remove the transferable shader cache.")); - } -} - -void GMainWindow::RemoveVulkanDriverPipelineCache(u64 program_id) { - static constexpr std::string_view target_file_name = "vulkan_pipelines.bin"; - - const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); - const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); - const auto target_file = shader_cache_folder_path / target_file_name; - - if (!Common::FS::Exists(target_file)) { - return; - } - if (!Common::FS::RemoveFile(target_file)) { - QMessageBox::warning(this, tr("Error Removing Vulkan Driver Pipeline Cache"), - tr("Failed to remove the driver pipeline cache.")); - } -} - -void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { - const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); - const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); - - if (!Common::FS::Exists(program_shader_cache_dir)) { - QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"), - tr("A shader cache for this title does not exist.")); - return; - } - if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) { - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed the transferable shader caches.")); - } else { - QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"), - tr("Failed to remove the transferable shader cache directory.")); - } -} - -void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& game_path) { - const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); - const auto config_file_name = - program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini") - : fmt::format("{:016X}.ini", program_id); - const auto custom_config_file_path = - Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) / "custom" / config_file_name; - - if (!Common::FS::Exists(custom_config_file_path)) { - QMessageBox::warning(this, tr("Error Removing Custom Configuration"), - tr("A custom configuration for this title does not exist.")); - return; - } - - if (Common::FS::RemoveFile(custom_config_file_path)) { - QMessageBox::information(this, tr("Successfully Removed"), - tr("Successfully removed the custom game configuration.")); - } else { - QMessageBox::warning(this, tr("Error Removing Custom Configuration"), - tr("Failed to remove the custom game configuration.")); - } -} - -void GMainWindow::RemoveCacheStorage(u64 program_id) { - const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir); - auto vfs_nand_dir = - vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); - - const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( - {}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Cache, - 0 /* program_id */, {}, 0); - - const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); - - // Not an error if it wasn't cleared. - Common::FS::RemoveDirRecursively(path); -} - void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target) { const auto failed = [this] { @@ -2936,7 +2642,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa }; const auto loader = - Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read)); + Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read)); if (loader == nullptr) { failed(); return; @@ -2945,7 +2651,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa FileSys::VirtualFile packed_update_raw{}; loader->ReadUpdateRaw(packed_update_raw); - const auto& installed = system->GetContentProvider(); + const auto& installed = QtCommon::system->GetContentProvider(); u64 title_id{}; u8 raw_type{}; @@ -2977,14 +2683,14 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); - const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; + const FileSys::PatchManager pm{title_id, QtCommon::system->GetFileSystemController(), installed}; auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); - const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::OpenMode::ReadWrite); + const auto out = VfsFilesystemCreateDirectoryWrapper(path, FileSys::OpenMode::ReadWrite); if (out == nullptr) { failed(); - vfs->DeleteDirectory(path); + QtCommon::vfs->DeleteDirectory(path); return; } @@ -2998,7 +2704,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa selections, 0, false, &ok); if (!ok) { failed(); - vfs->DeleteDirectory(path); + QtCommon::vfs->DeleteDirectory(path); return; } @@ -3038,41 +2744,13 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa } else { progress.close(); failed(); - vfs->DeleteDirectory(path); + QtCommon::vfs->DeleteDirectory(path); } } +// END void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { - const auto NotImplemented = [this] { - QMessageBox::warning(this, tr("Integrity verification couldn't be performed!"), - tr("File contents were not checked for validity.")); - }; - - QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); - progress.setWindowModality(Qt::WindowModal); - progress.setMinimumDuration(100); - progress.setAutoClose(false); - progress.setAutoReset(false); - - const auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { - progress.setValue(static_cast((processed_size * 100) / total_size)); - return progress.wasCanceled(); - }; - - const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback); - progress.close(); - switch (result) { - case ContentManager::GameVerificationResult::Success: - QMessageBox::information(this, tr("Integrity verification succeeded!"), - tr("The operation completed successfully.")); - break; - case ContentManager::GameVerificationResult::Failed: - QMessageBox::critical(this, tr("Integrity verification failed!"), - tr("File contents may be corrupt.")); - break; - case ContentManager::GameVerificationResult::NotImplemented: - NotImplemented(); - } + QtCommon::Content::VerifyGameContents(game_path); } void GMainWindow::OnGameListCopyTID(u64 program_id) { @@ -3093,172 +2771,12 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QUrl(QStringLiteral("https://eden-emulator.github.io/game/") + directory)); } -bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, - const std::string& comment, - const std::filesystem::path& icon_path, - const std::filesystem::path& command, - const std::string& arguments, const std::string& categories, - const std::string& keywords, const std::string& name) try { -#ifdef _WIN32 // Windows - HRESULT hr = CoInitialize(nullptr); - if (FAILED(hr)) { - LOG_ERROR(Frontend, "CoInitialize failed"); - return false; - } - SCOPE_EXIT { - CoUninitialize(); - }; - IShellLinkW* ps1 = nullptr; - IPersistFile* persist_file = nullptr; - SCOPE_EXIT { - if (persist_file != nullptr) { - persist_file->Release(); - } - if (ps1 != nullptr) { - ps1->Release(); - } - }; - HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - reinterpret_cast(&ps1)); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); - return false; - } - hres = ps1->SetPath(command.c_str()); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to set path"); - return false; - } - if (!arguments.empty()) { - hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to set arguments"); - return false; - } - } - if (!comment.empty()) { - hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to set description"); - return false; - } - } - if (std::filesystem::is_regular_file(icon_path)) { - hres = ps1->SetIconLocation(icon_path.c_str(), 0); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to set icon location"); - return false; - } - } - hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast(&persist_file)); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to get IPersistFile interface"); - return false; - } - hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE); - if (FAILED(hres)) { - LOG_ERROR(Frontend, "Failed to save shortcut"); - return false; - } - return true; -#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX - std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); - std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); - if (!shortcut_stream.is_open()) { - LOG_ERROR(Frontend, "Failed to create shortcut"); - return false; - } - // TODO: Migrate fmt::print to std::print in futures STD C++ 23. - fmt::print(shortcut_stream, "[Desktop Entry]\n"); - fmt::print(shortcut_stream, "Type=Application\n"); - fmt::print(shortcut_stream, "Version=1.0\n"); - fmt::print(shortcut_stream, "Name={}\n", name); - if (!comment.empty()) { - fmt::print(shortcut_stream, "Comment={}\n", comment); - } - if (std::filesystem::is_regular_file(icon_path)) { - fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); - } - fmt::print(shortcut_stream, "TryExec={}\n", command.string()); - fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); - if (!categories.empty()) { - fmt::print(shortcut_stream, "Categories={}\n", categories); - } - if (!keywords.empty()) { - fmt::print(shortcut_stream, "Keywords={}\n", keywords); - } - return true; -#else // Unsupported platform - return false; -#endif -} catch (const std::exception& e) { - LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); - return false; -} -// Messages in pre-defined message boxes for less code spaghetti -bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { - int result = 0; - QMessageBox::StandardButtons buttons; - switch (imsg) { - case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: - buttons = QMessageBox::Yes | QMessageBox::No; - result = - QMessageBox::information(parent, tr("Create Shortcut"), - tr("Do you want to launch the game in fullscreen?"), buttons); - return result == QMessageBox::Yes; - case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: - QMessageBox::information(parent, tr("Create Shortcut"), - tr("Successfully created a shortcut to %1").arg(game_title)); - return false; - case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: - buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; - result = - QMessageBox::warning(this, tr("Create Shortcut"), - tr("This will create a shortcut to the current AppImage. This may " - "not work well if you update. Continue?"), - buttons); - return result == QMessageBox::Ok; - default: - buttons = QMessageBox::Ok; - QMessageBox::critical(parent, tr("Create Shortcut"), - tr("Failed to create a shortcut to %1").arg(game_title), buttons); - return false; - } -} - -bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, - std::filesystem::path& out_icon_path) { - // Get path to Yuzu icons directory & icon extension - std::string ico_extension = "png"; -#if defined(_WIN32) - out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir); - ico_extension = "ico"; -#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) - out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; -#endif - // Create icons directory if it doesn't exist - if (!Common::FS::CreateDirs(out_icon_path)) { - QMessageBox::critical( - this, tr("Create Icon"), - tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") - .arg(QString::fromStdString(out_icon_path.string())), - QMessageBox::StandardButton::Ok); - out_icon_path.clear(); - return false; - } - - // Create icon file path - out_icon_path /= (program_id == 0 ? fmt::format("eden-{}.{}", game_file_name, ico_extension) - : fmt::format("eden-{:016X}.{}", program_id, ico_extension)); - return true; -} - void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, - GameListShortcutTarget target) { + const QtCommon::Game::ShortcutTarget target) { // Create shortcut std::string arguments = fmt::format("-g \"{:s}\"", game_path); - CreateShortcut(game_path, program_id, "", target, arguments, true); + QtCommon::Game::CreateShortcut(game_path, program_id, "", target, arguments, true); } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { @@ -3313,8 +2831,8 @@ void GMainWindow::OnGameListShowList(bool show) { void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { u64 title_id{}; - const auto v_file = Core::GetGameFileFromPath(vfs, file); - const auto loader = Loader::GetLoader(*system, v_file); + const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, file); + const auto loader = Loader::GetLoader(*QtCommon::system, v_file); if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { QMessageBox::information(this, tr("Properties"), @@ -3441,8 +2959,8 @@ void GMainWindow::OnMenuInstallToNAND() { } return false; }; - future = QtConcurrent::run([this, &file, progress_callback] { - return ContentManager::InstallNSP(*system, *vfs, file.toStdString(), + future = QtConcurrent::run([&file, progress_callback] { + return ContentManager::InstallNSP(*QtCommon::system, *QtCommon::vfs, file.toStdString(), progress_callback); }); @@ -3534,7 +3052,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) { } const bool is_application = index >= static_cast(FileSys::TitleType::Application); - const auto& fs_controller = system->GetFileSystemController(); + const auto& fs_controller = QtCommon::system->GetFileSystemController(); auto* registered_cache = is_application ? fs_controller.GetUserNANDContents() : fs_controller.GetSystemNANDContents(); @@ -3545,7 +3063,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) { } return false; }; - return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache, + return ContentManager::InstallNCA(*QtCommon::vfs, filename.toStdString(), *registered_cache, static_cast(index), progress_callback); } @@ -3574,7 +3092,7 @@ void GMainWindow::OnStartGame() { UpdateMenuState(); OnTasStateChanged(); - play_time_manager->SetProgramId(system->GetApplicationProcessProgramID()); + play_time_manager->SetProgramId(QtCommon::system->GetApplicationProcessProgramID()); play_time_manager->Start(); discord_rpc->Update(); @@ -3585,7 +3103,7 @@ void GMainWindow::OnStartGame() { } void GMainWindow::OnRestartGame() { - if (!system->IsPoweredOn()) { + if (!QtCommon::system->IsPoweredOn()) { return; } @@ -3633,7 +3151,7 @@ void GMainWindow::OnStopGame() { bool GMainWindow::ConfirmShutdownGame() { if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) { - if (system->GetExitLocked()) { + if (QtCommon::system->GetExitLocked()) { if (!ConfirmForceLockedExit()) { return false; } @@ -3645,7 +3163,7 @@ bool GMainWindow::ConfirmShutdownGame() { } else { if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game && - system->GetExitLocked()) { + QtCommon::system->GetExitLocked()) { if (!ConfirmForceLockedExit()) { return false; } @@ -3672,12 +3190,12 @@ void GMainWindow::OnExit() { } void GMainWindow::OnSaveConfig() { - system->ApplySettings(); + QtCommon::system->ApplySettings(); config->SaveAllValues(); } void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { - error_applet = new OverlayDialog(render_window, *system, error_code, error_text, QString{}, + error_applet = new OverlayDialog(render_window, *QtCommon::system, error_code, error_text, QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter); SCOPE_EXIT { error_applet->deleteLater(); @@ -3906,7 +3424,7 @@ void GMainWindow::OnConfigure() { Settings::SetConfiguringGlobal(true); ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get(), - vk_device_records, *system, + vk_device_records, *QtCommon::system, !multiplayer_state->IsHostingPublicRoom()); connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, &GMainWindow::OnLanguageChanged); @@ -4007,7 +3525,7 @@ void GMainWindow::OnConfigure() { UpdateStatusButtons(); controller_dialog->refreshConfiguration(); - system->ApplySettings(); + QtCommon::system->ApplySettings(); } void GMainWindow::OnConfigureTas() { @@ -4015,7 +3533,7 @@ void GMainWindow::OnConfigureTas() { const auto result = dialog.exec(); if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { - Settings::RestoreGlobalState(system->IsPoweredOn()); + Settings::RestoreGlobalState(QtCommon::system->IsPoweredOn()); return; } else if (result == QDialog::Accepted) { dialog.ApplyConfiguration(); @@ -4029,7 +3547,7 @@ void GMainWindow::OnTasStartStop() { } // Disable system buttons to prevent TAS from executing a hotkey - auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* controller = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); controller->ResetSystemButtons(); input_subsystem->GetTas()->StartStop(); @@ -4045,7 +3563,7 @@ void GMainWindow::OnTasRecord() { } // Disable system buttons to prevent TAS from recording a hotkey - auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* controller = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); controller->ResetSystemButtons(); const bool is_recording = input_subsystem->GetTas()->Record(); @@ -4067,8 +3585,8 @@ void GMainWindow::OnTasReset() { void GMainWindow::OnToggleDockedMode() { const bool is_docked = Settings::IsDockedMode(); - auto* player_1 = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - auto* handheld = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); + auto* player_1 = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); + auto* handheld = QtCommon::system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); if (!is_docked && handheld->IsConnected()) { QMessageBox::warning(this, tr("Invalid config detected"), @@ -4083,7 +3601,7 @@ void GMainWindow::OnToggleDockedMode() { Settings::values.use_docked_mode.SetValue(is_docked ? Settings::ConsoleMode::Handheld : Settings::ConsoleMode::Docked); UpdateDockedButton(); - OnDockedModeChanged(is_docked, !is_docked, *system); + OnDockedModeChanged(is_docked, !is_docked, *QtCommon::system); } void GMainWindow::OnToggleGpuAccuracy() { @@ -4100,7 +3618,7 @@ void GMainWindow::OnToggleGpuAccuracy() { } } - system->ApplySettings(); + QtCommon::system->ApplySettings(); UpdateGPUAccuracyButton(); } @@ -4165,20 +3683,20 @@ void GMainWindow::OnToggleGraphicsAPI() { } void GMainWindow::OnConfigurePerGame() { - const u64 title_id = system->GetApplicationProcessProgramID(); + const u64 title_id = QtCommon::system->GetApplicationProcessProgramID(); OpenPerGameConfiguration(title_id, current_game_path.toStdString()); } void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) { - const auto v_file = Core::GetGameFileFromPath(vfs, file_name); + const auto v_file = Core::GetGameFileFromPath(QtCommon::vfs, file_name); Settings::SetConfiguringGlobal(false); - ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *system); + ConfigurePerGame dialog(this, title_id, file_name, vk_device_records, *QtCommon::system); dialog.LoadFromFile(v_file); const auto result = dialog.exec(); if (result != QDialog::Accepted && !UISettings::values.configuration_applied) { - Settings::RestoreGlobalState(system->IsPoweredOn()); + Settings::RestoreGlobalState(QtCommon::system->IsPoweredOn()); return; } else if (result == QDialog::Accepted) { dialog.ApplyConfiguration(); @@ -4190,9 +3708,9 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } // Do not cause the global config to write local settings into the config file - const bool is_powered_on = system->IsPoweredOn(); + const bool is_powered_on = QtCommon::system->IsPoweredOn(); Settings::RestoreGlobalState(is_powered_on); - system->HIDCore().ReloadInputDevices(); + QtCommon::system->HIDCore().ReloadInputDevices(); UISettings::values.configuration_applied = false; @@ -4236,6 +3754,7 @@ void GMainWindow::OnLoadAmiibo() { LoadAmiibo(filename); } +// TODO(crueter): does this need to be ported to QML? bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { @@ -4246,7 +3765,7 @@ bool GMainWindow::question(QWidget* parent, const QString& title, const QString& box_dialog->setDefaultButton(defaultButton); ControllerNavigation* controller_navigation = - new ControllerNavigation(system->HIDCore(), box_dialog); + new ControllerNavigation(QtCommon::system->HIDCore(), box_dialog); connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, [box_dialog](Qt::Key key) { QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); @@ -4287,182 +3806,35 @@ void GMainWindow::LoadAmiibo(const QString& filename) { } void GMainWindow::OnOpenRootDataFolder() { - QDesktopServices::openUrl( - QUrl(QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::EdenDir)))); + QtCommon::Game::OpenRootDataFolder(); } -void GMainWindow::OnOpenNANDFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir)))); +void GMainWindow::OnOpenNANDFolder() +{ + QtCommon::Game::OpenNANDFolder(); } -void GMainWindow::OnOpenSDMCFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir)))); +void GMainWindow::OnOpenSDMCFolder() +{ + QtCommon::Game::OpenSDMCFolder(); } -void GMainWindow::OnOpenModFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LoadDir)))); +void GMainWindow::OnOpenModFolder() +{ + QtCommon::Game::OpenModFolder(); } -void GMainWindow::OnOpenLogFolder() { - QDesktopServices::openUrl(QUrl::fromLocalFile( - QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::LogDir)))); +void GMainWindow::OnOpenLogFolder() +{ + QtCommon::Game::OpenLogFolder(); } void GMainWindow::OnVerifyInstalledContents() { - // Initialize a progress dialog. - QProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, this); - progress.setWindowModality(Qt::WindowModal); - progress.setMinimumDuration(100); - progress.setAutoClose(false); - progress.setAutoReset(false); - - // Declare progress callback. - auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { - progress.setValue(static_cast((processed_size * 100) / total_size)); - return progress.wasCanceled(); - }; - - const std::vector result = - ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback); - progress.close(); - - if (result.empty()) { - QMessageBox::information(this, tr("Integrity verification succeeded!"), - tr("The operation completed successfully.")); - } else { - const auto failed_names = - QString::fromStdString(fmt::format("{}", fmt::join(result, "\n"))); - QMessageBox::critical( - this, tr("Integrity verification failed!"), - tr("Verification failed for the following files:\n\n%1").arg(failed_names)); - } + QtCommon::Content::VerifyInstalledContents(); } void GMainWindow::InstallFirmware(const QString& location, bool recursive) { - QProgressDialog progress(tr("Installing Firmware..."), tr("Cancel"), 0, 100, this); - progress.setWindowModality(Qt::WindowModal); - progress.setMinimumDuration(100); - progress.setAutoClose(false); - progress.setAutoReset(false); - progress.show(); - - // Declare progress callback. - auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { - progress.setValue(static_cast((processed_size * 100) / total_size)); - return progress.wasCanceled(); - }; - - LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); - - // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in - // there.) - std::filesystem::path firmware_source_path = location.toStdString(); - if (!Common::FS::IsDir(firmware_source_path)) { - progress.close(); - return; - } - - std::vector out; - const Common::FS::DirEntryCallable callback = - [&out](const std::filesystem::directory_entry& entry) { - if (entry.path().has_extension() && entry.path().extension() == ".nca") { - out.emplace_back(entry.path()); - } - - return true; - }; - - QtProgressCallback(100, 10); - - if (recursive) { - Common::FS::IterateDirEntriesRecursively(firmware_source_path, callback, - Common::FS::DirEntryFilter::File); - } else { - Common::FS::IterateDirEntries(firmware_source_path, callback, - Common::FS::DirEntryFilter::File); - } - - if (out.size() <= 0) { - progress.close(); - QMessageBox::warning(this, tr("Firmware install failed"), - tr("Unable to locate potential firmware NCA files")); - return; - } - - // Locate and erase the content of nand/system/Content/registered/*.nca, if any. - auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); - if (!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { - progress.close(); - QMessageBox::critical(this, tr("Firmware install failed"), - tr("Failed to delete one or more firmware file.")); - return; - } - - LOG_INFO(Frontend, - "Cleaned nand/system/Content/registered folder in preparation for new firmware."); - - QtProgressCallback(100, 20); - - auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered"); - - bool success = true; - int i = 0; - for (const auto& firmware_src_path : out) { - i++; - auto firmware_src_vfile = - vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read); - auto firmware_dst_vfile = - firmware_vdir->CreateFileRelative(firmware_src_path.filename().string()); - - if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) { - LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!", - firmware_src_path.generic_string(), firmware_src_path.filename().string()); - success = false; - } - - if (QtProgressCallback( - 100, 20 + static_cast(((i) / static_cast(out.size())) * 70.0))) { - progress.close(); - QMessageBox::warning( - this, tr("Firmware install failed"), - tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. " - "Restart Eden or re-install firmware.")); - return; - } - } - - if (!success) { - progress.close(); - QMessageBox::critical(this, tr("Firmware install failed"), - tr("One or more firmware files failed to copy into NAND.")); - return; - } - - // Re-scan VFS for the newly placed firmware files. - system->GetFileSystemController().CreateFactories(*vfs); - - auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) { - progress.setValue(90 + static_cast((processed_size * 10) / total_size)); - return progress.wasCanceled(); - }; - - auto result = - ContentManager::VerifyInstalledContents(*system, *provider, VerifyFirmwareCallback, true); - - if (result.size() > 0) { - const auto failed_names = - QString::fromStdString(fmt::format("{}", fmt::join(result, "\n"))); - progress.close(); - QMessageBox::critical( - this, tr("Firmware integrity verification failed!"), - tr("Verification failed for the following files:\n\n%1").arg(failed_names)); - return; - } - - progress.close(); + QtCommon::Content::InstallFirmware(location, recursive); OnCheckFirmwareDecryption(); } @@ -4509,32 +3881,14 @@ void GMainWindow::OnInstallFirmwareFromZIP() { return; } - namespace fs = std::filesystem; - fs::path tmp{std::filesystem::temp_directory_path()}; + const QString qCacheDir = QtCommon::Content::UnzipFirmwareToTmp(firmware_zip_location); - if (!std::filesystem::create_directories(tmp / "eden" / "firmware")) { - goto unzipFailed; - } - - { - tmp /= "eden"; - tmp /= "firmware"; - - QString qCacheDir = QString::fromStdString(tmp.string()); - - QFile zip(firmware_zip_location); - - QStringList result = JlCompress::extractDir(&zip, qCacheDir); - if (result.isEmpty()) { - goto unzipFailed; - } - - // In this case, it has to be done recursively, since sometimes people - // will pack it into a subdirectory after dumping + // In this case, it has to be done recursively, since sometimes people + // will pack it into a subdirectory after dumping + if (!qCacheDir.isEmpty()) { InstallFirmware(qCacheDir, true); - std::error_code ec; - std::filesystem::remove_all(tmp, ec); + std::filesystem::remove_all(std::filesystem::temp_directory_path() / "eden" / "firmware", ec); if (ec) { QMessageBox::warning(this, tr("Firmware cleanup failed"), @@ -4543,14 +3897,7 @@ void GMainWindow::OnInstallFirmwareFromZIP() { "again.\nOS reported error: %1") .arg(QString::fromStdString(ec.message()))); } - - return; } -unzipFailed: - QMessageBox::critical( - this, tr("Firmware unzip failed"), - tr("Check write permissions in the system temp directory and try again.")); - return; } void GMainWindow::OnInstallDecryptionKeys() { @@ -4559,30 +3906,9 @@ void GMainWindow::OnInstallDecryptionKeys() { return; } - const QString key_source_location = QFileDialog::getOpenFileName( - this, tr("Select Dumped Keys Location"), {}, QStringLiteral("Decryption Keys (*.keys)"), {}, - QFileDialog::ReadOnly); - if (key_source_location.isEmpty()) { - return; - } + QtCommon::Content::InstallKeys(); - FirmwareManager::KeyInstallResult result = - FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys"); - - system->GetFileSystemController().CreateFactories(*vfs); game_list->PopulateAsync(UISettings::values.game_dirs); - - switch (result) { - case FirmwareManager::KeyInstallResult::Success: - QMessageBox::information(this, tr("Decryption Keys install succeeded"), - tr("Decryption Keys were successfully installed")); - break; - default: - QMessageBox::critical(this, tr("Decryption Keys install failed"), - tr(FirmwareManager::GetKeyInstallResultString(result))); - break; - } - OnCheckFirmwareDecryption(); } @@ -4609,18 +3935,20 @@ void GMainWindow::OnToggleStatusBar() { statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); } -void GMainWindow::OnGameListRefresh() { - // force reload add-ons etc - game_list->ForceRefreshGameDirectory(); +void GMainWindow::OnGameListRefresh() +{ + // Resets metadata cache and reloads + QtCommon::Game::ResetMetadata(); + game_list->RefreshGameDirectory(); SetFirmwareVersion(); } void GMainWindow::OnAlbum() { constexpr u64 AlbumId = static_cast(Service::AM::AppletProgramId::PhotoViewer); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), - tr("Please install the firmware to use the Album applet.")); + tr("Please install firmware to use the Album applet.")); return; } @@ -4631,7 +3959,7 @@ void GMainWindow::OnAlbum() { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer); const auto filename = QString::fromStdString(album_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4640,10 +3968,10 @@ void GMainWindow::OnAlbum() { void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { constexpr u64 CabinetId = static_cast(Service::AM::AppletProgramId::Cabinet); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), - tr("Please install the firmware to use the Cabinet applet.")); + tr("Please install firmware to use the Cabinet applet.")); return; } @@ -4654,8 +3982,8 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet); - system->GetFrontendAppletHolder().SetCabinetMode(mode); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet); + QtCommon::system->GetFrontendAppletHolder().SetCabinetMode(mode); const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4664,10 +3992,10 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { void GMainWindow::OnMiiEdit() { constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), - tr("Please install the firmware to use the Mii editor.")); + tr("Please install firmware to use the Mii editor.")); return; } @@ -4678,7 +4006,7 @@ void GMainWindow::OnMiiEdit() { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit); const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4687,10 +4015,10 @@ void GMainWindow::OnMiiEdit() { void GMainWindow::OnOpenControllerMenu() { constexpr u64 ControllerAppletId = static_cast(Service::AM::AppletProgramId::Controller); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), - tr("Please install the firmware to use the Controller Menu.")); + tr("Please install firmware to use the Controller Menu.")); return; } @@ -4702,7 +4030,7 @@ void GMainWindow::OnOpenControllerMenu() { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller); const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4711,7 +4039,7 @@ void GMainWindow::OnOpenControllerMenu() { } void GMainWindow::OnHomeMenu() { - auto result = FirmwareManager::VerifyFirmware(*system.get()); + auto result = FirmwareManager::VerifyFirmware(*QtCommon::system.get()); switch (result) { case FirmwareManager::ErrorFirmwareMissing: @@ -4746,7 +4074,7 @@ void GMainWindow::OnHomeMenu() { } constexpr u64 QLaunchId = static_cast(Service::AM::AppletProgramId::QLaunch); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); if (!qlaunch_applet_nca) { @@ -4755,7 +4083,7 @@ void GMainWindow::OnHomeMenu() { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::QLaunch); const auto filename = QString::fromStdString((qlaunch_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4764,10 +4092,10 @@ void GMainWindow::OnHomeMenu() { void GMainWindow::OnInitialSetup() { constexpr u64 Starter = static_cast(Service::AM::AppletProgramId::Starter); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), - tr("Please install the firmware to use Starter.")); + tr("Please install firmware to use Starter.")); return; } @@ -4778,7 +4106,7 @@ void GMainWindow::OnInitialSetup() { return; } - system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Starter); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Starter); const auto filename = QString::fromStdString((qlaunch_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); @@ -4786,158 +4114,11 @@ void GMainWindow::OnInitialSetup() { } void GMainWindow::OnCreateHomeMenuDesktopShortcut() { - OnCreateHomeMenuShortcut(GameListShortcutTarget::Desktop); + QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Desktop); } void GMainWindow::OnCreateHomeMenuApplicationMenuShortcut() { - OnCreateHomeMenuShortcut(GameListShortcutTarget::Applications); -} - -std::filesystem::path GMainWindow::GetEdenCommand() { - std::filesystem::path command; - - QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE")); - if (!appimage.isEmpty()) { - command = std::filesystem::path{appimage.toStdString()}; - } else { - const QStringList args = QApplication::arguments(); - command = args[0].toStdString(); - } - - // If relative path, make it an absolute path - if (command.c_str()[0] == '.') { - command = Common::FS::GetCurrentDir() / command; - } - - return command; -} - -std::filesystem::path GMainWindow::GetShortcutPath(GameListShortcutTarget target) { - std::filesystem::path shortcut_path{}; - if (target == GameListShortcutTarget::Desktop) { - shortcut_path = - QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); - } else if (target == GameListShortcutTarget::Applications) { - shortcut_path = - QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); - } - - return shortcut_path; -} - -void GMainWindow::CreateShortcut(const std::string& game_path, const u64 program_id, - const std::string& game_title_, GameListShortcutTarget target, - std::string arguments_, const bool needs_title) { - // Get path to yuzu executable - std::filesystem::path command = GetEdenCommand(); - - // Shortcut path - std::filesystem::path shortcut_path = GetShortcutPath(target); - - if (!std::filesystem::exists(shortcut_path)) { - GMainWindow::CreateShortcutMessagesGUI( - this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, - QString::fromStdString(shortcut_path.generic_string())); - LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string()); - return; - } - - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), - system->GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - const auto loader = - Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read)); - - std::string game_title{game_title_}; - - // Delete illegal characters from title - if (needs_title) { - game_title = fmt::format("{:016X}", program_id); - if (control.first != nullptr) { - game_title = control.first->GetApplicationName(); - } else { - loader->ReadTitle(game_title); - } - } - - const std::string illegal_chars = "<>:\"/\\|?*."; - for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { - if (illegal_chars.find(*it) != std::string::npos) { - game_title.erase(it.base() - 1); - } - } - - const QString qgame_title = QString::fromStdString(game_title); - - // Get icon from game file - std::vector icon_image_file{}; - if (control.second != nullptr) { - icon_image_file = control.second->ReadAllBytes(); - } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { - LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); - } - - QImage icon_data = - QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); - std::filesystem::path out_icon_path; - if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { - if (!SaveIconToFile(out_icon_path, icon_data)) { - LOG_ERROR(Frontend, "Could not write icon to file"); - } - } - -#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) - // Special case for AppImages - // Warn once if we are making a shortcut to a volatile AppImage - if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) { - if (!GMainWindow::CreateShortcutMessagesGUI( - this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qgame_title)) { - return; - } - UISettings::values.shortcut_already_warned = true; - } -#endif - - // Create shortcut - std::string arguments{arguments_}; - if (GMainWindow::CreateShortcutMessagesGUI( - this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qgame_title)) { - arguments = "-f " + arguments; - } - const std::string comment = fmt::format("Start {:s} with the eden Emulator", game_title); - const std::string categories = "Game;Emulator;Qt;"; - const std::string keywords = "Switch;Nintendo;"; - - if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, command, arguments, - categories, keywords, game_title)) { - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, - qgame_title); - return; - } - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, - qgame_title); -} - -void GMainWindow::OnCreateHomeMenuShortcut(GameListShortcutTarget target) { - constexpr u64 QLaunchId = static_cast(Service::AM::AppletProgramId::QLaunch); - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - QMessageBox::warning(this, tr("No firmware available"), - tr("Please install firmware to use the home menu.")); - return; - } - - auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); - if (!qlaunch_nca) { - QMessageBox::warning(this, tr("Home Menu Applet"), - tr("Home Menu is not available. Please reinstall firmware.")); - return; - } - - auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program); - const auto game_path = qlaunch_applet_nca->GetFullPath(); - - CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false); + QtCommon::Game::CreateHomeMenuShortcut(QtCommon::Game::ShortcutTarget::Applications); } void GMainWindow::OnCaptureScreenshot() { @@ -4945,7 +4126,7 @@ void GMainWindow::OnCaptureScreenshot() { return; } - const u64 title_id = system->GetApplicationProcessProgramID(); + const u64 title_id = QtCommon::system->GetApplicationProcessProgramID(); const auto screenshot_path = QString::fromStdString(Common::FS::GetEdenPathString(Common::FS::EdenPath::ScreenshotsDir)); const auto date = @@ -5107,7 +4288,7 @@ void GMainWindow::OnTasStateChanged() { } void GMainWindow::UpdateStatusBar() { - if (emu_thread == nullptr || !system->IsPoweredOn()) { + if (emu_thread == nullptr || !QtCommon::system->IsPoweredOn()) { status_bar_update_timer.stop(); return; } @@ -5118,8 +4299,8 @@ void GMainWindow::UpdateStatusBar() { tas_label->clear(); } - auto results = system->GetAndResetPerfStats(); - auto& shader_notify = system->GPU().ShaderNotify(); + auto results = QtCommon::system->GetAndResetPerfStats(); + auto& shader_notify = QtCommon::system->GPU().ShaderNotify(); const int shaders_building = shader_notify.ShadersBuilding(); if (shaders_building > 0) { @@ -5265,7 +4446,6 @@ void GMainWindow::OnMouseActivity() { } void GMainWindow::OnCheckFirmwareDecryption() { - system->GetFileSystemController().CreateFactories(*vfs); if (!ContentManager::AreKeysPresent()) { QMessageBox::warning(this, tr("Derivation Components Missing"), tr("Encryption keys are missing.")); @@ -5274,47 +4454,12 @@ void GMainWindow::OnCheckFirmwareDecryption() { UpdateMenuState(); } -bool GMainWindow::OnCheckNcaVerification() { - if (!Settings::values.disable_nca_verification.GetValue()) - return true; - - const bool currently_hidden = Settings::values.hide_nca_verification_popup.GetValue(); - LOG_INFO(Frontend, "NCA Verification is disabled. Popup State={}", currently_hidden); - if (currently_hidden) - return true; - - QMessageBox msgbox(this); - msgbox.setWindowTitle(tr("NCA Verification Disabled")); - msgbox.setText(tr("NCA Verification is disabled.\n" - "This is required to run new games and updates.\n" - "Running without verification can cause instability or crashes if NCA files " - "are corrupt, modified, or tampered.\n" - "If unsure, re-enable verification in Eden's Settings and use firmware " - "version 19.0.1 or below.")); - msgbox.setIcon(QMessageBox::Warning); - msgbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - msgbox.setDefaultButton(QMessageBox::Ok); - - QCheckBox* cb = new QCheckBox(tr("Don't show again"), &msgbox); - cb->setChecked(currently_hidden); - msgbox.setCheckBox(cb); - - int result = msgbox.exec(); - - const bool hide = cb->isChecked(); - if (hide != currently_hidden) { - Settings::values.hide_nca_verification_popup.SetValue(hide); - } - - return result == static_cast(QMessageBox::Ok); -} - bool GMainWindow::CheckFirmwarePresence() { - return FirmwareManager::CheckFirmwarePresence(*system.get()); + return FirmwareManager::CheckFirmwarePresence(*QtCommon::system.get()); } void GMainWindow::SetFirmwareVersion() { - const auto pair = FirmwareManager::GetFirmwareVersion(*system.get()); + const auto pair = FirmwareManager::GetFirmwareVersion(*QtCommon::system.get()); const auto firmware_data = pair.first; const auto result = pair.second; @@ -5400,7 +4545,7 @@ bool GMainWindow::ConfirmClose() { UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { return true; } - if (!system->GetExitLocked() && + if (!QtCommon::system->GetExitLocked() && UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { return true; } @@ -5430,7 +4575,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { render_window->close(); multiplayer_state->Close(); - system->HIDCore().UnloadInputDevices(); + QtCommon::system->HIDCore().UnloadInputDevices(); Network::Shutdown(); QWidget::closeEvent(event); @@ -5501,12 +4646,12 @@ bool GMainWindow::ConfirmForceLockedExit() { } void GMainWindow::RequestGameExit() { - if (!system->IsPoweredOn()) { + if (!QtCommon::system->IsPoweredOn()) { return; } - system->SetExitRequested(true); - system->GetAppletManager().RequestExit(); + QtCommon::system->SetExitRequested(true); + QtCommon::system->GetAppletManager().RequestExit(); } void GMainWindow::filterBarSetChecked(bool state) { @@ -5616,7 +4761,7 @@ void GMainWindow::OnLanguageChanged(const QString& locale) { void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { #ifdef USE_DISCORD_PRESENCE if (state) { - discord_rpc = std::make_unique(*system); + discord_rpc = std::make_unique(*QtCommon::system); } else { discord_rpc = std::make_unique(); } diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7857788fcf..e3922759b0 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -17,9 +17,10 @@ #include #include "common/common_types.h" -#include "configuration/qt_config.h" #include "frontend_common/content_manager.h" #include "input_common/drivers/tas_input.h" +#include "qt_common/qt_config.h" +#include "qt_common/qt_game_util.h" #include "user_data_migration.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -53,10 +54,7 @@ class QSlider; class QHBoxLayout; class WaitTreeWidget; enum class GameListOpenTarget; -enum class GameListRemoveTarget; -enum class GameListShortcutTarget; enum class DumpRomFSTarget; -enum class InstalledEntryType; class GameListPlaceholder; class QtAmiiboSettingsDialog; @@ -72,7 +70,6 @@ enum class StartGameType { namespace Core { enum class SystemResultStatus : u32; -class System; } // namespace Core namespace Core::Frontend { @@ -163,13 +160,6 @@ class GMainWindow : public QMainWindow { /// Max number of recently loaded items to keep track of static const int max_recent_files_item = 10; - enum { - CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, - CREATE_SHORTCUT_MSGBOX_SUCCESS, - CREATE_SHORTCUT_MSGBOX_ERROR, - CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, - }; - public: void filterBarSetChecked(bool state); void UpdateUITheme(); @@ -179,7 +169,7 @@ public: bool DropAction(QDropEvent* event); void AcceptDropEvent(QDropEvent* event); - std::filesystem::path GetShortcutPath(GameListShortcutTarget target); + std::filesystem::path GetShortcutPath(QtCommon::Game::ShortcutTarget target); signals: @@ -258,8 +248,6 @@ private: void LinkActionShortcut(QAction* action, const QString& action_name, const bool tas_allowed = false); - void RegisterMetaTypes(); - void InitializeWidgets(); void InitializeDebugWidgets(); void InitializeRecentFileMenuActions(); @@ -353,9 +341,8 @@ private slots: void OnGameListLoadFile(QString game_path, u64 program_id); void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, const std::string& game_path); - void OnTransferableShaderCacheOpenFile(u64 program_id); - void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); - void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target, + void OnGameListRemoveInstalledEntry(u64 program_id, QtCommon::Game::InstalledEntryType type); + void OnGameListRemoveFile(u64 program_id, QtCommon::Game::GameListRemoveTarget target, const std::string& game_path); void OnGameListRemovePlayTimeData(u64 program_id); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path, DumpRomFSTarget target); @@ -363,8 +350,9 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); - void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, - GameListShortcutTarget target); + void OnGameListCreateShortcut(u64 program_id, + const std::string& game_path, + const QtCommon::Game::ShortcutTarget target); void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); @@ -421,7 +409,6 @@ private slots: void OnInitialSetup(); void OnCreateHomeMenuDesktopShortcut(); void OnCreateHomeMenuApplicationMenuShortcut(); - void OnCreateHomeMenuShortcut(GameListShortcutTarget target); void OnCaptureScreenshot(); void OnCheckFirmwareDecryption(); void OnLanguageChanged(const QString& locale); @@ -436,16 +423,7 @@ private slots: #endif private: - QString GetGameListErrorRemoving(InstalledEntryType type) const; - void RemoveBaseContent(u64 program_id, InstalledEntryType type); - void RemoveUpdateContent(u64 program_id, InstalledEntryType type); - void RemoveAddOnContent(u64 program_id, InstalledEntryType type); - void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); - void RemoveVulkanDriverPipelineCache(u64 program_id); - void RemoveAllTransferableShaderCaches(u64 program_id); - void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); void RemovePlayTimeData(u64 program_id); - void RemoveCacheStorage(u64 program_id); bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, u64* selected_title_id, u8* selected_content_record_type); ContentManager::InstallResult InstallNCA(const QString& filename); @@ -478,15 +456,6 @@ private: QString GetTasStateDescription() const; bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); - bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, - std::filesystem::path& out_icon_path); - bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, - const std::filesystem::path& icon_path, - const std::filesystem::path& command, const std::string& arguments, - const std::string& categories, const std::string& keywords, - const std::string& name); - - bool OnCheckNcaVerification(); /** * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog @@ -501,7 +470,6 @@ private: std::unique_ptr ui; - std::unique_ptr system; std::unique_ptr discord_rpc; std::unique_ptr play_time_manager; std::shared_ptr input_subsystem; @@ -561,10 +529,6 @@ private: QString startup_icon_theme; - // FS - std::shared_ptr vfs; - std::unique_ptr provider; - // Debugger panes WaitTreeWidget* waitTreeWidget; ControllerDialog* controller_dialog; @@ -611,7 +575,7 @@ private: void CreateShortcut(const std::string& game_path, const u64 program_id, const std::string& game_title, - GameListShortcutTarget target, + QtCommon::Game::ShortcutTarget target, std::string arguments, const bool needs_title); diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp index 30f37c2b4a..deac3b9e59 100644 --- a/src/yuzu/multiplayer/direct_connect.cpp +++ b/src/yuzu/multiplayer/direct_connect.cpp @@ -21,7 +21,7 @@ #include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/state.h" #include "yuzu/multiplayer/validation.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" enum class ConnectionType : u8 { TraversalServer, IP }; diff --git a/src/yuzu/multiplayer/host_room.cpp b/src/yuzu/multiplayer/host_room.cpp index d8e63da600..4dd3958550 100644 --- a/src/yuzu/multiplayer/host_room.cpp +++ b/src/yuzu/multiplayer/host_room.cpp @@ -25,7 +25,7 @@ #include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/state.h" #include "yuzu/multiplayer/validation.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #ifdef ENABLE_WEB_SERVICE #include "web_service/verify_user_jwt.h" #endif diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index ed6ba6a15c..84723041df 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -22,7 +22,7 @@ #include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/state.h" #include "yuzu/multiplayer/validation.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #ifdef ENABLE_WEB_SERVICE #include "web_service/web_backend.h" #endif diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp index d5529712b0..8ff1d991ec 100644 --- a/src/yuzu/multiplayer/state.cpp +++ b/src/yuzu/multiplayer/state.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -19,7 +22,7 @@ #include "yuzu/multiplayer/lobby.h" #include "yuzu/multiplayer/message.h" #include "yuzu/multiplayer/state.h" -#include "yuzu/uisettings.h" +#include "qt_common/uisettings.h" #include "yuzu/util/clickable_label.h" MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, diff --git a/src/yuzu/play_time_manager.cpp b/src/yuzu/play_time_manager.cpp index 8317386816..6d06bc7614 100644 --- a/src/yuzu/play_time_manager.cpp +++ b/src/yuzu/play_time_manager.cpp @@ -1,7 +1,9 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/alignment.h" #include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/path_util.h" diff --git a/src/yuzu/play_time_manager.h b/src/yuzu/play_time_manager.h index 1714b91313..cd81bdb061 100644 --- a/src/yuzu/play_time_manager.h +++ b/src/yuzu/play_time_manager.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -9,8 +12,9 @@ #include "common/common_funcs.h" #include "common/common_types.h" -#include "common/polyfill_thread.h" +#include +// TODO(crueter): Extract this into frontend_common namespace Service::Account { class ProfileManager; } diff --git a/src/yuzu/qt_common.h b/src/yuzu/qt_common.h deleted file mode 100644 index 9c63f08f34..0000000000 --- a/src/yuzu/qt_common.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "core/frontend/emu_window.h" - -namespace QtCommon { - -Core::Frontend::WindowSystemType GetWindowSystemType(); - -Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window); - -} // namespace QtCommon diff --git a/src/yuzu/user_data_migration.h b/src/yuzu/user_data_migration.h index 3684d14916..a3ac2c15d3 100644 --- a/src/yuzu/user_data_migration.h +++ b/src/yuzu/user_data_migration.h @@ -10,6 +10,7 @@ #include #include "migration_worker.h" +// TODO(crueter): Quick implementation class UserDataMigrator { public: UserDataMigrator(QMainWindow* main_window); diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp index ab0d39c256..d961d550a1 100644 --- a/src/yuzu/vk_device_info.cpp +++ b/src/yuzu/vk_device_info.cpp @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include #include -#include "yuzu/qt_common.h" +#include "qt_common/qt_common.h" #include "common/dynamic_library.h" #include "common/logging/log.h" diff --git a/tools/cpm-fetch-all.sh b/tools/cpm-fetch-all.sh index eac0f861a4..66f55df94d 100755 --- a/tools/cpm-fetch-all.sh +++ b/tools/cpm-fetch-all.sh @@ -6,6 +6,6 @@ # SPDX-FileCopyrightText: 2025 crueter # SPDX-License-Identifier: GPL-3.0-or-later -LIBS=$(find . externals src/yuzu/externals src/dynarmic -maxdepth 2 -name cpmfile.json -exec jq -j 'keys_unsorted | join(" ")' {} \; -printf " ") +LIBS=$(find . externals src/qt_common src/dynarmic -maxdepth 2 -name cpmfile.json -exec jq -j 'keys_unsorted | join(" ")' {} \; -printf " ") tools/cpm-fetch.sh $LIBS \ No newline at end of file diff --git a/tools/cpm-fetch.sh b/tools/cpm-fetch.sh index 5620996433..996cf76a97 100755 --- a/tools/cpm-fetch.sh +++ b/tools/cpm-fetch.sh @@ -105,7 +105,8 @@ ci_package() { for package in $@ do # prepare for cancer - JSON=$(find . externals src/yuzu/externals externals/ffmpeg src/dynarmic/externals externals/nx_tzdb -maxdepth 1 -name cpmfile.json -exec jq -r ".\"$package\" | select( . != null )" {} \;) + # TODO(crueter): Fetch json once? + JSON=$(find . externals src/qt_common src/dynarmic -maxdepth 1 -name cpmfile.json -exec jq -r ".\"$package\" | select( . != null )" {} \;) [ -z "$JSON" ] && echo "No cpmfile definition for $package" && continue