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)
begin="#"
;;
*.kt*|*.cpp|*.h|*.qml)
*.kt*|*.cpp|*.h)
begin="//"
;;
*)
@@ -193,7 +193,7 @@ if [ "$UPDATE" = "true" ]; then
begin="#"
shell=true
;;
*)
*.kt*|*.cpp|*.h)
begin="//"
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",
"repo": "madler/zlib",
"tag": "v%VERSION%",
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
"hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
"version": "1.2",
"git_version": "1.3.2",
"git_version": "1.3.1.2",
"options": [
"ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF"
@@ -98,9 +98,9 @@
"package": "VVL",
"repo": "KhronosGroup/Vulkan-ValidationLayers",
"tag": "vulkan-sdk-%VERSION%",
"git_version": "1.4.341.0",
"git_version": "1.4.335.0",
"artifact": "android-binaries-%VERSION%.zip",
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753"
"hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
},
"quazip": {
"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.
If clang has errors, try using `g++11`.
If clang has errors, try using `g++-11`.
## FreeBSD
@@ -107,8 +107,6 @@ hw.usb.usbhid.enable="0"
## 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`:
```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:
- `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/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.
`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
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:
```sh
+12 -13
View File
@@ -9,7 +9,7 @@
},
"sirit": {
"repo": "eden-emulator/sirit",
"git_version": "1.0.4",
"git_version": "1.0.3",
"tag": "v%VERSION%",
"artifact": "sirit-source-%VERSION%.tar.zst",
"hash_suffix": "sha512sum",
@@ -28,12 +28,11 @@
"httplib": {
"repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%",
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded",
"git_version": "0.37.0",
"hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
"git_version": "0.30.1",
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch",
"0002-fix-zstd.patch"
"0001-mingw.patch"
],
"options": [
"HTTPLIB_REQUIRE_OPENSSL ON"
@@ -56,8 +55,8 @@
"package": "xbyak",
"repo": "herumi/xbyak",
"tag": "v%VERSION%",
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
"git_version": "7.35.2"
"hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47",
"git_version": "7.33.2"
},
"oaknut": {
"repo": "eden-emulator/oaknut",
@@ -147,9 +146,9 @@
"package": "Catch2",
"repo": "catchorg/Catch2",
"tag": "v%VERSION%",
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
"hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
"version": "3.0.1",
"git_version": "3.13.0",
"git_version": "3.12.0",
"patches": [
"0001-solaris-isnan-fix.patch"
]
@@ -257,15 +256,15 @@
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
"git_version": "1.4.345",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
"git_version": "1.4.345",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"tag": "v%VERSION%"
},
"frozen": {
+1 -1
View File
@@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -242,6 +241,7 @@ if (YUZU_CMD)
endif()
if (ENABLE_QT)
add_definitions(-DYUZU_QT_WIDGETS)
add_subdirectory(qt_common)
add_subdirectory(yuzu)
endif()
@@ -40,21 +40,11 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
}
}
val canDelete = model.isRemovable
binding.deleteCard.isEnabled = canDelete
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)
val deleteAction = {
addonViewModel.setAddonToDelete(model)
}
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.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import java.util.Locale
class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
@@ -47,6 +48,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
binding = DialogLobbyBrowserBinding.inflate(layoutInflater)
setContentView(binding.root)
BackgroundHelper.applyBackground(binding.backgroundLogo, context)
binding.emptyRefreshButton.setOnClickListener {
binding.progressBar.visibility = View.VISIBLE
@@ -105,6 +105,14 @@ object Settings {
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
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) {
Unspecified(0),
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.view.PathSetting
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.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.*
@@ -178,9 +179,17 @@ class SettingsFragment : Fragment() {
}
presenter.onViewCreated()
applyBackgroundPreference()
setInsets()
}
private fun getPlayerIndex(): Int =
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun getPlayerIndex(): Int =
when (args.menuTag) {
Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0
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 {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
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.NativeConfig
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -1071,6 +1072,26 @@ class SettingsFragmentPresenter(
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 {
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
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 {
override fun getInt(needsGlobal: Boolean): Int =
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(BooleanSetting.ENABLE_FOLDER_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.databinding.FragmentAboutBinding
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.NativeLibrary
@@ -53,6 +54,8 @@ class AboutFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarAbout.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
@@ -105,6 +108,11 @@ class AboutFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent)
@@ -127,4 +135,8 @@ class AboutFragment : Fragment() {
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.HomeViewModel
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.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
@@ -59,6 +60,7 @@ class AddonsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.toolbarAddons.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
@@ -122,6 +124,7 @@ class AddonsFragment : Fragment() {
override fun onResume() {
super.onResume()
addonViewModel.onAddonsViewStarted(args.game)
applyBackgroundPreference()
}
override fun onDestroy() {
@@ -202,4 +205,8 @@ class AddonsFragment : Fragment() {
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
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.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() {
@@ -48,6 +49,7 @@ class AppletLauncherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarApplets.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
@@ -91,6 +93,11 @@ class AppletLauncherFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -108,4 +115,8 @@ class AppletLauncherFragment : Fragment() {
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.model.DriverViewModel
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.ViewUtils.updateMargins
import java.io.IOException
@@ -141,6 +142,8 @@ class DriverFetcherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
@@ -154,6 +157,11 @@ class DriverFetcherFragment : Fragment() {
fetchDrivers()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun fetchDrivers() {
binding.loadingIndicator.isVisible = true
@@ -242,6 +250,10 @@ class DriverFetcherFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
data class Artifact(val url: URL, val name: String)
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
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.GpuDriverHelper
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.collect
import java.io.File
@@ -66,6 +67,7 @@ class DriverManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
@@ -138,6 +140,11 @@ class DriverManagerFragment : Fragment() {
driverViewModel.onCloseDriverManager(args.game)
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -256,4 +263,8 @@ class DriverManagerFragment : Fragment() {
.setCancelable(false)
.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.databinding.FragmentFreedrenoSettingsBinding
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.FreedrenoPresets
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -68,6 +69,7 @@ class FreedrenoSettingsFragment : Fragment() {
setupAdapters()
loadCurrentSettings()
setupButtonListeners()
applyBackgroundPreference()
setupWindowInsets()
}
@@ -193,7 +195,17 @@ class FreedrenoSettingsFragment : Fragment() {
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()
}
@@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
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.collect
@@ -57,6 +58,7 @@ class GameFoldersFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarFolders.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
@@ -100,6 +102,11 @@ class GameFoldersFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
override fun onStop() {
super.onStop()
gamesViewModel.onCloseGameFoldersFragment()
@@ -133,4 +140,8 @@ class GameFoldersFragment : Fragment() {
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
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.model.GameVerificationResult
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.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -60,6 +61,7 @@ class GameInfoFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.apply {
toolbarInfo.title = args.game.title
@@ -143,6 +145,11 @@ class GameInfoFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun copyToClipboard(label: String, body: String) {
val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@@ -175,4 +182,8 @@ class GameInfoFragment : Fragment() {
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.GpuDriverHelper
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.updateMargins
import org.yuzu.yuzu_emu.utils.collect
@@ -83,6 +84,7 @@ class GamePropertiesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true)
applyBackgroundPreference()
binding.buttonBack.setOnClickListener {
view.findNavController().popBackStack()
@@ -487,6 +489,7 @@ class GamePropertiesFragment : Fragment() {
override fun onResume() {
super.onResume()
applyBackgroundPreference()
driverViewModel.updateDriverNameForGame(args.game)
getPlayTime()
reloadList()
@@ -711,4 +714,8 @@ class GamePropertiesFragment : Fragment() {
).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.HomeViewModel
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.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
@@ -76,6 +77,7 @@ class HomeSettingsFragment : Fragment() {
findNavController().popBackStack()
}
binding.toolbarHomeSettings.title = getString(R.string.preferences_settings)
applyBackgroundPreference()
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
add(
@@ -315,6 +317,7 @@ class HomeSettingsFragment : Fragment() {
override fun onResume() {
super.onResume()
driverViewModel.updateDriverNameForGame(null)
applyBackgroundPreference()
}
override fun onDestroyView() {
@@ -500,4 +503,8 @@ class HomeSettingsFragment : Fragment() {
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
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.FileUtil
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.collect
import java.io.BufferedOutputStream
@@ -68,6 +69,7 @@ class InstallableFragment : Fragment() {
val mainActivity = requireActivity() as MainActivity
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarInstallables.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
@@ -162,6 +164,11 @@ class InstallableFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -325,4 +332,8 @@ class InstallableFragment : Fragment() {
}
}.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.model.HomeViewModel
import org.yuzu.yuzu_emu.model.UserProfile
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
class ProfileManagerFragment : Fragment() {
@@ -49,6 +50,7 @@ class ProfileManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarProfiles.setNavigationOnClickListener {
findNavController().popBackStack()
@@ -75,6 +77,7 @@ class ProfileManagerFragment : Fragment() {
override fun onResume() {
super.onResume()
loadProfiles()
applyBackgroundPreference()
}
private fun setupRecyclerView() {
@@ -187,4 +190,8 @@ class ProfileManagerFragment : Fragment() {
NativeConfig.saveGlobalConfig()
_binding = null
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -16,17 +16,5 @@ data class Patch(
val type: Int,
val programId: String,
val titleId: String,
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
}
val numericVersion: Long = 0
)
@@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
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.collect
import info.debatty.java.stringsimilarity.Jaccard
@@ -106,6 +107,7 @@ class GamesFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true)
mainActivity = requireActivity() as MainActivity
applyBackgroundPreference()
if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
@@ -252,6 +254,7 @@ class GamesFragment : Fragment() {
override fun onResume() {
super.onResume()
applyBackgroundPreference()
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
(binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true)
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
@@ -497,6 +500,10 @@ class GamesFragment : Fragment() {
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
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, std::to_string(patch.program_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);
++i;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

@@ -6,6 +6,17 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -8,6 +8,29 @@
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
android:id="@+id/header"
android:layout_width="match_parent"
@@ -259,4 +282,4 @@
app:rippleColor="#99FFFFFF"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -8,6 +8,17 @@
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
android:id="@+id/appbar_about"
android:layout_width="match_parent"
@@ -7,6 +7,16 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_info"
android:layout_width="match_parent"
@@ -7,6 +7,20 @@
android:layout_height="match_parent"
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
android:id="@+id/list_all"
android:layout_width="0dp"
@@ -6,6 +6,17 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -246,4 +257,4 @@
style="@style/Widget.Material3.Button.ElevatedButton" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -8,6 +8,17 @@
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
android:id="@+id/appbar_about"
android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_addons"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_applets"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,6 +7,20 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_drivers"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,20 @@
android:layout_height="match_parent"
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
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent"
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
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -6,6 +6,16 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_freedreno"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,6 +7,16 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_info"
android:layout_width="match_parent"
@@ -6,6 +6,20 @@
android:layout_height="match_parent"
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
android:id="@+id/list_all"
android:layout_width="match_parent"
@@ -8,6 +8,29 @@
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
android:id="@+id/header"
android:layout_width="match_parent"
@@ -266,4 +289,4 @@
app:rippleColor="#99FFFFFF"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -6,6 +6,20 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_home_settings"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_installables"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -8,6 +8,17 @@
android:background="?attr/colorSurface"
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
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,6 +6,17 @@
android:layout_height="match_parent"
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
android:id="@+id/appbar_settings"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -383,6 +383,15 @@
<item>2</item>
</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">
<item>@string/app_language_system</item>
<item>@string/app_language_english</item>
@@ -1160,6 +1160,12 @@
<string name="theme_material_you">Material You</string>
<string name="app_settings">App Settings</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 -->
<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_constructor = env->GetMethodID(
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_name_field = env->GetFieldID(patch_class, "name", "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/time_zone.h"
#if defined(__linux__ ) && defined(ARCHITECTURE_arm64)
#include <unistd.h>
#endif
namespace Settings {
// Clang 14 and earlier have errors when explicitly instantiating these classes
@@ -182,11 +178,7 @@ bool IsFastmemEnabled() {
return bool(values.cpuopt_fastmem);
else if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe)
return bool(values.cpuopt_unsafe_host_mmu);
#if defined(__linux__) && defined(ARCHITECTURE_arm64)
// Only 4kb systems support host MMU right now
// TODO: Support this
return getpagesize() == 4096;
#elif !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && !defined(__linux__)
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
return false;
#else
return true;
-1
View File
@@ -777,7 +777,6 @@ struct Values {
Setting<bool> reporting_services{
linkage, false, "reporting_services", Category::Debugging, Specialization::Default, false};
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",
Category::DebuggingGraphics};
Setting<bool> disable_macro_hle{linkage, false, "disable_macro_hle",
+1 -3
View File
@@ -19,12 +19,10 @@
#include <windows.h>
#include "common/string_util.h"
#else
#if defined(__FreeBSD__)
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/cpuset.h>
#include <sys/_cpuset.h>
#include <pthread_np.h>
#elif defined(__DragonFly__) || defined(__OpenBSD__) || defined(__Bitrig__)
#include <pthread_np.h>
#endif
#include <pthread.h>
#include <sched.h>
+1 -1
View File
@@ -1220,7 +1220,7 @@ target_link_libraries(core PRIVATE
RenderDoc::API
ZLIB::ZLIB)
target_link_libraries(core PUBLIC httplib::httplib zstd::zstd)
target_link_libraries(core PRIVATE httplib::httplib)
if (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/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/crypto/aes_util.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);
if (!Common::FS::CreateDir(keys_dir))
LOG_ERROR(Core, "Failed to create the keys directory.");
if (Settings::values.use_dev_keys.GetValue()) {
dev_mode = true;
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 / "prod.keys_autogenerated", false);
LoadFromFile(keys_dir / "prod.keys", false);
LoadFromFile(keys_dir / "title.keys_autogenerated", true);
LoadFromFile(keys_dir / "title.keys", true);
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";
if (category == KeyCategory::Standard) {
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
filename = "prod.keys_autogenerated";
} else if (category == KeyCategory::Console) {
filename = "console.keys_autogenerated";
}
@@ -944,8 +936,6 @@ bool KeyManager::KeyFileExists(bool title) {
const auto keys_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::KeysDir);
if (title)
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");
}
+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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -314,7 +314,6 @@ private:
std::array<u8, 576> eticket_extended_kek{};
RSAKeyPair<2048> eticket_rsa_keypair{};
bool dev_mode;
void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys);
template <size_t Size>
+11 -15
View File
@@ -76,16 +76,16 @@ public:
template <typename Func>
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)];
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);
return;
}
InnerGatherDeviceAddresses(buffer, address);
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 subbits = PAddr(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
PAddr subbits = static_cast<PAddr>(address & page_mask);
auto paddr = compressed_physical_ptr[(address >> page_bits)];
if (paddr == 0) {
return 0;
}
return (PAddr(paddr - 1) << page_bits) + subbits;
return (static_cast<PAddr>(paddr - 1) << page_bits) + subbits;
}
template <typename T>
@@ -172,14 +172,9 @@ private:
const uintptr_t physical_base;
DeviceInterface* device_inter;
struct TrackedEntry {
VAddr cpu_backing_address;
u32 continuity_tracker;
u32 compressed_physical_ptr;
};
Common::VirtualBuffer<u32> compressed_physical_ptr;
Common::VirtualBuffer<u32> compressed_device_addr;
Common::VirtualBuffer<TrackedEntry> tracked_entries;
Common::VirtualBuffer<u32> continuity_tracker;
// Process memory interfaces
@@ -194,16 +189,17 @@ private:
static constexpr size_t asid_start_bit = guest_max_as_bits;
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 Asid asid{static_cast<size_t>(content >> asid_start_bit)};
return std::make_pair(asid, address);
}
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{};
u32 cache_cursor = 0;
using CounterType = u8;
+48 -31
View File
@@ -166,21 +166,29 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
, device_inter{nullptr}
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
{
: physical_base{reinterpret_cast<const uintptr_t>(device_memory_.buffer.BackingBasePointer())},
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)),
continuity_tracker(device_as_size >> Memory::YUZU_PAGEBITS),
cpu_backing_address(device_as_size >> Memory::YUZU_PAGEBITS) {
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
tracked_entries[i].compressed_physical_ptr = 0;
tracked_entries[i].continuity_tracker = 1;
tracked_entries[i].cpu_backing_address = 0;
compressed_physical_ptr[i] = 0;
continuity_tracker[i] = 1;
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++) {
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;
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
if (ptr == nullptr) [[unlikely]] {
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
compressed_physical_ptr[start_page_d + i] = 0;
continue;
}
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);
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
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);
std::scoped_lock lk(mapping_guard);
for (size_t i = 0; i < num_pages; i++) {
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
tracked_entries[start_page_d + i].cpu_backing_address = 0;
auto phys_addr = compressed_physical_ptr[start_page_d + i];
compressed_physical_ptr[start_page_d + i] = 0;
cpu_backing_address[start_page_d + i] = 0;
if (phys_addr != 0) [[likely]] {
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
@@ -292,14 +300,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
page_count = 1;
}
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>
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
size_t page_index = src_addr >> page_bits;
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 nullptr;
@@ -309,7 +317,7 @@ template <typename Traits>
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 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 nullptr;
@@ -334,10 +342,12 @@ template <typename T>
T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = tracked_entries[index].compressed_physical_ptr;
if (phys_addr == 0) [[unlikely]]
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
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>
@@ -345,10 +355,12 @@ template <typename T>
const T* DeviceMemoryManager<Traits>::GetPointer(DAddr address) const {
const size_t index = address >> Memory::YUZU_PAGEBITS;
const size_t offset = address & Memory::YUZU_PAGEMASK;
auto phys_addr = tracked_entries[index].compressed_physical_ptr;
if (phys_addr == 0)
auto phys_addr = compressed_physical_ptr[index];
if (phys_addr == 0) [[unlikely]] {
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>
@@ -374,14 +386,18 @@ T DeviceMemoryManager<Traits>::Read(DAddr address) const {
}
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 page_index = addr >> Memory::YUZU_PAGEBITS;
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
while (remaining_size) {
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
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 auto current_vaddr =
static_cast<u64>((page_index << Memory::YUZU_PAGEBITS) + page_offset);
SCOPE_EXIT{
page_index += next_pages;
page_offset = 0;
@@ -389,12 +405,13 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
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) {
on_unmapped(copy_amount, current_vaddr);
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);
}
}
@@ -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 auto phys_addr = tracked_entries[page_index].compressed_physical_ptr;
const auto phys_addr = compressed_physical_ptr[page_index];
if (phys_addr != 0) {
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};
@@ -471,7 +488,7 @@ void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_poin
}
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) {
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};
+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() ||
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
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);
for (const auto& [slot, entry] : all_updates) {
(void)entry;
if (slot == ContentProviderUnionSlot::External ||
slot == ContentProviderUnionSlot::FrontendManual) {
continue;
@@ -820,7 +786,7 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
source_suffix = " (NAND)";
break;
case ContentProviderUnionSlot::SDMC:
source_type = PatchSource::SDMC;
source_type = PatchSource::NAND;
source_suffix = " (SDMC)";
break;
default:
@@ -829,16 +795,19 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::string version_str;
u32 numeric_ver = 0;
const auto* slot_provider = content_union->GetSlotProvider(slot);
version_str = GetUpdateVersionStringFromSlot(slot_provider, update_tid);
PatchManager update{update_tid, fs_controller, content_provider};
const auto metadata = update.GetControlMetadata();
const auto& nacp = metadata.first;
if (slot_provider != nullptr) {
const auto slot_ver = slot_provider->GetEntryVersion(update_tid);
if (slot_ver.has_value()) {
numeric_ver = *slot_ver;
if (version_str.empty() && numeric_ver != 0) {
version_str = FormatTitleVersion(numeric_ver);
}
if (nacp != nullptr) {
version_str = nacp->GetVersionString();
}
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
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
const auto dlc_entries =
content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match;
bool has_external_dlc = false;
bool has_nand_dlc = false;
bool has_sdmc_dlc = false;
bool has_other_dlc = false;
const auto dlc_entries_with_origin =
content_union->ListEntriesFilterOrigin(std::nullopt, TitleType::AOC, ContentRecordType::Data);
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this](const ContentProviderEntry& entry) {
const auto base_tid = GetBaseTitleID(entry.title_id);
const bool matches_base = base_tid == title_id;
dlc_match.reserve(dlc_entries_with_origin.size());
for (const auto& [slot, entry] : dlc_entries_with_origin) {
const auto base_tid = GetBaseTitleID(entry.title_id);
const bool matches_base = base_tid == title_id;
if (!matches_base) {
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
entry.title_id, base_tid, title_id);
continue;
}
if (!matches_base) {
LOG_DEBUG(Loader, "DLC {:016X} base {:016X} doesn't match title {:016X}",
entry.title_id, base_tid, title_id);
return false;
}
const auto* slot_provider = content_union->GetSlotProvider(slot);
if (slot_provider == nullptr) {
continue;
}
auto nca = content_provider.GetEntry(entry);
if (!nca) {
LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id);
return false;
}
auto nca = slot_provider->GetEntry(entry);
if (!nca) {
LOG_DEBUG(Loader, "Failed to get NCA for DLC {:016X}", entry.title_id);
continue;
}
const auto status = nca->GetStatus();
if (status != Loader::ResultStatus::Success) {
LOG_DEBUG(Loader, "DLC {:016X} NCA has status {}",
entry.title_id, static_cast<int>(status));
return false;
}
const auto status = nca->GetStatus();
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);
}
return true;
});
if (!dlc_match.empty()) {
// 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 =
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,
.name = "DLC",
.version = std::move(list),
.type = PatchType::DLC,
.program_id = title_id,
.title_id = dlc_match.back().title_id,
.source = dlc_source});
.source = PatchSource::Unknown});
}
return out;
-1
View File
@@ -34,7 +34,6 @@ enum class PatchType { Update, DLC, Mod };
enum class PatchSource {
Unknown,
NAND,
SDMC,
External,
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-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) {
SCOPE_EXIT {
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{},
out_names.size() > 1 ? out_names[1] : Service::PSC::Time::LocationName{});
index, *out_count, out_names[0], out_names[1]);
};
std::scoped_lock l{m_mutex};
@@ -212,8 +208,7 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0},
out_times.size() > 1 ? out_times[1] : s64{0});
calendar_time, *out_count, out_times[0], out_times[1]);
};
R_RETURN(m_wrapped_service->ToPosixTime(out_count, out_times, calendar_time, rule));
@@ -225,8 +220,7 @@ Result TimeZoneService::ToPosixTimeWithMyRule(
SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={}",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0},
out_times.size() > 1 ? out_times[1] : s64{0});
calendar_time, *out_count, out_times[0], out_times[1]);
};
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-License-Identifier: GPL-2.0-or-later
@@ -151,8 +148,7 @@ Result TimeZoneService::ToPosixTime(Out<u32> out_count,
SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0},
out_times.size() > 1 ? out_times[1] : s64{0});
calendar_time, *out_count, out_times[0], out_times[1]);
};
R_RETURN(
@@ -165,8 +161,7 @@ Result TimeZoneService::ToPosixTimeWithMyRule(Out<u32> out_count,
SCOPE_EXIT {
LOG_DEBUG(Service_Time,
"called. calendar_time={} out_count={} out_times[0]={} out_times[1]={} ",
calendar_time, *out_count, out_times.size() > 0 ? out_times[0] : s64{0},
out_times.size() > 1 ? out_times[1] : s64{0});
calendar_time, *out_count, out_times[0], out_times[1]);
};
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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -29,13 +29,10 @@ namespace Service {
return function_string;
}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, u32 max_sessions_, InvokerFn* handler_invoker_)
: SessionRequestHandler(system_.Kernel(), service_name_)
, system{system_}
, service_name{service_name_}
, handler_invoker{handler_invoker_}
, max_sessions{max_sessions_}
{}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
u32 max_sessions_, InvokerFn* handler_invoker_)
: SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
service_name{service_name_}, handler_invoker{handler_invoker_}, max_sessions{max_sessions_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() {
// 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
handlers_tipc.reserve(handlers_tipc.size() + n);
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,
+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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@@ -8,7 +8,8 @@
#include <cstddef>
#include <mutex>
#include <ankerl/unordered_dense.h>
#include <string>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/hle/service/hle_ipc.h"
@@ -77,6 +78,13 @@ protected:
[[nodiscard]] virtual std::unique_lock<std::mutex> LockService() noexcept {
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:
template <typename T>
friend class ServiceFramework;
@@ -98,19 +106,16 @@ private:
void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n);
void ReportUnimplementedFunction(HLERequestContext& ctx, const FunctionInfoBase* info);
protected:
ankerl::unordered_dense::map<u32, FunctionInfoBase> handlers;
ankerl::unordered_dense::map<u32, FunctionInfoBase> handlers_tipc;
boost::container::flat_map<u32, FunctionInfoBase> handlers;
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
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.
InvokerFn* handler_invoker;
/// Maximum number of concurrent sessions that this service can handle.
u32 max_sessions;
/// Flag to store if a port was already create/installed to detect multiple install attempts,
/// which is not supported.
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
* 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) {}
/// Registers handlers in the service.
@@ -213,7 +219,7 @@ private:
static void Invoker(ServiceFrameworkBase* object, HandlerFnP<ServiceFrameworkBase> member,
HLERequestContext& ctx) {
// 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& 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:
// 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};
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.GetImmediateU1()) {
// 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
/* 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 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 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-License-Identifier: GPL-3.0-or-later
set(CMAKE_AUTOMOC ON)
add_library(qt_common STATIC
qt_common.h
qt_common.cpp
@@ -28,7 +26,7 @@ add_library(qt_common STATIC
util/mod.h util/mod.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_compat.h
@@ -95,5 +93,3 @@ if (UNIX AND NOT APPLE)
target_link_libraries(qt_common PRIVATE Qt6::GuiPrivate)
endif()
endif()
create_target_directory_groups(qt_common)
+56 -8
View File
@@ -1,27 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QLineEdit>
#include "frontend.h"
#include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS
#include <QFileDialog>
#endif
#include <QAbstractButton>
#include <QInputDialog>
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,
QString* selectedFilter) {
return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter);
QString* selectedFilter, Options options) {
#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,
QString* selectedFilter) {
return QFileDialog::getOpenFileNames(rootObject, title, dir, filter, selectedFilter);
QString* selectedFilter, Options options) {
#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,
QString* selectedFilter) {
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter);
QString* selectedFilter, Options options) {
#ifdef YUZU_QT_WIDGETS
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif
}
const QString GetExistingDirectory(const QString& caption, const QString& dir) {
return QFileDialog::getExistingDirectory(rootObject, caption, dir);
const QString GetExistingDirectory(const QString& caption, const QString& dir, Options options) {
#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
+95 -57
View File
@@ -7,7 +7,11 @@
#include <QGuiApplication>
#include "qt_common/qt_common.h"
#ifdef YUZU_QT_WIDGETS
#include <QFileDialog>
#include <QWidget>
#include <QMessageBox>
#endif
/**
* manages common functionality e.g. message boxes and such for Qt/QML
@@ -16,39 +20,60 @@ namespace QtCommon::Frontend {
Q_NAMESPACE
#ifdef YUZU_QT_WIDGETS
using Options = QFileDialog::Options;
using Option = QFileDialog::Option;
using StandardButton = QMessageBox::StandardButton;
using StandardButtons = QMessageBox::StandardButtons;
using Icon = QMessageBox::Icon;
#else
enum Option {
ShowDirsOnly = 0x00000001,
DontResolveSymlinks = 0x00000002,
DontConfirmOverwrite = 0x00000004,
DontUseNativeDialog = 0x00000008,
ReadOnly = 0x00000010,
HideNameFilterDetails = 0x00000020,
DontUseCustomDirectoryIcons = 0x00000040
};
Q_ENUM_NS(Option)
Q_DECLARE_FLAGS(Options, Option)
Q_FLAG_NS(Options)
enum StandardButton {
// keep this in sync with QDialogButtonBox::StandardButton and
// QPlatformDialogHelper::StandardButton
NoButton = 0x00000000,
Ok = 0x00000400,
Save = 0x00000800,
SaveAll = 0x00001000,
Open = 0x00002000,
Yes = 0x00004000,
YesToAll = 0x00008000,
No = 0x00010000,
NoToAll = 0x00020000,
Abort = 0x00040000,
Retry = 0x00080000,
Ignore = 0x00100000,
Close = 0x00200000,
Cancel = 0x00400000,
Discard = 0x00800000,
Help = 0x01000000,
Apply = 0x02000000,
Reset = 0x04000000,
RestoreDefaults = 0x08000000,
// keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
NoButton = 0x00000000,
Ok = 0x00000400,
Save = 0x00000800,
SaveAll = 0x00001000,
Open = 0x00002000,
Yes = 0x00004000,
YesToAll = 0x00008000,
No = 0x00010000,
NoToAll = 0x00020000,
Abort = 0x00040000,
Retry = 0x00080000,
Ignore = 0x00100000,
Close = 0x00200000,
Cancel = 0x00400000,
Discard = 0x00800000,
Help = 0x01000000,
Apply = 0x02000000,
Reset = 0x04000000,
RestoreDefaults = 0x08000000,
FirstButton = Ok, // internal
LastButton = RestoreDefaults, // internal
FirstButton = Ok, // internal
LastButton = RestoreDefaults, // internal
YesAll = YesToAll, // obsolete
NoAll = NoToAll, // obsolete
YesAll = YesToAll, // obsolete
NoAll = NoToAll, // obsolete
Default = 0x00000100, // obsolete
Escape = 0x00000200, // obsolete
FlagMask = 0x00000300, // obsolete
ButtonMask = ~FlagMask // obsolete
Default = 0x00000100, // obsolete
Escape = 0x00000200, // obsolete
FlagMask = 0x00000300, // obsolete
ButtonMask = ~FlagMask // obsolete
};
Q_ENUM_NS(StandardButton)
@@ -58,7 +83,7 @@ typedef StandardButton Button;
Q_DECLARE_FLAGS(StandardButtons, StandardButton)
Q_FLAG_NS(StandardButtons)
enum class Icon {
enum Icon {
// keep this in sync with QMessageDialogOptions::StandardIcon
NoIcon = 0,
Information = 1,
@@ -68,26 +93,29 @@ enum class Icon {
};
Q_ENUM_NS(Icon)
StandardButton ShowMessage(Icon icon, const QString& title, const QString& text,
StandardButtons buttons = StandardButton::NoButton,
QObject* parent = nullptr);
#endif
#define UTIL_OVERRIDES(level) \
inline StandardButton level(QObject* parent, const QString& title, const QString& text, \
StandardButtons buttons) { \
return ShowMessage(Icon::level, title, text, buttons, parent); \
} \
inline StandardButton level(QObject* parent, const QString& title, const QString& text, \
int buttons = StandardButton::Ok) { \
return ShowMessage(Icon::level, title, text, StandardButtons(buttons), parent); \
} \
inline StandardButton level(const QString title, const QString& text, \
StandardButtons buttons) { \
return ShowMessage(Icon::level, title, text, buttons, rootObject); \
} \
inline StandardButton level(const QString& title, const QString& text, \
int buttons = StandardButton::Ok) { \
return ShowMessage(Icon::level, title, text, StandardButtons(buttons), rootObject); \
// TODO(crueter) widgets-less impl, choices et al.
StandardButton ShowMessage(Icon icon,
const QString &title,
const QString &text,
StandardButtons buttons = StandardButton::NoButton,
QObject *parent = nullptr);
#define UTIL_OVERRIDES(level) \
inline StandardButton level(QObject *parent, \
const QString &title, \
const QString &text, \
StandardButtons buttons = StandardButton::Ok) \
{ \
return ShowMessage(Icon::level, title, text, buttons, parent); \
} \
inline StandardButton level(const QString title, \
const QString &text, \
StandardButtons buttons \
= StandardButton::Ok) \
{ \
return ShowMessage(Icon::level, title, text, buttons, rootObject); \
}
UTIL_OVERRIDES(Information)
@@ -95,17 +123,27 @@ UTIL_OVERRIDES(Warning)
UTIL_OVERRIDES(Critical)
UTIL_OVERRIDES(Question)
const QString GetOpenFileName(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter = nullptr);
const QString GetOpenFileName(const QString &title,
const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QStringList GetOpenFileNames(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter = nullptr);
const QStringList GetOpenFileNames(const QString &title,
const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QString GetSaveFileName(const QString& title, const QString& dir, const QString& filter,
QString* selectedFilter = nullptr);
const QString GetSaveFileName(const QString &title,
const QString &dir,
const QString &filter,
QString *selectedFilter = nullptr,
Options options = Options());
const QString GetExistingDirectory(const QString& caption = QString(),
const QString& dir = QString());
const QString GetExistingDirectory(const QString &caption = QString(),
const QString &dir = QString(),
Options options = Option::ShowDirsOnly);
int Choice(const QString& title = QString(), const QString& caption = QString(),
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() {
BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout));
BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList));
ReadCategory(Settings::Category::UiLayout);
@@ -578,10 +578,10 @@ void QtConfig::SaveMultiplayerValues() {
}
std::vector<Settings::BasicSetting*>& QtConfig::FindRelevantList(Settings::Category category) {
auto& list = Settings::values.linkage.by_category[category];
if (!list.empty())
return list;
auto& map = Settings::values.linkage.by_category;
if (map.contains(category)) {
return Settings::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 <map>
#include <memory>
#include <utility>
#include <QCoreApplication>
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.h"
#include "qt_common/config/uisettings.h"
#include <map>
#include <memory>
#include <utility>
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>();
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)}})
// 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, wifi_web_auth_applet_mode, tr("Wifi web auth"), QString());
INSERT(Settings, my_page_applet_mode, tr("My page"), QString());
INSERT(Settings, enable_overlay, tr("Enable Overlay Applet"),
tr("Enables Horizon\'s built-in overlay applet. Press and hold the home button for 1 "
"second to show it."));
INSERT(Settings,
enable_overlay,
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
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());
// Core
INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"),
tr("This option increases CPU emulation thread use from 1 to the maximum of 4.\n"
"This is mainly a debug option and shouldn't be disabled."));
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_multi_core,
tr("Multicore CPU Emulation"),
tr("This option increases CPU emulation thread use from 1 to the maximum of 4.\n"
"This is mainly a debug option and shouldn't be disabled."));
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, 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 "
"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 "
@@ -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 "
"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 "
"without affecting game speed (animations, physics, etc.).\n"
"Can help reduce stuttering at lower framerates."));
// Cpu
INSERT(Settings, cpu_accuracy, tr("Accuracy:"),
INSERT(Settings,
cpu_accuracy,
tr("Accuracy:"),
tr("Change the accuracy of the emulated CPU (for debugging only)."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, fast_cpu_time, tr("CPU Overclock"),
tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see "
"reduced performance, "
"and certain games may behave improperly.\nUse Boost (1700MHz) to run at the "
"Switch's highest native "
INSERT(Settings,
fast_cpu_time,
tr("CPU Overclock"),
tr("Overclocks the emulated CPU to remove some FPS limiters. Weaker CPUs may see reduced performance, "
"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."));
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 "
"cause deadlocks. A range of 77-21000 is recommended."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, vtable_bouncing, tr("Virtual Table Bouncing"),
tr("Bounces (by emulating a 0-valued return) any functions that triggers a prefetch "
"abort"));
INSERT(Settings, vtable_bouncing,
tr("Virtual Table Bouncing"),
tr("Bounces (by emulating a 0-valued return) any functions that triggers a prefetch abort"));
// Cpu Debug
// 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(
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(
Settings, cpuopt_unsafe_unfuse_fma,
Settings,
cpuopt_unsafe_unfuse_fma,
tr("Unfuse FMA (improve performance on CPUs without FMA)"),
tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on "
"CPUs without native FMA support."));
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 "
"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("This option improves the speed of 32 bits ASIMD floating-point functions by running "
"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 "
"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 "
"operation.\nDisabling it may allow arbitrary code execution."));
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 "
"safety of exclusive access instructions.\nPlease note this may result in deadlocks and "
"other race conditions."));
// Renderer
INSERT(Settings, renderer_backend, tr("API:"),
tr("Changes the output graphics API.\nVulkan is recommended."));
INSERT(Settings, vulkan_device, tr("Device:"),
INSERT(
Settings,
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)."));
INSERT(Settings, resolution_setup, tr("Resolution:"),
INSERT(Settings,
resolution_setup,
tr("Resolution:"),
tr("Forces to render at a different resolution.\n"
"Higher resolutions require more VRAM and bandwidth.\n"
"Options lower than 1X can cause artifacts."));
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."));
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 "
"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 "
"compatibility with the on-screen keyboard that some games request for "
"input.\nExclusive "
"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 "
"16:9, so modifications are required to get other ratios.\nAlso controls the "
"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 "
"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"
"Will increase time required for shader compilation.\nMay slightly improve "
"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 "
"decoding, or perform no decoding at all (black screen on videos).\n"
"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"
"CPU: Use the CPU for decoding.\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"
"ASTC decoding\nstuttering but may present artifacts."));
INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"),
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,
astc_recompression,
tr("ASTC Recompression Method:"),
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)"),
tr("Controls how the emulator manages frame pacing to reduce stuttering and make the "
"frame rate smoother and more consistent."));
INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
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."));
INSERT(Settings, skip_cpu_inner_invalidation, tr("Skip CPU Inner Invalidation"),
tr("Controls how the emulator manages frame pacing to reduce stuttering and make the frame rate smoother and more consistent."));
INSERT(Settings,
vram_usage_mode,
tr("VRAM Usage Mode:"),
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."));
INSERT(Settings,
skip_cpu_inner_invalidation,
tr("Skip CPU Inner Invalidation"),
tr("Skips certain cache invalidations during memory updates, reducing CPU usage and "
"improving latency. This may cause soft-crashes."));
INSERT(Settings, vsync_mode, tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"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,
vsync_mode,
tr("VSync Mode:"),
tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
"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_green, 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, sync_memory_operations, tr("Sync Memory Operations"),
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."));
INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
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."));
INSERT(Settings,
async_presentation,
tr("Enable asynchronous presentation (Vulkan only)"),
tr("Slightly improves performance by moving presentation to a separate CPU thread."));
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 "
"lowering its clock speed."));
INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"),
tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on "
"most GPUs."));
INSERT(Settings, gpu_accuracy, tr("GPU Mode:"),
tr("Controls the GPU emulation mode.\nMost games render fine with Fast or Balanced "
"modes, but Accurate is still "
INSERT(Settings,
max_anisotropy,
tr("Anisotropic Filtering:"),
tr("Controls the quality of texture rendering at oblique angles.\nSafe to set at 16x on most GPUs."));
INSERT(Settings,
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."));
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
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"),
INSERT(Settings,
dma_accuracy,
tr("DMA Accuracy:"),
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."));
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 "
"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"
"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"
"While the GPU is faster for medium and large textures, the CPU may be more "
"efficient for very small ones.\n"
"While the GPU is faster for medium and large textures, the CPU may be more efficient for very small ones.\n"
"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"
"Higher values can reduce stutter during texture loading but may impact frame "
"consistency."));
INSERT(Settings, gpu_unswizzle_chunk_size, tr("GPU Unswizzle Chunk Size"),
"Higher values can reduce stutter during texture loading but may impact frame consistency."));
INSERT(Settings,
gpu_unswizzle_chunk_size,
tr("GPU Unswizzle Chunk Size"),
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 "
"timeouts on weaker hardware."));
"Increasing this can improve throughput on high-end GPUs but may cause TDR or driver 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 "
"time significantly in cases where the Vulkan driver does not store pipeline cache "
"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(
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 "
"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 "
"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."));
INSERT(Settings, enable_buffer_history, tr("Enable buffer history"),
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,
enable_buffer_history,
tr("Enable buffer history"),
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"),
tr("May fix rescale issues in some games by relying on behavior from the previous "
"implementation.\n"
"Legacy behavior workaround that fixes line artifacts on AMD and Intel GPUs, and "
"grey texture flicker on Nvidia GPUs in Luigis Mansion 3."));
INSERT(Settings,
rescale_hack,
tr("Enable Legacy Rescale Pass"),
tr("May fix rescale issues in some games by relying on behavior from the previous implementation.\n"
"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)
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 "
"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."));
INSERT(Settings, provoking_vertex, tr("Provoking Vertex"),
INSERT(Settings,
provoking_vertex,
tr("Provoking Vertex"),
tr("Improves lighting and vertex handling in some games.\n"
"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"
"Some Vulkan 1.1+ and all 1.2+ devices support this extension."));
INSERT(
Settings, sample_shading, tr("Sample Shading"),
tr("Allows the fragment shader to execute per sample in a multi-sampled fragment "
"instead of once per fragment. Improves graphics quality at the cost of performance.\n"
"Higher values improve quality but degrade performance."));
INSERT(Settings,
sample_shading,
tr("Sample Shading"),
tr("Allows the fragment shader to execute per sample in a multi-sampled fragment "
"instead of once per fragment. Improves graphics quality at the cost of performance.\n"
"Higher values improve quality but degrade performance."));
// Renderer (Debug)
// 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."));
INSERT(Settings, rng_seed_enabled, QString(), QString());
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"
"Can be used to manipulate time in games."));
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"));
INSERT(Settings, language_index, tr("Language:"),
INSERT(Settings,
language_index,
tr("Language:"),
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, time_zone_index, tr("Time Zone:"), tr("The time zone of the console."));
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 "
"their resolution, details and supported controllers and depending on this setting.\n"
"Setting to Handheld can help improve performance for low end systems."));
@@ -346,19 +444,31 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
// Ui
// 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."));
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."));
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 "
"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."));
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"
"When a program attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings, check_for_updates, tr("Check for updates"),
"When a program attempts to open the controller applet, it is immediately closed."));
INSERT(UISettings,
check_for_updates,
tr("Check for updates"),
tr("Whether or not to check for updates upon startup."));
// Linux
@@ -379,9 +489,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
return translations;
}
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
std::unique_ptr<ComboboxTranslationMap> translations =
std::make_unique<ComboboxTranslationMap>();
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent)
{
std::unique_ptr<ComboboxTranslationMap> translations = std::make_unique<ComboboxTranslationMap>();
const auto& tr = [&](const char* text, const char* context = "") {
return parent->tr(text, context);
};
@@ -427,15 +537,15 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
PAIR(VramUsageMode, Conservative, tr("Conservative")),
PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::RendererBackend>::Index(),
{PAIR(RendererBackend, Vulkan, tr("Vulkan")),
translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), {
PAIR(RendererBackend, Vulkan, tr("Vulkan")),
#ifdef HAS_OPENGL
PAIR(RendererBackend, OpenGL_GLSL, tr("OpenGL GLSL")),
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_GLSL, tr("OpenGL GLSL")),
PAIR(RendererBackend, OpenGL_GLASM, tr("OpenGL GLASM (Assembly Shaders, NVIDIA Only)")),
PAIR(RendererBackend, OpenGL_SPIRV, tr("OpenGL SPIR-V (Experimental, AMD/Mesa Only)")),
#endif
PAIR(RendererBackend, Null, tr("Null"))}});
PAIR(RendererBackend, Null, tr("Null"))
}});
translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
{
PAIR(GpuAccuracy, Low, tr("Fast")),
@@ -569,58 +679,58 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
translations->insert(
{Settings::EnumMetadata<Settings::TimeZone>::Index(),
{
{static_cast<u32>(Settings::TimeZone::Auto),
tr("Auto (%1)", "Auto select time zone")
.arg(QString::fromStdString(
Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
{static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, tr("CET")),
PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
PAIR(TimeZone, Cuba, tr("Cuba")),
PAIR(TimeZone, Eet, tr("EET")),
PAIR(TimeZone, Egypt, tr("Egypt")),
PAIR(TimeZone, Eire, tr("Eire")),
PAIR(TimeZone, Est, tr("EST")),
PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
PAIR(TimeZone, Gb, tr("GB")),
PAIR(TimeZone, GbEire, tr("GB-Eire")),
PAIR(TimeZone, Gmt, tr("GMT")),
PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
PAIR(TimeZone, GmtZero, tr("GMT0")),
PAIR(TimeZone, Greenwich, tr("Greenwich")),
PAIR(TimeZone, Hongkong, tr("Hongkong")),
PAIR(TimeZone, Hst, tr("HST")),
PAIR(TimeZone, Iceland, tr("Iceland")),
PAIR(TimeZone, Iran, tr("Iran")),
PAIR(TimeZone, Israel, tr("Israel")),
PAIR(TimeZone, Jamaica, tr("Jamaica")),
PAIR(TimeZone, Japan, tr("Japan")),
PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
PAIR(TimeZone, Libya, tr("Libya")),
PAIR(TimeZone, Met, tr("MET")),
PAIR(TimeZone, Mst, tr("MST")),
PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
PAIR(TimeZone, Navajo, tr("Navajo")),
PAIR(TimeZone, Nz, tr("NZ")),
PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
PAIR(TimeZone, Poland, tr("Poland")),
PAIR(TimeZone, Portugal, tr("Portugal")),
PAIR(TimeZone, Prc, tr("PRC")),
PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
PAIR(TimeZone, Roc, tr("ROC")),
PAIR(TimeZone, Rok, tr("ROK")),
PAIR(TimeZone, Singapore, tr("Singapore")),
PAIR(TimeZone, Turkey, tr("Turkey")),
PAIR(TimeZone, Uct, tr("UCT")),
PAIR(TimeZone, Universal, tr("Universal")),
PAIR(TimeZone, Utc, tr("UTC")),
PAIR(TimeZone, WSu, tr("W-SU")),
PAIR(TimeZone, Wet, tr("WET")),
PAIR(TimeZone, Zulu, tr("Zulu")),
}});
{static_cast<u32>(Settings::TimeZone::Auto),
tr("Auto (%1)", "Auto select time zone")
.arg(QString::fromStdString(
Settings::GetTimeZoneString(Settings::TimeZone::Auto)))},
{static_cast<u32>(Settings::TimeZone::Default),
tr("Default (%1)", "Default time zone")
.arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
PAIR(TimeZone, Cet, tr("CET")),
PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
PAIR(TimeZone, Cuba, tr("Cuba")),
PAIR(TimeZone, Eet, tr("EET")),
PAIR(TimeZone, Egypt, tr("Egypt")),
PAIR(TimeZone, Eire, tr("Eire")),
PAIR(TimeZone, Est, tr("EST")),
PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
PAIR(TimeZone, Gb, tr("GB")),
PAIR(TimeZone, GbEire, tr("GB-Eire")),
PAIR(TimeZone, Gmt, tr("GMT")),
PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
PAIR(TimeZone, GmtZero, tr("GMT0")),
PAIR(TimeZone, Greenwich, tr("Greenwich")),
PAIR(TimeZone, Hongkong, tr("Hongkong")),
PAIR(TimeZone, Hst, tr("HST")),
PAIR(TimeZone, Iceland, tr("Iceland")),
PAIR(TimeZone, Iran, tr("Iran")),
PAIR(TimeZone, Israel, tr("Israel")),
PAIR(TimeZone, Jamaica, tr("Jamaica")),
PAIR(TimeZone, Japan, tr("Japan")),
PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
PAIR(TimeZone, Libya, tr("Libya")),
PAIR(TimeZone, Met, tr("MET")),
PAIR(TimeZone, Mst, tr("MST")),
PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
PAIR(TimeZone, Navajo, tr("Navajo")),
PAIR(TimeZone, Nz, tr("NZ")),
PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
PAIR(TimeZone, Poland, tr("Poland")),
PAIR(TimeZone, Portugal, tr("Portugal")),
PAIR(TimeZone, Prc, tr("PRC")),
PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
PAIR(TimeZone, Roc, tr("ROC")),
PAIR(TimeZone, Rok, tr("ROK")),
PAIR(TimeZone, Singapore, tr("Singapore")),
PAIR(TimeZone, Turkey, tr("Turkey")),
PAIR(TimeZone, Uct, tr("UCT")),
PAIR(TimeZone, Universal, tr("Universal")),
PAIR(TimeZone, Utc, tr("UTC")),
PAIR(TimeZone, WSu, tr("W-SU")),
PAIR(TimeZone, Wet, tr("WET")),
PAIR(TimeZone, Zulu, tr("Zulu")),
}});
translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
{
PAIR(AudioMode, Mono, tr("Mono")),
@@ -693,9 +803,9 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
}});
translations->insert({Settings::EnumMetadata<Settings::GameListMode>::Index(),
{
PAIR(GameListMode, TreeView, tr("Tree View")),
PAIR(GameListMode, GridView, tr("Grid View")),
{
PAIR(GameListMode, TreeView, tr("Tree View")),
PAIR(GameListMode, GridView, tr("Grid View")),
}});
#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-FileCopyrightText: Copyright 2024 Torzu Emulator Project
@@ -13,8 +13,8 @@
#include <memory>
#include <utility>
#include <vector>
#include <QObject>
#include <QString>
#include <QObject>
#include "common/common_types.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 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);
@@ -39,15 +39,15 @@ static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map
{Settings::ScalingFilter::Bilinear,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
{Settings::ScalingFilter::Spline1, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
{Settings::ScalingFilter::Spline1,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
{Settings::ScalingFilter::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,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
{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 = {
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL_GLSL,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLSL"))},
{Settings::RendererBackend::OpenGL_SPIRV,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL SPIRV"))},
{Settings::RendererBackend::OpenGL_GLASM,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLASM"))},
{Settings::RendererBackend::OpenGL_GLSL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL GLSL"))},
{Settings::RendererBackend::OpenGL_SPIRV, 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"))},
};
+5 -7
View File
@@ -145,15 +145,14 @@ struct Values {
// Linux/MinGW may support (requires libdl support)
SwitchableSetting<bool> enable_gamemode{linkage,
#ifndef _MSC_VER
true,
true,
#else
false,
false,
#endif
"enable_gamemode", Category::UiGeneral};
"enable_gamemode", Category::UiGeneral};
#ifdef __unix__
SwitchableSetting<bool> gui_force_x11{linkage, false, "gui_force_x11", Category::UiGeneral};
Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning",
Category::UiGeneral};
Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning", Category::UiGeneral};
#endif
// Discord RPC
@@ -211,8 +210,7 @@ struct Values {
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_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView,
"game_list_mode", Category::UiGameList};
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList};
Setting<bool> show_game_name{linkage, true, "show_game_name", Category::UiGameList};
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-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_IMAGE[] =
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
"eden.png";
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";
void DiscordImpl::UpdateGameStatus(bool use_default) {
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
@@ -122,9 +120,7 @@ void DiscordImpl::Update() {
return;
}
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
DiscordRichPresence presence{};
presence.largeImageKey = DEFAULT_DISCORD_IMAGE;
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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -9,9 +9,9 @@
#ifdef __unix__
#include <gamemode_client.h>
#endif
#include "qt_common/gamemode.h"
#include "common/logging/log.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/gamemode.h"
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
#include "gui_settings.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "gui_settings.h"
namespace FS = Common::FS;
namespace GraphicsBackend {
QString GuiConfigPath() {
return QString::fromStdString(
FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "gui_config.ini"));
return QString::fromStdString(FS::PathToUTF8String(FS::GetEdenPath(FS::EdenPath::ConfigDir) / "gui_config.ini"));
}
void SetForceX11(bool state) {
+33 -19
View File
@@ -1,16 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/fs/fs.h"
#include "qt_common.h"
#include "common/fs/fs.h"
#include "common/fs/ryujinx_compat.h"
#include <QGuiApplication>
#include <QStringLiteral>
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include "qt_common/abstract/frontend.h"
#include "qt_common/qt_string_lookup.h"
#include <QFile>
#include <QMessageBox>
#include <JlCompress.h>
#if !defined(WIN32) && !defined(__APPLE__)
@@ -22,13 +27,18 @@
namespace QtCommon {
#ifdef YUZU_QT_WIDGETS
QWidget* rootObject = nullptr;
#else
QObject* rootObject = nullptr;
#endif
std::unique_ptr<Core::System> system = nullptr;
std::shared_ptr<FileSys::RealVfsFilesystem> vfs = nullptr;
std::unique_ptr<FileSys::ManualContentProvider> provider = nullptr;
Core::Frontend::WindowSystemType GetWindowSystemType() {
Core::Frontend::WindowSystemType GetWindowSystemType()
{
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
@@ -50,7 +60,8 @@ Core::Frontend::WindowSystemType GetWindowSystemType() {
return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
{
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
@@ -58,8 +69,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast<void*>(window->winId());
#elif defined(__APPLE__)
id layer = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
id layer = reinterpret_cast<id (*) (id, SEL)>(
objc_msgSend)(reinterpret_cast<id>(window->winId()), sel_registerName("layer"));
// In Qt 6, the layer of the NSView might be a QContainerLayer.
// 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;
if (layer) {
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
if (reinterpret_cast<bool (*) (id, SEL, Class)>(objc_msgSend)(layer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = layer;
} else {
id sublayers = reinterpret_cast<id (*)(id, SEL)>(objc_msgSend)(
layer, sel_registerName("sublayers"));
id sublayers = reinterpret_cast<id (*) (id, SEL)>(objc_msgSend)(layer, sel_registerName("sublayers"));
if (sublayers) {
unsigned long count = reinterpret_cast<unsigned long (*)(id, SEL)>(objc_msgSend)(
sublayers, sel_registerName("count"));
unsigned long count = reinterpret_cast<unsigned long (*) (id, SEL)>(objc_msgSend)(sublayers, sel_registerName("count"));
for (unsigned long i = 0; i < count; ++i) {
id sublayer = reinterpret_cast<id (*)(id, SEL, unsigned long)>(objc_msgSend)(
sublayers, sel_registerName("objectAtIndex:"), i);
if (reinterpret_cast<bool (*)(id, SEL, Class)>(objc_msgSend)(
sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
id sublayer = reinterpret_cast<id (*) (id, SEL, unsigned long)>(objc_msgSend)(sublayers, sel_registerName("objectAtIndex:"), i);
if (reinterpret_cast<bool (*) (id, SEL, Class)>(objc_msgSend)(sublayer, sel_registerName("isKindOfClass:"), metal_layer_class)) {
metal_layer = sublayer;
break;
}
@@ -102,22 +108,30 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
return wsi;
}
const QString tr(const char* str) {
const QString tr(const char* 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());
}
void Init(QWidget* root) {
#ifdef YUZU_QT_WIDGETS
void Init(QWidget* root)
#else
void Init(QObject* root)
#endif
{
system = std::make_unique<Core::System>();
rootObject = root;
vfs = std::make_unique<FileSys::RealVfsFilesystem>();
provider = std::make_unique<FileSys::ManualContentProvider>();
}
std::filesystem::path GetEdenCommand() {
std::filesystem::path GetEdenCommand()
{
std::filesystem::path command;
// 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
#ifndef QT_COMMON_H
#define QT_COMMON_H
#include <memory>
#include <QWindow>
#include <core/frontend/emu_window.h>
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include <core/frontend/emu_window.h>
#include <memory>
#include <core/file_sys/vfs/vfs_real.h>
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::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::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 std::string& str);
const QString tr(const char *str);
const QString tr(const std::string &str);
std::filesystem::path GetEdenCommand();
} // namespace QtCommon
+11 -9
View File
@@ -11,9 +11,8 @@
/// Small helper to look up enums.
/// res = the result code
/// base = the base matching value in the StringKey table
#define LOOKUP_ENUM(res, base) \
QtCommon::StringLookup::Lookup( \
QtCommon::StringLookup::StringKey((int)res + (int)QtCommon::StringLookup::base))
#define LOOKUP_ENUM(res, base) QtCommon::StringLookup::Lookup( \
QtCommon::StringLookup::StringKey((int) res + (int) QtCommon::StringLookup::base))
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"
"Select the corresponding button to migrate data from that emulator.\n"
"This may take a while.")},
{MigrationTooltipClearShader, QT_TR_NOOP("Clearing shader cache is recommended for all "
"users.\nDo not uncheck unless you know what "
"you're doing.")},
{MigrationTooltipClearShader,
QT_TR_NOOP("Clearing shader cache is recommended for all "
"users.\nDo not uncheck unless you know what "
"you're doing.")},
{MigrationTooltipKeepOld,
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.")},
{MigrationTooltipClearOld, QT_TR_NOOP("Deletes the old data directory.\nThis is recommended on "
"devices with space constraints.")},
{MigrationTooltipClearOld,
QT_TR_NOOP("Deletes the old data directory.\nThis is recommended on "
"devices with space constraints.")},
{MigrationTooltipLinkOld,
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.")},
@@ -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.")},
};
static inline const QString Lookup(StringKey key) {
static inline const QString Lookup(StringKey key)
{
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
#ifndef QT_APPLET_UTIL_H
#define QT_APPLET_UTIL_H
// TODO
namespace QtCommon::Applets {}
namespace QtCommon::Applets {
}
#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
#include "compress.h"
@@ -10,8 +10,11 @@
/** This is a modified version of JlCompress **/
namespace QtCommon::Compress {
bool compressDir(QString fileCompressed, QString dir, const Options& options,
QtCommon::QtProgressCallback callback) {
bool compressDir(QString fileCompressed,
QString dir,
const Options &options,
QtCommon::QtProgressCallback callback)
{
// Create zip
QuaZip zip(fileCompressed);
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
// good approx. of total progress
// 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);
std::size_t total = 0;
@@ -50,8 +54,14 @@ bool compressDir(QString fileCompressed, QString dir, const Options& options,
return true;
}
bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& options,
std::size_t total, std::size_t& progress, QtProgressCallback callback) {
bool compressSubDir(QuaZip *zip,
QString dir,
QString origDir,
const Options &options,
std::size_t total,
std::size_t &progress,
QtProgressCallback callback)
{
// zip: object where to add the file
// dir: current real directory
// origDir: original real directory
@@ -59,20 +69,22 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
if (!zip)
return false;
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend &&
zip->getMode() != QuaZip::mdAdd)
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
&& zip->getMode() != QuaZip::mdAdd)
return false;
QDir directory(dir);
if (!directory.exists())
return false;
QDir origDirectory(origDir);
if (dir != origDir) {
QuaZipFile dirZipFile(zip);
std::unique_ptr<QuaZipNewInfo> qzni;
qzni = std::make_unique<QuaZipNewInfo>(
origDirectory.relativeFilePath(dir) + QLatin1String("/"), dir);
qzni = std::make_unique<QuaZipNewInfo>(origDirectory.relativeFilePath(dir)
+ QLatin1String("/"),
dir);
if (!dirZipFile.open(QIODevice::WriteOnly, *qzni, nullptr, 0, 0)) {
return false;
}
@@ -80,18 +92,18 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
}
// For each subfolder
QFileInfoList subfiles =
directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::Dirs);
for (const auto& file : std::as_const(subfiles)) {
if (!compressSubDir(zip, file.absoluteFilePath(), origDir, options, total, progress,
callback)) {
QFileInfoList subfiles = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot
| QDir::Hidden | QDir::Dirs);
for (const auto &file : std::as_const(subfiles)) {
if (!compressSubDir(
zip, file.absoluteFilePath(), origDir, options, total, progress, callback)) {
return false;
}
}
// For each file in directory
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 (!file.isFile() || file.absoluteFilePath() == zip->getZipName())
continue;
@@ -100,8 +112,7 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
// Compress the file
if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress,
callback)) {
if (!compressFile(zip, file.absoluteFilePath(), filename, options, total, progress, callback)) {
return false;
}
}
@@ -109,26 +120,40 @@ bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& op
return true;
}
bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options& options,
std::size_t total, std::size_t& progress, QtCommon::QtProgressCallback callback) {
bool compressFile(QuaZip *zip,
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
// fileName: real file name
// fileDest: file name inside the zip object
if (!zip)
return false;
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend &&
zip->getMode() != QuaZip::mdAdd)
if (zip->getMode() != QuaZip::mdCreate && zip->getMode() != QuaZip::mdAppend
&& zip->getMode() != QuaZip::mdAdd)
return false;
QuaZipFile outFile(zip);
if (options.getDateTime().isNull()) {
if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, fileName), nullptr, 0,
options.getCompressionMethod(), options.getCompressionLevel()))
if (!outFile.open(QIODevice::WriteOnly,
QuaZipNewInfo(fileDest, fileName),
nullptr,
0,
options.getCompressionMethod(),
options.getCompressionLevel()))
return false;
} else {
if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, fileName), nullptr, 0,
options.getCompressionMethod(), options.getCompressionLevel()))
if (!outFile.open(QIODevice::WriteOnly,
QuaZipNewInfo(fileDest, fileName),
nullptr,
0,
options.getCompressionMethod(),
options.getCompressionLevel()))
return false;
}
@@ -146,8 +171,7 @@ bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options
if (!inFile.open(QIODevice::ReadOnly)) {
return false;
}
if (!copyData(inFile, outFile, total, progress, callback) ||
outFile.getZipError() != UNZ_OK) {
if (!copyData(inFile, outFile, total, progress, callback) || outFile.getZipError() != UNZ_OK) {
return false;
}
inFile.close();
@@ -157,8 +181,12 @@ bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options
return outFile.getZipError() == UNZ_OK;
}
bool copyData(QIODevice& inFile, QIODevice& outFile, std::size_t total, std::size_t& progress,
QtProgressCallback callback) {
bool copyData(QIODevice &inFile,
QIODevice &outFile,
std::size_t total,
std::size_t &progress,
QtProgressCallback callback)
{
while (!inFile.atEnd()) {
char 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;
}
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback) {
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback)
{
// Open zip
QuaZip zip(fileCompressed);
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)) {
return QStringList();
}
@@ -196,7 +226,7 @@ QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCall
}
std::size_t total = 0;
for (const QuaZipFileInfo64& info : zip.getFileInfoList64()) {
for (const QuaZipFileInfo64 &info : zip.getFileInfoList64()) {
total += info.uncompressedSize;
}
@@ -226,8 +256,13 @@ QStringList extractDir(QuaZip& zip, const QString& dir, QtCommon::QtProgressCall
return extracted;
}
bool extractFile(QuaZip* zip, QString fileName, QString fileDest, std::size_t total,
std::size_t& progress, QtCommon::QtProgressCallback callback) {
bool extractFile(QuaZip *zip,
QString fileName,
QString fileDest,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback)
{
// zip: object where to add the file
// filename: real file name
// 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;
}
bool removeFile(QStringList listFile) {
bool removeFile(QStringList listFile)
{
bool ret = true;
// For each file
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
#pragma once
#include <QDir>
#include <QString>
#include "qt_common/qt_common.h"
#include <quazip.h>
#include <zlib.h>
#include "qt_common/qt_common.h"
/** This is a modified version of JlCompress **/
namespace QtCommon::Compress {
@@ -15,49 +15,49 @@ namespace QtCommon::Compress {
class Options {
public:
/**
* 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 left character indicating the compression method,
* and the right character indicating the compression level.
*
* method == 0 indicates that the file is not compressed but rather stored as is.
* method == 8(Z_DEFLATED) indicates that zlib compression is used.
*
* A higher value of level indicates a smaller size of the compressed file,
* although it also implies more time consumed during the compression process.
*/
enum CompressionStrategy {
* 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 left character indicating the compression method,
* and the right character indicating the compression level.
*
* method == 0 indicates that the file is not compressed but rather stored as is.
* method == 8(Z_DEFLATED) indicates that zlib compression is used.
*
* A higher value of level indicates a smaller size of the compressed file,
* although it also implies more time consumed during the compression process.
*/
enum CompressionStrategy
{
/// Storage without compression
Storage = 0x00, // Z_NO_COMPRESSION 0
/// The fastest compression speed
Fastest = 0x81, // Z_BEST_SPEED 1
/// Relatively fast compression speed
Faster = 0x83,
Storage = 0x00, // Z_NO_COMPRESSION 0
/// The fastest compression speed
Fastest = 0x81, // Z_BEST_SPEED 1
/// Relatively fast compression speed
Faster = 0x83,
/// Standard compression speed and ratio
Standard = 0x86,
/// Better compression ratio
Better = 0x87,
Better = 0x87,
/// The best compression ratio
Best = 0x89, // Z_BEST_COMPRESSION 9
/// The default compression strategy, according to the open function of
/// quazipfile.h, the value of method is Z_DEFLATED, and the value of level is
/// Z_DEFAULT_COMPRESSION -1 (equals lvl 6)
Default = 0xff
Best = 0x89, // Z_BEST_COMPRESSION 9
/// The default compression strategy, according to the open function of quazipfile.h,
/// the value of method is Z_DEFLATED, and the value of level is Z_DEFAULT_COMPRESSION -1 (equals lvl 6)
Default = 0xff
};
public:
explicit Options(const CompressionStrategy& strategy) : m_compressionStrategy(strategy) {}
explicit Options(const CompressionStrategy& strategy)
: m_compressionStrategy(strategy) {}
explicit Options(const QDateTime& dateTime = QDateTime(),
const CompressionStrategy& strategy = Default)
explicit Options(const QDateTime& dateTime = QDateTime(), const CompressionStrategy& strategy = Default)
: m_dateTime(dateTime), m_compressionStrategy(strategy) {}
QDateTime getDateTime() const {
return m_dateTime;
}
void setDateTime(const QDateTime& dateTime) {
void setDateTime(const QDateTime &dateTime) {
m_dateTime = dateTime;
}
@@ -70,11 +70,10 @@ public:
}
int getCompressionLevel() const {
return m_compressionStrategy != Default ? m_compressionStrategy & 0x0f
: Z_DEFAULT_COMPRESSION;
return m_compressionStrategy != Default ? m_compressionStrategy & 0x0f : Z_DEFAULT_COMPRESSION;
}
void setCompressionStrategy(const CompressionStrategy& strategy) {
void setCompressionStrategy(const CompressionStrategy &strategy) {
m_compressionStrategy = strategy;
}
@@ -90,21 +89,34 @@ private:
* @param fileCompressed Destination file
* @param dir The directory to compress
* @param options Compression level, etc
* @param callback Callback that takes in two std::size_t (total, progress) and returns false
* if the current operation should be cancelled.
* @param callback Callback that takes in two std::size_t (total, progress) and returns false 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 = {});
// Internal //
bool compressSubDir(QuaZip* zip, QString dir, QString origDir, const Options& options,
std::size_t total, std::size_t& progress,
bool compressSubDir(QuaZip *zip,
QString dir,
QString origDir,
const Options &options,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback);
bool compressFile(QuaZip* zip, QString fileName, QString fileDest, const Options& options,
std::size_t total, std::size_t& progress, QtCommon::QtProgressCallback callback);
bool compressFile(QuaZip *zip,
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);
// 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.
* @param fileCompressed Compressed file
* @param dir The directory to push the results to
* @param callback Callback that takes in two std::size_t (total, progress) and returns false
* if the current operation should be cancelled.
* @param callback Callback that takes in two std::size_t (total, progress) and returns false if the current operation should be cancelled.
*/
QStringList extractDir(QString fileCompressed, QString dir,
QtCommon::QtProgressCallback callback = {});
QStringList extractDir(QString fileCompressed, QString dir, QtCommon::QtProgressCallback callback = {});
// 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,
std::size_t& progress, QtCommon::QtProgressCallback callback);
bool extractFile(QuaZip *zip,
QString fileName,
QString fileDest,
std::size_t total,
std::size_t &progress,
QtCommon::QtProgressCallback callback);
bool removeFile(QStringList listFile);
} // namespace QtCommon::Compress
}
+185 -129
View File
@@ -12,54 +12,63 @@
#include "compress.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 <JlCompress.h>
#include <QFuture>
#include <QFutureWatcher>
#include <QtConcurrentRun>
#include <JlCompress.h>
namespace QtCommon::Content {
bool CheckGameFirmware(u64 program_id) {
if (FirmwareManager::GameRequiresFirmware(program_id) &&
!FirmwareManager::CheckFirmwarePresence(*system)) {
auto result = QtCommon::Frontend::Warning(
bool CheckGameFirmware(u64 program_id, QObject* parent)
{
if (FirmwareManager::GameRequiresFirmware(program_id)
&& !FirmwareManager::CheckFirmwarePresence(*system)) {
auto result = QtCommon::Frontend::ShowMessage(
QMessageBox::Warning,
tr("Game Requires Firmware"),
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'>"
"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;
}
void InstallFirmware(const QString& location, bool recursive) {
// Initialize a progress dialog.
auto progress =
QtCommon::Frontend::newProgressDialog(tr("Installing Firmware..."), tr("Cancel"), 0, 100);
progress->show();
QGuiApplication::processEvents();
void InstallFirmware(const QString& location, bool recursive)
{
QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."),
tr("Cancel"),
0,
100,
rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
progress.show();
// Declare progress callback.
auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled();
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
QString failedTitle = tr("Firmware Install Failed");
QString successTitle = tr("Firmware Install Succeeded");
QtCommon::Frontend::Icon icon;
QMessageBox::Icon icon;
FirmwareInstallResult result;
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());
@@ -84,26 +93,28 @@ void InstallFirmware(const QString& location, bool recursive) {
callback(100, 10);
if (recursive) {
Common::FS::IterateDirEntriesRecursively(firmware_source_path, dir_callback,
Common::FS::IterateDirEntriesRecursively(firmware_source_path,
dir_callback,
Common::FS::DirEntryFilter::File);
} else {
Common::FS::IterateDirEntries(firmware_source_path, dir_callback,
Common::FS::IterateDirEntries(firmware_source_path,
dir_callback,
Common::FS::DirEntryFilter::File);
}
if (out.size() <= 0) {
result = FirmwareInstallResult::NoNCAs;
icon = QtCommon::Frontend::Icon::Warning;
icon = QMessageBox::Warning;
ShowMessage();
return;
}
// Locate and erase the content of nand/system/Content/registered/*.nca, if any.
auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
if (sysnand_content_vdir->IsWritable() &&
!sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
if (sysnand_content_vdir->IsWritable()
&& !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
result = FirmwareInstallResult::FailedDelete;
icon = QtCommon::Frontend::Icon::Critical;
icon = QMessageBox::Critical;
ShowMessage();
return;
}
@@ -119,20 +130,22 @@ void InstallFirmware(const QString& location, bool recursive) {
int i = 0;
for (const auto& firmware_src_path : out) {
i++;
auto firmware_src_vfile =
vfs->OpenFile(firmware_src_path.generic_string(), FileSys::OpenMode::Read);
auto firmware_dst_vfile =
firmware_vdir->CreateFileRelative(firmware_src_path.filename().string());
auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
FileSys::OpenMode::Read);
auto firmware_dst_vfile = firmware_vdir->CreateFileRelative(
firmware_src_path.filename().string());
if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
LOG_ERROR(Frontend, "Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(), firmware_src_path.filename().string());
LOG_ERROR(Frontend,
"Failed to copy firmware file {} to {} in registered folder!",
firmware_src_path.generic_string(),
firmware_src_path.filename().string());
success = false;
}
if (callback(100, 20 + static_cast<int>(((i) / static_cast<float>(out.size())) * 70.0))) {
result = FirmwareInstallResult::FailedCorrupted;
icon = QtCommon::Frontend::Icon::Warning;
icon = QMessageBox::Warning;
ShowMessage();
return;
}
@@ -140,7 +153,7 @@ void InstallFirmware(const QString& location, bool recursive) {
if (!success) {
result = FirmwareInstallResult::FailedCopy;
icon = QtCommon::Frontend::Icon::Critical;
icon = QMessageBox::Critical;
ShowMessage();
return;
}
@@ -149,37 +162,39 @@ void InstallFirmware(const QString& location, bool recursive) {
system->GetFileSystemController().CreateFactories(*vfs);
auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress->wasCanceled();
progress.setValue(90 + static_cast<int>((processed_size * 10) / total_size));
return progress.wasCanceled();
};
auto results = ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider,
VerifyFirmwareCallback, true);
auto results = ContentManager::VerifyInstalledContents(*QtCommon::system,
*QtCommon::provider,
VerifyFirmwareCallback,
true);
if (results.size() > 0) {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(results, "\n")));
progress->close();
const auto failed_names = QString::fromStdString(
fmt::format("{}", fmt::join(results, "\n")));
progress.close();
QtCommon::Frontend::Critical(
tr("Firmware integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
return;
}
progress->close();
QGuiApplication::processEvents();
progress.close();
const auto pair = FirmwareManager::GetFirmwareVersion(*system);
const auto firmware_data = pair.first;
const std::string display_version(firmware_data.display_version.data());
result = FirmwareInstallResult::Success;
QtCommon::Frontend::Information(successTitle, GetFirmwareInstallResultString(result).arg(
QString::fromStdString(display_version)));
QtCommon::Frontend::Information(successTitle,
GetFirmwareInstallResultString(result).arg(
QString::fromStdString(display_version)));
}
QString UnzipFirmwareToTmp(const QString& location) {
QString UnzipFirmwareToTmp(const QString& location)
{
namespace fs = std::filesystem;
fs::path tmp{fs::temp_directory_path()};
@@ -204,47 +219,59 @@ QString UnzipFirmwareToTmp(const QString& location) {
}
// Content //
void VerifyGameContents(const std::string& game_path) {
auto progress =
QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100);
progress->show();
QGuiApplication::processEvents();
void VerifyGameContents(const std::string& game_path)
{
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
tr("Cancel"),
0,
100,
rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
const auto callback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled();
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const auto result = ContentManager::VerifyGameContents(*system, game_path, callback);
switch (result) {
case ContentManager::GameVerificationResult::Success:
QtCommon::Frontend::Information(rootObject, tr("Integrity verification succeeded!"),
QtCommon::Frontend::Information(rootObject,
tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
break;
case ContentManager::GameVerificationResult::Failed:
QtCommon::Frontend::Critical(rootObject, tr("Integrity verification failed!"),
QtCommon::Frontend::Critical(rootObject,
tr("Integrity verification failed!"),
tr("File contents may be corrupt or missing."));
break;
case ContentManager::GameVerificationResult::NotImplemented:
QtCommon::Frontend::Warning(
rootObject, tr("Integrity verification couldn't be performed"),
rootObject,
tr("Integrity verification couldn't be performed"),
tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. "
"File contents could not be checked for validity."));
}
}
void InstallKeys() {
const QString key_source_location = QtCommon::Frontend::GetOpenFileName(
tr("Select Dumped Keys Location"), {}, QStringLiteral("Decryption Keys (*.keys)"), {});
void InstallKeys()
{
const QString key_source_location
= QtCommon::Frontend::GetOpenFileName(tr("Select Dumped Keys Location"),
{},
QStringLiteral("Decryption Keys (*.keys)"),
{},
QtCommon::Frontend::Option::ReadOnly);
if (key_source_location.isEmpty())
return;
FirmwareManager::KeyInstallResult result =
FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
FirmwareManager::KeyInstallResult result
= FirmwareManager::InstallKeys(key_source_location.toStdString(), "keys");
system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
@@ -259,42 +286,46 @@ void InstallKeys() {
}
}
void VerifyInstalledContents() {
void VerifyInstalledContents()
{
// Initialize a progress dialog.
auto progress =
QtCommon::Frontend::newProgressDialog(tr("Verifying integrity..."), tr("Cancel"), 0, 100);
progress->show();
QGuiApplication::processEvents();
QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
tr("Cancel"),
0,
100,
rootObject);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
progress.setAutoClose(false);
progress.setAutoReset(false);
// Declare progress callback.
auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
QGuiApplication::processEvents();
progress->setValue(static_cast<int>((processed_size * 100) / total_size));
return progress->wasCanceled();
progress.setValue(static_cast<int>((processed_size * 100) / total_size));
return progress.wasCanceled();
};
const std::vector<std::string> result = ContentManager::VerifyInstalledContents(
*QtCommon::system, *QtCommon::provider, QtProgressCallback);
progress->close();
const std::vector<std::string> result
= ContentManager::VerifyInstalledContents(*QtCommon::system,
*QtCommon::provider,
QtProgressCallback);
progress.close();
if (result.empty()) {
QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
tr("The operation completed successfully."));
} else {
const auto failed_names =
QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
const auto failed_names = QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
QtCommon::Frontend::Critical(
tr("Integrity verification failed!"),
tr("Verification failed for the following files:\n\n%1").arg(failed_names));
}
}
void FixProfiles() {
void FixProfiles()
{
// 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
// not default
// Doing it at init time causes profiles to read from the wrong place entirely if NAND dir is not default
// TODO: better solution
system->GetProfileManager().ResetUserSaveFile();
std::vector<std::string> orphaned = system->GetProfileManager().FindOrphanedProfiles();
@@ -334,66 +365,79 @@ void FixProfiles() {
"%2<br><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, "
"delete all orphaned profiles, and move your copied contents to the good "
"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>")
"delete all orphaned profiles, and move your copied contents to the good 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>")
.arg(qorphaned, qgood));
QtCommon::Game::OpenSaveFolder();
}
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);
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id)
{
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;
result = Warning(tr("Are you REALLY sure?"),
tr("Once deleted, your data will NOT come back!\n"
"Only do this if you're 100% sure you want to delete this data."),
Yes | No);
result = QtCommon::Frontend::Warning(
tr("Are you REALLY sure?"),
tr("Once deleted, your data will NOT come back!\n"
"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;
auto dialog = newProgressDialog(tr("Clearing..."), QString(), 0, 0);
dialog->show();
QtCommon::Frontend::QtProgressDialog dialog(tr("Clearing..."), QString(), 0, 0);
dialog.show();
FrontendCommon::DataManager::ClearDir(dir, user_id);
dialog->close();
dialog.close();
}
void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::string& user_id,
const QString& name, std::function<void()> callback) {
void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir,
const std::string& user_id,
const QString& name,
std::function<void()> callback)
{
using namespace QtCommon::Frontend;
const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id);
const QString zip_dump_location = GetSaveFileName(
tr("Select Export Location"), tr("%1.zip").arg(name), tr("Zipped Archives (*.zip)"));
const QString zip_dump_location = GetSaveFileName(tr("Select Export Location"),
tr("%1.zip").arg(name),
tr("Zipped Archives (*.zip)"));
if (zip_dump_location.isEmpty())
return;
auto progress = QtCommon::Frontend::newProgressDialogPtr(
tr("Exporting data. This may take a while..."), tr("Cancel"), 0, 100);
QtProgressDialog* progress = new QtProgressDialog(
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();
QGuiApplication::processEvents();
auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod(
progress, "setValue", Qt::DirectConnection,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size)));
QMetaObject::invokeMethod(progress,
"setValue",
Qt::DirectConnection,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled();
};
QFuture<bool> future = QtConcurrent::run([=]() {
return QtCommon::Compress::compressDir(zip_dump_location, QString::fromStdString(dir),
QtCommon::Compress::Options(), progress_callback);
return QtCommon::Compress::compressDir(zip_dump_location,
QString::fromStdString(dir),
QtCommon::Compress::Options(),
progress_callback);
});
QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>(rootObject);
@@ -420,32 +464,42 @@ void ExportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::str
watcher->setFuture(future);
}
void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir, const std::string& user_id,
std::function<void()> callback) {
void ImportDataDir(FrontendCommon::DataManager::DataDir data_dir,
const std::string& user_id,
std::function<void()> callback)
{
const std::string dir = FrontendCommon::DataManager::GetDataDirString(data_dir, user_id);
using namespace QtCommon::Frontend;
const QString zip_dump_location =
GetOpenFileName(tr("Select Import Location"), {}, tr("Zipped Archives (*.zip)"));
const QString zip_dump_location = GetOpenFileName(tr("Select Import Location"),
{},
tr("Zipped Archives (*.zip)"));
if (zip_dump_location.isEmpty())
return;
StandardButton button =
Warning(tr("Import Warning"),
tr("All previous data in this directory will be deleted. Are you sure you wish to "
"proceed?"),
StandardButton::Yes | StandardButton::No);
StandardButton button = Warning(
tr("Import Warning"),
tr("All previous data in this directory will be deleted. Are you sure you wish to "
"proceed?"),
StandardButton::Yes | StandardButton::No);
if (button != QtCommon::Frontend::Yes)
if (button != QMessageBox::Yes)
return;
QtProgressDialog* progress =
newProgressDialogPtr(tr("Importing data. This may take a while..."), tr("Cancel"), 0, 100);
QtProgressDialog* progress = new QtProgressDialog(
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->setValue(0);
QGuiApplication::processEvents();
// to prevent GUI mangling we have to run this in a thread as well
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, [=]() {
auto progress_callback = [=](size_t total_size, size_t processed_size) {
QMetaObject::invokeMethod(
progress, "setValue", Qt::DirectConnection,
Q_ARG(int, static_cast<int>((processed_size * 100) / total_size)));
QMetaObject::invokeMethod(progress,
"setValue",
Qt::DirectConnection,
Q_ARG(int,
static_cast<int>((processed_size * 100) / total_size)));
return !progress->wasCanceled();
};
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)
.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
+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
#ifndef QT_CONTENT_UTIL_H
@@ -13,7 +13,7 @@
namespace QtCommon::Content {
//
bool CheckGameFirmware(u64 program_id);
bool CheckGameFirmware(u64 program_id, QObject *parent);
enum class FirmwareInstallResult {
Success,
@@ -23,7 +23,8 @@ enum class FirmwareInstallResult {
FailedCorrupted,
};
inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result) {
inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result)
{
return LOOKUP_ENUM(result, FwInstallSuccess);
}
@@ -32,29 +33,30 @@ inline const QString GetFirmwareInstallResultString(FirmwareInstallResult result
* \param result The 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);
}
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 //
void InstallKeys();
// Content //
void VerifyGameContents(const std::string& game_path);
void VerifyGameContents(const std::string &game_path);
void VerifyInstalledContents();
void ClearDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "");
void ExportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string& user_id = "",
const QString& name = QStringLiteral("export"),
std::function<void()> callback = {});
void ImportDataDir(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 = "",
const QString &name = QStringLiteral("export"),
std::function<void()> callback = {});
void ImportDataDir(FrontendCommon::DataManager::DataDir dir, const std::string &user_id = "", std::function<void()> callback = {});
// Profiles //
void FixProfiles();
} // namespace QtCommon::Content
}
#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
#include <algorithm>
@@ -13,7 +13,8 @@ namespace fs = std::filesystem;
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;
// "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 {
QtCommon::Frontend::Critical(
tr("Failed to link save data"),
tr("Could not link directory:\n\t%1\nTo:\n\t%2")
.arg(QString::fromStdString(from.string()), QString::fromStdString(to.string())));
tr("Could not link directory:\n\t%1\nTo:\n\t%2").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 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,
// NOT the junction
fs::remove(linked);
} catch (std::exception& e) {
} catch (std::exception &e) {
QtCommon::Frontend::Critical(
tr("Failed to unlink old directory"),
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
try {
fs::copy(orig, linked, fs::copy_options::recursive);
} catch (std::exception& e) {
} catch (std::exception &e) {
QtCommon::Frontend::Critical(
tr("Failed to copy save data"),
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;
}
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 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 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 "
"folder to use?"),
StandardButton::Yes | StandardButton::No);
"folder to use?"), StandardButton::Yes | StandardButton::No);
if (res == StandardButton::Yes) {
auto selected_path =
GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath())
.toStdString();
auto selected_path = GetExistingDirectory(tr("Ryujinx Portable Location"), QDir::homePath()).toStdString();
if (selected_path.empty())
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);
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)
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
#include "common/common_types.h"
#include <filesystem>
#include <optional>
#include "common/common_types.h"
#pragma once
namespace QtCommon::FS {
void LinkRyujinx(std::filesystem::path& from, std::filesystem::path& to);
const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path& path_hint,
const u64& program_id);
void LinkRyujinx(std::filesystem::path &from, std::filesystem::path &to);
const std::filesystem::path GetRyujinxSavePath(const std::filesystem::path &path_hint, const u64 &program_id);
/// 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
+119 -73
View File
@@ -18,34 +18,40 @@
#include <QUrl>
#ifdef _WIN32
#include <shlobj.h>
#include <windows.h>
#include "common/scope_exit.h"
#include "common/string_util.h"
#include <shlobj.h>
#include <windows.h>
#else
#include <fstream>
#include "fmt/ostream.h"
#include <fstream>
#endif
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& command, const std::string& arguments,
const std::string& categories, const std::string& keywords,
const std::string& name) try {
const std::filesystem::path& command,
const std::string& arguments,
const std::string& categories,
const std::string& keywords,
const std::string& name)
try {
#ifdef _WIN32 // Windows
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
LOG_ERROR(Frontend, "CoInitialize failed");
return false;
}
SCOPE_EXIT {
SCOPE_EXIT
{
CoUninitialize();
};
IShellLinkW* ps1 = nullptr;
IPersistFile* persist_file = nullptr;
SCOPE_EXIT {
SCOPE_EXIT
{
if (persist_file != nullptr) {
persist_file->Release();
}
@@ -53,7 +59,10 @@ bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::s
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));
if (FAILED(hres)) {
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;
}
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
std::filesystem::path& out_icon_path) {
bool MakeShortcutIcoPath(const u64 program_id,
const std::string_view game_file_name,
std::filesystem::path& out_icon_path)
{
// Get path to Yuzu icons directory & icon extension
std::string ico_extension = "png";
#if defined(_WIN32)
@@ -155,38 +166,46 @@ bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_
return true;
}
void OpenEdenFolder(const Common::FS::EdenPath& path) {
void OpenEdenFolder(const Common::FS::EdenPath& path)
{
QDesktopServices::openUrl(
QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path))));
}
void OpenRootDataFolder() {
void OpenRootDataFolder()
{
OpenEdenFolder(Common::FS::EdenPath::EdenDir);
}
void OpenNANDFolder() {
void OpenNANDFolder()
{
OpenEdenFolder(Common::FS::EdenPath::NANDDir);
}
void OpenSaveFolder() {
const auto path =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "user/save/0000000000000000";
void OpenSaveFolder()
{
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path.string())));
}
void OpenSDMCFolder() {
void OpenSDMCFolder()
{
OpenEdenFolder(Common::FS::EdenPath::SDMCDir);
}
void OpenModFolder() {
void OpenModFolder()
{
OpenEdenFolder(Common::FS::EdenPath::LoadDir);
}
void OpenLogFolder() {
void OpenLogFolder()
{
OpenEdenFolder(Common::FS::EdenPath::LogDir);
}
static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type) {
static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type)
{
switch (type) {
case QtCommon::Game::InstalledEntryType::Game:
return tr("Error Removing Contents");
@@ -200,9 +219,10 @@ static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type)
}
// Game Content //
void RemoveBaseContent(u64 program_id, InstalledEntryType type) {
const auto res =
ContentManager::RemoveBaseContent(system->GetFileSystemController(), program_id);
void RemoveBaseContent(u64 program_id, InstalledEntryType type)
{
const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(),
program_id);
if (res) {
QtCommon::Frontend::Information(tr("Successfully Removed"),
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);
if (res) {
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);
if (count == 0) {
QtCommon::Frontend::Warning(GetGameListErrorRemoving(type),
@@ -239,7 +261,8 @@ void RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
// Global Content //
void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) {
void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
{
const auto target_file_name = [target] {
switch (target) {
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";
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 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 config_file_name =
program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
: fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path =
Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir) / "custom" / config_file_name;
const auto config_file_name
= program_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()).append(".ini")
: fmt::format("{:016X}.ini", program_id);
const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir)
/ "custom" / config_file_name;
if (!Common::FS::Exists(custom_config_file_path)) {
QtCommon::Frontend::Warning(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);
auto vfs_nand_dir =
vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
auto vfs_nand_dir = vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir),
FileSys::OpenMode::Read);
const auto cache_storage_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfs_nand_dir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Cache,
0 /* program_id */, {}, 0);
const auto cache_storage_path
= FileSys::SaveDataFactory::GetFullPath({},
vfs_nand_dir,
FileSys::SaveDataSpaceId::User,
FileSys::SaveDataType::Cache,
0 /* program_id */,
{},
0);
const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
@@ -368,31 +400,32 @@ void ResetMetadata(bool show_message) {
// Uhhh //
// 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;
using namespace QtCommon::Frontend;
int buttons;
QMessageBox::StandardButtons buttons;
switch (imsg) {
case ShortcutMessages::Fullscreen:
buttons = Yes | No;
result = QtCommon::Frontend::Information(
tr("Create Shortcut"), tr("Do you want to launch the game in fullscreen?"), buttons);
return result == Yes;
buttons = QMessageBox::Yes | QMessageBox::No;
result
= QtCommon::Frontend::Information(tr("Create Shortcut"),
tr("Do you want to launch the game in fullscreen?"),
buttons);
return result == QMessageBox::Yes;
case ShortcutMessages::Success:
QtCommon::Frontend::Information(
tr("Shortcut Created"), tr("Successfully created a shortcut to %1").arg(game_title));
QtCommon::Frontend::Information(tr("Shortcut Created"),
tr("Successfully created a shortcut to %1").arg(game_title));
return false;
case ShortcutMessages::Volatile:
buttons = Ok | Cancel;
buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
result = QtCommon::Frontend::Warning(
tr("Shortcut may be Volatile!"),
tr("This will create a shortcut to the current AppImage. This may "
"not work well if you update. Continue?"),
buttons);
return result == Ok;
return result == QMessageBox::Ok;
default:
buttons = Ok;
buttons = QMessageBox::Ok;
QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"),
tr("Failed to create a shortcut to %1").arg(game_title),
buttons);
@@ -400,9 +433,13 @@ inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QSt
}
}
void CreateShortcut(const std::string& game_path, const u64 program_id,
const std::string& game_title_, const ShortcutTarget& target,
std::string arguments_, const bool needs_title) {
void CreateShortcut(const std::string& game_path,
const u64 program_id,
const std::string& game_title_,
const ShortcutTarget& target,
std::string arguments_,
const bool needs_title)
{
// Get path to Eden executable
std::filesystem::path command = GetEdenCommand();
@@ -416,11 +453,13 @@ void CreateShortcut(const std::string& game_path, const u64 program_id,
return;
}
const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(),
const FileSys::PatchManager pm{program_id,
QtCommon::system->GetFileSystemController(),
QtCommon::system->GetContentProvider()};
const auto control = pm.GetControlMetadata();
const auto loader = Loader::GetLoader(
*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read));
const auto loader = Loader::GetLoader(*QtCommon::system,
QtCommon::vfs->OpenFile(game_path,
FileSys::OpenMode::Read));
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);
}
QImage icon_data =
QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
QImage icon_data = QImage::fromData(icon_image_file.data(),
static_cast<int>(icon_image_file.size()));
std::filesystem::path out_icon_path;
if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
if (!SaveIconToFile(out_icon_path, icon_data)) {
@@ -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 keywords = "Switch;Nintendo;";
if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
arguments, categories, keywords, game_title)) {
if (QtCommon::Game::CreateShortcutLink(shortcut_path,
comment,
out_icon_path,
command,
arguments,
categories,
keywords,
game_title)) {
CreateShortcutMessagesGUI(ShortcutMessages::Success, qgame_title);
return;
}
CreateShortcutMessagesGUI(ShortcutMessages::Failed, qgame_title);
}
// TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string
// literal
std::string GetShortcutPath(ShortcutTarget target) {
// TODO: You want this to be constexpr? Well too bad, clang19 doesn't believe this is a string literal
std::string GetShortcutPath(ShortcutTarget target)
{
{
std::string shortcut_path{};
if (target == ShortcutTarget::Desktop) {
shortcut_path =
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
shortcut_path
= QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
} else if (target == ShortcutTarget::Applications) {
shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)
.toStdString();
shortcut_path
= QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
}
return shortcut_path;
}
}
void CreateHomeMenuShortcut(ShortcutTarget target) {
void CreateHomeMenuShortcut(ShortcutTarget target)
{
constexpr u64 QLaunchId = static_cast<u64>(Service::AM::AppletProgramId::QLaunch);
auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
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
#ifndef QT_GAME_UTIL_H
@@ -29,18 +29,27 @@ enum class ShortcutTarget {
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& command, const std::string& arguments,
const std::string& categories, const std::string& keywords,
const std::filesystem::path& command,
const std::string& arguments,
const std::string& categories,
const std::string& keywords,
const std::string& name);
bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
bool MakeShortcutIcoPath(const u64 program_id,
const std::string_view game_file_name,
std::filesystem::path& out_icon_path);
void OpenEdenFolder(const Common::FS::EdenPath& path);
void OpenEdenFolder(const Common::FS::EdenPath &path);
void OpenRootDataFolder();
void OpenNANDFolder();
void OpenSaveFolder();
@@ -62,13 +71,16 @@ void RemoveCacheStorage(u64 program_id);
void ResetMetadata(bool show_message = true);
// Shortcuts //
void CreateShortcut(const std::string& game_path, const u64 program_id,
const std::string& game_title_, const ShortcutTarget& target,
std::string arguments_, const bool needs_title);
void CreateShortcut(const std::string& game_path,
const u64 program_id,
const std::string& game_title_,
const ShortcutTarget& target,
std::string arguments_,
const bool needs_title);
std::string GetShortcutPath(ShortcutTarget target);
void CreateHomeMenuShortcut(ShortcutTarget target);
} // namespace QtCommon::Game
}
#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
#include "qt_common/util/meta.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
@@ -8,11 +9,11 @@
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/am/frontend/applet_web_browser_types.h"
#include "qt_common/util/meta.h"
namespace QtCommon::Meta {
void RegisterMetaTypes() {
void RegisterMetaTypes()
{
// Register integral and floating point types
qRegisterMetaType<u8>("u8");
qRegisterMetaType<u16>("u16");
@@ -71,4 +72,4 @@ void RegisterMetaTypes() {
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
#ifndef QT_META_H
@@ -11,5 +11,5 @@ namespace QtCommon::Meta {
//
void RegisterMetaTypes();
} // namespace QtCommon::Meta
}
#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(
tr("Mod Name"), tr("What should this mod be called?"), default_name);
if (name.isEmpty())
return {};
if (name.isEmpty()) return {};
// 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
+3 -3
View File
@@ -7,8 +7,8 @@
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
#include "qt_common/util/path.h"
#include <QDesktopServices>
#include <QString>
#include <QUrl>
#include <fmt/format.h>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "qt_common/abstract/frontend.h"
#include "qt_common/util/path.h"
#include <fmt/format.h>
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_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
QtCommon::Frontend::Warning(tr("Error Opening Shader Cache"),
tr("Failed to create or open shader cache for this title, "
"ensure your app data directory has write permissions."));
QtCommon::Frontend::ShowMessage(QMessageBox::Warning,
tr("Error Opening Shader Cache"),
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)};
@@ -26,4 +30,4 @@ bool OpenShaderCache(u64 program_id, QObject* parent) {
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