mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 21:04:41 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc2569a724 |
@@ -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"
|
||||
;;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Vendored
+12
-13
@@ -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
@@ -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),
|
||||
|
||||
+14
-1
@@ -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()
|
||||
|
||||
+94
-20
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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;");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,7 +34,6 @@ enum class PatchType { Update, DLC, Mod };
|
||||
enum class PatchSource {
|
||||
Unknown,
|
||||
NAND,
|
||||
SDMC,
|
||||
External,
|
||||
Packed,
|
||||
};
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {});
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"))},
|
||||
};
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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?
|
||||
|
||||
@@ -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 +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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user