Compare commits

..

1 Commits

Author SHA1 Message Date
xbzk fc2569a724 [android,ui] background support 2026-03-08 16:58:52 -03:00
198 changed files with 2887 additions and 2446 deletions
+2 -2
View File
@@ -115,7 +115,7 @@ for file in $FILES; do
*.cmake|*.sh|*CMakeLists.txt) *.cmake|*.sh|*CMakeLists.txt)
begin="#" begin="#"
;; ;;
*.kt*|*.cpp|*.h|*.qml) *.kt*|*.cpp|*.h)
begin="//" begin="//"
;; ;;
*) *)
@@ -193,7 +193,7 @@ if [ "$UPDATE" = "true" ]; then
begin="#" begin="#"
shell=true shell=true
;; ;;
*) *.kt*|*.cpp|*.h)
begin="//" begin="//"
shell="false" shell="false"
;; ;;
-89
View File
@@ -1,89 +0,0 @@
From 509be32bbfa6eb95014860f7c9ea6d45c8ddaa56 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sun, 8 Mar 2026 15:11:12 -0400
Subject: [PATCH] [cmake] Simplify zstd find logic, and support pre-existing
zstd target
Some deduplication work on the zstd required/if-available logic. Also
adds support for pre-existing `zstd::libzstd` which is useful for
projects that bundle their own zstd in a way that doesn't get caught by
`CONFIG`
Signed-off-by: crueter <crueter@eden-emu.dev>
---
CMakeLists.txt | 46 ++++++++++++++++++++++++++--------------------
1 file changed, 26 insertions(+), 20 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1874e36be0..8d31198006 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -241,28 +241,34 @@ endif()
# NOTE:
# zstd < 1.5.6 does not provide the CMake imported target `zstd::libzstd`.
# Older versions must be consumed via their pkg-config file.
-if(HTTPLIB_REQUIRE_ZSTD)
- find_package(zstd 1.5.6 CONFIG)
- if(NOT zstd_FOUND)
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)
- add_library(zstd::libzstd ALIAS PkgConfig::zstd)
- endif()
- set(HTTPLIB_IS_USING_ZSTD TRUE)
-elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE)
- find_package(zstd 1.5.6 CONFIG QUIET)
- if(NOT zstd_FOUND)
- find_package(PkgConfig QUIET)
- if(PKG_CONFIG_FOUND)
- pkg_check_modules(zstd QUIET IMPORTED_TARGET libzstd)
-
- if(TARGET PkgConfig::zstd)
+if (HTTPLIB_REQUIRE_ZSTD)
+ set(HTTPLIB_ZSTD_REQUESTED ON)
+ set(HTTPLIB_ZSTD_REQUIRED REQUIRED)
+elseif (HTTPLIB_USE_ZSTD_IF_AVAILABLE)
+ set(HTTPLIB_ZSTD_REQUESTED ON)
+ set(HTTPLIB_ZSTD_REQUIRED QUIET)
+endif()
+
+if (HTTPLIB_ZSTD_REQUESTED)
+ if (TARGET zstd::libzstd)
+ set(HTTPLIB_IS_USING_ZSTD TRUE)
+ else()
+ find_package(zstd 1.5.6 CONFIG QUIET)
+
+ if (NOT zstd_FOUND)
+ find_package(PkgConfig ${HTTPLIB_ZSTD_REQUIRED})
+ pkg_check_modules(zstd ${HTTPLIB_ZSTD_REQUIRED} IMPORTED_TARGET libzstd)
+
+ if (TARGET PkgConfig::zstd)
add_library(zstd::libzstd ALIAS PkgConfig::zstd)
endif()
endif()
+
+ # This will always be true if zstd is required.
+ # If zstd *isn't* found when zstd is set to required,
+ # CMake will error out earlier in this block.
+ set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND})
endif()
- # Both find_package and PkgConf set a XXX_FOUND var
- set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND})
endif()
# Used for default, common dirs that the end-user can change (if needed)
@@ -317,13 +323,13 @@ if(HTTPLIB_COMPILE)
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
)
-
+
# Add C++20 module support if requested
# Include from separate file to prevent parse errors on older CMake versions
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
include(cmake/modules.cmake)
endif()
-
+
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
+4 -4
View File
@@ -46,9 +46,9 @@
"package": "ZLIB", "package": "ZLIB",
"repo": "madler/zlib", "repo": "madler/zlib",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4", "hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
"version": "1.2", "version": "1.2",
"git_version": "1.3.2", "git_version": "1.3.1.2",
"options": [ "options": [
"ZLIB_BUILD_SHARED OFF", "ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF" "ZLIB_INSTALL OFF"
@@ -98,9 +98,9 @@
"package": "VVL", "package": "VVL",
"repo": "KhronosGroup/Vulkan-ValidationLayers", "repo": "KhronosGroup/Vulkan-ValidationLayers",
"tag": "vulkan-sdk-%VERSION%", "tag": "vulkan-sdk-%VERSION%",
"git_version": "1.4.341.0", "git_version": "1.4.335.0",
"artifact": "android-binaries-%VERSION%.zip", "artifact": "android-binaries-%VERSION%.zip",
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753" "hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
}, },
"quazip": { "quazip": {
"package": "QuaZip-Qt6", "package": "QuaZip-Qt6",
+2 -8
View File
@@ -91,7 +91,7 @@ After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up. `-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up.
If clang has errors, try using `g++11`. If clang has errors, try using `g++-11`.
## FreeBSD ## FreeBSD
@@ -107,8 +107,6 @@ hw.usb.usbhid.enable="0"
## NetBSD ## NetBSD
2026-02-07: `vulkan-headers` must not be installed, since the version found in `pkgsrc` is older than required. Either wait for binary packages to update or build newer versions from source.
Install `pkgin` if not already `pkg_add pkgin`, see also the general [pkgsrc guide](https://www.netbsd.org/docs/pkgsrc/using.html). For NetBSD 10.1 provide `echo 'PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/"' >/etc/pkg_install.conf`. If `pkgin` is taking too much time consider adding the following to `/etc/rc.conf`: Install `pkgin` if not already `pkg_add pkgin`, see also the general [pkgsrc guide](https://www.netbsd.org/docs/pkgsrc/using.html). For NetBSD 10.1 provide `echo 'PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/"' >/etc/pkg_install.conf`. If `pkgin` is taking too much time consider adding the following to `/etc/rc.conf`:
```sh ```sh
@@ -118,7 +116,7 @@ ip6addrctl_policy=ipv4_prefer
System provides a default `g++-10` which doesn't support the current C++ codebase; install `clang-19` with `pkgin install clang-19`. Or install `gcc14` (or `gcc15` with current pkgsrc). Provided that, the following CMake commands may work: System provides a default `g++-10` which doesn't support the current C++ codebase; install `clang-19` with `pkgin install clang-19`. Or install `gcc14` (or `gcc15` with current pkgsrc). Provided that, the following CMake commands may work:
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -Bbuild` (Recommended) - `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc14/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -Bbuild` - `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc14/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc15/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc15/bin/g++ -Bbuild` - `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc15/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc15/bin/g++ -Bbuild`
@@ -140,12 +138,8 @@ cmake --install build
However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version. However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version.
`QtCore` on NetBSD is included, but due to misconfigurations(!) we MUST include one of the standard headers that include `bits/c++config.h`, since source_location (required by `QtCore`) isn't properly configured to intake `bits/c++config.h` (none of the experimental library is). This is a bug with NetBSD packaging and not our fault, but alas.
## DragonFlyBSD ## DragonFlyBSD
2026-02-07: `vulkan-headers` and `vulkan-utility-libraries` must NOT be uninstalled, since they're too old: `1.3.289`. Either wait for binary packages to update or build newer versions from source.
If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt: If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt:
```sh ```sh
+12 -13
View File
@@ -9,7 +9,7 @@
}, },
"sirit": { "sirit": {
"repo": "eden-emulator/sirit", "repo": "eden-emulator/sirit",
"git_version": "1.0.4", "git_version": "1.0.3",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"artifact": "sirit-source-%VERSION%.tar.zst", "artifact": "sirit-source-%VERSION%.tar.zst",
"hash_suffix": "sha512sum", "hash_suffix": "sha512sum",
@@ -28,12 +28,11 @@
"httplib": { "httplib": {
"repo": "yhirose/cpp-httplib", "repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded", "hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
"git_version": "0.37.0", "git_version": "0.30.1",
"find_args": "MODULE GLOBAL", "find_args": "MODULE GLOBAL",
"patches": [ "patches": [
"0001-mingw.patch", "0001-mingw.patch"
"0002-fix-zstd.patch"
], ],
"options": [ "options": [
"HTTPLIB_REQUIRE_OPENSSL ON" "HTTPLIB_REQUIRE_OPENSSL ON"
@@ -56,8 +55,8 @@
"package": "xbyak", "package": "xbyak",
"repo": "herumi/xbyak", "repo": "herumi/xbyak",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a", "hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47",
"git_version": "7.35.2" "git_version": "7.33.2"
}, },
"oaknut": { "oaknut": {
"repo": "eden-emulator/oaknut", "repo": "eden-emulator/oaknut",
@@ -147,9 +146,9 @@
"package": "Catch2", "package": "Catch2",
"repo": "catchorg/Catch2", "repo": "catchorg/Catch2",
"tag": "v%VERSION%", "tag": "v%VERSION%",
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35", "hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
"version": "3.0.1", "version": "3.0.1",
"git_version": "3.13.0", "git_version": "3.12.0",
"patches": [ "patches": [
"0001-solaris-isnan-fix.patch" "0001-solaris-isnan-fix.patch"
] ]
@@ -257,15 +256,15 @@
"repo": "KhronosGroup/Vulkan-Headers", "repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders", "package": "VulkanHeaders",
"version": "1.4.317", "version": "1.4.317",
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b", "hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.345", "git_version": "1.4.342",
"tag": "v%VERSION%" "tag": "v%VERSION%"
}, },
"vulkan-utility-libraries": { "vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries", "repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries", "package": "VulkanUtilityLibraries",
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1", "hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.345", "git_version": "1.4.342",
"tag": "v%VERSION%" "tag": "v%VERSION%"
}, },
"frozen": { "frozen": {
+1 -1
View File
@@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project # SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
@@ -242,6 +241,7 @@ if (YUZU_CMD)
endif() endif()
if (ENABLE_QT) if (ENABLE_QT)
add_definitions(-DYUZU_QT_WIDGETS)
add_subdirectory(qt_common) add_subdirectory(qt_common)
add_subdirectory(yuzu) add_subdirectory(yuzu)
endif() endif()
@@ -40,21 +40,11 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
} }
} }
val canDelete = model.isRemovable val deleteAction = {
binding.deleteCard.isEnabled = canDelete addonViewModel.setAddonToDelete(model)
binding.buttonDelete.isEnabled = canDelete
binding.deleteCard.alpha = if (canDelete) 1f else 0.38f
if (canDelete) {
val deleteAction = {
addonViewModel.setAddonToDelete(model)
}
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
} else {
binding.deleteCard.setOnClickListener(null)
binding.buttonDelete.setOnClickListener(null)
} }
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
} }
} }
} }
@@ -31,6 +31,7 @@ import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding
import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetPlayManager import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import java.util.Locale import java.util.Locale
class LobbyBrowser(context: Context) : BottomSheetDialog(context) { class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
@@ -47,6 +48,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
binding = DialogLobbyBrowserBinding.inflate(layoutInflater) binding = DialogLobbyBrowserBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
BackgroundHelper.applyBackground(binding.backgroundLogo, context)
binding.emptyRefreshButton.setOnClickListener { binding.emptyRefreshButton.setOnClickListener {
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
@@ -105,6 +105,14 @@ object Settings {
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds" const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val PREF_STATIC_THEME_COLOR = "StaticThemeColor" const val PREF_STATIC_THEME_COLOR = "StaticThemeColor"
// App background preference keys
const val PREF_HOME_BACKGROUND_STYLE = "HomeBackgroundStyle"
const val HOME_BACKGROUND_STYLE_NONE = 0
const val HOME_BACKGROUND_STYLE_EDEN = 1
const val HOME_BACKGROUND_STYLE_DEFAULT = HOME_BACKGROUND_STYLE_NONE
const val PREF_HOME_BACKGROUND_ALPHA = "HomeBackgroundAlpha"
const val HOME_BACKGROUND_ALPHA_DEFAULT = 40
enum class EmulationOrientation(val int: Int) { enum class EmulationOrientation(val int: Int) {
Unspecified(0), Unspecified(0),
SensorLandscape(5), SensorLandscape(5),
@@ -33,6 +33,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.PathUtil import org.yuzu.yuzu_emu.utils.PathUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
@@ -178,9 +179,17 @@ class SettingsFragment : Fragment() {
} }
presenter.onViewCreated() presenter.onViewCreated()
applyBackgroundPreference()
setInsets() setInsets()
} }
private fun getPlayerIndex(): Int =
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun getPlayerIndex(): Int =
when (args.menuTag) { when (args.menuTag) {
Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0 Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0
Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1 Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1
@@ -232,6 +241,10 @@ private fun getPlayerIndex(): Int =
} }
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun hasAllFilesPermission(): Boolean { private fun hasAllFilesPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager() Environment.isExternalStorageManager()
@@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -1071,6 +1072,26 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.theme_and_color)) add(HeaderSetting(R.string.theme_and_color))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntriesA12,
valuesId = R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntries,
valuesId = R.array.themeValues
)
)
}
val themeMode: AbstractIntSetting = object : AbstractIntSetting { val themeMode: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt() override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
override fun setInt(value: Int) { override fun setInt(value: Int) {
@@ -1101,26 +1122,6 @@ class SettingsFragmentPresenter(
) )
) )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntriesA12,
valuesId = R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntries,
valuesId = R.array.themeValues
)
)
}
val staticThemeColor: AbstractIntSetting = object : AbstractIntSetting { val staticThemeColor: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int = override fun getInt(needsGlobal: Boolean): Int =
IntSetting.STATIC_THEME_COLOR.getInt(needsGlobal) IntSetting.STATIC_THEME_COLOR.getInt(needsGlobal)
@@ -1187,6 +1188,79 @@ class SettingsFragmentPresenter(
) )
) )
val backgroundStyleSetting: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
BackgroundHelper.getBackgroundStyle(context)
override fun setInt(value: Int) {
BackgroundHelper.setBackgroundStyle(context, value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_HOME_BACKGROUND_STYLE
override val isRuntimeModifiable: Boolean = true
override val pairedSettingKey: String = ""
override val isSwitchable: Boolean = false
override var global: Boolean = true
override val isSaveable: Boolean = true
override val defaultValue: Int = Settings.HOME_BACKGROUND_STYLE_DEFAULT
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() {
setInt(defaultValue)
}
}
add(
SingleChoiceSetting(
backgroundStyleSetting,
titleId = R.string.home_background,
descriptionId = R.string.home_background_description,
choicesId = R.array.homeBackgroundEntries,
valuesId = R.array.homeBackgroundValues
)
)
val backgroundAlphaSetting: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
(BackgroundHelper.getBackgroundAlpha(context) * 100).toInt()
override fun setInt(value: Int) {
BackgroundHelper.setBackgroundAlpha(context, value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_HOME_BACKGROUND_ALPHA
override val isRuntimeModifiable: Boolean = true
override val pairedSettingKey: String = ""
override val isSwitchable: Boolean = false
override var global: Boolean = true
override val isSaveable: Boolean = true
override val defaultValue: Int = Settings.HOME_BACKGROUND_ALPHA_DEFAULT
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() {
setInt(defaultValue)
}
}
if (BackgroundHelper.getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE) {
add(
SliderSetting(
backgroundAlphaSetting,
titleId = R.string.home_background_opacity,
descriptionId = R.string.home_background_opacity_description,
min = 0,
max = 100,
units = "%"
)
)
}
add(HeaderSetting(R.string.buttons)) add(HeaderSetting(R.string.buttons))
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key) add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key) add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)
@@ -25,6 +25,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
@@ -53,6 +54,8 @@ class AboutFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarAbout.setNavigationOnClickListener { binding.toolbarAbout.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
} }
@@ -105,6 +108,11 @@ class AboutFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun openLink(link: String) { private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent) startActivity(intent)
@@ -127,4 +135,8 @@ class AboutFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -25,6 +25,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAddonsBinding
import org.yuzu.yuzu_emu.model.AddonViewModel import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
@@ -59,6 +60,7 @@ class AddonsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false) homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.toolbarAddons.setNavigationOnClickListener { binding.toolbarAddons.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
@@ -122,6 +124,7 @@ class AddonsFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
addonViewModel.onAddonsViewStarted(args.game) addonViewModel.onAddonsViewStarted(args.game)
applyBackgroundPreference()
} }
override fun onDestroy() { override fun onDestroy() {
@@ -202,4 +205,8 @@ class AddonsFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() { class AppletLauncherFragment : Fragment() {
@@ -48,6 +49,7 @@ class AppletLauncherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarApplets.setNavigationOnClickListener { binding.toolbarApplets.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
@@ -91,6 +93,11 @@ class AppletLauncherFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@@ -108,4 +115,8 @@ class AppletLauncherFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -30,6 +30,7 @@ import org.yuzu.yuzu_emu.databinding.FragmentDriverFetcherBinding
import org.yuzu.yuzu_emu.features.fetcher.DriverGroupAdapter import org.yuzu.yuzu_emu.features.fetcher.DriverGroupAdapter
import org.yuzu.yuzu_emu.model.DriverViewModel import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.IOException import java.io.IOException
@@ -141,6 +142,8 @@ class DriverFetcherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarDrivers.setNavigationOnClickListener { binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
} }
@@ -154,6 +157,11 @@ class DriverFetcherFragment : Fragment() {
fetchDrivers() fetchDrivers()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun fetchDrivers() { private fun fetchDrivers() {
binding.loadingIndicator.isVisible = true binding.loadingIndicator.isVisible = true
@@ -242,6 +250,10 @@ class DriverFetcherFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
data class Artifact(val url: URL, val name: String) data class Artifact(val url: URL, val name: String)
data class Release( data class Release(
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
@@ -33,6 +33,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
import java.io.File import java.io.File
@@ -66,6 +67,7 @@ class DriverManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
driverViewModel.onOpenDriverManager(args.game) driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) { if (NativeConfig.isPerGameConfigLoaded()) {
@@ -138,6 +140,11 @@ class DriverManagerFragment : Fragment() {
driverViewModel.onCloseDriverManager(args.game) driverViewModel.onCloseDriverManager(args.game)
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@@ -256,4 +263,8 @@ class DriverManagerFragment : Fragment() {
.setCancelable(false) .setCancelable(false)
.show() .show()
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -20,6 +20,7 @@ import org.yuzu.yuzu_emu.adapters.FreedrenoPresetAdapter
import org.yuzu.yuzu_emu.adapters.FreedrenoVariableAdapter import org.yuzu.yuzu_emu.adapters.FreedrenoVariableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFreedrenoSettingsBinding import org.yuzu.yuzu_emu.databinding.FragmentFreedrenoSettingsBinding
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.FreedrenoPresets import org.yuzu.yuzu_emu.utils.FreedrenoPresets
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -68,6 +69,7 @@ class FreedrenoSettingsFragment : Fragment() {
setupAdapters() setupAdapters()
loadCurrentSettings() loadCurrentSettings()
setupButtonListeners() setupButtonListeners()
applyBackgroundPreference()
setupWindowInsets() setupWindowInsets()
} }
@@ -193,7 +195,17 @@ class FreedrenoSettingsFragment : Fragment() {
insets insets
} }
} }
private fun showSnackbar(message: String) {
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun showSnackbar(message: String) {
Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
} }
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
@@ -57,6 +58,7 @@ class GameFoldersFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarFolders.setNavigationOnClickListener { binding.toolbarFolders.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
@@ -100,6 +102,11 @@ class GameFoldersFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
gamesViewModel.onCloseGameFoldersFragment() gamesViewModel.onCloseGameFoldersFragment()
@@ -133,4 +140,8 @@ class GameFoldersFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.GameMetadata import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -60,6 +61,7 @@ class GameInfoFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false) homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.apply { binding.apply {
toolbarInfo.title = args.game.title toolbarInfo.title = args.game.title
@@ -143,6 +145,11 @@ class GameInfoFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun copyToClipboard(label: String, body: String) { private fun copyToClipboard(label: String, body: String) {
val clipBoard = val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@@ -175,4 +182,8 @@ class GameInfoFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -48,6 +48,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
@@ -83,6 +84,7 @@ class GamePropertiesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true) homeViewModel.setStatusBarShadeVisibility(true)
applyBackgroundPreference()
binding.buttonBack.setOnClickListener { binding.buttonBack.setOnClickListener {
view.findNavController().popBackStack() view.findNavController().popBackStack()
@@ -487,6 +489,7 @@ class GamePropertiesFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
applyBackgroundPreference()
driverViewModel.updateDriverNameForGame(args.game) driverViewModel.updateDriverNameForGame(args.game)
getPlayTime() getPlayTime()
reloadList() reloadList()
@@ -711,4 +714,8 @@ class GamePropertiesFragment : Fragment() {
).show() ).show()
} }
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
@@ -76,6 +77,7 @@ class HomeSettingsFragment : Fragment() {
findNavController().popBackStack() findNavController().popBackStack()
} }
binding.toolbarHomeSettings.title = getString(R.string.preferences_settings) binding.toolbarHomeSettings.title = getString(R.string.preferences_settings)
applyBackgroundPreference()
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply { val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
add( add(
@@ -315,6 +317,7 @@ class HomeSettingsFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
driverViewModel.updateDriverNameForGame(null) driverViewModel.updateDriverNameForGame(null)
applyBackgroundPreference()
} }
override fun onDestroyView() { override fun onDestroyView() {
@@ -500,4 +503,8 @@ class HomeSettingsFragment : Fragment() {
windowInsets windowInsets
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.logoImage, requireContext())
}
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
@@ -32,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
@@ -68,6 +69,7 @@ class InstallableFragment : Fragment() {
val mainActivity = requireActivity() as MainActivity val mainActivity = requireActivity() as MainActivity
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarInstallables.setNavigationOnClickListener { binding.toolbarInstallables.setNavigationOnClickListener {
binding.root.findNavController().popBackStack() binding.root.findNavController().popBackStack()
@@ -162,6 +164,11 @@ class InstallableFragment : Fragment() {
setInsets() setInsets()
} }
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@@ -325,4 +332,8 @@ class InstallableFragment : Fragment() {
} }
}.show(parentFragmentManager, ProgressDialogFragment.TAG) }.show(parentFragmentManager, ProgressDialogFragment.TAG)
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.adapters.ProfileAdapter
import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.UserProfile import org.yuzu.yuzu_emu.model.UserProfile
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.NativeConfig import org.yuzu.yuzu_emu.utils.NativeConfig
class ProfileManagerFragment : Fragment() { class ProfileManagerFragment : Fragment() {
@@ -49,6 +50,7 @@ class ProfileManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false) homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarProfiles.setNavigationOnClickListener { binding.toolbarProfiles.setNavigationOnClickListener {
findNavController().popBackStack() findNavController().popBackStack()
@@ -75,6 +77,7 @@ class ProfileManagerFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
loadProfiles() loadProfiles()
applyBackgroundPreference()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
@@ -187,4 +190,8 @@ class ProfileManagerFragment : Fragment() {
NativeConfig.saveGlobalConfig() NativeConfig.saveGlobalConfig()
_binding = null _binding = null
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
} }
@@ -16,17 +16,5 @@ data class Patch(
val type: Int, val type: Int,
val programId: String, val programId: String,
val titleId: String, val titleId: String,
val numericVersion: Long = 0, val numericVersion: Long = 0
val source: Int = 0 )
) {
companion object {
const val SOURCE_UNKNOWN = 0
const val SOURCE_NAND = 1
const val SOURCE_SDMC = 2
const val SOURCE_EXTERNAL = 3
const val SOURCE_PACKED = 4
}
val isRemovable: Boolean
get() = source != SOURCE_EXTERNAL && source != SOURCE_PACKED
}
@@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect import org.yuzu.yuzu_emu.utils.collect
import info.debatty.java.stringsimilarity.Jaccard import info.debatty.java.stringsimilarity.Jaccard
@@ -106,6 +107,7 @@ class GamesFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true) homeViewModel.setStatusBarShadeVisibility(true)
mainActivity = requireActivity() as MainActivity mainActivity = requireActivity() as MainActivity
applyBackgroundPreference()
if (savedInstanceState != null) { if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
@@ -252,6 +254,7 @@ class GamesFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
applyBackgroundPreference()
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) { if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
(binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true) (binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true)
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition) (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
@@ -497,6 +500,10 @@ class GamesFragment : Fragment() {
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
} }
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun setInsets() = private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
binding.root binding.root
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.view.View
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.Settings
object BackgroundHelper {
fun getBackgroundStyle(context: Context): Int {
return PreferenceManager.getDefaultSharedPreferences(context).getInt(
Settings.PREF_HOME_BACKGROUND_STYLE,
Settings.HOME_BACKGROUND_STYLE_DEFAULT
)
}
fun setBackgroundStyle(context: Context, style: Int) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putInt(Settings.PREF_HOME_BACKGROUND_STYLE, style)
}
}
fun getBackgroundAlpha(context: Context): Float {
val alphaPercent = PreferenceManager.getDefaultSharedPreferences(context).getInt(
Settings.PREF_HOME_BACKGROUND_ALPHA,
Settings.HOME_BACKGROUND_ALPHA_DEFAULT
).coerceIn(0, 100)
return alphaPercent / 100f
}
fun setBackgroundAlpha(context: Context, alphaPercent: Int) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putInt(Settings.PREF_HOME_BACKGROUND_ALPHA, alphaPercent.coerceIn(0, 100))
}
}
fun applyBackground(view: View, context: Context) {
val isEnabled = getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE
val visibilityStrength = getBackgroundAlpha(context)
val parent = view.parent as? View
val scrim = parent?.findViewById<View>(R.id.background_scrim)
view.visibility = if (isEnabled) View.VISIBLE else View.GONE
if (!isEnabled) {
scrim?.visibility = View.GONE
return
}
if (scrim != null) {
// Keep background image opaque; control perceived intensity through a cheap flat-color scrim.
view.alpha = 1f
scrim.visibility = View.VISIBLE
scrim.alpha = (1f - visibilityStrength).coerceIn(0f, 1f)
} else {
view.alpha = visibilityStrength
}
}
}
+1 -1
View File
@@ -1407,7 +1407,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type), Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
Common::Android::ToJString(env, std::to_string(patch.program_id)), Common::Android::ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.title_id)), Common::Android::ToJString(env, std::to_string(patch.title_id)),
static_cast<jlong>(patch.numeric_version), static_cast<jint>(patch.source)); static_cast<jlong>(patch.numeric_version));
env->SetObjectArrayElement(jpatchArray, i, jpatch); env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i; ++i;
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

@@ -6,6 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar" android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -8,6 +8,29 @@
android:clipChildren="false" android:clipChildren="false"
> >
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/background_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout <LinearLayout
android:id="@+id/header" android:id="@+id/header"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -259,4 +282,4 @@
app:rippleColor="#99FFFFFF" app:rippleColor="#99FFFFFF"
/> />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -8,6 +8,17 @@
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
> >
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about" android:id="@+id/appbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -7,6 +7,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_info" android:id="@+id/appbar_info"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -7,6 +7,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/list_all" android:id="@+id/list_all"
android:layout_width="0dp" android:layout_width="0dp"
@@ -6,6 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar" android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -246,4 +257,4 @@
style="@style/Widget.Material3.Button.ElevatedButton" /> style="@style/Widget.Material3.Button.ElevatedButton" />
</LinearLayout> </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -8,6 +8,17 @@
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
> >
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about" android:id="@+id/appbar_about"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_addons" android:id="@+id/appbar_addons"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_applets" android:id="@+id/appbar_applets"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,6 +7,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_drivers" android:id="@+id/appbar_drivers"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout" android:id="@+id/coordinatorLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@@ -6,6 +6,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_freedreno" android:id="@+id/appbar_freedreno"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,6 +7,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_info" android:id="@+id/appbar_info"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/list_all" android:id="@+id/list_all"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -8,6 +8,29 @@
android:clipChildren="false" android:clipChildren="false"
> >
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/background_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout <LinearLayout
android:id="@+id/header" android:id="@+id/header"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -266,4 +289,4 @@
app:rippleColor="#99FFFFFF" app:rippleColor="#99FFFFFF"
/> />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@@ -6,6 +6,20 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/logo_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.12"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_home_settings" android:id="@+id/appbar_home_settings"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_installables" android:id="@+id/appbar_installables"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -8,6 +8,17 @@
android:background="?attr/colorSurface" android:background="?attr/colorSurface"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar" android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_settings" android:id="@+id/appbar_settings"
style="@style/Widget.Eden.TransparentTopAppBarLayout" style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -383,6 +383,15 @@
<item>2</item> <item>2</item>
</integer-array> </integer-array>
<string-array name="homeBackgroundEntries">
<item>@string/background_none</item>
<item>@string/background_eden_default</item>
</string-array>
<integer-array name="homeBackgroundValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="appLanguageNames"> <string-array name="appLanguageNames">
<item>@string/app_language_system</item> <item>@string/app_language_system</item>
<item>@string/app_language_english</item> <item>@string/app_language_english</item>
@@ -1160,6 +1160,12 @@
<string name="theme_material_you">Material You</string> <string name="theme_material_you">Material You</string>
<string name="app_settings">App Settings</string> <string name="app_settings">App Settings</string>
<string name="theme_and_color">Theme And Color</string> <string name="theme_and_color">Theme And Color</string>
<string name="home_background">Home background</string>
<string name="home_background_description">Show a decorative background across app screens.</string>
<string name="home_background_opacity">Background opacity</string>
<string name="home_background_opacity_description">Adjust the background visibility strength.</string>
<string name="background_none">None</string>
<string name="background_eden_default">Eden (default)</string>
<!-- Theme Modes --> <!-- Theme Modes -->
<string name="change_theme_mode">Change theme mode</string> <string name="change_theme_mode">Change theme mode</string>
+1 -1
View File
@@ -516,7 +516,7 @@ namespace Common::Android {
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class)); s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
s_patch_constructor = env->GetMethodID( s_patch_constructor = env->GetMethodID(
patch_class, "<init>", patch_class, "<init>",
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V"); "(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;J)V");
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z"); s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;"); s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;"); s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
+1 -9
View File
@@ -27,10 +27,6 @@
#include "common/settings.h" #include "common/settings.h"
#include "common/time_zone.h" #include "common/time_zone.h"
#if defined(__linux__ ) && defined(ARCHITECTURE_arm64)
#include <unistd.h>
#endif
namespace Settings { namespace Settings {
// Clang 14 and earlier have errors when explicitly instantiating these classes // Clang 14 and earlier have errors when explicitly instantiating these classes
@@ -182,11 +178,7 @@ bool IsFastmemEnabled() {
return bool(values.cpuopt_fastmem); return bool(values.cpuopt_fastmem);
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe) else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
return bool(values.cpuopt_unsafe_host_mmu); return bool(values.cpuopt_unsafe_host_mmu);
#if defined(__linux__) && defined(ARCHITECTURE_arm64) #if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
// Only 4kb systems support host MMU right now
// TODO: Support this
return getpagesize() == 4096;
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__)
return false; return false;
#else #else
return true; return true;
-1
View File
@@ -777,7 +777,6 @@ struct Values {
Setting<bool> reporting_services{ Setting<bool> reporting_services{
linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false}; linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false};
Setting<bool> quest_flag{linkage, false, "quest_flag", Category::Debugging}; Setting<bool> quest_flag{linkage, false, "quest_flag", Category::Debugging};
Setting<bool> use_dev_keys{linkage, false, "use_dev_keys", Category::Debugging};
Setting<bool> disable_macro_jit{linkage, false, "disable_macro_jit", Setting<bool> disable_macro_jit{linkage, false, "disable_macro_jit",
Category::DebuggingGraphics}; Category::DebuggingGraphics};
Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle", Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle",
+1 -3
View File
@@ -19,12 +19,10 @@
#include <windows.h> #include <windows.h>
#include "common/string_util.h" #include "common/string_util.h"
#else #else
#if defined(__FreeBSD__) #if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/cpuset.h> #include <sys/cpuset.h>
#include <sys/_cpuset.h> #include <sys/_cpuset.h>
#include <pthread_np.h> #include <pthread_np.h>
#elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__Bitrig__)
#include <pthread_np.h>
#endif #endif
#include <pthread.h> #include <pthread.h>
#include <sched.h> #include <sched.h>
+1 -1
View File
@@ -1220,7 +1220,7 @@ target_link_libraries(core PRIVATE
RenderDoc::API RenderDoc::API
ZLIB::ZLIB) ZLIB::ZLIB)
target_link_libraries(core PUBLIC httplib::httplib zstd::zstd) target_link_libraries(core PRIVATE httplib::httplib)
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PUBLIC ENABLE_WEB_SERVICE) target_compile_definitions(core PUBLIC ENABLE_WEB_SERVICE)
+3 -13
View File
@@ -21,7 +21,6 @@
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "common/hex_util.h" #include "common/hex_util.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/crypto/aes_util.h" #include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h" #include "core/crypto/key_manager.h"
@@ -643,15 +642,8 @@ void KeyManager::ReloadKeys() {
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
if (!Common::FS::CreateDir(keys_dir)) if (!Common::FS::CreateDir(keys_dir))
LOG_ERROR(Core, "Failed to create the keys directory."); LOG_ERROR(Core, "Failed to create the keys directory.");
if (Settings::values.use_dev_keys.GetValue()) { LoadFromFile(keys_dir / "prod.keys_autogenerated", false);
dev_mode = true; LoadFromFile(keys_dir / "prod.keys", false);
LoadFromFile(keys_dir / "dev.keys_autogenerated", false);
LoadFromFile(keys_dir / "dev.keys", false);
} else {
dev_mode = false;
LoadFromFile(keys_dir / "prod.keys_autogenerated", false);
LoadFromFile(keys_dir / "prod.keys", false);
}
LoadFromFile(keys_dir / "title.keys_autogenerated", true); LoadFromFile(keys_dir / "title.keys_autogenerated", true);
LoadFromFile(keys_dir / "title.keys", true); LoadFromFile(keys_dir / "title.keys", true);
LoadFromFile(keys_dir / "console.keys_autogenerated", false); LoadFromFile(keys_dir / "console.keys_autogenerated", false);
@@ -846,7 +838,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
std::string filename = "title.keys_autogenerated"; std::string filename = "title.keys_autogenerated";
if (category == KeyCategory::Standard) { if (category == KeyCategory::Standard) {
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; filename = "prod.keys_autogenerated";
} else if (category == KeyCategory::Console) { } else if (category == KeyCategory::Console) {
filename = "console.keys_autogenerated"; filename = "console.keys_autogenerated";
} }
@@ -944,8 +936,6 @@ bool KeyManager::KeyFileExists(bool title) {
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir); const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
if (title) if (title)
return Common::FS::Exists(keys_dir / "title.keys"); return Common::FS::Exists(keys_dir / "title.keys");
if (Settings::values.use_dev_keys.GetValue())
return Common::FS::Exists(keys_dir / "dev.keys");
return Common::FS::Exists(keys_dir / "prod.keys"); return Common::FS::Exists(keys_dir / "prod.keys");
} }
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -314,7 +314,6 @@ private:
std::array<u8, 576> eticket_extended_kek{}; std::array<u8, 576> eticket_extended_kek{};
RSAKeyPair<2048> eticket_rsa_keypair{}; RSAKeyPair<2048> eticket_rsa_keypair{};
bool dev_mode;
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
template <size_t Size> template <size_t Size>
+11 -15
View File
@@ -76,16 +76,16 @@ public:
template <typename Func> template <typename Func>
void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) { void ApplyOpOnPAddr(PAddr address, Common::ScratchBuffer<u32>& buffer, Func&& operation) {
DAddr subbits = DAddr(address & page_mask); DAddr subbits = static_cast<DAddr>(address & page_mask);
const u32 base = compressed_device_addr[(address >> page_bits)]; const u32 base = compressed_device_addr[(address >> page_bits)];
if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] { if ((base >> MULTI_FLAG_BITS) == 0) [[likely]] {
const DAddr d_address = (DAddr(base) << page_bits) + subbits; const DAddr d_address = (static_cast<DAddr>(base) << page_bits) + subbits;
operation(d_address); operation(d_address);
return; return;
} }
InnerGatherDeviceAddresses(buffer, address); InnerGatherDeviceAddresses(buffer, address);
for (u32 value : buffer) { for (u32 value : buffer) {
operation((DAddr(value) << page_bits) + subbits); operation((static_cast<DAddr>(value) << page_bits) + subbits);
} }
} }
@@ -96,12 +96,12 @@ public:
} }
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const { PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = PAddr(address & page_mask); PAddr subbits = static_cast<PAddr>(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr; auto paddr = compressed_physical_ptr[(address >> page_bits)];
if (paddr == 0) { if (paddr == 0) {
return 0; return 0;
} }
return (PAddr(paddr - 1) << page_bits) + subbits; return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits;
} }
template <typename T> template <typename T>
@@ -172,14 +172,9 @@ private:
const uintptr_t physical_base; const uintptr_t physical_base;
DeviceInterface* device_inter; DeviceInterface* device_inter;
Common::VirtualBuffer<u32> compressed_physical_ptr;
struct TrackedEntry {
VAddr cpu_backing_address;
u32 continuity_tracker;
u32 compressed_physical_ptr;
};
Common::VirtualBuffer<u32> compressed_device_addr; Common::VirtualBuffer<u32> compressed_device_addr;
Common::VirtualBuffer<TrackedEntry> tracked_entries; Common::VirtualBuffer<u32> continuity_tracker;
// Process memory interfaces // Process memory interfaces
@@ -194,16 +189,17 @@ private:
static constexpr size_t asid_start_bit = guest_max_as_bits; static constexpr size_t asid_start_bit = guest_max_as_bits;
std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) { std::pair<Asid, VAddr> ExtractCPUBacking(size_t page_index) {
auto content = tracked_entries[page_index].cpu_backing_address; auto content = cpu_backing_address[page_index];
const VAddr address = content & guest_mask; const VAddr address = content & guest_mask;
const Asid asid{static_cast<size_t>(content >> asid_start_bit)}; const Asid asid{static_cast<size_t>(content >> asid_start_bit)};
return std::make_pair(asid, address); return std::make_pair(asid, address);
} }
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) { void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit); cpu_backing_address[page_index] = address | (asid.id << asid_start_bit);
} }
Common::VirtualBuffer<VAddr> cpu_backing_address;
std::array<TranslationEntry, 4> t_slot{}; std::array<TranslationEntry, 4> t_slot{};
u32 cache_cursor = 0; u32 cache_cursor = 0;
using CounterType = u8; using CounterType = u8;
+48 -31
View File
@@ -166,21 +166,29 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits> template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} : physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())},
, device_inter{nullptr} device_inter{nullptr}, compressed_physical_ptr(device_as_size >> Memory::YUZU_PAGEBITS),
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) Settings::MemoryLayout::Memory_4Gb
{ ? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS)),
continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS),
cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>(); impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>(); cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS; const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) { for (size_t i = 0; i < total_virtual; i++) {
tracked_entries[i].compressed_physical_ptr = 0; compressed_physical_ptr[i] = 0;
tracked_entries[i].continuity_tracker = 1; continuity_tracker[i] = 1;
tracked_entries[i].cpu_backing_address = 0; cpu_backing_address[i] = 0;
} }
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS); const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() ==
Settings::MemoryLayout::Memory_4Gb
? physical_min_bits
: physical_max_bits) -
Memory::YUZU_PAGEBITS);
for (size_t i = 0; i < total_phys; i++) { for (size_t i = 0; i < total_phys; i++) {
compressed_device_addr[i] = 0; compressed_device_addr[i] = 0;
} }
@@ -220,11 +228,11 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE; const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress)); auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] { if (ptr == nullptr) [[unlikely]] {
tracked_entries[start_page_d + i].compressed_physical_ptr = 0; compressed_physical_ptr[start_page_d + i] = 0;
continue; continue;
} }
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U; auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr; compressed_physical_ptr[start_page_d + i] = phys_addr;
InsertCPUBacking(start_page_d + i, new_vaddress, asid); InsertCPUBacking(start_page_d + i, new_vaddress, asid);
const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 base_dev = compressed_device_addr[phys_addr - 1U];
const u32 new_dev = static_cast<u32>(start_page_d + i); const u32 new_dev = static_cast<u32>(start_page_d + i);
@@ -252,9 +260,9 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
device_inter->InvalidateRegion(address, size); device_inter->InvalidateRegion(address, size);
std::scoped_lock lk(mapping_guard); std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) { for (size_t i = 0; i < num_pages; i++) {
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr; auto phys_addr = compressed_physical_ptr[start_page_d + i];
tracked_entries[start_page_d + i].compressed_physical_ptr = 0; compressed_physical_ptr[start_page_d + i] = 0;
tracked_entries[start_page_d + i].cpu_backing_address = 0; cpu_backing_address[start_page_d + i] = 0;
if (phys_addr != 0) [[likely]] { if (phys_addr != 0) [[likely]] {
const u32 base_dev = compressed_device_addr[phys_addr - 1U]; const u32 base_dev = compressed_device_addr[phys_addr - 1U];
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] { if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
@@ -292,14 +300,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
page_count = 1; page_count = 1;
} }
last_ptr = new_ptr; last_ptr = new_ptr;
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count); continuity_tracker[start_page_d + index] = static_cast<u32>(page_count);
} }
} }
template <typename Traits> template <typename Traits>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) { u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits; size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr); return GetPointer<u8>(src_addr);
} }
return nullptr; return nullptr;
@@ -309,7 +317,7 @@ template <typename Traits>
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const { const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
size_t page_index = src_addr >> page_bits; size_t page_index = src_addr >> page_bits;
size_t subbits = src_addr & page_mask; size_t subbits = src_addr & page_mask;
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) { if ((static_cast<size_t>(continuity_tracker[page_index]) << page_bits) >= size + subbits) {
return GetPointer<u8>(src_addr); return GetPointer<u8>(src_addr);
} }
return nullptr; return nullptr;
@@ -334,10 +342,12 @@ template <typename T>
T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) { T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) {
const size_t index = address >> Memory::YUZU_PAGEBITS; const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK; const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = tracked_entries[index].compressed_physical_ptr; auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] if (phys_addr == 0) [[unlikely]] {
return nullptr; return nullptr;
return GetPointerFromRaw<T>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + offset); }
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
} }
template <typename Traits> template <typename Traits>
@@ -345,10 +355,12 @@ template <typename T>
const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const { const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const {
const size_t index = address >> Memory::YUZU_PAGEBITS; const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK; const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = tracked_entries[index].compressed_physical_ptr; auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) if (phys_addr == 0) [[unlikely]] {
return nullptr; return nullptr;
return GetPointerFromRaw<T>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + offset); }
return GetPointerFromRaw<T>((static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) +
offset);
} }
template <typename Traits> template <typename Traits>
@@ -374,14 +386,18 @@ T DeviceMemoryManager<Traits>::Read(DAddr address) const {
} }
template <typename Traits> template <typename Traits>
void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped, auto on_memory, auto increment) { void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto on_unmapped,
auto on_memory, auto increment) {
std::size_t remaining_size = size; std::size_t remaining_size = size;
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS; std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK; std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) { while (remaining_size) {
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker); const size_t next_pages = static_cast<std::size_t>(continuity_tracker[page_index]);
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size); const std::size_t copy_amount =
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset); (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT{ SCOPE_EXIT{
page_index += next_pages; page_index += next_pages;
page_offset = 0; page_offset = 0;
@@ -389,12 +405,13 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
remaining_size -= copy_amount; remaining_size -= copy_amount;
}; };
auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr == 0) { if (phys_addr == 0) {
on_unmapped(copy_amount, current_vaddr); on_unmapped(copy_amount, current_vaddr);
continue; continue;
} }
auto* mem_ptr = GetPointerFromRaw<u8>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset); auto* mem_ptr = GetPointerFromRaw<u8>(
(static_cast<PAddr>(phys_addr - 1) << Memory::YUZU_PAGEBITS) + page_offset);
on_memory(copy_amount, mem_ptr); on_memory(copy_amount, mem_ptr);
} }
} }
@@ -413,7 +430,7 @@ void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, s
} }
const std::size_t page_index = address >> Memory::YUZU_PAGEBITS; const std::size_t page_index = address >> Memory::YUZU_PAGEBITS;
const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; const auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr != 0) { if (phys_addr != 0) {
auto* const mem_ptr = GetPointerFromRaw<u8>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS)); auto* const mem_ptr = GetPointerFromRaw<u8>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS));
t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr}; t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr};
@@ -471,7 +488,7 @@ void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_poin
} }
const std::size_t page_index = address >> Memory::YUZU_PAGEBITS; const std::size_t page_index = address >> Memory::YUZU_PAGEBITS;
const auto phys_addr = tracked_entries[page_index].compressed_physical_ptr; const auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr != 0) { if (phys_addr != 0) {
auto* const mem_ptr = GetPointerFromRaw<u8>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS)); auto* const mem_ptr = GetPointerFromRaw<u8>((PAddr(phys_addr - 1) << Memory::YUZU_PAGEBITS));
t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr}; t_slot[cache_cursor % t_slot.size()] = TranslationEntry{.guest_page = guest_page, .host_ptr = mem_ptr};
+40 -103
View File
@@ -123,39 +123,6 @@ bool IsVersionedExternalUpdateDisabled(const std::vector<std::string>& disabled,
return std::find(disabled.cbegin(), disabled.cend(), disabled_key) != disabled.cend() || return std::find(disabled.cbegin(), disabled.cend(), disabled_key) != disabled.cend() ||
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend(); std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
} }
std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64 update_tid) {
if (provider == nullptr) {
return {};
}
auto control_nca = provider->GetEntry(update_tid, ContentRecordType::Control);
if (control_nca == nullptr ||
control_nca->GetStatus() != Loader::ResultStatus::Success) {
return {};
}
const auto romfs = control_nca->GetRomFS();
if (romfs == nullptr) {
return {};
}
const auto extracted = ExtractRomFS(romfs);
if (extracted == nullptr) {
return {};
}
auto nacp_file = extracted->GetFile("control.nacp");
if (nacp_file == nullptr) {
nacp_file = extracted->GetFile("Control.nacp");
}
if (nacp_file == nullptr) {
return {};
}
NACP nacp{nacp_file};
return nacp.GetVersionString();
}
} // Anonymous namespace } // Anonymous namespace
PatchManager::PatchManager(u64 title_id_, PatchManager::PatchManager(u64 title_id_,
@@ -804,7 +771,6 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::nullopt, std::nullopt, ContentRecordType::Program, update_tid); std::nullopt, std::nullopt, ContentRecordType::Program, update_tid);
for (const auto& [slot, entry] : all_updates) { for (const auto& [slot, entry] : all_updates) {
(void)entry;
if (slot == ContentProviderUnionSlot::External || if (slot == ContentProviderUnionSlot::External ||
slot == ContentProviderUnionSlot::FrontendManual) { slot == ContentProviderUnionSlot::FrontendManual) {
continue; continue;
@@ -820,7 +786,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
source_suffix = " (NAND)"; source_suffix = " (NAND)";
break; break;
case ContentProviderUnionSlot::SDMC: case ContentProviderUnionSlot::SDMC:
source_type = PatchSource::SDMC; source_type = PatchSource::NAND;
source_suffix = " (SDMC)"; source_suffix = " (SDMC)";
break; break;
default: default:
@@ -829,16 +795,19 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::string version_str; std::string version_str;
u32 numeric_ver = 0; u32 numeric_ver = 0;
const auto* slot_provider = content_union->GetSlotProvider(slot); PatchManager update{update_tid, fs_controller, content_provider};
version_str = GetUpdateVersionStringFromSlot(slot_provider, update_tid); const auto metadata = update.GetControlMetadata();
const auto& nacp = metadata.first;
if (slot_provider != nullptr) { if (nacp != nullptr) {
const auto slot_ver = slot_provider->GetEntryVersion(update_tid); version_str = nacp->GetVersionString();
if (slot_ver.has_value()) { }
numeric_ver = *slot_ver;
if (version_str.empty() && numeric_ver != 0) { const auto meta_ver = content_provider.GetEntryVersion(update_tid);
version_str = FormatTitleVersion(numeric_ver); if (meta_ver.has_value()) {
} numeric_ver = *meta_ver;
if (version_str.empty() && numeric_ver != 0) {
version_str = FormatTitleVersion(numeric_ver);
} }
} }
@@ -987,60 +956,37 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
} }
// DLC // DLC
const auto dlc_entries =
content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match; std::vector<ContentProviderEntry> dlc_match;
bool has_external_dlc = false; dlc_match.reserve(dlc_entries.size());
bool has_nand_dlc = false; std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
bool has_sdmc_dlc = false; [this](const ContentProviderEntry& entry) {
bool has_other_dlc = false; const auto base_tid = GetBaseTitleID(entry.title_id);
const auto dlc_entries_with_origin = const bool matches_base = base_tid == title_id;
content_union->ListEntriesFilterOrigin(std::nullopt, TitleType::AOC, ContentRecordType::Data);
dlc_match.reserve(dlc_entries_with_origin.size()); if (!matches_base) {
for (const auto& [slot, entry] : dlc_entries_with_origin) { LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
const auto base_tid = GetBaseTitleID(entry.title_id); entry.title_id, base_tid, title_id);
const bool matches_base = base_tid == title_id; return false;
if (!matches_base) { }
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
entry.title_id, base_tid, title_id);
continue;
}
const auto* slot_provider = content_union->GetSlotProvider(slot); auto nca = content_provider.GetEntry(entry);
if (slot_provider == nullptr) { if (!nca) {
continue; LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id);
} return false;
}
auto nca = slot_provider->GetEntry(entry); const auto status = nca->GetStatus();
if (!nca) { if (status != Loader::ResultStatus::Success) {
LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id); LOG_DEBUG(Loader, "DLC {:016X} NCA has status {}",
continue; entry.title_id, static_cast<int>(status));
} return false;
}
const auto status = nca->GetStatus(); return true;
if (status != Loader::ResultStatus::Success) { });
LOG_DEBUG(Loader, "DLC {:016X} NCA has status {}", entry.title_id,
static_cast<int>(status));
continue;
}
switch (slot) {
case ContentProviderUnionSlot::External:
case ContentProviderUnionSlot::FrontendManual:
has_external_dlc = true;
break;
case ContentProviderUnionSlot::UserNAND:
case ContentProviderUnionSlot::SysNAND:
has_nand_dlc = true;
break;
case ContentProviderUnionSlot::SDMC:
has_sdmc_dlc = true;
break;
default:
has_other_dlc = true;
break;
}
dlc_match.push_back(entry);
}
if (!dlc_match.empty()) { if (!dlc_match.empty()) {
// Ensure sorted so DLC IDs show in order. // Ensure sorted so DLC IDs show in order.
@@ -1054,22 +1000,13 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
const auto dlc_disabled = const auto dlc_disabled =
std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end();
PatchSource dlc_source = PatchSource::Unknown;
if (has_external_dlc && !has_nand_dlc && !has_sdmc_dlc && !has_other_dlc) {
dlc_source = PatchSource::External;
} else if (has_nand_dlc && !has_external_dlc && !has_sdmc_dlc && !has_other_dlc) {
dlc_source = PatchSource::NAND;
} else if (has_sdmc_dlc && !has_external_dlc && !has_nand_dlc && !has_other_dlc) {
dlc_source = PatchSource::SDMC;
}
out.push_back({.enabled = !dlc_disabled, out.push_back({.enabled = !dlc_disabled,
.name = "DLC", .name = "DLC",
.version = std::move(list), .version = std::move(list),
.type = PatchType::DLC, .type = PatchType::DLC,
.program_id = title_id, .program_id = title_id,
.title_id = dlc_match.back().title_id, .title_id = dlc_match.back().title_id,
.source = dlc_source}); .source = PatchSource::Unknown});
} }
return out; return out;
-1
View File
@@ -34,7 +34,6 @@ enum class PatchType { Update, DLC, Mod };
enum class PatchSource { enum class PatchSource {
Unknown, Unknown,
NAND, NAND,
SDMC,
External, External,
Packed, Packed,
}; };
+3 -9
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -107,8 +104,7 @@ Result TimeZoneService::LoadLocationNameList(
OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) { OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index) {
SCOPE_EXIT { SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}", LOG_DEBUG(Service_Time, "called. index={} out_count={} out_names[0]={} out_names[1]={}",
index, *out_count, out_names.size() > 0 ? out_names[0] : Service::PSC::Time::LocationName{}, index, *out_count, out_names[0], out_names[1]);
out_names.size() > 1 ? out_names[1] : Service::PSC::Time::LocationName{});
}; };
std::scoped_lock l{m_mutex}; std::scoped_lock l{m_mutex};
@@ -212,8 +208,7 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
SCOPE_EXIT { SCOPE_EXIT {
LOG_DEBUG(Service_Time, LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, calendar_time, *out_count, out_times[0], out_times[1]);
out_times.size() > 1 ? out_times[1] : s64{0});
}; };
R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule)); R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
@@ -225,8 +220,7 @@ Result TimeZoneService::ToPosixTimeWithMyRule(
SCOPE_EXIT { SCOPE_EXIT {
LOG_DEBUG(Service_Time, LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}", "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, calendar_time, *out_count, out_times[0], out_times[1]);
out_times.size() > 1 ? out_times[1] : s64{0});
}; };
R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time)); R_RETURN(m_wrapped_service->ToPosixTimeWithMyRule(out_count, out_times, calendar_time));
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -151,8 +148,7 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
SCOPE_EXIT { SCOPE_EXIT {
LOG_DEBUG(Service_Time, LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, calendar_time, *out_count, out_times[0], out_times[1]);
out_times.size() > 1 ? out_times[1] : s64{0});
}; };
R_RETURN( R_RETURN(
@@ -165,8 +161,7 @@ Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
SCOPE_EXIT { SCOPE_EXIT {
LOG_DEBUG(Service_Time, LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ", "called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0}, calendar_time, *out_count, out_times[0], out_times[1]);
out_times.size() > 1 ? out_times[1] : s64{0});
}; };
R_RETURN( R_RETURN(
+7 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -29,13 +29,10 @@ namespace Service {
return function_string; return function_string;
} }
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_) ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
: SessionRequestHandler(system_.Kernel(), service_name_) u32 max_sessions_, InvokerFn* handler_invoker_)
, system{system_} : SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
, service_name{service_name_} service_name{service_name_}, handler_invoker{handler_invoker_}, max_sessions{max_sessions_} {}
, handler_invoker{handler_invoker_}
, max_sessions{max_sessions_}
{}
ServiceFrameworkBase::~ServiceFrameworkBase() { ServiceFrameworkBase::~ServiceFrameworkBase() {
// Wait for other threads to release access before destroying // Wait for other threads to release access before destroying
@@ -53,7 +50,8 @@ void ServiceFrameworkBase::RegisterHandlersBaseTipc(const FunctionInfoBase* func
// Usually this array is sorted by id already, so hint to insert at the end // Usually this array is sorted by id already, so hint to insert at the end
handlers_tipc.reserve(handlers_tipc.size() + n); handlers_tipc.reserve(handlers_tipc.size() + n);
for (std::size_t i = 0; i < n; ++i) for (std::size_t i = 0; i < n; ++i)
handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header, functions[i]); handlers_tipc.emplace_hint(handlers_tipc.cend(), functions[i].expected_header,
functions[i]);
} }
void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx, void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
+17 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -8,7 +8,8 @@
#include <cstddef> #include <cstddef>
#include <mutex> #include <mutex>
#include <ankerl/unordered_dense.h> #include <string>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/service/hle_ipc.h" #include "core/hle/service/hle_ipc.h"
@@ -77,6 +78,13 @@ protected:
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() noexcept { [[nodiscard]] virtual std::unique_lock<std::mutex> LockService() noexcept {
return std::unique_lock{lock_service}; return std::unique_lock{lock_service};
} }
/// System context that the service operates under.
Core::System& system;
/// Identifier string used to connect to the service.
std::string service_name;
private: private:
template <typename T> template <typename T>
friend class ServiceFramework; friend class ServiceFramework;
@@ -98,19 +106,16 @@ private:
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n); void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info); void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info);
protected: boost::container::flat_map<u32, FunctionInfoBase> handlers;
ankerl::unordered_dense::map<u32, FunctionInfoBase> handlers; boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
ankerl::unordered_dense::map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
std::mutex lock_service; std::mutex lock_service;
/// System context that the service operates under.
Core::System& system;
/// Identifier string used to connect to the service.
const char* service_name;
/// Function used to safely up-cast pointers to the derived class before invoking a handler. /// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker; InvokerFn* handler_invoker;
/// Maximum number of concurrent sessions that this service can handle. /// Maximum number of concurrent sessions that this service can handle.
u32 max_sessions; u32 max_sessions;
/// Flag to store if a port was already create/installed to detect multiple install attempts, /// Flag to store if a port was already create/installed to detect multiple install attempts,
/// which is not supported. /// which is not supported.
bool service_registered = false; bool service_registered = false;
@@ -154,7 +159,8 @@ protected:
* @param max_sessions_ Maximum number of sessions that can be connected to this service at the * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
* same time. * same time.
*/ */
explicit ServiceFramework(Core::System& system_, const char* service_name_, u32 max_sessions_ = ServerSessionCountMax) explicit ServiceFramework(Core::System& system_, const char* service_name_,
u32 max_sessions_ = ServerSessionCountMax)
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
/// Registers handlers in the service. /// Registers handlers in the service.
@@ -213,7 +219,7 @@ private:
static void Invoker(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member, static void Invoker(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member,
HLERequestContext& ctx) { HLERequestContext& ctx) {
// Cast back up to our original types and call the member function // Cast back up to our original types and call the member function
(static_cast<Self*>(object)->*HandlerFnP<Self>(member))(ctx); (static_cast<Self*>(object)->*static_cast<HandlerFnP<Self>>(member))(ctx);
} }
}; };
+12
View File
@@ -0,0 +1,12 @@
Copyright (C) 2017 merryhime <git@mary.rs>
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -687,7 +687,7 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& arg = args[0]; auto& arg = args[0];
const u64 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE; const u32 upper_without_t = (ctx.EndLocation().SetSingleStepping(false).UniqueHash() >> 32) & 0xFFFFFFFE;
// Pseudocode: // Pseudocode:
// if (new_pc & 1) { // if (new_pc & 1) {
@@ -947,7 +947,7 @@ static void EmitAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, size_t
const Xbyak::Reg8 overflow = overflow_inst ? ctx.reg_alloc.ScratchGpr(code).cvt8() : Xbyak::Reg8{-1}; const Xbyak::Reg8 overflow = overflow_inst ? ctx.reg_alloc.ScratchGpr(code).cvt8() : Xbyak::Reg8{-1};
if (args[1].IsImmediate() && args[1].GetType() == IR::Type::U32) { if (args[1].IsImmediate() && args[1].GetType() == IR::Type::U32) {
const u64 op_arg = args[1].GetImmediateU64(); const u32 op_arg = args[1].GetImmediateU32();
if (carry_in.IsImmediate()) { if (carry_in.IsImmediate()) {
if (carry_in.GetImmediateU1()) { if (carry_in.GetImmediateU1()) {
// In range for a valid LEA materialisation // In range for a valid LEA materialisation
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@@ -135,7 +135,7 @@ void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
const u32 mask = (1u << N) - 1; const u32 mask = (1u << N) - 1;
const u32 positive_saturated_value = (1u << (N - 1)) - 1; const u32 positive_saturated_value = (1u << (N - 1)) - 1;
const u64 negative_saturated_value = 1u << (N - 1); const u32 negative_saturated_value = 1u << (N - 1);
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr(code).cvt32(); const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr(code).cvt32();
const Xbyak::Reg32 reg_a = ctx.reg_alloc.UseGpr(code, args[0]).cvt32(); const Xbyak::Reg32 reg_a = ctx.reg_alloc.UseGpr(code, args[0]).cvt32();
+1 -5
View File
@@ -1,8 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project # SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
set(CMAKE_AUTOMOC ON)
add_library(qt_common STATIC add_library(qt_common STATIC
qt_common.h qt_common.h
qt_common.cpp qt_common.cpp
@@ -28,7 +26,7 @@ add_library(qt_common STATIC
util/mod.h util/mod.cpp util/mod.h util/mod.cpp
abstract/frontend.h abstract/frontend.cpp abstract/frontend.h abstract/frontend.cpp
abstract/progress.h abstract/progress.cpp abstract/qt_progress_dialog.h abstract/qt_progress_dialog.cpp
qt_string_lookup.h qt_string_lookup.h
qt_compat.h qt_compat.h
@@ -95,5 +93,3 @@ if (UNIX AND NOT APPLE)
target_link_libraries(qt_common PRIVATE Qt6::GuiPrivate) target_link_libraries(qt_common PRIVATE Qt6::GuiPrivate)
endif() endif()
endif() endif()
create_target_directory_groups(qt_common)
+56 -8
View File
@@ -1,27 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <QLineEdit>
#include "frontend.h" #include "frontend.h"
#include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS
#include <QFileDialog>
#endif
#include <QAbstractButton>
#include <QInputDialog>
namespace QtCommon::Frontend { 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<QMessageBox::StandardButton>(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, const QString GetOpenFileName(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter) { QString* selectedFilter, Options options) {
return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter); #ifdef YUZU_QT_WIDGETS
return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif
} }
const QStringList GetOpenFileNames(const QString& title, const QString& dir, const QString& filter, const QStringList GetOpenFileNames(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter) { QString* selectedFilter, Options options) {
return QFileDialog::getOpenFileNames(rootObject, title, dir, filter, selectedFilter); #ifdef YUZU_QT_WIDGETS
return QFileDialog::getOpenFileNames(rootObject, title, dir, filter, selectedFilter, options);
#endif
} }
const QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter, const QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter) { QString* selectedFilter, Options options) {
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter); #ifdef YUZU_QT_WIDGETS
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif
} }
const QString GetExistingDirectory(const QString& caption, const QString& dir) { const QString GetExistingDirectory(const QString& caption, const QString& dir, Options options) {
return QFileDialog::getExistingDirectory(rootObject, caption, dir); #ifdef YUZU_QT_WIDGETS
return QFileDialog::getExistingDirectory(rootObject, caption, dir, options);
#endif
}
int Choice(const QString& title, const QString& caption, const QStringList& options) {
QMessageBox box(rootObject);
box.setText(caption);
box.setWindowTitle(title);
for (const QString& opt : options) {
box.addButton(opt, QMessageBox::AcceptRole);
}
box.addButton(QMessageBox::Cancel);
box.exec();
auto button = box.clickedButton();
return options.indexOf(button->text());
}
const QString GetTextInput(const QString& title, const QString& caption,
const QString& defaultText) {
return QInputDialog::getText(rootObject, title, caption, QLineEdit::Normal, defaultText);
} }
} // namespace QtCommon::Frontend } // namespace QtCommon::Frontend
+95 -57
View File
@@ -7,7 +7,11 @@
#include <QGuiApplication> #include <QGuiApplication>
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS
#include <QFileDialog> #include <QFileDialog>
#include <QWidget>
#include <QMessageBox>
#endif
/** /**
* manages common functionality e.g. message boxes and such for Qt/QML * manages common functionality e.g. message boxes and such for Qt/QML
@@ -16,39 +20,60 @@ namespace QtCommon::Frontend {
Q_NAMESPACE 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 { enum StandardButton {
// keep this in sync with QDialogButtonBox::StandardButton and // keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
// QPlatformDialogHelper::StandardButton NoButton = 0x00000000,
NoButton = 0x00000000, Ok = 0x00000400,
Ok = 0x00000400, Save = 0x00000800,
Save = 0x00000800, SaveAll = 0x00001000,
SaveAll = 0x00001000, Open = 0x00002000,
Open = 0x00002000, Yes = 0x00004000,
Yes = 0x00004000, YesToAll = 0x00008000,
YesToAll = 0x00008000, No = 0x00010000,
No = 0x00010000, NoToAll = 0x00020000,
NoToAll = 0x00020000, Abort = 0x00040000,
Abort = 0x00040000, Retry = 0x00080000,
Retry = 0x00080000, Ignore = 0x00100000,
Ignore = 0x00100000, Close = 0x00200000,
Close = 0x00200000, Cancel = 0x00400000,
Cancel = 0x00400000, Discard = 0x00800000,
Discard = 0x00800000, Help = 0x01000000,
Help = 0x01000000, Apply = 0x02000000,
Apply = 0x02000000, Reset = 0x04000000,
Reset = 0x04000000, RestoreDefaults = 0x08000000,
RestoreDefaults = 0x08000000,
FirstButton = Ok, // internal FirstButton = Ok, // internal
LastButton = RestoreDefaults, // internal LastButton = RestoreDefaults, // internal
YesAll = YesToAll, // obsolete YesAll = YesToAll, // obsolete
NoAll = NoToAll, // obsolete NoAll = NoToAll, // obsolete
Default = 0x00000100, // obsolete Default = 0x00000100, // obsolete
Escape = 0x00000200, // obsolete Escape = 0x00000200, // obsolete
FlagMask = 0x00000300, // obsolete FlagMask = 0x00000300, // obsolete
ButtonMask = ~FlagMask // obsolete ButtonMask = ~FlagMask // obsolete
}; };
Q_ENUM_NS(StandardButton) Q_ENUM_NS(StandardButton)
@@ -58,7 +83,7 @@ typedef StandardButton Button;
Q_DECLARE_FLAGS(StandardButtons, StandardButton) Q_DECLARE_FLAGS(StandardButtons, StandardButton)
Q_FLAG_NS(StandardButtons) Q_FLAG_NS(StandardButtons)
enum class Icon { enum Icon {
// keep this in sync with QMessageDialogOptions::StandardIcon // keep this in sync with QMessageDialogOptions::StandardIcon
NoIcon = 0, NoIcon = 0,
Information = 1, Information = 1,
@@ -68,26 +93,29 @@ enum class Icon {
}; };
Q_ENUM_NS(Icon) Q_ENUM_NS(Icon)
StandardButton ShowMessage(Icon icon, const QString& title, const QString& text, #endif
StandardButtons buttons = StandardButton::NoButton,
QObject* parent = nullptr);
#define UTIL_OVERRIDES(level) \ // TODO(crueter) widgets-less impl, choices et al.
inline StandardButton level(QObject* parent, const QString& title, const QString& text, \ StandardButton ShowMessage(Icon icon,
StandardButtons buttons) { \ const QString &title,
return ShowMessage(Icon::level, title, text, buttons, parent); \ const QString &text,
} \ StandardButtons buttons = StandardButton::NoButton,
inline StandardButton level(QObject* parent, const QString& title, const QString& text, \ QObject *parent = nullptr);
int buttons = StandardButton::Ok) { \
return ShowMessage(Icon::level, title, text, StandardButtons(buttons), parent); \ #define UTIL_OVERRIDES(level) \
} \ inline StandardButton level(QObject *parent, \
inline StandardButton level(const QString title, const QString& text, \ const QString &title, \
StandardButtons buttons) { \ const QString &text, \
return ShowMessage(Icon::level, title, text, buttons, rootObject); \ StandardButtons buttons = StandardButton::Ok) \
} \ { \
inline StandardButton level(const QString& title, const QString& text, \ return ShowMessage(Icon::level, title, text, buttons, parent); \
int buttons = StandardButton::Ok) { \ } \
return ShowMessage(Icon::level, title, text, StandardButtons(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(Information)
@@ -95,17 +123,27 @@ UTIL_OVERRIDES(Warning)
UTIL_OVERRIDES(Critical) UTIL_OVERRIDES(Critical)
UTIL_OVERRIDES(Question) UTIL_OVERRIDES(Question)
const QString GetOpenFileName(const QString& title, const QString& dir, const QString& filter, const QString GetOpenFileName(const QString &title,
QString* selectedFilter = nullptr); const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QStringList GetOpenFileNames(const QString& title, const QString& dir, const QString& filter, const QStringList GetOpenFileNames(const QString &title,
QString* selectedFilter = nullptr); const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter, const QString GetSaveFileName(const QString &title,
QString* selectedFilter = nullptr); const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QString GetExistingDirectory(const QString& caption = QString(), const QString GetExistingDirectory(const QString &caption = QString(),
const QString& dir = QString()); const QString &dir = QString(),
Options options = Option::ShowDirsOnly);
int Choice(const QString& title = QString(), const QString& caption = QString(), int Choice(const QString& title = QString(), const QString& caption = QString(),
const QStringList& options = {}); const QStringList& options = {});
-12
View File
@@ -1,12 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "progress.h"
namespace QtCommon::Frontend {
QtProgressDialog::QtProgressDialog(const QString&, const QString&, int, int, QObject* parent,
Qt::WindowFlags)
: QObject(parent) {}
} // namespace QtCommon::Frontend
-45
View File
@@ -1,45 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <memory>
#include <QObject>
namespace QtCommon::Frontend {
class QtProgressDialog : public QObject {
Q_OBJECT
public:
QtProgressDialog(const QString& labelText, const QString& cancelButtonText, int minimum,
int maximum, QObject* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
virtual ~QtProgressDialog() = default;
virtual bool wasCanceled() const = 0;
virtual void setWindowModality(Qt::WindowModality modality) = 0;
virtual void setMinimumDuration(int durationMs) = 0;
virtual void setAutoClose(bool autoClose) = 0;
virtual void setAutoReset(bool autoReset) = 0;
public slots:
virtual void setTitle(QString title) = 0;
virtual void setLabelText(QString text) = 0;
virtual void setMinimum(int min) = 0;
virtual void setMaximum(int max) = 0;
virtual void setValue(int value) = 0;
virtual bool close() = 0;
virtual void show() = 0;
};
std::unique_ptr<QtProgressDialog> newProgressDialog(const QString& labelText,
const QString& cancelButtonText, int minimum,
int maximum,
Qt::WindowFlags f = Qt::WindowFlags());
QtProgressDialog* newProgressDialogPtr(const QString& labelText, const QString& cancelButtonText,
int minimum, int maximum,
Qt::WindowFlags f = Qt::WindowFlags());
} // namespace QtCommon::Frontend
@@ -0,0 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "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 <QWindow>
#ifdef YUZU_QT_WIDGETS
#include <QProgressDialog>
#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
+5 -5
View File
@@ -319,7 +319,7 @@ void QtConfig::ReadUIGamelistValues() {
} }
void QtConfig::ReadUILayoutValues() { void QtConfig::ReadUILayoutValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout)); BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
ReadCategory(Settings::Category::UiLayout); ReadCategory(Settings::Category::UiLayout);
@@ -578,10 +578,10 @@ void QtConfig::SaveMultiplayerValues() {
} }
std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) { std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
auto& list = Settings::values.linkage.by_category[category]; auto& map = Settings::values.linkage.by_category;
if (!list.empty()) if (map.contains(category)) {
return list; return Settings::values.linkage.by_category[category];
}
return UISettings::values.linkage.by_category[category]; return UISettings::values.linkage.by_category[category];
} }
+306 -196
View File
@@ -9,23 +9,24 @@
#include "shared_translation.h" #include "shared_translation.h"
#include <map>
#include <memory>
#include <utility>
#include <QCoreApplication> #include <QCoreApplication>
#include "common/settings.h" #include "common/settings.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "common/settings_setting.h" #include "common/settings_setting.h"
#include "common/time_zone.h" #include "common/time_zone.h"
#include "qt_common/config/uisettings.h" #include "qt_common/config/uisettings.h"
#include <map>
#include <memory>
#include <utility>
namespace ConfigurationShared { namespace ConfigurationShared {
std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) { std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
{
std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>(); std::unique_ptr<TranslationMap> translations = std::make_unique<TranslationMap>();
const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); };
#define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \
translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}}) translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}})
// A setting can be ignored by giving it a blank name // A setting can be ignored by giving it a blank name
@@ -46,9 +47,10 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, login_share_applet_mode, tr("Login share"), QString()); INSERT(Settings, login_share_applet_mode, tr("Login share"), QString());
INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString()); INSERT(Settings, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString());
INSERT(Settings, my_page_applet_mode, tr("My page"), QString()); INSERT(Settings, my_page_applet_mode, tr("My page"), QString());
INSERT(Settings, enable_overlay, tr("Enable Overlay Applet"), INSERT(Settings,
tr("Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 " enable_overlay,
"second to show it.")); tr("Enable Overlay Applet"),
tr("Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 second to show it."));
// Audio // Audio
INSERT(Settings, sink_id, tr("Output Engine:"), QString()); INSERT(Settings, sink_id, tr("Output Engine:"), QString());
@@ -60,16 +62,23 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), QString()); INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), QString());
// Core // Core
INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), INSERT(
tr("This option increases CPU emulation thread use from 1 to the maximum of 4.\n" Settings,
"This is mainly a debug option and shouldn't be disabled.")); use_multi_core,
INSERT(Settings, memory_layout_mode, tr("Memory Layout"), tr("Multicore CPU Emulation"),
tr("Increases the amount of emulated RAM.\nDoesn't affect performance/stability but may " tr("This option increases CPU emulation thread use from 1 to the maximum of 4.\n"
"allow HD texture " "This is mainly a debug option and shouldn't be disabled."));
"mods to load.")); INSERT(
Settings,
memory_layout_mode,
tr("Memory Layout"),
tr("Increases the amount of emulated RAM.\nDoesn't affect performance/stability but may allow HD texture "
"mods to load."));
INSERT(Settings, use_speed_limit, QString(), QString()); INSERT(Settings, use_speed_limit, QString(), QString());
INSERT(Settings, current_speed_mode, QString(), QString()); INSERT(Settings, current_speed_mode, QString(), QString());
INSERT(Settings, speed_limit, tr("Limit Speed Percent"), INSERT(Settings,
speed_limit,
tr("Limit Speed Percent"),
tr("Controls the game's maximum rendering speed, but it's up to each game if it runs " tr("Controls the game's maximum rendering speed, but it's up to each game if it runs "
"faster or not.\n200% for a 30 FPS game is 60 FPS, and for a " "faster or not.\n200% for a 30 FPS game is 60 FPS, and for a "
"60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the " "60 FPS game it will be 120 FPS.\nDisabling it means unlocking the framerate to the "
@@ -82,128 +91,171 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("When the Slow Speed hotkey is pressed, the speed will be limited to this " tr("When the Slow Speed hotkey is pressed, the speed will be limited to this "
"percentage.")); "percentage."));
INSERT(Settings, sync_core_speed, tr("Synchronize Core Speed"), INSERT(Settings,
sync_core_speed,
tr("Synchronize Core Speed"),
tr("Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS " tr("Synchronizes CPU core speed with the game's maximum rendering speed to boost FPS "
"without affecting game speed (animations, physics, etc.).\n" "without affecting game speed (animations, physics, etc.).\n"
"Can help reduce stuttering at lower framerates.")); "Can help reduce stuttering at lower framerates."));
// Cpu // Cpu
INSERT(Settings, cpu_accuracy, tr("Accuracy:"), INSERT(Settings,
cpu_accuracy,
tr("Accuracy:"),
tr("Change the accuracy of the emulated CPU (for debugging only).")); tr("Change the accuracy of the emulated CPU (for debugging only)."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString()); INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, fast_cpu_time, tr("CPU Overclock"), INSERT(Settings,
tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see " fast_cpu_time,
"reduced performance, " tr("CPU Overclock"),
"and certain games may behave improperly.\nUse Boost (1700MHz) to run at the " tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see reduced performance, "
"Switch's highest native " "and certain games may behave improperly.\nUse Boost (1700MHz) to run at the Switch's highest native "
"clock, or Fast (2000MHz) to run at 2x clock.")); "clock, or Fast (2000MHz) to run at 2x clock."));
INSERT(Settings, use_custom_cpu_ticks, QString(), QString()); INSERT(Settings, use_custom_cpu_ticks, QString(), QString());
INSERT(Settings, cpu_ticks, tr("Custom CPU Ticks"), INSERT(Settings,
cpu_ticks,
tr("Custom CPU Ticks"),
tr("Set a custom value of CPU ticks. Higher values can increase performance, but may " tr("Set a custom value of CPU ticks. Higher values can increase performance, but may "
"cause deadlocks. A range of 77-21000 is recommended.")); "cause deadlocks. A range of 77-21000 is recommended."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString()); INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, vtable_bouncing, tr("Virtual Table Bouncing"), INSERT(Settings, vtable_bouncing,
tr("Bounces (by emulating a 0-valued return) any functions that triggers a prefetch " tr("Virtual Table Bouncing"),
"abort")); tr("Bounces (by emulating a 0-valued return) any functions that triggers a prefetch abort"));
// Cpu Debug // Cpu Debug
// Cpu Unsafe // Cpu Unsafe
INSERT(Settings, cpuopt_unsafe_host_mmu, tr("Enable Host MMU Emulation (fastmem)"),
tr("This optimization speeds up memory accesses by the guest program.\nEnabling it causes guest memory reads/writes to be done directly into memory and make use of Host's MMU.\nDisabling this forces all memory accesses to use Software MMU Emulation."));
INSERT( INSERT(
Settings, cpuopt_unsafe_host_mmu, tr("Enable Host MMU Emulation (fastmem)"), Settings,
tr("This optimization speeds up memory accesses by the guest program.\nEnabling it causes " cpuopt_unsafe_unfuse_fma,
"guest memory reads/writes to be done directly into memory and make use of Host's "
"MMU.\nDisabling this forces all memory accesses to use Software MMU Emulation."));
INSERT(
Settings, cpuopt_unsafe_unfuse_fma,
tr("Unfuse FMA (improve performance on CPUs without FMA)"), tr("Unfuse FMA (improve performance on CPUs without FMA)"),
tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on " tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support.")); "CPUs without native FMA support."));
INSERT( INSERT(
Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"), Settings,
cpuopt_unsafe_reduce_fp_error,
tr("Faster FRSQRTE and FRECPE"),
tr("This option improves the speed of some approximate floating-point functions by using " tr("This option improves the speed of some approximate floating-point functions by using "
"less accurate native approximations.")); "less accurate native approximations."));
INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, INSERT(Settings,
cpuopt_unsafe_ignore_standard_fpcr,
tr("Faster ASIMD instructions (32 bits only)"), tr("Faster ASIMD instructions (32 bits only)"),
tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"with incorrect rounding modes.")); "with incorrect rounding modes."));
INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), INSERT(Settings,
cpuopt_unsafe_inaccurate_nan,
tr("Inaccurate NaN handling"),
tr("This option improves speed by removing NaN checking.\nPlease note this also reduces " tr("This option improves speed by removing NaN checking.\nPlease note this also reduces "
"accuracy of certain floating-point instructions.")); "accuracy of certain floating-point instructions."));
INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), INSERT(Settings,
cpuopt_unsafe_fastmem_check,
tr("Disable address space checks"),
tr("This option improves speed by eliminating a safety check before every memory " tr("This option improves speed by eliminating a safety check before every memory "
"operation.\nDisabling it may allow arbitrary code execution.")); "operation.\nDisabling it may allow arbitrary code execution."));
INSERT( INSERT(
Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), Settings,
cpuopt_unsafe_ignore_global_monitor,
tr("Ignore global monitor"),
tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " tr("This option improves speed by relying only on the semantics of cmpxchg to ensure "
"safety of exclusive access instructions.\nPlease note this may result in deadlocks and " "safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
"other race conditions.")); "other race conditions."));
// Renderer // Renderer
INSERT(Settings, renderer_backend, tr("API:"), INSERT(
tr("Changes the output graphics API.\nVulkan is recommended.")); Settings,
INSERT(Settings, vulkan_device, tr("Device:"), renderer_backend,
tr("API:"),
tr("Changes the output graphics API.\nVulkan is recommended."));
INSERT(Settings,
vulkan_device,
tr("Device:"),
tr("This setting selects the GPU to use (Vulkan only).")); tr("This setting selects the GPU to use (Vulkan only)."));
INSERT(Settings, resolution_setup, tr("Resolution:"), INSERT(Settings,
resolution_setup,
tr("Resolution:"),
tr("Forces to render at a different resolution.\n" tr("Forces to render at a different resolution.\n"
"Higher resolutions require more VRAM and bandwidth.\n" "Higher resolutions require more VRAM and bandwidth.\n"
"Options lower than 1X can cause artifacts.")); "Options lower than 1X can cause artifacts."));
INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QString()); INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QString());
INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), INSERT(Settings,
fsr_sharpening_slider,
tr("FSR Sharpness:"),
tr("Determines how sharpened the image will look using FSR's dynamic contrast.")); tr("Determines how sharpened the image will look using FSR's dynamic contrast."));
INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), INSERT(Settings,
anti_aliasing,
tr("Anti-Aliasing Method:"),
tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA " tr("The anti-aliasing method to use.\nSMAA offers the best quality.\nFXAA "
"can produce a more stable picture in lower resolutions.")); "can produce a more stable picture in lower resolutions."));
INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), INSERT(Settings,
fullscreen_mode,
tr("Fullscreen Mode:"),
tr("The method used to render the window in fullscreen.\nBorderless offers the best " tr("The method used to render the window in fullscreen.\nBorderless offers the best "
"compatibility with the on-screen keyboard that some games request for " "compatibility with the on-screen keyboard that some games request for "
"input.\nExclusive " "input.\nExclusive "
"fullscreen may offer better performance and better Freesync/Gsync support.")); "fullscreen may offer better performance and better Freesync/Gsync support."));
INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), INSERT(Settings,
aspect_ratio,
tr("Aspect Ratio:"),
tr("Stretches the renderer to fit the specified aspect ratio.\nMost games only support " tr("Stretches the renderer to fit the specified aspect ratio.\nMost games only support "
"16:9, so modifications are required to get other ratios.\nAlso controls the " "16:9, so modifications are required to get other ratios.\nAlso controls the "
"aspect ratio of captured screenshots.")); "aspect ratio of captured screenshots."));
INSERT(Settings, use_disk_shader_cache, tr("Use persistent pipeline cache"), INSERT(Settings,
use_disk_shader_cache,
tr("Use persistent pipeline cache"),
tr("Allows saving shaders to storage for faster loading on following game " tr("Allows saving shaders to storage for faster loading on following game "
"boots.\nDisabling it is only intended for debugging.")); "boots.\nDisabling it is only intended for debugging."));
INSERT(Settings, optimize_spirv_output, tr("Optimize SPIRV output"), INSERT(Settings,
optimize_spirv_output,
tr("Optimize SPIRV output"),
tr("Runs an additional optimization pass over generated SPIRV shaders.\n" tr("Runs an additional optimization pass over generated SPIRV shaders.\n"
"Will increase time required for shader compilation.\nMay slightly improve " "Will increase time required for shader compilation.\nMay slightly improve "
"performance.\nThis feature is experimental.")); "performance.\nThis feature is experimental."));
INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), INSERT(Settings,
nvdec_emulation,
tr("NVDEC emulation:"),
tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for " tr("Specifies how videos should be decoded.\nIt can either use the CPU or the GPU for "
"decoding, or perform no decoding at all (black screen on videos).\n" "decoding, or perform no decoding at all (black screen on videos).\n"
"In most cases, GPU decoding provides the best performance.")); "In most cases, GPU decoding provides the best performance."));
INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), INSERT(Settings,
accelerate_astc,
tr("ASTC Decoding Method:"),
tr("This option controls how ASTC textures should be decoded.\n" tr("This option controls how ASTC textures should be decoded.\n"
"CPU: Use the CPU for decoding.\n" "CPU: Use the CPU for decoding.\n"
"GPU: Use the GPU's compute shaders to decode ASTC textures (recommended).\n" "GPU: Use the GPU's compute shaders to decode ASTC textures (recommended).\n"
"CPU Asynchronously: Use the CPU to decode ASTC textures on demand. Eliminates" "CPU Asynchronously: Use the CPU to decode ASTC textures on demand. Eliminates"
"ASTC decoding\nstuttering but may present artifacts.")); "ASTC decoding\nstuttering but may present artifacts."));
INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), INSERT(
tr("Most GPUs lack support for ASTC textures and must decompress to an" Settings,
"intermediate format: RGBA8.\n" astc_recompression,
"BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format,\n" tr("ASTC Recompression Method:"),
" saving VRAM but degrading image quality.")); tr("Most GPUs lack support for ASTC textures and must decompress to an"
"intermediate format: RGBA8.\n"
"BC1/BC3: The intermediate format will be recompressed to BC1 or BC3 format,\n"
" saving VRAM but degrading image quality."));
INSERT(Settings, frame_pacing_mode, tr("Frame Pacing Mode (Vulkan only)"), INSERT(Settings, frame_pacing_mode, tr("Frame Pacing Mode (Vulkan only)"),
tr("Controls how the emulator manages frame pacing to reduce stuttering and make the " tr("Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent."));
"frame rate smoother and more consistent.")); INSERT(Settings,
INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"), vram_usage_mode,
tr("Selects whether the emulator should prefer to conserve memory or make maximum usage " tr("VRAM Usage Mode:"),
"of available video memory for performance.\nAggressive mode may impact performance " tr("Selects whether the emulator should prefer to conserve memory or make maximum usage of available video memory for performance.\nAggressive mode may impact performance of other applications such as recording software."));
"of other applications such as recording software.")); INSERT(Settings,
INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"), skip_cpu_inner_invalidation,
tr("Skip CPU Inner Invalidation"),
tr("Skips certain cache invalidations during memory updates, reducing CPU usage and " tr("Skips certain cache invalidations during memory updates, reducing CPU usage and "
"improving latency. This may cause soft-crashes.")); "improving latency. This may cause soft-crashes."));
INSERT(Settings, vsync_mode, tr("VSync Mode:"), INSERT(
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " Settings,
"refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n" vsync_mode,
"Mailbox can have lower latency than FIFO and does not tear but may drop " tr("VSync Mode:"),
"frames.\nImmediate (no synchronization) presents whatever is available and can " tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"exhibit tearing.")); "refresh rate.\nFIFO Relaxed allows tearing as it recovers from a slow down.\n"
"Mailbox can have lower latency than FIFO and does not tear but may drop "
"frames.\nImmediate (no synchronization) presents whatever is available and can "
"exhibit tearing."));
INSERT(Settings, bg_red, QString(), QString()); INSERT(Settings, bg_red, QString(), QString());
INSERT(Settings, bg_green, QString(), QString()); INSERT(Settings, bg_green, QString(), QString());
INSERT(Settings, bg_blue, QString(), QString()); INSERT(Settings, bg_blue, QString(), QString());
@@ -212,74 +264,103 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
INSERT(Settings, use_asynchronous_gpu_emulation, QString(), QString()); INSERT(Settings, use_asynchronous_gpu_emulation, QString(), QString());
INSERT(Settings, sync_memory_operations, tr("Sync Memory Operations"), INSERT(Settings, sync_memory_operations, tr("Sync Memory Operations"),
tr("Ensures data consistency between compute and memory operations.\nThis option fixes " tr("Ensures data consistency between compute and memory operations.\nThis option fixes issues in games, but may degrade performance.\nUnreal Engine 4 games often see the most significant changes thereof."));
"issues in games, but may degrade performance.\nUnreal Engine 4 games often see the " INSERT(Settings,
"most significant changes thereof.")); async_presentation,
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), tr("Enable asynchronous presentation (Vulkan only)"),
tr("Slightly improves performance by moving presentation to a separate CPU thread.")); tr("Slightly improves performance by moving presentation to a separate CPU thread."));
INSERT( INSERT(
Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), Settings,
renderer_force_max_clock,
tr("Force maximum clocks (Vulkan only)"),
tr("Runs work in the background while waiting for graphics commands to keep the GPU from " tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
"lowering its clock speed.")); "lowering its clock speed."));
INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), INSERT(Settings,
tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on " max_anisotropy,
"most GPUs.")); tr("Anisotropic Filtering:"),
INSERT(Settings, gpu_accuracy, tr("GPU Mode:"), tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on most GPUs."));
tr("Controls the GPU emulation mode.\nMost games render fine with Fast or Balanced " INSERT(Settings,
"modes, but Accurate is still " gpu_accuracy,
tr("GPU Mode:"),
tr("Controls the GPU emulation mode.\nMost games render fine with Fast or Balanced modes, but Accurate is still "
"required for some.\nParticles tend to only render correctly with Accurate mode.")); "required for some.\nParticles tend to only render correctly with Accurate mode."));
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"), INSERT(Settings,
tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but " dma_accuracy,
"may degrade performance.")); tr("DMA Accuracy:"),
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"), tr("Controls the DMA precision accuracy. Safe precision fixes issues in some games but may degrade performance."));
INSERT(Settings,
use_asynchronous_shaders,
tr("Enable asynchronous shader compilation"),
tr("May reduce shader stutter.")); tr("May reduce shader stutter."));
INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"), INSERT(Settings,
fast_gpu_time,
tr("Fast GPU Time"),
tr("Overclocks the emulated GPU to increase dynamic resolution and render " tr("Overclocks the emulated GPU to increase dynamic resolution and render "
"distance.\nUse 256 for maximal performance and 512 for maximal graphics fidelity.")); "distance.\nUse 256 for maximal performance and 512 for maximal graphics fidelity."));
INSERT(Settings, gpu_unswizzle_enabled, tr("GPU Unswizzle"), INSERT(Settings,
gpu_unswizzle_enabled,
tr("GPU Unswizzle"),
tr("Accelerates BCn 3D texture decoding using GPU compute.\n" tr("Accelerates BCn 3D texture decoding using GPU compute.\n"
"Disable if experiencing crashes or graphical glitches.")); "Disable if experiencing crashes or graphical glitches."));
INSERT(Settings, gpu_unswizzle_texture_size, tr("GPU Unswizzle Max Texture Size"), INSERT(Settings,
gpu_unswizzle_texture_size,
tr("GPU Unswizzle Max Texture Size"),
tr("Sets the maximum size (MiB) for GPU-based texture unswizzling.\n" tr("Sets the maximum size (MiB) for GPU-based texture unswizzling.\n"
"While the GPU is faster for medium and large textures, the CPU may be more " "While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones.\n"
"efficient for very small ones.\n"
"Adjust this to find the balance between GPU acceleration and CPU overhead.")); "Adjust this to find the balance between GPU acceleration and CPU overhead."));
INSERT(Settings, gpu_unswizzle_stream_size, tr("GPU Unswizzle Stream Size"), INSERT(Settings,
gpu_unswizzle_stream_size,
tr("GPU Unswizzle Stream Size"),
tr("Sets the maximum amount of texture data (in MiB) processed per frame.\n" tr("Sets the maximum amount of texture data (in MiB) processed per frame.\n"
"Higher values can reduce stutter during texture loading but may impact frame " "Higher values can reduce stutter during texture loading but may impact frame consistency."));
"consistency.")); INSERT(Settings,
INSERT(Settings, gpu_unswizzle_chunk_size, tr("GPU Unswizzle Chunk Size"), gpu_unswizzle_chunk_size,
tr("GPU Unswizzle Chunk Size"),
tr("Determines the number of depth slices processed in a single dispatch.\n" tr("Determines the number of depth slices processed in a single dispatch.\n"
"Increasing this can improve throughput on high-end GPUs but may cause TDR or driver " "Increasing this can improve throughput on high-end GPUs but may cause TDR or driver timeouts on weaker hardware."));
"timeouts on weaker hardware."));
INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), INSERT(Settings,
use_vulkan_driver_pipeline_cache,
tr("Use Vulkan pipeline cache"),
tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading " tr("Enables GPU vendor-specific pipeline cache.\nThis option can improve shader loading "
"time significantly in cases where the Vulkan driver does not store pipeline cache " "time significantly in cases where the Vulkan driver does not store pipeline cache "
"files internally.")); "files internally."));
INSERT(Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
tr("Required by some games.\nThis setting only exists for Intel "
"proprietary drivers and may crash if enabled.\nCompute pipelines are always enabled "
"on all other drivers."));
INSERT( INSERT(
Settings, use_reactive_flushing, tr("Enable Reactive Flushing"), Settings,
enable_compute_pipelines,
tr("Enable Compute Pipelines (Intel Vulkan Only)"),
tr("Required by some games.\nThis setting only exists for Intel "
"proprietary drivers and may crash if enabled.\nCompute pipelines are always enabled "
"on all other drivers."));
INSERT(
Settings,
use_reactive_flushing,
tr("Enable Reactive Flushing"),
tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory " tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
"syncing.")); "syncing."));
INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"), INSERT(Settings,
use_video_framerate,
tr("Sync to framerate of video playback"),
tr("Run the game at normal speed during video playback, even when the framerate is " tr("Run the game at normal speed during video playback, even when the framerate is "
"unlocked.")); "unlocked."));
INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"), INSERT(Settings,
barrier_feedback_loops,
tr("Barrier feedback loops"),
tr("Improves rendering of transparency effects in specific games.")); tr("Improves rendering of transparency effects in specific games."));
INSERT(Settings, enable_buffer_history, tr("Enable buffer history"), INSERT(Settings,
tr("Enables access to previous buffer states.\nThis option may improve rendering " enable_buffer_history,
"quality and performance consistency in some games.")); tr("Enable buffer history"),
INSERT(Settings, fix_bloom_effects, tr("Fix bloom effects"), tr("Removes bloom in Burnout.")); tr("Enables access to previous buffer states.\nThis option may improve rendering quality and performance consistency in some games."));
INSERT(Settings,
fix_bloom_effects,
tr("Fix bloom effects"),
tr("Removes bloom in Burnout."));
INSERT(Settings, rescale_hack, tr("Enable Legacy Rescale Pass"), INSERT(Settings,
tr("May fix rescale issues in some games by relying on behavior from the previous " rescale_hack,
"implementation.\n" tr("Enable Legacy Rescale Pass"),
"Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and " tr("May fix rescale issues in some games by relying on behavior from the previous implementation.\n"
"grey texture flicker on Nvidia GPUs in Luigis Mansion 3.")); "Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and grey texture flicker on Nvidia GPUs in Luigis Mansion 3."));
// Renderer (Extensions) // Renderer (Extensions)
INSERT(Settings, dyna_state, tr("Extended Dynamic State"), INSERT(Settings, dyna_state, tr("Extended Dynamic State"),
@@ -287,42 +368,59 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
"Higher states allow for more features and can increase performance, but may cause " "Higher states allow for more features and can increase performance, but may cause "
"additional graphical issues.")); "additional graphical issues."));
INSERT(Settings, vertex_input_dynamic_state, tr("Vertex Input Dynamic State"), INSERT(Settings,
vertex_input_dynamic_state,
tr("Vertex Input Dynamic State"),
tr("Enables vertex input dynamic state feature for better quality and performance.")); tr("Enables vertex input dynamic state feature for better quality and performance."));
INSERT(Settings, provoking_vertex, tr("Provoking Vertex"), INSERT(Settings,
provoking_vertex,
tr("Provoking Vertex"),
tr("Improves lighting and vertex handling in some games.\n" tr("Improves lighting and vertex handling in some games.\n"
"Only Vulkan 1.0+ devices support this extension.")); "Only Vulkan 1.0+ devices support this extension."));
INSERT(Settings, descriptor_indexing, tr("Descriptor Indexing"), INSERT(Settings,
descriptor_indexing,
tr("Descriptor Indexing"),
tr("Improves texture & buffer handling and the Maxwell translation layer.\n" tr("Improves texture & buffer handling and the Maxwell translation layer.\n"
"Some Vulkan 1.1+ and all 1.2+ devices support this extension.")); "Some Vulkan 1.1+ and all 1.2+ devices support this extension."));
INSERT( INSERT(Settings,
Settings, sample_shading, tr("Sample Shading"), sample_shading,
tr("Allows the fragment shader to execute per sample in a multi-sampled fragment " tr("Sample Shading"),
"instead of once per fragment. Improves graphics quality at the cost of performance.\n" tr("Allows the fragment shader to execute per sample in a multi-sampled fragment "
"Higher values improve quality but degrade performance.")); "instead of once per fragment. Improves graphics quality at the cost of performance.\n"
"Higher values improve quality but degrade performance."));
// Renderer (Debug) // Renderer (Debug)
// System // System
INSERT(Settings, rng_seed, tr("RNG Seed"), INSERT(Settings,
rng_seed,
tr("RNG Seed"),
tr("Controls the seed of the random number generator.\nMainly used for speedrunning.")); tr("Controls the seed of the random number generator.\nMainly used for speedrunning."));
INSERT(Settings, rng_seed_enabled, QString(), QString()); INSERT(Settings, rng_seed_enabled, QString(), QString());
INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console.")); INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console."));
INSERT(Settings, custom_rtc, tr("Custom RTC Date:"), INSERT(Settings,
custom_rtc,
tr("Custom RTC Date:"),
tr("This option allows to change the clock of the console.\n" tr("This option allows to change the clock of the console.\n"
"Can be used to manipulate time in games.")); "Can be used to manipulate time in games."));
INSERT(Settings, custom_rtc_enabled, QString(), QString()); INSERT(Settings, custom_rtc_enabled, QString(), QString());
INSERT(Settings, custom_rtc_offset, QStringLiteral(" "), INSERT(Settings,
custom_rtc_offset,
QStringLiteral(" "),
tr("The number of seconds from the current unix time")); tr("The number of seconds from the current unix time"));
INSERT(Settings, language_index, tr("Language:"), INSERT(Settings,
language_index,
tr("Language:"),
tr("This option can be overridden when region setting is auto-select")); tr("This option can be overridden when region setting is auto-select"));
INSERT(Settings, region_index, tr("Region:"), tr("The region of the console.")); INSERT(Settings, region_index, tr("Region:"), tr("The region of the console."));
INSERT(Settings, time_zone_index, tr("Time Zone:"), tr("The time zone of the console.")); INSERT(Settings, time_zone_index, tr("Time Zone:"), tr("The time zone of the console."));
INSERT(Settings, sound_index, tr("Sound Output Mode:"), QString()); INSERT(Settings, sound_index, tr("Sound Output Mode:"), QString());
INSERT(Settings, use_docked_mode, tr("Console Mode:"), INSERT(Settings,
use_docked_mode,
tr("Console Mode:"),
tr("Selects if the console is in Docked or Handheld mode.\nGames will change " tr("Selects if the console is in Docked or Handheld mode.\nGames will change "
"their resolution, details and supported controllers and depending on this setting.\n" "their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems.")); "Setting to Handheld can help improve performance for low end systems."));
@@ -346,19 +444,31 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
// Ui // Ui
// Ui General // Ui General
INSERT(UISettings, select_user_on_boot, tr("Prompt for user profile on boot"), INSERT(UISettings,
select_user_on_boot,
tr("Prompt for user profile on boot"),
tr("Useful if multiple people use the same PC.")); tr("Useful if multiple people use the same PC."));
INSERT(UISettings, pause_when_in_background, tr("Pause when not in focus"), INSERT(UISettings,
pause_when_in_background,
tr("Pause when not in focus"),
tr("Pauses emulation when focusing on other windows.")); tr("Pauses emulation when focusing on other windows."));
INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), INSERT(UISettings,
confirm_before_stopping,
tr("Confirm before stopping emulation"),
tr("Overrides prompts asking to confirm stopping the emulation.\nEnabling " tr("Overrides prompts asking to confirm stopping the emulation.\nEnabling "
"it bypasses such prompts and directly exits the emulation.")); "it bypasses such prompts and directly exits the emulation."));
INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), INSERT(UISettings,
hide_mouse,
tr("Hide mouse on inactivity"),
tr("Hides the mouse after 2.5s of inactivity.")); tr("Hides the mouse after 2.5s of inactivity."));
INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), INSERT(UISettings,
controller_applet_disabled,
tr("Disable controller applet"),
tr("Forcibly disables the use of the controller applet in emulated programs.\n" tr("Forcibly disables the use of the controller applet in emulated programs.\n"
"When a program attempts to open the controller applet, it is immediately closed.")); "When a program attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings, check_for_updates, tr("Check for updates"), INSERT(UISettings,
check_for_updates,
tr("Check for updates"),
tr("Whether or not to check for updates upon startup.")); tr("Whether or not to check for updates upon startup."));
// Linux // Linux
@@ -379,9 +489,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
return translations; return translations;
} }
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) { std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
std::unique_ptr<ComboboxTranslationMap> translations = {
std::make_unique<ComboboxTranslationMap>(); std::unique_ptr<ComboboxTranslationMap> translations = std::make_unique<ComboboxTranslationMap>();
const auto& tr = [&](const char* text, const char* context = "") { const auto& tr = [&](const char* text, const char* context = "") {
return parent->tr(text, context); return parent->tr(text, context);
}; };
@@ -427,15 +537,15 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
PAIR(VramUsageMode, Conservative, tr("Conservative")), PAIR(VramUsageMode, Conservative, tr("Conservative")),
PAIR(VramUsageMode, Aggressive, tr("Aggressive")), PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
}}); }});
translations->insert( translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), {
{Settings::EnumMetadata<Settings::RendererBackend>::Index(), PAIR(RendererBackend, Vulkan, tr("Vulkan")),
{PAIR(RendererBackend, Vulkan, tr("Vulkan")),
#ifdef HAS_OPENGL #ifdef HAS_OPENGL
PAIR(RendererBackend, OpenGL_GLSL, tr("OpenGL GLSL")), PAIR(RendererBackend, OpenGL_GLSL, tr("OpenGL GLSL")),
PAIR(RendererBackend, OpenGL_GLASM, tr("OpenGL GLASM (Assembly Shaders, NVIDIA Only)")), PAIR(RendererBackend, OpenGL_GLASM, tr("OpenGL GLASM (Assembly Shaders, NVIDIA Only)")),
PAIR(RendererBackend, OpenGL_SPIRV, tr("OpenGL SPIR-V (Experimental, AMD/Mesa Only)")), PAIR(RendererBackend, OpenGL_SPIRV, tr("OpenGL SPIR-V (Experimental, AMD/Mesa Only)")),
#endif #endif
PAIR(RendererBackend, Null, tr("Null"))}}); PAIR(RendererBackend, Null, tr("Null"))
}});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{ {
PAIR(GpuAccuracy, Low, tr("Fast")), PAIR(GpuAccuracy, Low, tr("Fast")),
@@ -569,58 +679,58 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
translations->insert( translations->insert(
{Settings::EnumMetadata<Settings::TimeZone>::Index(), {Settings::EnumMetadata<Settings::TimeZone>::Index(),
{ {
{static_cast<u32>(Settings::TimeZone::Auto), {static_cast<u32>(Settings::TimeZone::Auto),
tr("Auto (%1)", "Auto select time zone") tr("Auto (%1)", "Auto select time zone")
.arg(QString::fromStdString( .arg(QString::fromStdString(
Settings::GetTimeZoneString(Settings::TimeZone::Auto)))}, Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
{static_cast<u32>(Settings::TimeZone::Default), {static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone") tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, tr("CET")), PAIR(TimeZone, Cet, tr("CET")),
PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
PAIR(TimeZone, Cuba, tr("Cuba")), PAIR(TimeZone, Cuba, tr("Cuba")),
PAIR(TimeZone, Eet, tr("EET")), PAIR(TimeZone, Eet, tr("EET")),
PAIR(TimeZone, Egypt, tr("Egypt")), PAIR(TimeZone, Egypt, tr("Egypt")),
PAIR(TimeZone, Eire, tr("Eire")), PAIR(TimeZone, Eire, tr("Eire")),
PAIR(TimeZone, Est, tr("EST")), PAIR(TimeZone, Est, tr("EST")),
PAIR(TimeZone, Est5Edt, tr("EST5EDT")), PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
PAIR(TimeZone, Gb, tr("GB")), PAIR(TimeZone, Gb, tr("GB")),
PAIR(TimeZone, GbEire, tr("GB-Eire")), PAIR(TimeZone, GbEire, tr("GB-Eire")),
PAIR(TimeZone, Gmt, tr("GMT")), PAIR(TimeZone, Gmt, tr("GMT")),
PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
PAIR(TimeZone, GmtZero, tr("GMT0")), PAIR(TimeZone, GmtZero, tr("GMT0")),
PAIR(TimeZone, Greenwich, tr("Greenwich")), PAIR(TimeZone, Greenwich, tr("Greenwich")),
PAIR(TimeZone, Hongkong, tr("Hongkong")), PAIR(TimeZone, Hongkong, tr("Hongkong")),
PAIR(TimeZone, Hst, tr("HST")), PAIR(TimeZone, Hst, tr("HST")),
PAIR(TimeZone, Iceland, tr("Iceland")), PAIR(TimeZone, Iceland, tr("Iceland")),
PAIR(TimeZone, Iran, tr("Iran")), PAIR(TimeZone, Iran, tr("Iran")),
PAIR(TimeZone, Israel, tr("Israel")), PAIR(TimeZone, Israel, tr("Israel")),
PAIR(TimeZone, Jamaica, tr("Jamaica")), PAIR(TimeZone, Jamaica, tr("Jamaica")),
PAIR(TimeZone, Japan, tr("Japan")), PAIR(TimeZone, Japan, tr("Japan")),
PAIR(TimeZone, Kwajalein, tr("Kwajalein")), PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
PAIR(TimeZone, Libya, tr("Libya")), PAIR(TimeZone, Libya, tr("Libya")),
PAIR(TimeZone, Met, tr("MET")), PAIR(TimeZone, Met, tr("MET")),
PAIR(TimeZone, Mst, tr("MST")), PAIR(TimeZone, Mst, tr("MST")),
PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
PAIR(TimeZone, Navajo, tr("Navajo")), PAIR(TimeZone, Navajo, tr("Navajo")),
PAIR(TimeZone, Nz, tr("NZ")), PAIR(TimeZone, Nz, tr("NZ")),
PAIR(TimeZone, NzChat, tr("NZ-CHAT")), PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
PAIR(TimeZone, Poland, tr("Poland")), PAIR(TimeZone, Poland, tr("Poland")),
PAIR(TimeZone, Portugal, tr("Portugal")), PAIR(TimeZone, Portugal, tr("Portugal")),
PAIR(TimeZone, Prc, tr("PRC")), PAIR(TimeZone, Prc, tr("PRC")),
PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
PAIR(TimeZone, Roc, tr("ROC")), PAIR(TimeZone, Roc, tr("ROC")),
PAIR(TimeZone, Rok, tr("ROK")), PAIR(TimeZone, Rok, tr("ROK")),
PAIR(TimeZone, Singapore, tr("Singapore")), PAIR(TimeZone, Singapore, tr("Singapore")),
PAIR(TimeZone, Turkey, tr("Turkey")), PAIR(TimeZone, Turkey, tr("Turkey")),
PAIR(TimeZone, Uct, tr("UCT")), PAIR(TimeZone, Uct, tr("UCT")),
PAIR(TimeZone, Universal, tr("Universal")), PAIR(TimeZone, Universal, tr("Universal")),
PAIR(TimeZone, Utc, tr("UTC")), PAIR(TimeZone, Utc, tr("UTC")),
PAIR(TimeZone, WSu, tr("W-SU")), PAIR(TimeZone, WSu, tr("W-SU")),
PAIR(TimeZone, Wet, tr("WET")), PAIR(TimeZone, Wet, tr("WET")),
PAIR(TimeZone, Zulu, tr("Zulu")), PAIR(TimeZone, Zulu, tr("Zulu")),
}}); }});
translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
{ {
PAIR(AudioMode, Mono, tr("Mono")), PAIR(AudioMode, Mono, tr("Mono")),
@@ -693,9 +803,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
}}); }});
translations->insert({Settings::EnumMetadata<Settings::GameListMode>::Index(), translations->insert({Settings::EnumMetadata<Settings::GameListMode>::Index(),
{ {
PAIR(GameListMode, TreeView, tr("Tree View")), PAIR(GameListMode, TreeView, tr("Tree View")),
PAIR(GameListMode, GridView, tr("Grid View")), PAIR(GameListMode, GridView, tr("Grid View")),
}}); }});
#undef PAIR #undef PAIR
+12 -15
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
@@ -13,8 +13,8 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <QObject>
#include <QString> #include <QString>
#include <QObject>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/settings_enums.h" #include "common/settings_enums.h"
@@ -23,7 +23,7 @@ using TranslationMap = std::map<u32, std::pair<QString, QString>>;
using ComboboxTranslations = std::vector<std::pair<u32, QString>>; using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>; using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent); std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent); std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent);
@@ -39,15 +39,15 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
{Settings::ScalingFilter::Bilinear, {Settings::ScalingFilter::Bilinear,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))}, {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, {Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))}, {Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell, {Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))}, {Settings::ScalingFilter::Spline1,
{Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
{Settings::ScalingFilter::Gaussian, {Settings::ScalingFilter::Gaussian,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Gaussian"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Gaussian"))},
{Settings::ScalingFilter::Lanczos, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Lanczos"))}, {Settings::ScalingFilter::Lanczos,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Lanczos"))},
{Settings::ScalingFilter::ScaleForce, {Settings::ScalingFilter::ScaleForce,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))}, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))}, {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))},
@@ -68,12 +68,9 @@ static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = { static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))}, {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL_GLSL, {Settings::RendererBackend::OpenGL_GLSL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLSL"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLSL"))}, {Settings::RendererBackend::OpenGL_SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL SPIRV"))},
{Settings::RendererBackend::OpenGL_SPIRV, {Settings::RendererBackend::OpenGL_GLASM, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLASM"))},
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL SPIRV"))},
{Settings::RendererBackend::OpenGL_GLASM,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLASM"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Null"))}, {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Null"))},
}; };
+5 -7
View File
@@ -145,15 +145,14 @@ struct Values {
// Linux/MinGW may support (requires libdl support) // Linux/MinGW may support (requires libdl support)
SwitchableSetting<bool> enable_gamemode{linkage, SwitchableSetting<bool> enable_gamemode{linkage,
#ifndef _MSC_VER #ifndef _MSC_VER
true, true,
#else #else
false, false,
#endif #endif
"enable_gamemode", Category::UiGeneral}; "enable_gamemode", Category::UiGeneral};
#ifdef __unix__ #ifdef __unix__
SwitchableSetting<bool> gui_force_x11{linkage, false, "gui_force_x11", Category::UiGeneral}; SwitchableSetting<bool> gui_force_x11{linkage, false, "gui_force_x11", Category::UiGeneral};
Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning", Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning", Category::UiGeneral};
Category::UiGeneral};
#endif #endif
// Discord RPC // Discord RPC
@@ -211,8 +210,7 @@ struct Values {
Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList}; Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList}; Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList}; Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList};
"game_list_mode", Category::UiGameList};
Setting<bool> show_game_name{linkage, true, "show_game_name", Category::UiGameList}; Setting<bool> show_game_name{linkage, true, "show_game_name", Category::UiGameList};
std::atomic_bool is_game_list_reload_pending{false}; std::atomic_bool is_game_list_reload_pending{false};
+3 -7
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2018 Citra Emulator Project // SPDX-FileCopyrightText: 2018 Citra Emulator Project
@@ -70,9 +70,7 @@ std::string DiscordImpl::GetGameString(const std::string& title) {
} }
static constexpr char DEFAULT_DISCORD_TEXT[] = "Eden is an emulator for the Nintendo Switch"; static constexpr char DEFAULT_DISCORD_TEXT[] = "Eden is an emulator for the Nintendo Switch";
static constexpr char DEFAULT_DISCORD_IMAGE[] = static constexpr char DEFAULT_DISCORD_IMAGE[] = "https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/eden.png";
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
"eden.png";
void DiscordImpl::UpdateGameStatus(bool use_default) { void DiscordImpl::UpdateGameStatus(bool use_default) {
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url; const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
@@ -122,9 +120,7 @@ void DiscordImpl::Update() {
return; return;
} }
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>( s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::chrono::system_clock::now().time_since_epoch())
.count();
DiscordRichPresence presence{}; DiscordRichPresence presence{};
presence.largeImageKey = DEFAULT_DISCORD_IMAGE; presence.largeImageKey = DEFAULT_DISCORD_IMAGE;
presence.largeImageText = DEFAULT_DISCORD_TEXT; presence.largeImageText = DEFAULT_DISCORD_TEXT;
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -9,9 +9,9 @@
#ifdef __unix__ #ifdef __unix__
#include <gamemode_client.h> #include <gamemode_client.h>
#endif #endif
#include "qt_common/gamemode.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "qt_common/config/uisettings.h" #include "qt_common/config/uisettings.h"
#include "qt_common/gamemode.h"
namespace Common::FeralGamemode { namespace Common::FeralGamemode {
@@ -49,4 +49,4 @@ void Stop() noexcept {
} }
} }
} // namespace Common::FeralGamemode } // namespace Common::Linux
+3 -4
View File
@@ -1,17 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "gui_settings.h"
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "gui_settings.h"
namespace FS = Common::FS; namespace FS = Common::FS;
namespace GraphicsBackend { namespace GraphicsBackend {
QString GuiConfigPath() { QString GuiConfigPath() {
return QString::fromStdString( return QString::fromStdString(FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "gui_config.ini"));
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "gui_config.ini"));
} }
void SetForceX11(bool state) { void SetForceX11(bool state) {
+33 -19
View File
@@ -1,16 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "common/fs/fs.h"
#include "qt_common.h" #include "qt_common.h"
#include "common/fs/fs.h"
#include "common/fs/ryujinx_compat.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QStringLiteral> #include <QStringLiteral>
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "qt_common/abstract/frontend.h"
#include "qt_common/qt_string_lookup.h"
#include <QFile> #include <QFile>
#include <QMessageBox>
#include <JlCompress.h> #include <JlCompress.h>
#if !defined(WIN32) && !defined(__APPLE__) #if !defined(WIN32) && !defined(__APPLE__)
@@ -22,13 +27,18 @@
namespace QtCommon { namespace QtCommon {
#ifdef YUZU_QT_WIDGETS
QWidget* rootObject = nullptr; QWidget* rootObject = nullptr;
#else
QObject* rootObject = nullptr;
#endif
std::unique_ptr<Core::System> system = nullptr; std::unique_ptr<Core::System> system = nullptr;
std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr; std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr;
std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr; std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr;
Core::Frontend::WindowSystemType GetWindowSystemType() { Core::Frontend::WindowSystemType GetWindowSystemType()
{
// Determine WSI type based on Qt platform. // Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName(); QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows")) if (platform_name == QStringLiteral("windows"))
@@ -50,7 +60,8 @@ Core::Frontend::WindowSystemType GetWindowSystemType() {
return Core::Frontend::WindowSystemType::Windows; return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType } // namespace Core::Frontend::WindowSystemType
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) { Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
{
Core::Frontend::EmuWindow::WindowSystemInfo wsi; Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType(); wsi.type = GetWindowSystemType();
@@ -58,8 +69,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
// Our Win32 Qt external doesn't have the private API. // Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId()); wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__) #elif defined(__APPLE__)
id layer = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)( id layer = reinterpret_cast<id (*) (id, SEL)>(
reinterpret_cast<id>(window->winId()), sel_registerName("layer")); objc_msgSend)(reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
// In Qt 6, the layer of the NSView might be a QContainerLayer. // In Qt 6, the layer of the NSView might be a QContainerLayer.
// MoltenVK needs a CAMetalLayer. We search for it in sublayers. // MoltenVK needs a CAMetalLayer. We search for it in sublayers.
@@ -67,20 +78,15 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
id metal_layer = nullptr; id metal_layer = nullptr;
if (layer) { if (layer) {
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)( if (reinterpret_cast<bool (*) (id, SEL, Class)>(objc_msgSend)(layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = layer; metal_layer = layer;
} else { } else {
id sublayers = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)( id sublayers = reinterpret_cast<id (*) (id, SEL)>(objc_msgSend)(layer, sel_registerName("sublayers"));
layer, sel_registerName("sublayers"));
if (sublayers) { if (sublayers) {
unsigned long count = reinterpret_cast<unsigned long (*)(id, SEL)>(objc_msgSend)( unsigned long count = reinterpret_cast<unsigned long (*) (id, SEL)>(objc_msgSend)(sublayers, sel_registerName("count"));
sublayers, sel_registerName("count"));
for (unsigned long i = 0; i < count; ++i) { for (unsigned long i = 0; i < count; ++i) {
id sublayer = reinterpret_cast<id (*)(id, SEL, unsigned long)>(objc_msgSend)( id sublayer = reinterpret_cast<id (*) (id, SEL, unsigned long)>(objc_msgSend)(sublayers, sel_registerName("objectAtIndex:"), i);
sublayers, sel_registerName("objectAtIndex:"), i); if (reinterpret_cast<bool (*) (id, SEL, Class)>(objc_msgSend)(sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = sublayer; metal_layer = sublayer;
break; break;
} }
@@ -102,22 +108,30 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
return wsi; return wsi;
} }
const QString tr(const char* str) { const QString tr(const char* str)
{
return QGuiApplication::tr(str); return QGuiApplication::tr(str);
} }
const QString tr(const std::string& str) { const QString tr(const std::string& str)
{
return QGuiApplication::tr(str.c_str()); return QGuiApplication::tr(str.c_str());
} }
void Init(QWidget* root) { #ifdef YUZU_QT_WIDGETS
void Init(QWidget* root)
#else
void Init(QObject* root)
#endif
{
system = std::make_unique<Core::System>(); system = std::make_unique<Core::System>();
rootObject = root; rootObject = root;
vfs = std::make_unique<FileSys::RealVfsFilesystem>(); vfs = std::make_unique<FileSys::RealVfsFilesystem>();
provider = std::make_unique<FileSys::ManualContentProvider>(); provider = std::make_unique<FileSys::ManualContentProvider>();
} }
std::filesystem::path GetEdenCommand() { std::filesystem::path GetEdenCommand()
{
std::filesystem::path command; std::filesystem::path command;
// TODO: flatpak? // TODO: flatpak?
+16 -8
View File
@@ -1,20 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_COMMON_H #ifndef QT_COMMON_H
#define QT_COMMON_H #define QT_COMMON_H
#include <memory>
#include <QWindow> #include <QWindow>
#include <core/frontend/emu_window.h>
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/registered_cache.h" #include "core/file_sys/registered_cache.h"
#include <core/frontend/emu_window.h>
#include <memory>
#include <core/file_sys/vfs/vfs_real.h> #include <core/file_sys/vfs/vfs_real.h>
namespace QtCommon { namespace QtCommon {
extern QWidget* rootObject; #ifdef YUZU_QT_WIDGETS
extern QWidget *rootObject;
#else
extern QObject *rootObject;
#endif
extern std::unique_ptr<Core::System> system; extern std::unique_ptr<Core::System> system;
extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs; extern std::shared_ptr<FileSys::RealVfsFilesystem> vfs;
@@ -24,12 +28,16 @@ typedef std::function<bool(std::size_t, std::size_t)> QtProgressCallback;
Core::Frontend::WindowSystemType GetWindowSystemType(); Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window); Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window);
void Init(QWidget* root); #ifdef YUZU_QT_WIDGETS
void Init(QWidget *root);
#else
void Init(QObject *root);
#endif
const QString tr(const char* str); const QString tr(const char *str);
const QString tr(const std::string& str); const QString tr(const std::string &str);
std::filesystem::path GetEdenCommand(); std::filesystem::path GetEdenCommand();
} // namespace QtCommon } // namespace QtCommon
+11 -9
View File
@@ -11,9 +11,8 @@
/// Small helper to look up enums. /// Small helper to look up enums.
/// res = the result code /// res = the result code
/// base = the base matching value in the StringKey table /// base = the base matching value in the StringKey table
#define LOOKUP_ENUM(res, base) \ #define LOOKUP_ENUM(res, base) QtCommon::StringLookup::Lookup( \
QtCommon::StringLookup::Lookup( \ QtCommon::StringLookup::StringKey((int) res + (int) QtCommon::StringLookup::base))
QtCommon::StringLookup::StringKey((int)res + (int)QtCommon::StringLookup::base))
namespace QtCommon::StringLookup { namespace QtCommon::StringLookup {
@@ -112,14 +111,16 @@ static const constexpr frozen::map<StringKey, frozen::string, 29> strings = {
QT_TR_NOOP("Would you like to migrate your data for use in Eden?\n" QT_TR_NOOP("Would you like to migrate your data for use in Eden?\n"
"Select the corresponding button to migrate data from that emulator.\n" "Select the corresponding button to migrate data from that emulator.\n"
"This may take a while.")}, "This may take a while.")},
{MigrationTooltipClearShader, QT_TR_NOOP("Clearing shader cache is recommended for all " {MigrationTooltipClearShader,
"users.\nDo not uncheck unless you know what " QT_TR_NOOP("Clearing shader cache is recommended for all "
"you're doing.")}, "users.\nDo not uncheck unless you know what "
"you're doing.")},
{MigrationTooltipKeepOld, {MigrationTooltipKeepOld,
QT_TR_NOOP("Keeps the old data directory. This is recommended if you aren't\n" QT_TR_NOOP("Keeps the old data directory. This is recommended if you aren't\n"
"space-constrained and want to keep separate data for the old emulator.")}, "space-constrained and want to keep separate data for the old emulator.")},
{MigrationTooltipClearOld, QT_TR_NOOP("Deletes the old data directory.\nThis is recommended on " {MigrationTooltipClearOld,
"devices with space constraints.")}, QT_TR_NOOP("Deletes the old data directory.\nThis is recommended on "
"devices with space constraints.")},
{MigrationTooltipLinkOld, {MigrationTooltipLinkOld,
QT_TR_NOOP("Creates a filesystem link between the old directory and Eden directory.\n" QT_TR_NOOP("Creates a filesystem link between the old directory and Eden directory.\n"
"This is recommended if you want to share data between emulators.")}, "This is recommended if you want to share data between emulators.")},
@@ -134,7 +135,8 @@ static const constexpr frozen::map<StringKey, frozen::string, 29> strings = {
{RyujinxNoSaveId, QT_TR_NOOP("Title %1 not found in Ryujinx title database.")}, {RyujinxNoSaveId, QT_TR_NOOP("Title %1 not found in Ryujinx title database.")},
}; };
static inline const QString Lookup(StringKey key) { static inline const QString Lookup(StringKey key)
{
return QObject::tr(strings.at(key).data()); return QObject::tr(strings.at(key).data());
} }
+4 -2
View File
@@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_APPLET_UTIL_H #ifndef QT_APPLET_UTIL_H
#define QT_APPLET_UTIL_H #define QT_APPLET_UTIL_H
// TODO // TODO
namespace QtCommon::Applets {} namespace QtCommon::Applets {
}
#endif // QT_APPLET_UTIL_H #endif // QT_APPLET_UTIL_H
+72 -36
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "compress.h" #include "compress.h"
@@ -10,8 +10,11 @@
/** This is a modified version of JlCompress **/ /** This is a modified version of JlCompress **/
namespace QtCommon::Compress { namespace QtCommon::Compress {
bool compressDir(QString fileCompressed, QString dir, const Options& options, bool compressDir(QString fileCompressed,
QtCommon::QtProgressCallback callback) { QString dir,
const Options &options,
QtCommon::QtProgressCallback callback)
{
// Create zip // Create zip
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
QDir().mkpath(QFileInfo(fileCompressed).absolutePath()); QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
@@ -23,7 +26,8 @@ bool compressDir(QString fileCompressed, QString dir, const Options& options,
// See how big the overall fs structure is // See how big the overall fs structure is
// good approx. of total progress // good approx. of total progress
// TODO(crueter): QDirListing impl... or fs::recursive_dir_iterator // TODO(crueter): QDirListing impl... or fs::recursive_dir_iterator
QDirIterator iter(dir, QDir::NoDotAndDotDot | QDir::Hidden | QDir::Files, QDirIterator iter(dir,
QDir::NoDotAndDotDot | QDir::Hidden | QDir::Files,
QDirIterator::Subdirectories); QDirIterator::Subdirectories);
std::size_t total = 0; std::size_t total = 0;
@@ -50,8 +54,14 @@ bool compressDir(QString fileCompressed, QString dir, const Options& options,
return true; return true;
} }
bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& options, bool compressSubDir(QuaZip *zip,
std::size_t total, std::size_t& progress, QtProgressCallback callback) { QString dir,
QString origDir,
const Options &options,
std::size_t total,
std::size_t &progress,
QtProgressCallback callback)
{
// zip: object where to add the file // zip: object where to add the file
// dir: current real directory // dir: current real directory
// origDir: original real directory // origDir: original real directory
@@ -59,20 +69,22 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
if (!zip) if (!zip)
return false; return false;
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend && if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
zip->getMode() != QuaZip::mdAdd) && zip->getMode() != QuaZip::mdAdd)
return false; return false;
QDir directory(dir); QDir directory(dir);
if (!directory.exists()) if (!directory.exists())
return false; return false;
QDir origDirectory(origDir); QDir origDirectory(origDir);
if (dir != origDir) { if (dir != origDir) {
QuaZipFile dirZipFile(zip); QuaZipFile dirZipFile(zip);
std::unique_ptr<QuaZipNewInfo> qzni; std::unique_ptr<QuaZipNewInfo> qzni;
qzni = std::make_unique<QuaZipNewInfo>( qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
origDirectory.relativeFilePath(dir) + QLatin1String("/"), dir); + QLatin1String("/"),
dir);
if (!dirZipFile.open(QIODevice::WriteOnly, *qzni, nullptr, 0, 0)) { if (!dirZipFile.open(QIODevice::WriteOnly, *qzni, nullptr, 0, 0)) {
return false; return false;
} }
@@ -80,18 +92,18 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
} }
// For each subfolder // For each subfolder
QFileInfoList subfiles = QFileInfoList subfiles = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot
directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::Dirs); | QDir::Hidden | QDir::Dirs);
for (const auto& file : std::as_const(subfiles)) { for (const auto &file : std::as_const(subfiles)) {
if (!compressSubDir(zip, file.absoluteFilePath(), origDir, options, total, progress, if (!compressSubDir(
callback)) { zip, file.absoluteFilePath(), origDir, options, total, progress, callback)) {
return false; return false;
} }
} }
// For each file in directory // For each file in directory
QFileInfoList files = directory.entryInfoList(QDir::Hidden | QDir::Files); QFileInfoList files = directory.entryInfoList(QDir::Hidden | QDir::Files);
for (const auto& file : std::as_const(files)) { for (const auto &file : std::as_const(files)) {
// If it's not a file or it's the compressed file being created // If it's not a file or it's the compressed file being created
if (!file.isFile() || file.absoluteFilePath() == zip->getZipName()) if (!file.isFile() || file.absoluteFilePath() == zip->getZipName())
continue; continue;
@@ -100,8 +112,7 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
QString filename = origDirectory.relativeFilePath(file.absoluteFilePath()); QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
// Compress the file // Compress the file
if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress, if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress, callback)) {
callback)) {
return false; return false;
} }
} }
@@ -109,26 +120,40 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
return true; return true;
} }
bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options& options, bool compressFile(QuaZip *zip,
std::size_t total, std::size_t& progress, QtCommon::QtProgressCallback callback) { QString fileName,
QString fileDest,
const Options &options,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback)
{
// zip: object where to add the file // zip: object where to add the file
// fileName: real file name // fileName: real file name
// fileDest: file name inside the zip object // fileDest: file name inside the zip object
if (!zip) if (!zip)
return false; return false;
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend && if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
zip->getMode() != QuaZip::mdAdd) && zip->getMode() != QuaZip::mdAdd)
return false; return false;
QuaZipFile outFile(zip); QuaZipFile outFile(zip);
if (options.getDateTime().isNull()) { if (options.getDateTime().isNull()) {
if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, fileName), nullptr, 0, if (!outFile.open(QIODevice::WriteOnly,
options.getCompressionMethod(), options.getCompressionLevel())) QuaZipNewInfo(fileDest, fileName),
nullptr,
0,
options.getCompressionMethod(),
options.getCompressionLevel()))
return false; return false;
} else { } else {
if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, fileName), nullptr, 0, if (!outFile.open(QIODevice::WriteOnly,
options.getCompressionMethod(), options.getCompressionLevel())) QuaZipNewInfo(fileDest, fileName),
nullptr,
0,
options.getCompressionMethod(),
options.getCompressionLevel()))
return false; return false;
} }
@@ -146,8 +171,7 @@ bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options
if (!inFile.open(QIODevice::ReadOnly)) { if (!inFile.open(QIODevice::ReadOnly)) {
return false; return false;
} }
if (!copyData(inFile, outFile, total, progress, callback) || if (!copyData(inFile, outFile, total, progress, callback) || outFile.getZipError() != UNZ_OK) {
outFile.getZipError() != UNZ_OK) {
return false; return false;
} }
inFile.close(); inFile.close();
@@ -157,8 +181,12 @@ bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options
return outFile.getZipError() == UNZ_OK; return outFile.getZipError() == UNZ_OK;
} }
bool copyData(QIODevice& inFile, QIODevice& outFile, std::size_t total, std::size_t& progress, bool copyData(QIODevice &inFile,
QtProgressCallback callback) { QIODevice &outFile,
std::size_t total,
std::size_t &progress,
QtProgressCallback callback)
{
while (!inFile.atEnd()) { while (!inFile.atEnd()) {
char buf[4096]; char buf[4096];
qint64 readLen = inFile.read(buf, 4096); qint64 readLen = inFile.read(buf, 4096);
@@ -175,13 +203,15 @@ bool copyData(QIODevice& inFile, QIODevice& outFile, std::size_t total, std::siz
return true; return true;
} }
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback) { QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback)
{
// Open zip // Open zip
QuaZip zip(fileCompressed); QuaZip zip(fileCompressed);
return extractDir(zip, dir, callback); return extractDir(zip, dir, callback);
} }
QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCallback callback) { QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback)
{
if (!zip.open(QuaZip::mdUnzip)) { if (!zip.open(QuaZip::mdUnzip)) {
return QStringList(); return QStringList();
} }
@@ -196,7 +226,7 @@ QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCall
} }
std::size_t total = 0; std::size_t total = 0;
for (const QuaZipFileInfo64& info : zip.getFileInfoList64()) { for (const QuaZipFileInfo64 &info : zip.getFileInfoList64()) {
total += info.uncompressedSize; total += info.uncompressedSize;
} }
@@ -226,8 +256,13 @@ QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCall
return extracted; return extracted;
} }
bool extractFile(QuaZip* zip, QString fileName, QString fileDest, std::size_t total, bool extractFile(QuaZip *zip,
std::size_t& progress, QtCommon::QtProgressCallback callback) { QString fileName,
QString fileDest,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback)
{
// zip: object where to add the file // zip: object where to add the file
// filename: real file name // filename: real file name
// fileincompress: file name of the compressed file // fileincompress: file name of the compressed file
@@ -299,7 +334,8 @@ bool extractFile(QuaZip* zip, QString fileName, QString fileDest, std::size_t to
return true; return true;
} }
bool removeFile(QStringList listFile) { bool removeFile(QStringList listFile)
{
bool ret = true; bool ret = true;
// For each file // For each file
for (int i = 0; i < listFile.count(); i++) { for (int i = 0; i < listFile.count(); i++) {
+63 -49
View File
@@ -1,13 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once
#include <QDir> #include <QDir>
#include <QString> #include <QString>
#include "qt_common/qt_common.h"
#include <quazip.h> #include <quazip.h>
#include <zlib.h> #include <zlib.h>
#include "qt_common/qt_common.h"
/** This is a modified version of JlCompress **/ /** This is a modified version of JlCompress **/
namespace QtCommon::Compress { namespace QtCommon::Compress {
@@ -15,49 +15,49 @@ namespace QtCommon::Compress {
class Options { class Options {
public: public:
/** /**
* The enum values refer to the comments in the open function of the quazipfile.h file. * The enum values refer to the comments in the open function of the quazipfile.h file.
* *
* The value is represented by two hexadecimal characters, * The value is represented by two hexadecimal characters,
* the left character indicating the compression method, * the left character indicating the compression method,
* and the right character indicating the compression level. * and the right character indicating the compression level.
* *
* method == 0 indicates that the file is not compressed but rather stored as is. * method == 0 indicates that the file is not compressed but rather stored as is.
* method == 8(Z_DEFLATED) indicates that zlib compression is used. * method == 8(Z_DEFLATED) indicates that zlib compression is used.
* *
* A higher value of level indicates a smaller size of the compressed file, * A higher value of level indicates a smaller size of the compressed file,
* although it also implies more time consumed during the compression process. * although it also implies more time consumed during the compression process.
*/ */
enum CompressionStrategy { enum CompressionStrategy
{
/// Storage without compression /// Storage without compression
Storage = 0x00, // Z_NO_COMPRESSION 0 Storage = 0x00, // Z_NO_COMPRESSION 0
/// The fastest compression speed /// The fastest compression speed
Fastest = 0x81, // Z_BEST_SPEED 1 Fastest = 0x81, // Z_BEST_SPEED 1
/// Relatively fast compression speed /// Relatively fast compression speed
Faster = 0x83, Faster = 0x83,
/// Standard compression speed and ratio /// Standard compression speed and ratio
Standard = 0x86, Standard = 0x86,
/// Better compression ratio /// Better compression ratio
Better = 0x87, Better = 0x87,
/// The best compression ratio /// The best compression ratio
Best = 0x89, // Z_BEST_COMPRESSION 9 Best = 0x89, // Z_BEST_COMPRESSION 9
/// The default compression strategy, according to the open function of /// The default compression strategy, according to the open function of quazipfile.h,
/// quazipfile.h, the value of method is Z_DEFLATED, and the value of level is /// the value of method is Z_DEFLATED, and the value of level is Z_DEFAULT_COMPRESSION -1 (equals lvl 6)
/// Z_DEFAULT_COMPRESSION -1 (equals lvl 6) Default = 0xff
Default = 0xff
}; };
public: public:
explicit Options(const CompressionStrategy& strategy) : m_compressionStrategy(strategy) {} explicit Options(const CompressionStrategy& strategy)
: m_compressionStrategy(strategy) {}
explicit Options(const QDateTime& dateTime = QDateTime(), explicit Options(const QDateTime& dateTime = QDateTime(), const CompressionStrategy& strategy = Default)
const CompressionStrategy& strategy = Default)
: m_dateTime(dateTime), m_compressionStrategy(strategy) {} : m_dateTime(dateTime), m_compressionStrategy(strategy) {}
QDateTime getDateTime() const { QDateTime getDateTime() const {
return m_dateTime; return m_dateTime;
} }
void setDateTime(const QDateTime& dateTime) { void setDateTime(const QDateTime &dateTime) {
m_dateTime = dateTime; m_dateTime = dateTime;
} }
@@ -70,11 +70,10 @@ public:
} }
int getCompressionLevel() const { int getCompressionLevel() const {
return m_compressionStrategy != Default ? m_compressionStrategy & 0x0f return m_compressionStrategy != Default ? m_compressionStrategy & 0x0f : Z_DEFAULT_COMPRESSION;
: Z_DEFAULT_COMPRESSION;
} }
void setCompressionStrategy(const CompressionStrategy& strategy) { void setCompressionStrategy(const CompressionStrategy &strategy) {
m_compressionStrategy = strategy; m_compressionStrategy = strategy;
} }
@@ -90,21 +89,34 @@ private:
* @param fileCompressed Destination file * @param fileCompressed Destination file
* @param dir The directory to compress * @param dir The directory to compress
* @param options Compression level, etc * @param options Compression level, etc
* @param callback Callback that takes in two std::size_t (total, progress) and returns false * @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
* if the current operation should be cancelled.
*/ */
bool compressDir(QString fileCompressed, QString dir, const Options& options = Options(), bool compressDir(QString fileCompressed,
QString dir,
const Options& options = Options(),
QtCommon::QtProgressCallback callback = {}); QtCommon::QtProgressCallback callback = {});
// Internal // // Internal //
bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& options, bool compressSubDir(QuaZip *zip,
std::size_t total, std::size_t& progress, QString dir,
QString origDir,
const Options &options,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback); QtCommon::QtProgressCallback callback);
bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options& options, bool compressFile(QuaZip *zip,
std::size_t total, std::size_t& progress, QtCommon::QtProgressCallback callback); QString fileName,
QString fileDest,
const Options &options,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback);
bool copyData(QIODevice& inFile, QIODevice& outFile, std::size_t total, std::size_t& progress, bool copyData(QIODevice &inFile,
QIODevice &outFile,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback); QtCommon::QtProgressCallback callback);
// Extract // // Extract //
@@ -113,18 +125,20 @@ bool copyData(QIODevice& inFile, QIODevice& outFile, std::size_t total, std::siz
* @brief Extract a zip file and report its progress. * @brief Extract a zip file and report its progress.
* @param fileCompressed Compressed file * @param fileCompressed Compressed file
* @param dir The directory to push the results to * @param dir The directory to push the results to
* @param callback Callback that takes in two std::size_t (total, progress) and returns false * @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
* if the current operation should be cancelled.
*/ */
QStringList extractDir(QString fileCompressed, QString dir, QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback = {});
QtCommon::QtProgressCallback callback = {});
// Internal // // Internal //
QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCallback callback); QStringList extractDir(QuaZip &zip, const QString &dir, QtCommon::QtProgressCallback callback);
bool extractFile(QuaZip* zip, QString fileName, QString fileDest, std::size_t total, bool extractFile(QuaZip *zip,
std::size_t& progress, QtCommon::QtProgressCallback callback); QString fileName,
QString fileDest,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback);
bool removeFile(QStringList listFile); bool removeFile(QStringList listFile);
} // namespace QtCommon::Compress }
+185 -129
View File
@@ -12,54 +12,63 @@
#include "compress.h" #include "compress.h"
#include "qt_common/abstract/frontend.h" #include "qt_common/abstract/frontend.h"
#include "qt_common/abstract/progress.h" #include "qt_common/abstract/qt_progress_dialog.h"
#include "qt_common/qt_common.h" #include "qt_common/qt_common.h"
#include <JlCompress.h>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <JlCompress.h>
namespace QtCommon::Content { namespace QtCommon::Content {
bool CheckGameFirmware(u64 program_id) { bool CheckGameFirmware(u64 program_id, QObject* parent)
if (FirmwareManager::GameRequiresFirmware(program_id) && {
!FirmwareManager::CheckFirmwarePresence(*system)) { if (FirmwareManager::GameRequiresFirmware(program_id)
auto result = QtCommon::Frontend::Warning( && !FirmwareManager::CheckFirmwarePresence(*system)) {
auto result = QtCommon::Frontend::ShowMessage(
QMessageBox::Warning,
tr("Game Requires Firmware"), tr("Game Requires Firmware"),
tr("The game you are trying to launch requires firmware to boot or to get past the " tr("The game you are trying to launch requires firmware to boot or to get past the "
"opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>" "opening menu. Please <a href='https://yuzu-mirror.github.io/help/quickstart'>"
"dump and install firmware</a>, or press \"OK\" to launch anyways."), "dump and install firmware</a>, or press \"OK\" to launch anyways."),
QtCommon::Frontend::Ok | QtCommon::Frontend::Cancel); QMessageBox::Ok | QMessageBox::Cancel,
parent);
return result == QtCommon::Frontend::Ok; return result == QMessageBox::Ok;
} }
return true; return true;
} }
void InstallFirmware(const QString& location, bool recursive) { void InstallFirmware(const QString& location, bool recursive)
// Initialize a progress dialog. {
auto progress = QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."),
QtCommon::Frontend::newProgressDialog(tr("Installing Firmware..."), tr("Cancel"), 0, 100); tr("Cancel"),
progress->show(); 0,
100,
QGuiApplication::processEvents(); rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
progress.show();
// Declare progress callback. // Declare progress callback.
auto callback = [&](size_t total_size, size_t processed_size) { auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents(); progress.setValue(static_cast<int>((processed_size * 100) / total_size));
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); return progress.wasCanceled();
return progress->wasCanceled();
}; };
QString failedTitle = tr("Firmware Install Failed"); QString failedTitle = tr("Firmware Install Failed");
QString successTitle = tr("Firmware Install Succeeded"); QString successTitle = tr("Firmware Install Succeeded");
QtCommon::Frontend::Icon icon; QMessageBox::Icon icon;
FirmwareInstallResult result; FirmwareInstallResult result;
const auto ShowMessage = [&]() { const auto ShowMessage = [&]() {
QtCommon::Frontend::ShowMessage(icon, failedTitle, GetFirmwareInstallResultString(result)); QtCommon::Frontend::ShowMessage(icon,
failedTitle,
GetFirmwareInstallResultString(result));
}; };
LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString()); LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
@@ -84,26 +93,28 @@ void InstallFirmware(const QString& location, bool recursive) {
callback(100, 10); callback(100, 10);
if (recursive) { if (recursive) {
Common::FS::IterateDirEntriesRecursively(firmware_source_path, dir_callback, Common::FS::IterateDirEntriesRecursively(firmware_source_path,
dir_callback,
Common::FS::DirEntryFilter::File); Common::FS::DirEntryFilter::File);
} else { } else {
Common::FS::IterateDirEntries(firmware_source_path, dir_callback, Common::FS::IterateDirEntries(firmware_source_path,
dir_callback,
Common::FS::DirEntryFilter::File); Common::FS::DirEntryFilter::File);
} }
if (out.size() <= 0) { if (out.size() <= 0) {
result = FirmwareInstallResult::NoNCAs; result = FirmwareInstallResult::NoNCAs;
icon = QtCommon::Frontend::Icon::Warning; icon = QMessageBox::Warning;
ShowMessage(); ShowMessage();
return; return;
} }
// Locate and erase the content of nand/system/Content/registered/*.nca, if any. // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory(); auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (sysnand_content_vdir->IsWritable() && if (sysnand_content_vdir->IsWritable()
!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) { && !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
result = FirmwareInstallResult::FailedDelete; result = FirmwareInstallResult::FailedDelete;
icon = QtCommon::Frontend::Icon::Critical; icon = QMessageBox::Critical;
ShowMessage(); ShowMessage();
return; return;
} }
@@ -119,20 +130,22 @@ void InstallFirmware(const QString& location, bool recursive) {
int i = 0; int i = 0;
for (const auto& firmware_src_path : out) { for (const auto& firmware_src_path : out) {
i++; i++;
auto firmware_src_vfile = auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read); FileSys::OpenMode::Read);
auto firmware_dst_vfile = auto firmware_dst_vfile = firmware_vdir->CreateFileRelative(
firmware_vdir->CreateFileRelative(firmware_src_path.filename().string()); firmware_src_path.filename().string());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) { if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!", LOG_ERROR(Frontend,
firmware_src_path.generic_string(), firmware_src_path.filename().string()); "Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(),
firmware_src_path.filename().string());
success = false; success = false;
} }
if (callback(100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) { if (callback(100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
result = FirmwareInstallResult::FailedCorrupted; result = FirmwareInstallResult::FailedCorrupted;
icon = QtCommon::Frontend::Icon::Warning; icon = QMessageBox::Warning;
ShowMessage(); ShowMessage();
return; return;
} }
@@ -140,7 +153,7 @@ void InstallFirmware(const QString& location, bool recursive) {
if (!success) { if (!success) {
result = FirmwareInstallResult::FailedCopy; result = FirmwareInstallResult::FailedCopy;
icon = QtCommon::Frontend::Icon::Critical; icon = QMessageBox::Critical;
ShowMessage(); ShowMessage();
return; return;
} }
@@ -149,37 +162,39 @@ void InstallFirmware(const QString& location, bool recursive) {
system->GetFileSystemController().CreateFactories(*vfs); system->GetFileSystemController().CreateFactories(*vfs);
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) { auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents(); progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
progress->setValue(90 + static_cast<int>((processed_size * 10) / total_size)); return progress.wasCanceled();
return progress->wasCanceled();
}; };
auto results = ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, auto results = ContentManager::VerifyInstalledContents(*QtCommon::system,
VerifyFirmwareCallback, true); *QtCommon::provider,
VerifyFirmwareCallback,
true);
if (results.size() > 0) { if (results.size() > 0) {
const auto failed_names = const auto failed_names = QString::fromStdString(
QString::fromStdString(fmt::format("{}", fmt::join(results, "\n"))); fmt::format("{}", fmt::join(results, "\n")));
progress->close(); progress.close();
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Firmware integrity verification failed!"), tr("Firmware integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names)); tr("Verification failed for the following files:\n\n%1").arg(failed_names));
return; return;
} }
progress->close(); progress.close();
QGuiApplication::processEvents();
const auto pair = FirmwareManager::GetFirmwareVersion(*system); const auto pair = FirmwareManager::GetFirmwareVersion(*system);
const auto firmware_data = pair.first; const auto firmware_data = pair.first;
const std::string display_version(firmware_data.display_version.data()); const std::string display_version(firmware_data.display_version.data());
result = FirmwareInstallResult::Success; result = FirmwareInstallResult::Success;
QtCommon::Frontend::Information(successTitle, GetFirmwareInstallResultString(result).arg( QtCommon::Frontend::Information(successTitle,
QString::fromStdString(display_version))); GetFirmwareInstallResultString(result).arg(
QString::fromStdString(display_version)));
} }
QString UnzipFirmwareToTmp(const QString& location) { QString UnzipFirmwareToTmp(const QString& location)
{
namespace fs = std::filesystem; namespace fs = std::filesystem;
fs::path tmp{fs::temp_directory_path()}; fs::path tmp{fs::temp_directory_path()};
@@ -204,47 +219,59 @@ QString UnzipFirmwareToTmp(const QString& location) {
} }
// Content // // Content //
void VerifyGameContents(const std::string& game_path) { void VerifyGameContents(const std::string& game_path)
auto progress = {
QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100); QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
progress->show(); tr("Cancel"),
0,
QGuiApplication::processEvents(); 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) { const auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents(); progress.setValue(static_cast<int>((processed_size * 100) / total_size));
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); return progress.wasCanceled();
return progress->wasCanceled();
}; };
const auto result = ContentManager::VerifyGameContents(*system, game_path, callback); const auto result = ContentManager::VerifyGameContents(*system, game_path, callback);
switch (result) { switch (result) {
case ContentManager::GameVerificationResult::Success: case ContentManager::GameVerificationResult::Success:
QtCommon::Frontend::Information(rootObject, tr("Integrity verification succeeded!"), QtCommon::Frontend::Information(rootObject,
tr("Integrity verification succeeded!"),
tr("The operation completed successfully.")); tr("The operation completed successfully."));
break; break;
case ContentManager::GameVerificationResult::Failed: case ContentManager::GameVerificationResult::Failed:
QtCommon::Frontend::Critical(rootObject, tr("Integrity verification failed!"), QtCommon::Frontend::Critical(rootObject,
tr("Integrity verification failed!"),
tr("File contents may be corrupt or missing.")); tr("File contents may be corrupt or missing."));
break; break;
case ContentManager::GameVerificationResult::NotImplemented: case ContentManager::GameVerificationResult::NotImplemented:
QtCommon::Frontend::Warning( QtCommon::Frontend::Warning(
rootObject, tr("Integrity verification couldn't be performed"), rootObject,
tr("Integrity verification couldn't be performed"),
tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. " tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. "
"File contents could not be checked for validity.")); "File contents could not be checked for validity."));
} }
} }
void InstallKeys() { void InstallKeys()
const QString key_source_location = QtCommon::Frontend::GetOpenFileName( {
tr("Select Dumped Keys Location"), {}, QStringLiteral("Decryption Keys (*.keys)"), {}); 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()) if (key_source_location.isEmpty())
return; return;
FirmwareManager::KeyInstallResult result = FirmwareManager::KeyInstallResult result
FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys"); = FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
system->GetFileSystemController().CreateFactories(*QtCommon::vfs); system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
@@ -259,42 +286,46 @@ void InstallKeys() {
} }
} }
void VerifyInstalledContents() { void VerifyInstalledContents()
{
// Initialize a progress dialog. // Initialize a progress dialog.
auto progress = QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100); tr("Cancel"),
progress->show(); 0,
100,
QGuiApplication::processEvents(); rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
// Declare progress callback. // Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) { auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents(); progress.setValue(static_cast<int>((processed_size * 100) / total_size));
progress->setValue(static_cast<int>((processed_size * 100) / total_size)); return progress.wasCanceled();
return progress->wasCanceled();
}; };
const std::vector<std::string> result = ContentManager::VerifyInstalledContents( const std::vector<std::string> result
*QtCommon::system, *QtCommon::provider, QtProgressCallback); = ContentManager::VerifyInstalledContents(*QtCommon::system,
*QtCommon::provider,
progress->close(); QtProgressCallback);
progress.close();
if (result.empty()) { if (result.empty()) {
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"), QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
tr("The operation completed successfully.")); tr("The operation completed successfully."));
} else { } else {
const auto failed_names = const auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Integrity verification failed!"), tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names)); tr("Verification failed for the following files:\n\n%1").arg(failed_names));
} }
} }
void FixProfiles() { void FixProfiles()
{
// Reset user save files after config is initialized and migration is done. // Reset user save files after config is initialized and migration is done.
// Doing it at init time causes profiles to read from the wrong place entirely if NAND dir is // Doing it at init time causes profiles to read from the wrong place entirely if NAND dir is not default
// not default
// TODO: better solution // TODO: better solution
system->GetProfileManager().ResetUserSaveFile(); system->GetProfileManager().ResetUserSaveFile();
std::vector<std::string> orphaned = system->GetProfileManager().FindOrphanedProfiles(); std::vector<std::string> orphaned = system->GetProfileManager().FindOrphanedProfiles();
@@ -334,66 +365,79 @@ void FixProfiles() {
"%2<br><br>" "%2<br><br>"
"Click \"OK\" to open your save folder and fix up your profiles.<br>" "Click \"OK\" to open your save folder and fix up your profiles.<br>"
"Hint: copy the contents of the largest or last-modified folder elsewhere, " "Hint: copy the contents of the largest or last-modified folder elsewhere, "
"delete all orphaned profiles, and move your copied contents to the good " "delete all orphaned profiles, and move your copied contents to the good profile.<br><br>"
"profile.<br><br>" "Still confused? See the <a href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/Orphaned.md'>help page</a>.<br>")
"Still confused? See the <a "
"href='https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/"
"Orphaned.md'>help page</a>.<br>")
.arg(qorphaned, qgood)); .arg(qorphaned, qgood));
QtCommon::Game::OpenSaveFolder(); QtCommon::Game::OpenSaveFolder();
} }
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id) { void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id)
using namespace QtCommon::Frontend; {
auto result = Warning(tr("Really clear data?"), tr("Important data may be lost!"), Yes | No); auto result = QtCommon::Frontend::Warning(tr("Really clear data?"),
tr("Important data may be lost!"),
QMessageBox::Yes | QMessageBox::No);
if (result != Yes) if (result != QMessageBox::Yes)
return; return;
result = Warning(tr("Are you REALLY sure?"), result = QtCommon::Frontend::Warning(
tr("Once deleted, your data will NOT come back!\n" tr("Are you REALLY sure?"),
"Only do this if you're 100% sure you want to delete this data."), tr("Once deleted, your data will NOT come back!\n"
Yes | No); "Only do this if you're 100% sure you want to delete this data."),
QMessageBox::Yes | QMessageBox::No);
if (result != Yes) if (result != QMessageBox::Yes)
return; return;
auto dialog = newProgressDialog(tr("Clearing..."), QString(), 0, 0); QtCommon::Frontend::QtProgressDialog dialog(tr("Clearing..."), QString(), 0, 0);
dialog->show(); dialog.show();
FrontendCommon::DataManager::ClearDir(dir, user_id); FrontendCommon::DataManager::ClearDir(dir, user_id);
dialog->close(); dialog.close();
} }
void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::string& user_id, void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir,
const QString& name, std::function<void()> callback) { const std::string& user_id,
const QString& name,
std::function<void()> callback)
{
using namespace QtCommon::Frontend; using namespace QtCommon::Frontend;
const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id); const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id);
const QString zip_dump_location = GetSaveFileName( const QString zip_dump_location = GetSaveFileName(tr("Select Export Location"),
tr("Select Export Location"), tr("%1.zip").arg(name), tr("Zipped Archives (*.zip)")); tr("%1.zip").arg(name),
tr("Zipped Archives (*.zip)"));
if (zip_dump_location.isEmpty()) if (zip_dump_location.isEmpty())
return; return;
auto progress = QtCommon::Frontend::newProgressDialogPtr( QtProgressDialog* progress = new QtProgressDialog(
tr("Exporting data. This may take a while..."), tr("Cancel"), 0, 100); tr("Exporting data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
progress->setTitle(tr("Exporting")); progress->setWindowTitle(tr("Exporting"));
progress->setWindowModality(Qt::WindowModal);
progress->setMinimumDuration(100);
progress->setAutoClose(false);
progress->setAutoReset(false);
progress->show(); progress->show();
QGuiApplication::processEvents();
auto progress_callback = [=](size_t total_size, size_t processed_size) { auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(progress,
progress, "setValue", Qt::DirectConnection, "setValue",
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size))); Qt::DirectConnection,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled(); return !progress->wasCanceled();
}; };
QFuture<bool> future = QtConcurrent::run([=]() { QFuture<bool> future = QtConcurrent::run([=]() {
return QtCommon::Compress::compressDir(zip_dump_location, QString::fromStdString(dir), return QtCommon::Compress::compressDir(zip_dump_location,
QtCommon::Compress::Options(), progress_callback); QString::fromStdString(dir),
QtCommon::Compress::Options(),
progress_callback);
}); });
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject); QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
@@ -420,32 +464,42 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
watcher->setFuture(future); watcher->setFuture(future);
} }
void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::string& user_id, void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir,
std::function<void()> callback) { const std::string& user_id,
std::function<void()> callback)
{
const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id); const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id);
using namespace QtCommon::Frontend; using namespace QtCommon::Frontend;
const QString zip_dump_location = const QString zip_dump_location = GetOpenFileName(tr("Select Import Location"),
GetOpenFileName(tr("Select Import Location"), {}, tr("Zipped Archives (*.zip)")); {},
tr("Zipped Archives (*.zip)"));
if (zip_dump_location.isEmpty()) if (zip_dump_location.isEmpty())
return; return;
StandardButton button = StandardButton button = Warning(
Warning(tr("Import Warning"), tr("Import Warning"),
tr("All previous data in this directory will be deleted. Are you sure you wish to " tr("All previous data in this directory will be deleted. Are you sure you wish to "
"proceed?"), "proceed?"),
StandardButton::Yes | StandardButton::No); StandardButton::Yes | StandardButton::No);
if (button != QtCommon::Frontend::Yes) if (button != QMessageBox::Yes)
return; return;
QtProgressDialog* progress = QtProgressDialog* progress = new QtProgressDialog(
newProgressDialogPtr(tr("Importing data. This may take a while..."), tr("Cancel"), 0, 100); tr("Importing data. This may take a while..."), tr("Cancel"), 0, 100, rootObject);
progress->setTitle(tr("Importing")); progress->setWindowTitle(tr("Importing"));
progress->setWindowModality(Qt::WindowModal);
progress->setMinimumDuration(100);
progress->setAutoClose(false);
progress->setAutoReset(false);
progress->show(); progress->show();
progress->setValue(0);
QGuiApplication::processEvents();
// to prevent GUI mangling we have to run this in a thread as well // to prevent GUI mangling we have to run this in a thread as well
QFuture<bool> delete_future = QtConcurrent::run([=]() { QFuture<bool> delete_future = QtConcurrent::run([=]() {
@@ -458,14 +512,17 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() { QObject::connect(delete_watcher, &QFutureWatcher<bool>::finished, rootObject, [=]() {
auto progress_callback = [=](size_t total_size, size_t processed_size) { auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(progress,
progress, "setValue", Qt::DirectConnection, "setValue",
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size))); Qt::DirectConnection,
Q_ARG(int,
static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled(); return !progress->wasCanceled();
}; };
QFuture<bool> future = QtConcurrent::run([=]() { QFuture<bool> future = QtConcurrent::run([=]() {
return !QtCommon::Compress::extractDir(zip_dump_location, QString::fromStdString(dir), return !QtCommon::Compress::extractDir(zip_dump_location,
QString::fromStdString(dir),
progress_callback) progress_callback)
.empty(); .empty();
}); });
@@ -496,5 +553,4 @@ void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
}); });
} }
// TODO(crueter): Port InstallFirmware et al. from QML Branch
} // namespace QtCommon::Content } // namespace QtCommon::Content
+15 -13
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_CONTENT_UTIL_H #ifndef QT_CONTENT_UTIL_H
@@ -13,7 +13,7 @@
namespace QtCommon::Content { namespace QtCommon::Content {
// //
bool CheckGameFirmware(u64 program_id); bool CheckGameFirmware(u64 program_id, QObject *parent);
enum class FirmwareInstallResult { enum class FirmwareInstallResult {
Success, Success,
@@ -23,7 +23,8 @@ enum class FirmwareInstallResult {
FailedCorrupted, FailedCorrupted,
}; };
inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result) { inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result)
{
return LOOKUP_ENUM(result, FwInstallSuccess); return LOOKUP_ENUM(result, FwInstallSuccess);
} }
@@ -32,29 +33,30 @@ inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result
* \param result The result code. * \param result The result code.
* \return A string representation of the passed result code. * \return A string representation of the passed result code.
*/ */
inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult result) { inline const QString GetKeyInstallResultString(FirmwareManager::KeyInstallResult result)
{
return LOOKUP_ENUM(result, KeyInstallSuccess); return LOOKUP_ENUM(result, KeyInstallSuccess);
} }
void InstallFirmware(const QString& location, bool recursive); void InstallFirmware(const QString &location, bool recursive);
QString UnzipFirmwareToTmp(const QString& location); QString UnzipFirmwareToTmp(const QString &location);
// Keys // // Keys //
void InstallKeys(); void InstallKeys();
// Content // // Content //
void VerifyGameContents(const std::string& game_path); void VerifyGameContents(const std::string &game_path);
void VerifyInstalledContents(); void VerifyInstalledContents();
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = ""); void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "");
void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "", void ExportDataDir(FrontendCommon::DataManager::DataDir dir,
const QString& name = QStringLiteral("export"), const std::string &user_id = "",
std::function<void()> callback = {}); const QString &name = QStringLiteral("export"),
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "",
std::function<void()> callback = {}); std::function<void()> callback = {});
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
// Profiles // // Profiles //
void FixProfiles(); void FixProfiles();
} // namespace QtCommon::Content }
#endif // QT_CONTENT_UTIL_H #endif // QT_CONTENT_UTIL_H
+13 -14
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm> #include <algorithm>
@@ -13,7 +13,8 @@ namespace fs = std::filesystem;
namespace QtCommon::FS { namespace QtCommon::FS {
void LinkRyujinx(std::filesystem::path& from, std::filesystem::path& to) { void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to)
{
std::error_code ec; std::error_code ec;
// "ignore" errors--if the dir fails to be deleted, error handling later will handle it // "ignore" errors--if the dir fails to be deleted, error handling later will handle it
@@ -24,12 +25,12 @@ void LinkRyujinx(std::filesystem::path& from, std::filesystem::path& to) {
} else { } else {
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Failed to link save data"), tr("Failed to link save data"),
tr("Could not link directory:\n\t%1\nTo:\n\t%2") tr("Could not link directory:\n\t%1\nTo:\n\t%2").arg(QString::fromStdString(from.string()), QString::fromStdString(to.string())));
.arg(QString::fromStdString(from.string()), QString::fromStdString(to.string())));
} }
} }
bool CheckUnlink(const fs::path& eden_dir, const fs::path& ryu_dir) { bool CheckUnlink(const fs::path &eden_dir, const fs::path &ryu_dir)
{
bool eden_link = Common::FS::IsSymlink(eden_dir); bool eden_link = Common::FS::IsSymlink(eden_dir);
bool ryu_link = Common::FS::IsSymlink(ryu_dir); bool ryu_link = Common::FS::IsSymlink(ryu_dir);
@@ -63,7 +64,7 @@ bool CheckUnlink(const fs::path& eden_dir, const fs::path& ryu_dir) {
// NB: do NOT use remove_all, as Windows treats this as a remove_all to the target, // NB: do NOT use remove_all, as Windows treats this as a remove_all to the target,
// NOT the junction // NOT the junction
fs::remove(linked); fs::remove(linked);
} catch (std::exception& e) { } catch (std::exception &e) {
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Failed to unlink old directory"), tr("Failed to unlink old directory"),
tr("OS returned error: %1").arg(QString::fromStdString(e.what()))); tr("OS returned error: %1").arg(QString::fromStdString(e.what())));
@@ -73,7 +74,7 @@ bool CheckUnlink(const fs::path& eden_dir, const fs::path& ryu_dir) {
// then COPY the other dir // then COPY the other dir
try { try {
fs::copy(orig, linked, fs::copy_options::recursive); fs::copy(orig, linked, fs::copy_options::recursive);
} catch (std::exception& e) { } catch (std::exception &e) {
QtCommon::Frontend::Critical( QtCommon::Frontend::Critical(
tr("Failed to copy save data"), tr("Failed to copy save data"),
tr("OS returned error: %1").arg(QString::fromStdString(e.what()))); tr("OS returned error: %1").arg(QString::fromStdString(e.what())));
@@ -86,7 +87,8 @@ bool CheckUnlink(const fs::path& eden_dir, const fs::path& ryu_dir) {
return true; return true;
} }
const fs::path GetRyujinxSavePath(const fs::path& path_hint, const u64& program_id) { const fs::path GetRyujinxSavePath(const fs::path &path_hint, const u64 &program_id)
{
auto ryu_path = path_hint; auto ryu_path = path_hint;
auto kvdb_path = Common::FS::GetKvdbPath(ryu_path); auto kvdb_path = Common::FS::GetKvdbPath(ryu_path);
@@ -97,13 +99,10 @@ const fs::path GetRyujinxSavePath(const fs::path& path_hint, const u64& program_
tr("Could not find Ryujinx installation"), tr("Could not find Ryujinx installation"),
tr("Could not find a valid Ryujinx installation. This may typically occur if you are " tr("Could not find a valid Ryujinx installation. This may typically occur if you are "
"using Ryujinx in portable mode.\n\nWould you like to manually select a portable " "using Ryujinx in portable mode.\n\nWould you like to manually select a portable "
"folder to use?"), "folder to use?"), StandardButton::Yes | StandardButton::No);
StandardButton::Yes | StandardButton::No);
if (res == StandardButton::Yes) { if (res == StandardButton::Yes) {
auto selected_path = auto selected_path = GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath()).toStdString();
GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath())
.toStdString();
if (selected_path.empty()) if (selected_path.empty())
return fs::path{}; return fs::path{};
@@ -132,7 +131,7 @@ const fs::path GetRyujinxSavePath(const fs::path& path_hint, const u64& program_
Common::FS::IMENReadResult res = Common::FS::ReadKvdb(kvdb_path, imens); Common::FS::IMENReadResult res = Common::FS::ReadKvdb(kvdb_path, imens);
if (res == Common::FS::IMENReadResult::Success) { if (res == Common::FS::IMENReadResult::Success) {
for (const Common::FS::IMEN& imen : imens) { for (const Common::FS::IMEN &imen : imens) {
if (imen.title_id == program_id) if (imen.title_id == program_id)
return Common::FS::GetRyuSavePath(ryu_path, imen.save_id); return Common::FS::GetRyuSavePath(ryu_path, imen.save_id);
} }
+6 -6
View File
@@ -1,19 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "common/common_types.h"
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include "common/common_types.h"
#pragma once #pragma once
namespace QtCommon::FS { namespace QtCommon::FS {
void LinkRyujinx(std::filesystem::path& from, std::filesystem::path& to); void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to);
const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path& path_hint, const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path &path_hint, const u64 &program_id);
const u64& program_id);
/// returns FALSE if the dirs are NOT linked /// returns FALSE if the dirs are NOT linked
bool CheckUnlink(const std::filesystem::path& eden_dir, const std::filesystem::path& ryu_dir); bool CheckUnlink(const std::filesystem::path& eden_dir,
const std::filesystem::path& ryu_dir);
} // namespace QtCommon::FS } // namespace QtCommon::FS
+119 -73
View File
@@ -18,34 +18,40 @@
#include <QUrl> #include <QUrl>
#ifdef _WIN32 #ifdef _WIN32
#include <shlobj.h>
#include <windows.h>
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/string_util.h" #include "common/string_util.h"
#include <shlobj.h>
#include <windows.h>
#else #else
#include <fstream>
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include <fstream>
#endif #endif
namespace QtCommon::Game { namespace QtCommon::Game {
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path, const std::filesystem::path& icon_path,
const std::filesystem::path& command, const std::string& arguments, const std::filesystem::path& command,
const std::string& categories, const std::string& keywords, const std::string& arguments,
const std::string& name) try { const std::string& categories,
const std::string& keywords,
const std::string& name)
try {
#ifdef _WIN32 // Windows #ifdef _WIN32 // Windows
HRESULT hr = CoInitialize(nullptr); HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) { if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed"); LOG_ERROR(Frontend, "CoInitialize failed");
return false; return false;
} }
SCOPE_EXIT { SCOPE_EXIT
{
CoUninitialize(); CoUninitialize();
}; };
IShellLinkW* ps1 = nullptr; IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr; IPersistFile* persist_file = nullptr;
SCOPE_EXIT { SCOPE_EXIT
{
if (persist_file != nullptr) { if (persist_file != nullptr) {
persist_file->Release(); persist_file->Release();
} }
@@ -53,7 +59,10 @@ bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::s
ps1->Release(); ps1->Release();
} }
}; };
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, HRESULT hres = CoCreateInstance(CLSID_ShellLink,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IShellLinkW,
reinterpret_cast<void**>(&ps1)); reinterpret_cast<void**>(&ps1));
if (FAILED(hres)) { if (FAILED(hres)) {
LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
@@ -133,8 +142,10 @@ bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::s
return false; return false;
} }
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, bool MakeShortcutIcoPath(const u64 program_id,
std::filesystem::path& out_icon_path) { const std::string_view game_file_name,
std::filesystem::path& out_icon_path)
{
// Get path to Yuzu icons directory & icon extension // Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png"; std::string ico_extension = "png";
#if defined(_WIN32) #if defined(_WIN32)
@@ -155,38 +166,46 @@ bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_
return true; return true;
} }
void OpenEdenFolder(const Common::FS::EdenPath& path) { void OpenEdenFolder(const Common::FS::EdenPath& path)
{
QDesktopServices::openUrl( QDesktopServices::openUrl(
QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path)))); QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path))));
} }
void OpenRootDataFolder() { void OpenRootDataFolder()
{
OpenEdenFolder(Common::FS::EdenPath::EdenDir); OpenEdenFolder(Common::FS::EdenPath::EdenDir);
} }
void OpenNANDFolder() { void OpenNANDFolder()
{
OpenEdenFolder(Common::FS::EdenPath::NANDDir); OpenEdenFolder(Common::FS::EdenPath::NANDDir);
} }
void OpenSaveFolder() { void OpenSaveFolder()
const auto path = {
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000"; const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string()))); QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string())));
} }
void OpenSDMCFolder() { void OpenSDMCFolder()
{
OpenEdenFolder(Common::FS::EdenPath::SDMCDir); OpenEdenFolder(Common::FS::EdenPath::SDMCDir);
} }
void OpenModFolder() { void OpenModFolder()
{
OpenEdenFolder(Common::FS::EdenPath::LoadDir); OpenEdenFolder(Common::FS::EdenPath::LoadDir);
} }
void OpenLogFolder() { void OpenLogFolder()
{
OpenEdenFolder(Common::FS::EdenPath::LogDir); OpenEdenFolder(Common::FS::EdenPath::LogDir);
} }
static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type) { static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type)
{
switch (type) { switch (type) {
case QtCommon::Game::InstalledEntryType::Game: case QtCommon::Game::InstalledEntryType::Game:
return tr("Error Removing Contents"); return tr("Error Removing Contents");
@@ -200,9 +219,10 @@ static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type)
} }
// Game Content // // Game Content //
void RemoveBaseContent(u64 program_id, InstalledEntryType type) { void RemoveBaseContent(u64 program_id, InstalledEntryType type)
const auto res = {
ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id); const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(),
program_id);
if (res) { if (res) {
QtCommon::Frontend::Information(tr("Successfully Removed"), QtCommon::Frontend::Information(tr("Successfully Removed"),
tr("Successfully removed the installed base game.")); tr("Successfully removed the installed base game."));
@@ -214,7 +234,8 @@ void RemoveBaseContent(u64 program_id, InstalledEntryType type) {
} }
} }
void RemoveUpdateContent(u64 program_id, InstalledEntryType type) { void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
{
const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id); const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
if (res) { if (res) {
QtCommon::Frontend::Information(tr("Successfully Removed"), QtCommon::Frontend::Information(tr("Successfully Removed"),
@@ -225,7 +246,8 @@ void RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
} }
} }
void RemoveAddOnContent(u64 program_id, InstalledEntryType type) { void RemoveAddOnContent(u64 program_id, InstalledEntryType type)
{
const size_t count = ContentManager::RemoveAllDLC(*system, program_id); const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
if (count == 0) { if (count == 0) {
QtCommon::Frontend::Warning(GetGameListErrorRemoving(type), QtCommon::Frontend::Warning(GetGameListErrorRemoving(type),
@@ -239,7 +261,8 @@ void RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
// Global Content // // Global Content //
void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) { void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
{
const auto target_file_name = [target] { const auto target_file_name = [target] {
switch (target) { switch (target) {
case GameListRemoveTarget::GlShaderCache: case GameListRemoveTarget::GlShaderCache:
@@ -268,7 +291,8 @@ void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
} }
} }
void RemoveVulkanDriverPipelineCache(u64 program_id) { void RemoveVulkanDriverPipelineCache(u64 program_id)
{
static constexpr std::string_view target_file_name = "vulkan_pipelines.bin"; 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_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
@@ -284,7 +308,8 @@ void RemoveVulkanDriverPipelineCache(u64 program_id) {
} }
} }
void RemoveAllTransferableShaderCaches(u64 program_id) { void RemoveAllTransferableShaderCaches(u64 program_id)
{
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); 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); const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
@@ -304,13 +329,14 @@ void RemoveAllTransferableShaderCaches(u64 program_id) {
} }
} }
void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) { void RemoveCustomConfiguration(u64 program_id, const std::string& game_path)
{
const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
const auto config_file_name = const auto config_file_name
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini") = program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
: fmt::format("{:016X}.ini", program_id); : fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path = const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir)
Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) / "custom" / config_file_name; / "custom" / config_file_name;
if (!Common::FS::Exists(custom_config_file_path)) { if (!Common::FS::Exists(custom_config_file_path)) {
QtCommon::Frontend::Warning(tr("Error Removing Custom Configuration"), QtCommon::Frontend::Warning(tr("Error Removing Custom Configuration"),
@@ -327,14 +353,20 @@ void RemoveCustomConfiguration(u64 program_id, const std::string& game_path) {
} }
} }
void RemoveCacheStorage(u64 program_id) { void RemoveCacheStorage(u64 program_id)
{
const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir); const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
auto vfs_nand_dir = auto vfs_nand_dir = vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir),
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read); FileSys::OpenMode::Read);
const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath( const auto cache_storage_path
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Cache, = FileSys::SaveDataFactory::GetFullPath({},
0 /* program_id */, {}, 0); vfs_nand_dir,
FileSys::SaveDataSpaceId::User,
FileSys::SaveDataType::Cache,
0 /* program_id */,
{},
0);
const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path); const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
@@ -368,31 +400,32 @@ void ResetMetadata(bool show_message) {
// Uhhh // // Uhhh //
// Messages in pre-defined message boxes for less code spaghetti // Messages in pre-defined message boxes for less code spaghetti
inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title) { inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title)
{
int result = 0; int result = 0;
using namespace QtCommon::Frontend; QMessageBox::StandardButtons buttons;
int buttons;
switch (imsg) { switch (imsg) {
case ShortcutMessages::Fullscreen: case ShortcutMessages::Fullscreen:
buttons = Yes | No; buttons = QMessageBox::Yes | QMessageBox::No;
result = QtCommon::Frontend::Information( result
tr("Create Shortcut"), tr("Do you want to launch the game in fullscreen?"), buttons); = QtCommon::Frontend::Information(tr("Create Shortcut"),
return result == Yes; tr("Do you want to launch the game in fullscreen?"),
buttons);
return result == QMessageBox::Yes;
case ShortcutMessages::Success: case ShortcutMessages::Success:
QtCommon::Frontend::Information( QtCommon::Frontend::Information(tr("Shortcut Created"),
tr("Shortcut Created"), tr("Successfully created a shortcut to %1").arg(game_title)); tr("Successfully created a shortcut to %1").arg(game_title));
return false; return false;
case ShortcutMessages::Volatile: case ShortcutMessages::Volatile:
buttons = Ok | Cancel; buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result = QtCommon::Frontend::Warning( result = QtCommon::Frontend::Warning(
tr("Shortcut may be Volatile!"), tr("Shortcut may be Volatile!"),
tr("This will create a shortcut to the current AppImage. This may " tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"), "not work well if you update. Continue?"),
buttons); buttons);
return result == Ok; return result == QMessageBox::Ok;
default: default:
buttons = Ok; buttons = QMessageBox::Ok;
QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"), QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title), tr("Failed to create a shortcut to %1").arg(game_title),
buttons); buttons);
@@ -400,9 +433,13 @@ inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QSt
} }
} }
void CreateShortcut(const std::string& game_path, const u64 program_id, void CreateShortcut(const std::string& game_path,
const std::string& game_title_, const ShortcutTarget& target, const u64 program_id,
std::string arguments_, const bool needs_title) { const std::string& game_title_,
const ShortcutTarget& target,
std::string arguments_,
const bool needs_title)
{
// Get path to Eden executable // Get path to Eden executable
std::filesystem::path command = GetEdenCommand(); std::filesystem::path command = GetEdenCommand();
@@ -416,11 +453,13 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
return; return;
} }
const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(), const FileSys::PatchManager pm{program_id,
QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider()}; QtCommon::system->GetContentProvider()};
const auto control = pm.GetControlMetadata(); const auto control = pm.GetControlMetadata();
const auto loader = Loader::GetLoader( const auto loader = Loader::GetLoader(*QtCommon::system,
*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read)); QtCommon::vfs->OpenFile(game_path,
FileSys::OpenMode::Read));
std::string game_title{game_title_}; std::string game_title{game_title_};
@@ -451,8 +490,8 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
} }
QImage icon_data = QImage icon_data = QImage::fromData(icon_image_file.data(),
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path; std::filesystem::path out_icon_path;
if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) { if (!SaveIconToFile(out_icon_path, icon_data)) {
@@ -485,32 +524,39 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
const std::string categories = "Game;Emulator;Qt;"; const std::string categories = "Game;Emulator;Qt;";
const std::string keywords = "Switch;Nintendo;"; const std::string keywords = "Switch;Nintendo;";
if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command, if (QtCommon::Game::CreateShortcutLink(shortcut_path,
arguments, categories, keywords, game_title)) { comment,
out_icon_path,
command,
arguments,
categories,
keywords,
game_title)) {
CreateShortcutMessagesGUI(ShortcutMessages::Success, qgame_title); CreateShortcutMessagesGUI(ShortcutMessages::Success, qgame_title);
return; return;
} }
CreateShortcutMessagesGUI(ShortcutMessages::Failed, qgame_title); CreateShortcutMessagesGUI(ShortcutMessages::Failed, qgame_title);
} }
// TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string // TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string literal
// literal std::string GetShortcutPath(ShortcutTarget target)
std::string GetShortcutPath(ShortcutTarget target) { {
{ {
std::string shortcut_path{}; std::string shortcut_path{};
if (target == ShortcutTarget::Desktop) { if (target == ShortcutTarget::Desktop) {
shortcut_path = shortcut_path
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == ShortcutTarget::Applications) { } else if (target == ShortcutTarget::Applications) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) shortcut_path
.toStdString(); = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
} }
return shortcut_path; return shortcut_path;
} }
} }
void CreateHomeMenuShortcut(ShortcutTarget target) { void CreateHomeMenuShortcut(ShortcutTarget target)
{
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch); constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents(); auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
if (!bis_system) { if (!bis_system) {
+23 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_GAME_UTIL_H #ifndef QT_GAME_UTIL_H
@@ -29,18 +29,27 @@ enum class ShortcutTarget {
Applications, Applications,
}; };
enum class ShortcutMessages { Fullscreen = 0, Success = 1, Volatile = 2, Failed = 3 }; enum class ShortcutMessages{
Fullscreen = 0,
Success = 1,
Volatile = 2,
Failed = 3
};
bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
const std::string& comment,
const std::filesystem::path& icon_path, const std::filesystem::path& icon_path,
const std::filesystem::path& command, const std::string& arguments, const std::filesystem::path& command,
const std::string& categories, const std::string& keywords, const std::string& arguments,
const std::string& categories,
const std::string& keywords,
const std::string& name); const std::string& name);
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, bool MakeShortcutIcoPath(const u64 program_id,
const std::string_view game_file_name,
std::filesystem::path& out_icon_path); std::filesystem::path& out_icon_path);
void OpenEdenFolder(const Common::FS::EdenPath& path); void OpenEdenFolder(const Common::FS::EdenPath &path);
void OpenRootDataFolder(); void OpenRootDataFolder();
void OpenNANDFolder(); void OpenNANDFolder();
void OpenSaveFolder(); void OpenSaveFolder();
@@ -62,13 +71,16 @@ void RemoveCacheStorage(u64 program_id);
void ResetMetadata(bool show_message = true); void ResetMetadata(bool show_message = true);
// Shortcuts // // Shortcuts //
void CreateShortcut(const std::string& game_path, const u64 program_id, void CreateShortcut(const std::string& game_path,
const std::string& game_title_, const ShortcutTarget& target, const u64 program_id,
std::string arguments_, const bool needs_title); const std::string& game_title_,
const ShortcutTarget& target,
std::string arguments_,
const bool needs_title);
std::string GetShortcutPath(ShortcutTarget target); std::string GetShortcutPath(ShortcutTarget target);
void CreateHomeMenuShortcut(ShortcutTarget target); void CreateHomeMenuShortcut(ShortcutTarget target);
} // namespace QtCommon::Game }
#endif // QT_GAME_UTIL_H #endif // QT_GAME_UTIL_H
+5 -4
View File
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common/util/meta.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
#include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/cabinet.h"
@@ -8,11 +9,11 @@
#include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/am/frontend/applet_web_browser_types.h" #include "core/hle/service/am/frontend/applet_web_browser_types.h"
#include "qt_common/util/meta.h"
namespace QtCommon::Meta { namespace QtCommon::Meta {
void RegisterMetaTypes() { void RegisterMetaTypes()
{
// Register integral and floating point types // Register integral and floating point types
qRegisterMetaType<u8>("u8"); qRegisterMetaType<u8>("u8");
qRegisterMetaType<u16>("u16"); qRegisterMetaType<u16>("u16");
@@ -71,4 +72,4 @@ void RegisterMetaTypes() {
qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
} }
} // namespace QtCommon::Meta }
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#ifndef QT_META_H #ifndef QT_META_H
@@ -11,5 +11,5 @@ namespace QtCommon::Meta {
// //
void RegisterMetaTypes(); void RegisterMetaTypes();
} // namespace QtCommon::Meta }
#endif // QT_META_H #endif // QT_META_H
+1 -2
View File
@@ -43,8 +43,7 @@ QStringList GetModFolders(const QString& root, const QString& fallbackName) {
QString name = QtCommon::Frontend::GetTextInput( QString name = QtCommon::Frontend::GetTextInput(
tr("Mod Name"), tr("What should this mod be called?"), default_name); tr("Mod Name"), tr("What should this mod be called?"), default_name);
if (name.isEmpty()) if (name.isEmpty()) return {};
return {};
// if std_path is empty, frontend_common could not determine mod type and/or name. // if std_path is empty, frontend_common could not determine mod type and/or name.
// so we have to prompt the user and set up the structure ourselves // so we have to prompt the user and set up the structure ourselves
+3 -3
View File
@@ -7,8 +7,8 @@
namespace QtCommon::Mod { namespace QtCommon::Mod {
QStringList GetModFolders(const QString& root, const QString& fallbackName); QStringList GetModFolders(const QString &root, const QString &fallbackName);
const QString ExtractMod(const QString& path); const QString ExtractMod(const QString &path);
} // namespace QtCommon::Mod }
+12 -8
View File
@@ -1,24 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common/util/path.h"
#include <QDesktopServices> #include <QDesktopServices>
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <fmt/format.h>
#include "common/fs/fs.h" #include "common/fs/fs.h"
#include "common/fs/path_util.h" #include "common/fs/path_util.h"
#include "qt_common/abstract/frontend.h" #include "qt_common/abstract/frontend.h"
#include "qt_common/util/path.h" #include <fmt/format.h>
namespace QtCommon::Path { namespace QtCommon::Path {
bool OpenShaderCache(u64 program_id, QObject* parent) { bool OpenShaderCache(u64 program_id, QObject *parent)
{
const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir); 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 shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
if (!Common::FS::CreateDirs(shader_cache_folder_path)) { if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
QtCommon::Frontend::Warning(tr("Error Opening Shader Cache"), QtCommon::Frontend::ShowMessage(QMessageBox::Warning,
tr("Failed to create or open shader cache for this title, " tr("Error Opening Shader Cache"),
"ensure your app data directory has write permissions.")); tr("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 shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
@@ -26,4 +30,4 @@ bool OpenShaderCache(u64 program_id, QObject* parent) {
return QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path)); return QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
} }
} // namespace QtCommon::Path }

Some files were not shown because too many files have changed in this diff Show More