diff --git a/.ci/license-header.sh b/.ci/license-header.sh
index fecffaa7d3..3d4929d1c1 100755
--- a/.ci/license-header.sh
+++ b/.ci/license-header.sh
@@ -5,10 +5,13 @@ HEADER_HASH="$(cat "$PWD/.ci/license/header-hash.txt")"
echo "Getting branch changes"
-BRANCH=`git rev-parse --abbrev-ref HEAD`
-COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
-RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
-FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
+# BRANCH=`git rev-parse --abbrev-ref HEAD`
+# COMMITS=`git log ${BRANCH} --not master --pretty=format:"%h"`
+# RANGE="${COMMITS[${#COMMITS[@]}-1]}^..${COMMITS[0]}"
+# FILES=`git diff-tree --no-commit-id --name-only ${RANGE} -r`
+
+BASE=`git merge-base master HEAD`
+FILES=`git diff --name-only $BASE`
#FILES=$(git diff --name-only master)
diff --git a/.ci/windows/install-msvc.ps1 b/.ci/windows/install-msvc.ps1
new file mode 100755
index 0000000000..b88f727ed8
--- /dev/null
+++ b/.ci/windows/install-msvc.ps1
@@ -0,0 +1,42 @@
+# SPDX-FileCopyrightText: 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+$ErrorActionPreference = "Stop"
+
+# Check if running as administrator
+if (-not ([bool](net session 2>$null))) {
+ Write-Host "This script must be run with administrator privileges!"
+ Exit 1
+}
+
+$VSVer = "17"
+$ExeFile = "vs_BuildTools.exe"
+$Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile"
+$Destination = "./$ExeFile"
+
+Write-Host "Downloading Visual Studio Build Tools from $Uri"
+$WebClient = New-Object System.Net.WebClient
+$WebClient.DownloadFile($Uri, $Destination)
+Write-Host "Finished downloading $ExeFile"
+
+$VSROOT = "C:/VSBuildTools/$VSVer"
+$Arguments = @(
+ "--installPath `"$VSROOT`"", # set custom installation path
+ "--quiet", # suppress UI
+ "--wait", # wait for installation to complete
+ "--norestart", # prevent automatic restart
+ "--add Microsoft.VisualStudio.Workload.VCTools", # add C++ build tools workload
+ "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # add core x86/x64 C++ tools
+ "--add Microsoft.VisualStudio.Component.Windows10SDK.19041" # add specific Windows SDK
+)
+
+Write-Host "Installing Visual Studio Build Tools"
+$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments
+$ExitCode = $InstallProcess.ExitCode
+
+if ($ExitCode -ne 0) {
+ Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)"
+ Exit $ExitCode
+}
+
+Write-Host "Finished installing Visual Studio Build Tools"
diff --git a/.ci/windows/install-vulkan-sdk.ps1 b/.ci/windows/install-vulkan-sdk.ps1
index 1d4e1b20bf..4c5274d1b7 100755
--- a/.ci/windows/install-vulkan-sdk.ps1
+++ b/.ci/windows/install-vulkan-sdk.ps1
@@ -3,6 +3,12 @@
$ErrorActionPreference = "Stop"
+# Check if running as administrator
+if (-not ([bool](net session 2>$null))) {
+ Write-Host "This script must be run with administrator privileges!"
+ Exit 1
+}
+
$VulkanSDKVer = "1.4.321.1"
$ExeFile = "vulkansdk-windows-X64-$VulkanSDKVer.exe"
$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a9e15cfbd..673aab9e6e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,6 +44,13 @@ if (PLATFORM_SUN)
endif()
endif()
+# Needed for FFmpeg w/ VAAPI and DRM
+if (PLATFORM_OPENBSD)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/X11R6/include")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/X11R6/include")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/X11R6/lib")
+endif()
+
# Detect current compilation architecture and create standard definitions
# =======================================================================
@@ -88,7 +95,7 @@ message(STATUS "Target architecture: ${ARCHITECTURE}")
if (MSVC AND ARCHITECTURE_x86)
message(FATAL_ERROR "Attempting to build with the x86 environment is not supported. \
- This can typically happen if you used the Developer Command Prompt from the start menu;\
+ This can typically happen if you used the Developer Command Prompt from the start menu; \
instead, run vcvars64.bat directly, located at C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvars64.bat")
endif()
@@ -122,7 +129,7 @@ include(CMakeDependentOption)
include(CTest)
# Disable Warnings as Errors for MSVC
-if (CXX_CL)
+if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
endif()
@@ -159,8 +166,6 @@ option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_WIFI_SCAN "Enable WiFi scanning" OFF)
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" ${EXT_DEFAULT})
-option(YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES "Use Vulkan Utility Headers from externals" ${EXT_DEFAULT})
-option(YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS "Use SPIRV-Tools from externals" ${EXT_DEFAULT})
option(YUZU_USE_QT_MULTIMEDIA "Use QtMultimedia for Camera" OFF)
@@ -172,8 +177,6 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
CMAKE_DEPENDENT_OPTION(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF "ENABLE_QT" OFF)
-option(ENABLE_MICROPROFILE "Enables microprofile capabilities" OFF)
-
option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ${EXT_DEFAULT})
@@ -455,6 +458,28 @@ if (YUZU_USE_CPM)
)
endif()
endif()
+
+ # VulkanUtilityHeaders - pulls in headers and utility libs
+ AddJsonPackage(vulkan-utility-headers)
+
+ # small hack
+ if (NOT VulkanUtilityLibraries_ADDED)
+ find_package(VulkanHeaders 1.3.274 REQUIRED)
+ endif()
+
+ # SPIRV Headers
+ AddJsonPackage(spirv-headers)
+
+ # SPIRV Tools
+ AddJsonPackage(spirv-tools)
+
+ if (SPIRV-Tools_ADDED)
+ add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools-static)
+ target_link_libraries(SPIRV-Tools-static PRIVATE SPIRV-Tools-opt SPIRV-Tools-link)
+ endif()
+
+ # mbedtls
+ AddJsonPackage(mbedtls)
else()
# Enforce the search mode of non-required packages for better and shorter failure messages
find_package(fmt 8 REQUIRED)
@@ -467,7 +492,32 @@ else()
find_package(Opus 1.3 MODULE REQUIRED)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED MODULE)
- find_package(Boost 1.79.0 REQUIRED headers context system fiber)
+
+ # wow
+ if (PLATFORM_LINUX)
+ find_package(Boost 1.57.0 REQUIRED headers context system fiber)
+ else()
+ find_package(Boost 1.57.0 REQUIRED)
+ endif()
+
+ # OpenBSD does not package mbedtls3 (only 2)
+ if (PLATFORM_OPENBSD)
+ AddJsonPackage(mbedtls)
+ else()
+ find_package(MbedTLS 3 REQUIRED)
+ endif()
+
+ find_package(VulkanUtilityLibraries REQUIRED)
+ find_package(VulkanHeaders 1.3.274 REQUIRED)
+
+ # FreeBSD does not package spirv-headers
+ if (PLATFORM_FREEBSD)
+ AddJsonPackage(spirv-headers)
+ else()
+ find_package(SPIRV-Headers 1.3.274 REQUIRED)
+ endif()
+
+ find_package(SPIRV-Tools MODULE REQUIRED)
if (YUZU_TESTS)
find_package(Catch2 3.0.1 REQUIRED)
@@ -594,10 +644,8 @@ endfunction()
add_subdirectory(externals)
# pass targets from externals
-find_package(VulkanUtilityLibraries)
find_package(libusb)
find_package(VulkanMemoryAllocator)
-find_package(SPIRV-Tools)
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
find_package(xbyak)
diff --git a/CMakeModules/CPMUtil.cmake b/CMakeModules/CPMUtil.cmake
index db9cce4c66..f76a16c103 100644
--- a/CMakeModules/CPMUtil.cmake
+++ b/CMakeModules/CPMUtil.cmake
@@ -1,17 +1,6 @@
-# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
-# SPDX-License-Identifier: GPL-3.0-or-later
-
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
-# Created-By: crueter
-# Docs will come at a later date, mostly this is to just reduce boilerplate
-# and some cmake magic to allow for runtime viewing of dependency versions
-
-# Future crueter: Wow this was a lie and a half, at this point I might as well make my own CPN
-# haha just kidding... unless?
-
-# TODO(crueter): Remember to get more than 6 hours of sleep whenever making giant cmake changes
if (MSVC OR ANDROID)
set(BUNDLED_DEFAULT ON)
else()
@@ -27,6 +16,7 @@ option(CPMUTIL_FORCE_SYSTEM
cmake_minimum_required(VERSION 3.22)
include(CPM)
+# cpmfile parsing
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
if (EXISTS ${CPMUTIL_JSON_FILE})
@@ -35,12 +25,11 @@ else()
message(WARNING "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE} does not exist, AddJsonPackage will be a no-op")
endif()
-# utility
+# Utility stuff
function(cpm_utils_message level name message)
message(${level} "[CPMUtil] ${name}: ${message}")
endfunction()
-# utility
function(array_to_list array length out)
math(EXPR range "${length} - 1")
@@ -53,7 +42,6 @@ function(array_to_list array length out)
set("${out}" "${NEW_LIST}" PARENT_SCOPE)
endfunction()
-# utility
function(get_json_element object out member default)
string(JSON out_type ERROR_VARIABLE err TYPE "${object}" ${member})
@@ -73,14 +61,13 @@ function(get_json_element object out member default)
set("${out}" "${outvar}" PARENT_SCOPE)
endfunction()
-# Kinda cancerous but whatever
+# The preferred usage
function(AddJsonPackage)
set(oneValueArgs
NAME
# these are overrides that can be generated at runtime, so can be defined separately from the json
DOWNLOAD_ONLY
- SYSTEM_PACKAGE
BUNDLED_PACKAGE
)
@@ -90,6 +77,7 @@ function(AddJsonPackage)
"${ARGN}")
list(LENGTH ARGN argnLength)
+
# single name argument
if(argnLength EQUAL 1)
set(JSON_NAME "${ARGV0}")
@@ -199,7 +187,6 @@ function(AddJsonPackage)
endif()
set(options ${options} ${JSON_OPTIONS})
-
# end options
# system/bundled
@@ -241,7 +228,7 @@ endfunction()
function(AddPackage)
cpm_set_policies()
- # TODO(crueter): docs, git clone
+ # TODO(crueter): git clone?
#[[
URL configurations, descending order of precedence:
diff --git a/CMakeModules/Findmbedtls.cmake b/CMakeModules/Findmbedtls.cmake
deleted file mode 100644
index f5ebf1abdc..0000000000
--- a/CMakeModules/Findmbedtls.cmake
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-FileCopyrightText: 2025 Eden Emulator Project
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-include(FindPackageHandleStandardArgs)
-
-find_package(PkgConfig QUIET)
-pkg_search_module(mbedtls QUIET IMPORTED_TARGET mbedtls)
-find_package_handle_standard_args(mbedtls
- REQUIRED_VARS mbedtls_LINK_LIBRARIES
- VERSION_VAR mbedtls_VERSION
-)
-
-pkg_search_module(mbedcrypto QUIET IMPORTED_TARGET mbedcrypto)
-find_package_handle_standard_args(mbedcrypto
- REQUIRED_VARS mbedcrypto_LINK_LIBRARIES
- VERSION_VAR mbedcrypto_VERSION
-)
diff --git a/cpmfile.json b/cpmfile.json
index e071e0a8b8..887e958533 100644
--- a/cpmfile.json
+++ b/cpmfile.json
@@ -91,6 +91,42 @@
"OPUS_PRESUME_NEON ON"
]
},
+ "vulkan-utility-headers": {
+ "package": "VulkanUtilityLibraries",
+ "repo": "scripts/VulkanUtilityHeaders",
+ "tag": "1.4.326",
+ "artifact": "VulkanUtilityHeaders.tar.zst",
+ "git_host": "git.crueter.xyz",
+ "hash": "5924629755cb1605c4aa4eee20ef7957a9dd8d61e4df548be656d98054f2730c4109693c1bd35811f401f4705d2ccff9fc849be32b0d8480bc3f73541a5e0964"
+ },
+ "spirv-tools": {
+ "package": "SPIRV-Tools",
+ "repo": "KhronosGroup/SPIRV-Tools",
+ "sha": "40eb301f32",
+ "hash": "58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa",
+ "find_args": "MODULE",
+ "options": [
+ "SPIRV_SKIP_EXECUTABLES ON"
+ ]
+ },
+ "spirv-headers": {
+ "package": "SPIRV-Headers",
+ "repo": "KhronosGroup/SPIRV-Headers",
+ "sha": "4e209d3d7e",
+ "hash": "f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4",
+ "options": [
+ "SPIRV_WERROR OFF"
+ ]
+ },
+ "mbedtls": {
+ "package": "MbedTLS",
+ "repo": "Mbed-TLS/mbedtls",
+ "tag": "mbedtls-%VERSION%",
+ "hash": "6671fb8fcaa832e5b115dfdce8f78baa6a4aea71f5c89a640583634cdee27aefe3bf4be075744da91f7c3ae5ea4e0c765c8fc3937b5cfd9ea73d87ef496524da",
+ "version": "3",
+ "git_version": "3.6.4",
+ "artifact": "%TAG%.tar.bz2"
+ },
"cubeb": {
"repo": "mozilla/cubeb",
"sha": "fa02160712",
diff --git a/docs/CPM.md b/docs/CPM.md
index 2afcdaf164..bce224da40 100644
--- a/docs/CPM.md
+++ b/docs/CPM.md
@@ -245,6 +245,6 @@ include(CPMUtil)
Currently, `cpm-fetch.sh` defines the following directories for cpmfiles (max depth of 2, so subdirs are caught as well):
-`externals src/yuzu src/dynarmic .`
+`externals src/qt_common src/dynarmic .`
Whenever you add a new cpmfile, update the script accordingly
\ No newline at end of file
diff --git a/docs/build/Windows.md b/docs/build/Windows.md
index c1792983aa..76602e6d69 100644
--- a/docs/build/Windows.md
+++ b/docs/build/Windows.md
@@ -1,149 +1,195 @@
-# THIS GUIDE IS INTENDED FOR DEVELOPERS ONLY, SUPPORT WILL ONLY BE GIVEN IF YOU'RE A DEVELOPER.
+# ⚠️ This guide is for developers ONLY! Support will be provided to developers ONLY.
-## Method I: MSVC Build for Windows
+## 📋 Current building methods:
-### Minimal Dependencies
+* [ Minimal Dependencies](#minimal-dependencies)
+* [⚡ Method I: MSVC Build for Windows](#method-i-msvc-build-for-windows)
+* [🐧 Method II: MinGW-w64 Build with MSYS2](#method-ii-mingw-w64-build-with-msys2)
+* [🖥️ Method III: CLion Environment Setup](#method-iii-clion-environment-setup)
+* [💻 Building from the command line with MSVC](#building-from-the-command-line-with-msvc)
+* [📜 Building with Scripts](#building-with-scripts)
-On Windows, all library dependencies are automatically included within the `externals` folder, or can be downloaded on-demand. To build Eden, you need to install:
+---
- * **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - **Make sure to select C++ support in the installer. Make sure to update to the latest version if already installed.**
- * **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files. Does not matter if either 32-bit or 64-bit version is installed.
- * **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - **Make sure to select Latest SDK.**
- - A convenience script to install the latest SDK is provided in `.ci\windows\install-vulkan-sdk.ps1`.
- 
+## Minimal Dependencies
- * **Git** - We recommend [Git for Windows](https://gitforwindows.org).
+On Windows, **all** library dependencies are **automatically included** within the `externals` folder.
- 
+You still need to install:
- * While installing Git Bash, you should tell it to include Git in your system path. (Choose the "Git from the command line and also from 3rd-party software" option.) If you missed that, don't worry, you'll just have to manually tell CMake where your git.exe is, since it's used to include version info into the built executable.
+* **[CMake](https://cmake.org/download/)** - Used to generate Visual Studio project files.
+* **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** - Make sure to select **Latest SDK**.
- 
+ * *A convenience script to install the latest SDK is provided in `.ci/windows/install-vulkan-sdk.ps1`*
+* **[Git for Windows](https://gitforwindows.org)** - We recommend installing Git for command line use and version control integration.
-### Cloning Eden with Git
+
-**Master:**
- ```cmd
- git clone --recursive https://git.eden-emu.dev/eden-emu/eden
- cd eden
- ```
+ * *While installing Git Bash, select "Git from the command line and also from 3rd-party software". If missed, manually set `git.exe` path in CMake.*
- 
+---
-* *(Note: eden by default downloads to `C:\Users\\eden` (Master)
+## ⚡ Method I: MSVC Build for Windows
-### Building
+### a. Prerequisites to MSVC Build
-* Open the CMake GUI application and point it to the `eden` (Master)
+* **[Visual Studio 2022 Community](https://visualstudio.microsoft.com/downloads/)** - Make sure to **select C++ support** in the installer, or **update to the latest version** if already installed.
- 
+ * *A convenience script to install the **minimal** version (Visual Build Tools) is provided in `.ci/windows/install-msvc.ps1`*
-* For the build directory, use a `/build` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.)
+---
+
+### b. Clone the eden repository with Git
+
+Open Terminal on
+
+```cmd
+git clone https://git.eden-emu.dev/eden-emu/eden
+cd eden
+```
+
+* *By default `eden` downloads to `C:\Users\\eden`*
+
+---
+
+### c. Building
+
+* Open the CMake GUI application and point it to the `eden`
+
+
+
+* For the build directory, use a `build/` subdirectory inside the source directory or some other directory of your choice. (Tell CMake to create it.)
* Click the "Configure" button and choose `Visual Studio 17 2022`, with `x64` for the optional platform.
- 
-
- * *(Note: If you used GitHub's own app to clone, run `git submodule update --init --recursive` to get the remaining dependencies)*
+
* *(You may also want to disable `YUZU_TESTS` in this case since Catch2 is not yet supported with this.)*
- 
+
* Click "Generate" to create the project files.
- 
+
* Open the solution file `yuzu.sln` in Visual Studio 2022, which is located in the build folder.
- 
+
-* Depending if you want a graphical user interface or not (`eden` has the graphical user interface, while `eden-cmd` doesn't), select `eden` or `eden-cmd` in the Solution Explorer, right-click and `Set as StartUp Project`.
+* * Depending on whether you want a graphical user interface or not, select in the Solution Explorer:
+ * `eden` (GUI)
+ * `eden-cmd` (command-line only)
+ * Then **right-click** and choose `Set as StartUp Project`.
-  
+
+
-* Select the appropriate build type, Debug for debug purposes or Release for performance (in case of doubt choose Release).
+* Select the appropriate build type, `Debug` for debug purposes or `Release` for performance (in case of doubt choose `Release`).
- 
+
-* Right-click the project you want to build and press Build in the submenu or press F5.
+* **Right-click** the project you want to build and press **Build** in the submenu or press `F5`.
- 
+
-## Method II: MinGW-w64 Build with MSYS2
+---
-### Prerequisites to install
+## 🐧 Method II: MinGW-w64 Build with MSYS2
-* [MSYS2](https://www.msys2.org)
-* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - **Make sure to select Latest SDK.**
-* Make sure to follow the instructions and update to the latest version by running `pacman -Syu` as many times as needed.
+### a. Prerequisites to MinGW-w64
-### Install eden dependencies for MinGW-w64
+* **[MSYS2](https://www.msys2.org)** - A versatile and up-to-date development environment for Windows, providing a Unix-like shell, package manager, and toolchain.
-* Open the `MSYS2 MinGW 64-bit` (mingw64.exe) shell
-* Download and install all dependencies using: `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
-* Add MinGW binaries to the PATH: `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
-* Add glslangValidator to the PATH: `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
+---
-### Clone the eden repository with Git
+### b. Install eden dependencies for MinGW-w64
- ```bash
- git clone --recursive https://git.eden-emu.dev/eden-emu/eden
- cd eden
- ```
+* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`)
+* Download and install all dependencies using:
+ * `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
+* Add MinGW binaries to the PATH:
+ * `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
+* Add VulkanSDK to the PATH:
+ * `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
-### Run the following commands to build eden (dynamically linked build)
+---
+
+### c. Clone the eden repository with Git
+
+```cmd
+git clone https://git.eden-emu.dev/eden-emu/eden
+cd eden
+```
+
+---
+
+### d. Building dynamically-linked eden
+
+* This process will generate a *dynamically* linked build
```bash
+# Make build dir and enter
mkdir build && cd build
-cmake -G "MSYS Makefiles" -DYUZU_TESTS=OFF ..
+
+# Generate CMake Makefiles
+cmake .. -G "MSYS Makefiles" -DYUZU_TESTS=OFF
+
+# Build
make -j$(nproc)
-# test eden out with
+
+# Run eden!
./bin/eden.exe
```
-* *(Note: This build is not a static build meaning that you need to include all of the DLLs with the .exe in order to use it!)*
+* *Warning: This build is not a **static** build meaning that you **need** to include all of the DLLs with the .exe in order to use it or other systems!*
-e.g.
-```Bash
-cp externals/ffmpeg-*/bin/*.dll bin/
+---
+
+### Additional notes
+
+
+* Eden doesn't require the rather large Qt dependency, but you will lack a GUI frontend
+
+```bash
+# ...
+
+# Generate CMake Makefiles (withou QT)
+cmake .. -G "MSYS Makefiles" -DYUZU_TESTS=OFF -DENABLE_QT=no
+
+$ ...
```
-Bonus Note: Running programs from inside `MSYS2 MinGW x64` shell has a different %PATH% than directly from explorer. This different %PATH% has the locations of the other DLLs required.
-
+* Running programs from inside `MSYS2 MinGW x64` shell has a different `%PATH%` than directly from explorer.
+ * This different `%PATH%` has the locations of the other DLLs required.
+
-### Building without Qt (Optional)
+---
-Doesn't require the rather large Qt dependency, but you will lack a GUI frontend:
+## 🖥️ Method III: CLion Environment Setup
- * Pass the `-DENABLE_QT=no` flag to cmake
-
-## Method III: CLion Environment Setup
-
-### Minimal Dependencies
-
-To build eden, you need to install the following:
+### a. Prerequisites to CLion
* [CLion](https://www.jetbrains.com/clion/) - This IDE is not free; for a free alternative, check Method I
-* [Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows) - Make sure to select the Latest SDK.
-### Cloning eden with CLion
+---
+
+### b. Cloning eden with CLion
* Clone the Repository:
-
-
-
+
+
+
+---
-
-### Building & Setup
+### c. Building & Setup
* Once Cloned, You will be taken to a prompt like the image below:
-
+
* Set the settings to the image below:
* Change `Build type: Release`
@@ -152,42 +198,62 @@ To build eden, you need to install the following:
* Change `Generator: Let CMake decide`
* Change `Build directory: build`
-
+
* Click OK; now Clion will build a directory and index your code to allow for IntelliSense. Please be patient.
* Once this process has been completed (No loading bar bottom right), you can now build eden
* In the top right, click on the drop-down menu, select all configurations, then select eden
-
+
* Now run by clicking the play button or pressing Shift+F10, and eden will auto-launch once built.
-
+
-## Building from the command line with MSVC
+---
+
+## 💻 Building from the command line with MSVC
```cmd
-git clone --recursive https://git.eden-emu.dev/eden-emu/eden
+# Clone eden and enter
+git clone https://git.eden-emu.dev/eden-emu/eden
cd eden
-mkdir build
-cd build
-cmake .. -G "Visual Studio 17 2022" -A x64
+
+# Make build dir and enter
+mkdir build && cd build
+
+# Generate CMake Makefiles
+cmake .. -G "Visual Studio 17 2022" -A x64 -DYUZU_TESTS=OFF
+
+# Build
cmake --build . --config Release
```
-### Building with Scripts
-A convenience script for building is provided in `.ci/windows/build.sh`. You must run this with Bash, e.g. Git Bash or MinGW TTY. To use this script, you must have windeployqt installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location, e.g. `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`.
+## 📜 Building with Scripts
-Extra CMake flags should be placed in the arguments of the script.
+* A convenience script for building is provided in `.ci/windows/build.sh`.
+* You must run this with Bash, e.g. Git Bash or MinGW TTY.
+* To use this script, you must have `windeployqt` installed (usually bundled with Qt) and set the `WINDEPLOYQT` environment variable to its canonical Bash location:
+ * `WINDEPLOYQT="/c/Qt/6.9.1/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh`.
+* You can use `aqtinstall`, more info on and
-Additional environment variables can be used to control building:
-- `BUILD_TYPE`: Sets the build type to use. Defaults to `Release`
-The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
-- `DEVEL` (default FALSE): Disable Qt update checker
-- `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
-- `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
-- `BUNDLE_QT` (default FALSE): Use bundled Qt
- * Note that using system Qt requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`, e.g. `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
+* Extra CMake flags should be placed in the arguments of the script.
+
+#### Additional environment variables can be used to control building:
+
+* `BUILD_TYPE` (default `Release`): Sets the build type to use.
+
+* The following environment variables are boolean flags. Set to `true` to enable or `false` to disable:
+
+ * `DEVEL` (default FALSE): Disable Qt update checker
+ * `USE_WEBENGINE` (default FALSE): Enable Qt WebEngine
+ * `USE_MULTIMEDIA` (default TRUE): Enable Qt Multimedia
+ * `BUNDLE_QT` (default FALSE): Use bundled Qt
+
+ * Note that using **system Qt** requires you to include the Qt CMake directory in `CMAKE_PREFIX_PATH`
+ * `.ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6`
+
+* After building, a zip can be packaged via `.ci/windows/package.sh`. You must have 7-zip installed and in your PATH.
+ * The resulting zip will be placed into `artifacts` in the source directory.
-After building, a zip can be packaged via `.ci/windows/package.sh`. Note that you must have 7-zip installed and in your PATH. The resulting zip will be placed into `artifacts` in the source directory.
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 9f89cfc1f5..aba5451b6d 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -54,36 +54,27 @@ endif()
# Glad
add_subdirectory(glad)
-# mbedtls
-AddJsonPackage(mbedtls)
-
-if (mbedtls_ADDED)
- target_include_directories(mbedtls PUBLIC ${mbedtls_SOURCE_DIR}/include)
-
- if (NOT MSVC)
- target_compile_options(mbedcrypto PRIVATE
- -Wno-unused-but-set-variable
- -Wno-string-concatenation
- )
- elseif(CXX_CLANG)
- foreach(TARGET mbedtls mbedcrypto mbedx509)
- target_compile_options(${TARGET} PRIVATE
- -w
- )
- endforeach()
- endif()
-endif()
-
# libusb
if (ENABLE_LIBUSB)
add_subdirectory(libusb)
endif()
-# Sirit
-# TODO(crueter): spirv-tools doesn't work w/ system
-set(SPIRV_WERROR OFF)
-AddJsonPackage(spirv-headers)
+# VMA
+AddJsonPackage(vulkan-memory-allocator)
+if (VulkanMemoryAllocator_ADDED)
+ if (CXX_CLANG)
+ target_compile_options(VulkanMemoryAllocator INTERFACE
+ -Wno-unused-variable
+ )
+ elseif(MSVC)
+ target_compile_options(VulkanMemoryAllocator INTERFACE
+ /wd4189
+ )
+ endif()
+endif()
+
+# Sirit
AddJsonPackage(sirit)
if(MSVC AND USE_CCACHE AND sirit_ADDED)
@@ -117,46 +108,9 @@ if (YUZU_USE_BUNDLED_FFMPEG)
set(FFmpeg_INCLUDE_DIR "${FFmpeg_INCLUDE_DIR}" PARENT_SCOPE)
endif()
-# VulkanUtilityHeaders - pulls in headers and utility libs
-AddJsonPackage(
- NAME vulkan-utility-headers
- BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_UTILITY_LIBRARIES}
-)
-
-# small hack
-if (NOT VulkanUtilityLibraries_ADDED)
- find_package(VulkanHeaders 1.3.274 REQUIRED)
-endif()
-
-# SPIRV Tools
-AddJsonPackage(
- NAME spirv-tools
- BUNDLED_PACKAGE ${YUZU_USE_EXTERNAL_VULKAN_SPIRV_TOOLS}
-)
-
-if (SPIRV-Tools_ADDED)
- add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools-static)
- target_link_libraries(SPIRV-Tools-static PRIVATE SPIRV-Tools-opt SPIRV-Tools-link)
-endif()
-
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
-# VMA
-AddJsonPackage(vulkan-memory-allocator)
-
-if (VulkanMemoryAllocator_ADDED)
- if (CXX_CLANG)
- target_compile_options(VulkanMemoryAllocator INTERFACE
- -Wno-unused-variable
- )
- elseif(MSVC)
- target_compile_options(VulkanMemoryAllocator INTERFACE
- /wd4189
- )
- endif()
-endif()
-
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
diff --git a/externals/cpmfile.json b/externals/cpmfile.json
index 65f50ffdfc..283da76743 100644
--- a/externals/cpmfile.json
+++ b/externals/cpmfile.json
@@ -1,15 +1,10 @@
{
- "mbedtls": {
- "repo": "eden-emulator/mbedtls",
- "sha": "ce4f81f4a9",
- "hash": "f2e7f887651b28745e508149214d409fd7cfdb92cb94b4146b47ff1e0fc09e47143f203ac18e34c2c1814b5bd031d04c74828676c0d4342920a2ddb7fd35e9a5",
- "find_args": "MODULE"
- },
- "spirv-headers": {
- "package": "SPIRV-Headers",
- "repo": "KhronosGroup/SPIRV-Headers",
- "sha": "4e209d3d7e",
- "hash": "f48bbe18341ed55ea0fe280dbbbc0a44bf222278de6e716e143ca1e95ca320b06d4d23d6583fbf8d03e1428f3dac8fa00e5b82ddcd6b425e6236d85af09550a4"
+ "vulkan-memory-allocator": {
+ "package": "VulkanMemoryAllocator",
+ "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
+ "sha": "1076b348ab",
+ "hash": "a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772",
+ "find_args": "CONFIG"
},
"sirit": {
"repo": "eden-emulator/sirit",
@@ -35,31 +30,6 @@
"CPP_JWT_USE_VENDORED_NLOHMANN_JSON OFF"
]
},
- "vulkan-utility-headers": {
- "package": "VulkanUtilityLibraries",
- "repo": "scripts/VulkanUtilityHeaders",
- "tag": "1.4.326",
- "artifact": "VulkanUtilityHeaders.tar.zst",
- "git_host": "git.crueter.xyz",
- "hash": "5924629755cb1605c4aa4eee20ef7957a9dd8d61e4df548be656d98054f2730c4109693c1bd35811f401f4705d2ccff9fc849be32b0d8480bc3f73541a5e0964"
- },
- "vulkan-memory-allocator": {
- "package": "VulkanMemoryAllocator",
- "repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
- "sha": "1076b348ab",
- "hash": "a46b44e4286d08cffda058e856c47f44c7fed3da55fe9555976eb3907fdcc20ead0b1860b0c38319cda01dbf9b1aa5d4b4038c7f1f8fbd97283d837fa9af9772",
- "find_args": "CONFIG"
- },
- "spirv-tools": {
- "package": "SPIRV-Tools",
- "repo": "KhronosGroup/SPIRV-Tools",
- "sha": "40eb301f32",
- "hash": "58d0fb1047d69373cf24c73e6f78c73a72a6cca3b4df1d9f083b9dcc0962745ef154abf3dbe9b3623b835be20c6ec769431cf11733349f45e7568b3525f707aa",
- "find_args": "MODULE",
- "options": [
- "SPIRV_SKIP_EXECUTABLES ON"
- ]
- },
"xbyak_sun": {
"package": "xbyak",
"repo": "herumi/xbyak",
diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt
index 0a20ca94b8..4bf2421c53 100644
--- a/externals/libusb/CMakeLists.txt
+++ b/externals/libusb/CMakeLists.txt
@@ -1,14 +1,29 @@
+# SPDX-FileCopyrightText: 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
include(CPMUtil)
-AddJsonPackage(libusb)
+# we love our libraries don't we folks
+if (PLATFORM_SUN)
+ set(libusb_bundled ON)
+else()
+ set(libusb_bundled OFF)
+endif()
+
+# TODO(crueter): Fix on Solaris
+AddJsonPackage(
+ NAME libusb
+ BUNDLED_PACKAGE ${libusb_bundled}
+)
if (NOT libusb_ADDED)
return()
endif()
+# TODO: *BSD fails to compile--may need different configs/symbols
if (MINGW OR PLATFORM_LINUX OR APPLE)
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index eb66e55964..184b049d06 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -234,6 +234,8 @@ if (YUZU_ROOM_STANDALONE)
endif()
if (ENABLE_QT)
+ add_definitions(-DYUZU_QT_WIDGETS)
+ add_subdirectory(qt_common)
add_subdirectory(yuzu)
endif()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 98f342c274..9ea2a9ee17 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -36,17 +36,18 @@ import androidx.core.net.toUri
import androidx.core.content.edit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.databinding.CardGameGridCompactBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
-import org.yuzu.yuzu_emu.utils.NativeConfig
class GameAdapter(private val activity: AppCompatActivity) :
AbstractDiffAdapter(exact = false) {
companion object {
const val VIEW_TYPE_GRID = 0
- const val VIEW_TYPE_LIST = 1
- const val VIEW_TYPE_CAROUSEL = 2
+ const val VIEW_TYPE_GRID_COMPACT = 1
+ const val VIEW_TYPE_LIST = 2
+ const val VIEW_TYPE_CAROUSEL = 3
}
private var viewType = 0
@@ -80,6 +81,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
listBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
listBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
+
VIEW_TYPE_GRID -> {
val gridBinding = holder.binding as CardGameGridBinding
gridBinding.cardGameGrid.scaleX = 1f
@@ -89,6 +91,17 @@ class GameAdapter(private val activity: AppCompatActivity) :
gridBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
gridBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
+
+ VIEW_TYPE_GRID_COMPACT -> {
+ val gridCompactBinding = holder.binding as CardGameGridCompactBinding
+ gridCompactBinding.cardGameGridCompact.scaleX = 1f
+ gridCompactBinding.cardGameGridCompact.scaleY = 1f
+ gridCompactBinding.cardGameGridCompact.alpha = 1f
+ // Reset layout params to XML defaults (same as normal grid)
+ gridCompactBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ gridCompactBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
+ }
+
VIEW_TYPE_CAROUSEL -> {
val carouselBinding = holder.binding as CardGameCarouselBinding
// soothens transient flickering
@@ -105,16 +118,25 @@ class GameAdapter(private val activity: AppCompatActivity) :
parent,
false
)
+
VIEW_TYPE_GRID -> CardGameGridBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
+
+ VIEW_TYPE_GRID_COMPACT -> CardGameGridCompactBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+
VIEW_TYPE_CAROUSEL -> CardGameCarouselBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
+
else -> throw IllegalArgumentException("Invalid view type")
}
return GameViewHolder(binding, viewType)
@@ -130,6 +152,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
VIEW_TYPE_LIST -> bindListView(model)
VIEW_TYPE_GRID -> bindGridView(model)
VIEW_TYPE_CAROUSEL -> bindCarouselView(model)
+ VIEW_TYPE_GRID_COMPACT -> bindGridCompactView(model)
}
}
@@ -168,6 +191,23 @@ class GameAdapter(private val activity: AppCompatActivity) :
gridBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
+ private fun bindGridCompactView(model: Game) {
+ val gridCompactBinding = binding as CardGameGridCompactBinding
+
+ gridCompactBinding.imageGameScreenCompact.scaleType = ImageView.ScaleType.CENTER_CROP
+ GameIconUtils.loadGameIcon(model, gridCompactBinding.imageGameScreenCompact)
+
+ gridCompactBinding.textGameTitleCompact.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
+
+ gridCompactBinding.textGameTitleCompact.marquee()
+ gridCompactBinding.cardGameGridCompact.setOnClickListener { onClick(model) }
+ gridCompactBinding.cardGameGridCompact.setOnLongClickListener { onLongClick(model) }
+
+ // Reset layout params to XML defaults (same as normal grid)
+ gridCompactBinding.root.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
+ gridCompactBinding.root.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
+ }
+
private fun bindCarouselView(model: Game) {
val carouselBinding = binding as CardGameCarouselBinding
@@ -232,8 +272,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
binding.root.findNavController().navigate(action)
}
- val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
if (NativeLibrary.gameRequiresFirmware(game.programId) && !NativeLibrary.isFirmwareAvailable()) {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.loader_requires_firmware)
@@ -248,23 +286,6 @@ class GameAdapter(private val activity: AppCompatActivity) :
}
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
- } else if (BooleanSetting.DISABLE_NCA_VERIFICATION.getBoolean(false) && !preferences.getBoolean(
- Settings.PREF_HIDE_NCA_POPUP, false)) {
- MaterialAlertDialogBuilder(activity)
- .setTitle(R.string.nca_verification_disabled)
- .setMessage(activity.getString(R.string.nca_verification_disabled_description))
- .setPositiveButton(android.R.string.ok) { _, _ ->
- launch()
- }
- .setNeutralButton(R.string.dont_show_again) { _, _ ->
- preferences.edit {
- putBoolean(Settings.PREF_HIDE_NCA_POPUP, true)
- }
-
- launch()
- }
- .setNegativeButton(android.R.string.cancel) { _, _ -> }
- .show()
} else {
launch()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 6d4bfd97ac..3c5b9003de 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -35,7 +35,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_SAMPLE_SHADING("sample_shading"),
PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"),
- DISABLE_NCA_VERIFICATION("disable_nca_verification"),
BLACK_BACKGROUNDS("black_backgrounds"),
JOYSTICK_REL_CENTER("joystick_rel_center"),
DPAD_SLIDE("dpad_slide"),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 2564849ef4..a52f582031 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -37,7 +37,6 @@ object Settings {
const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning"
const val PREF_SHOULD_SHOW_EDENS_VEIL_DIALOG = "ShouldShowEdensVeilDialog"
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
- const val PREF_HIDE_NCA_POPUP = "HideNCAVerificationPopup"
const val SECTION_STATS_OVERLAY = "Stats Overlay"
// Deprecated input overlay preference keys
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 883d8efaef..1f2ba81a73 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -297,13 +297,6 @@ abstract class SettingsItem(
descriptionId = R.string.use_custom_rtc_description
)
)
- put(
- SwitchSetting(
- BooleanSetting.DISABLE_NCA_VERIFICATION,
- titleId = R.string.disable_nca_verification,
- descriptionId = R.string.disable_nca_verification_description
- )
- )
put(
StringInputSetting(
StringSetting.WEB_TOKEN,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 630bcb0d74..14d62ceec3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -210,7 +210,6 @@ class SettingsFragmentPresenter(
add(IntSetting.LANGUAGE_INDEX.key)
add(BooleanSetting.USE_CUSTOM_RTC.key)
add(LongSetting.CUSTOM_RTC.key)
- add(BooleanSetting.DISABLE_NCA_VERIFICATION.key)
add(HeaderSetting(R.string.network))
add(StringSetting.WEB_TOKEN.key)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 03fa1a3a2e..80055628e1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -194,6 +194,10 @@ class GamesFragment : Fragment() {
val columns = resources.getInteger(R.integer.game_columns_grid)
GridLayoutManager(context, columns)
}
+ GameAdapter.VIEW_TYPE_GRID_COMPACT -> {
+ val columns = resources.getInteger(R.integer.game_columns_grid)
+ GridLayoutManager(context, columns)
+ }
GameAdapter.VIEW_TYPE_LIST -> {
val columns = resources.getInteger(R.integer.game_columns_list)
GridLayoutManager(context, columns)
@@ -300,6 +304,7 @@ class GamesFragment : Fragment() {
val currentViewType = getCurrentViewType()
when (currentViewType) {
GameAdapter.VIEW_TYPE_LIST -> popup.menu.findItem(R.id.view_list).isChecked = true
+ GameAdapter.VIEW_TYPE_GRID_COMPACT -> popup.menu.findItem(R.id.view_grid_compact).isChecked = true
GameAdapter.VIEW_TYPE_GRID -> popup.menu.findItem(R.id.view_grid).isChecked = true
GameAdapter.VIEW_TYPE_CAROUSEL -> popup.menu.findItem(R.id.view_carousel).isChecked = true
}
@@ -314,6 +319,14 @@ class GamesFragment : Fragment() {
true
}
+ R.id.view_grid_compact -> {
+ if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) onPause()
+ setCurrentViewType(GameAdapter.VIEW_TYPE_GRID_COMPACT)
+ applyGridGamesBinding()
+ item.isChecked = true
+ true
+ }
+
R.id.view_list -> {
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) onPause()
setCurrentViewType(GameAdapter.VIEW_TYPE_LIST)
diff --git a/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml b/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml
new file mode 100644
index 0000000000..f74cfa0d05
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/gradient_overlay_bottom.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/src/android/app/src/main/res/layout/card_game_grid.xml b/src/android/app/src/main/res/layout/card_game_grid.xml
index 3e0bf6005c..f03a77c4f1 100644
--- a/src/android/app/src/main/res/layout/card_game_grid.xml
+++ b/src/android/app/src/main/res/layout/card_game_grid.xml
@@ -5,27 +5,32 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
- android:focusableInTouchMode="false">
+ android:focusableInTouchMode="false"
+ android:padding="4dp">
+ app:cardCornerRadius="16dp"
+ android:foreground="@color/eden_border_gradient_start">
+ android:paddingTop="14dp"
+ android:paddingLeft="6dp"
+ android:paddingRight="6dp"
+ android:paddingBottom="6dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/menu/menu_game_views.xml b/src/android/app/src/main/res/menu/menu_game_views.xml
index 241cdcb4b5..11bf3e695e 100644
--- a/src/android/app/src/main/res/menu/menu_game_views.xml
+++ b/src/android/app/src/main/res/menu/menu_game_views.xml
@@ -4,6 +4,9 @@
+
- يتخطى بعض عمليات إبطال ذاكرة التخزين المؤقت أثناء تحديثات الذاكرة، مما يقلل استخدام المعالج ويحسن أدائه. قد يسبب هذا أعطالاً أو تعطلًا في بعض الألعاب.
تمكين محاكاة MMU المضيف
يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.
- مستوى DMA
- يتحكم في دقة تحديد مستوى DMA. الدقة الأعلى يمكنها إصلاح بعض المشاكل في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا كنت غير متأكد، اتركه على الوضع الافتراضي.
+ دقة DMA
+ يتحكم في دقة تحديد DMA. يمكن أن تصلح الدقة الآمنة المشاكل في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا كنت غير متأكد، اترك هذا على الوضع الافتراضي.
4 جيجابايت (موصى به)
@@ -498,8 +498,6 @@
ساعة مخصصة في الوقت الحقيقي
يسمح لك بتعيين ساعة مخصصة في الوقت الفعلي منفصلة عن وقت النظام الحالي لديك
تعيين ساعة مخصصة في الوقت الحقيقي
- تعطيل التحقق من NCA
- يعطل التحقق من سلامة أرشيفات محتوى NCA. قد يحسن هذا من سرعة التحميل لكنه يخاطر بتلف البيانات أو تمرير ملفات غير صالحة دون اكتشاف. ضروري لجعل الألعاب والتحديثات التي تتطلب نظامًا أساسيًا 20+ تعمل.
توليد
@@ -794,9 +792,8 @@
افتراضي
- عادي
- عالي
- مفرط
+ غير آمن (سريع)
+ آمن (مستقر)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml
index af0eeeaa45..fe94f97dc5 100644
--- a/src/android/app/src/main/res/values-ckb/strings.xml
+++ b/src/android/app/src/main/res/values-ckb/strings.xml
@@ -128,8 +128,8 @@
هەندێک لە بازنەکردنەکانی هەڵگر لە کاتی نوێکردنەوەی بیرگە دەنێرێت، کەمکردنەوەی بەکارهێنانی CPU و باشترکردنی کارایی. لەوانەیە لە هەندێک یاری کێشە درووست بکات.
چالاککردنی میمیکردنی MMU میواندە
ئەم باشکردنە خێرایی دەستکەوتنی بیرگە لەلایەن پرۆگرامی میوانەکە زیاد دەکات. چالاککردنی وای لێدەکات کە خوێندنەوە/نووسینەکانی بیرگەی میوانەکە ڕاستەوخۆ لە بیرگە ئەنجام بدرێت و میمیکردنی MMU میواندە بەکاربهێنێت. ناچالاککردنی ئەمە هەموو دەستکەوتنەکانی بیرگە ڕەت دەکاتەوە لە بەکارهێنانی میمیکردنی MMU نەرمەکاڵا.
- ئاستی DMA
- کۆنتڕۆڵی وردی ڕێکخستنی DMA دەکات. وردی زیاتر دەتوانێ هەندێک کێشە لە هەندێک یاری چارەسەر بکات، بەڵام لە هەندێک حاڵەتدا کاریگەری لەسەر کارایی هەیە. ئەگەر دڵنیا نیت، بە ڕێکخستنی بنەڕەتی بێڵە.
+ وردیی DMA
+ کۆنتڕۆڵی وردیی وردیی DMA دەکات. وردییی پارێزراو دەتوانێت کێشەکان لە هەندێک یاری چارەسەر بکات، بەڵام لە هەندێک حاڵەتدا کاریگەری لەسەر کارایی هەیە. ئەگەر دڵنیا نیت، ئەمە بە سەر ڕەھەوادا بهێڵە.
4GB (پێشنیارکراو)
6GB (نائاسایش)
@@ -482,8 +482,6 @@
RTCی تایبەتمەند
ڕێگەت پێدەدات کاتژمێرێکی کاتی ڕاستەقینەی تایبەتمەند دابنێیت کە جیاوازە لە کاتی ئێستای سیستەمەکەت.
دانانی RTCی تایبەتمەند
- ناچالاککردنی پشکنینی NCA
- پشکنینی پێکهاتەی ئارشیڤەکانی ناوەڕۆکی NCA ناچالاک دەکات. ئەمە لەوانەیە خێرایی بارکردن بهرهوپێش ببات، بەڵام مەترسی لەناوچوونی داتا یان ئەوەی فایلە نادروستەکان بەبێ ئەوەی دۆزرایەوە تێپەڕبن زیاتر دەکات. بۆ ئەوەی یاری و نوێکردنەوەکان کار بکەن کە پێویستی بە فریموێری 20+ هەیە زۆر پێویستە.
بەرهەم هێنان
@@ -763,9 +761,8 @@
بنەڕەتی
- ئاسایی
- بەرز
- زۆر بەرز
+ نەپارێزراو (خێرا)
+ پارێزراو (جێگیر)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml
index 8d42b8303f..785f96b84c 100644
--- a/src/android/app/src/main/res/values-cs/strings.xml
+++ b/src/android/app/src/main/res/values-cs/strings.xml
@@ -127,8 +127,8 @@
Přeskočí některé invalidace mezipaměti na straně CPU během aktualizací paměti, čímž sníží zatížení CPU a zlepší jeho výkon. Může způsobit chyby nebo pády v některých hrách.
Povolit emulaci hostitelské MMU
Tato optimalizace zrychluje přístup do paměti hostovaného programu. Její povolení způsobí, že čtení a zápisy do paměti hosta se provádějí přímo v paměti a využívají hostitelskou MMU. Zakázání této funkce vynutí použití softwarové emulace MMU pro všechny přístupy do paměti.
- Úroveň DMA
- Ovládá přesnost DMA. Vyšší přesnost může opravit problémy v některých hrách, ale může také ovlivnit výkon. Pokud si nejste jisti, ponechejte výchozí nastavení.
+ Přesnost DMA
+ Ovládá přesnost DMA. Bezpečná přesnost může opravit problémy v některých hrách, ale v některých případech může také ovlivnit výkon. Pokud si nejste jisti, ponechte to na výchozím nastavení.
4GB (Doporučeno)
6GB (Nebezpečné)
@@ -458,8 +458,6 @@
Vlastní RTC
Vlastní nastavení času
Nastavit vlastní RTC
- Zakázat ověřování NCA
- Zakáže ověřování integrity archivů obsahu NCA. To může zlepšit rychlost načítání, ale hrozí poškození dat nebo neodhalení neplatných souborů. Je nutné, aby fungovaly hry a aktualizace vyžadující firmware 20+.
Generovat
@@ -737,9 +735,8 @@
Výchozí
- Normální
- Vysoká
- Extrémní
+ Nebezpečné (rychlé)
+ Bezpečné (stabilní)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 907b114388..495804e328 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -128,8 +128,8 @@
Überspringt bestimmte Cache-Invalidierungen auf CPU-Seite während Speicherupdates, reduziert die CPU-Auslastung und verbessert die Leistung. Kann in einigen Spielen zu Fehlern oder Abstürzen führen.
Host-MMU-Emulation aktivieren
Diese Optimierung beschleunigt Speicherzugriffe durch das Gastprogramm. Wenn aktiviert, erfolgen Speicherlese- und -schreibvorgänge des Gastes direkt im Speicher und nutzen die MMU des Hosts. Das Deaktivieren erzwingt die Verwendung der Software-MMU-Emulation für alle Speicherzugriffe.
- DMA-Level
- Steuert die DMA-Präzisionsgenauigkeit. Eine höhere Präzision kann Probleme in einigen Spielen beheben, kann aber in einigen Fällen auch die Leistung beeinträchtigen. Im Zweifel auf „Standard“ belassen.
+ DMA-Genauigkeit
+ Steuert die DMA-Präzisionsgenauigkeit. Sichere Präzision kann Probleme in einigen Spielen beheben, kann aber in einigen Fällen auch die Leistung beeinträchtigen. Im Zweifel lassen Sie dies auf Standard stehen.
4 GB (Empfohlen)
6 GB (Unsicher)
@@ -486,8 +486,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
RTC-Datum auswählen
RTC-Zeit auswählen
Benutzerdefinierte Echtzeituhr
- NCA-Verifizierung deaktivieren
- Deaktiviert die Integritätsprüfung von NCA-Inhaltsarchiven. Dies kann die Ladegeschwindigkeit verbessern, riskiert jedoch Datenbeschädigung oder dass ungültige Dateien unentdeckt bleiben. Ist notwendig, um Spiele und Updates, die Firmware 20+ benötigen, zum Laufen zu bringen.
Generieren
@@ -829,9 +827,8 @@ Wirklich fortfahren?
Standard
- Normal
- Hoch
- Extrem
+ Unsicher (schnell)
+ Sicher (stabil)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 12b7183cae..2ee0e1783a 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -128,8 +128,8 @@
Omite ciertas invalidaciones de caché durante actualizaciones de memoria, reduciendo el uso de CPU y mejorando su rendimiento. Puede causar fallos en algunos juegos.
Habilitar emulación de MMU del host
Esta optimización acelera los accesos a la memoria por parte del programa invitado. Al habilitarla, las lecturas/escrituras de memoria del invitado se realizan directamente en la memoria y utilizan la MMU del host. Deshabilitar esto obliga a que todos los accesos a la memoria utilicen la emulación de MMU por software.
- Nivel de DMA
- Controla la precisión del DMA. Una mayor precisión puede solucionar problemas en algunos juegos, pero también puede afectar el rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.
+ Precisión de DMA
+ Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.
4GB (Recomendado)
6GB (Inseguro)
@@ -506,8 +506,6 @@
RTC personalizado
Te permite tener un reloj personalizado en tiempo real diferente del tiempo del propio sistema.
Configurar RTC personalizado
- Desactivar verificación NCA
- Desactiva la verificación de integridad de los archivos de contenido NCA. Esto puede mejorar la velocidad de carga, pero arriesga corrupción de datos o que archivos inválidos pasen desapercibidos. Es necesario para que funcionen juegos y actualizaciones que requieren firmware 20+.
Generar
@@ -872,9 +870,8 @@
Predeterminado
- Normal
- Alto
- Extremo
+ Inseguro (rápido)
+ Seguro (estable)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml
index d7ac1b770a..79cf5f49e6 100644
--- a/src/android/app/src/main/res/values-fa/strings.xml
+++ b/src/android/app/src/main/res/values-fa/strings.xml
@@ -128,8 +128,8 @@
بعضی ابطالهای حافظه نهان در هنگام بهروزرسانیهای حافظه را رد میکند، استفاده از CPU را کاهش داده و عملکرد آن را بهبود میبخشد. ممکن است در برخی بازیها باعث مشکلات یا خرابی شود.
فعالسازی شبیهسازی MMU میزبان
این بهینهسازی دسترسیهای حافظه توسط برنامه میهمان را تسریع میکند. فعالسازی آن باعث میشود خواندن/نوشتن حافظه میهمان مستقیماً در حافظه انجام شود و از MMU میزبان استفاده کند. غیرفعال کردن این قابلیت، همه دسترسیهای حافظه را مجبور به استفاده از شبیهسازی نرمافزاری MMU میکند.
- سطح DMA
- دقت صحت DMA را کنترل می کند. دقت بالاتر می تواند مشکلات برخی بازی ها را برطرف کند، اما در برخی موارد نیز می تواند بر عملکرد تأثیر بگذارد. اگر مطمئن نیستید، آن را روی پیش فرض بگذارید.
+ دقت DMA
+ دقت صحت DMA را کنترل می کند. دقت ایمن می تواند مشکلات برخی بازی ها را برطرف کند، اما در برخی موارد نیز ممکن است بر عملکرد تأثیر بگذارد. اگر مطمئن نیستید، این گزینه را روی پیش فرض بگذارید.
4 گیگابایت (توصیه شده)
6 گیگابایت (ناامن)
@@ -504,8 +504,6 @@
زمان سفارشی
به شما امکان میدهد یک ساعت سفارشی جدا از زمان فعلی سیستم خود تنظیم کنید.
تنظیم زمان سفارشی
- غیرفعال کردن تأیید اعتبار NCA
- بررسی صحت آرشیوهای محتوای NCA را غیرفعال میکند. این ممکن است سرعت بارگذاری را بهبود بخشد اما خطر خرابی داده یا تشخیص داده نشدن فایلهای نامعتبر را به همراه دارد. برای کار کردن بازیها و بهروزرسانیهایی که به فرمور ۲۰+ نیاز دارند، ضروری است.
تولید
@@ -871,9 +869,8 @@
پیش فرض
- معمولی
- بالا
- فوق العاده
+ ناایمن (سریع)
+ ایمن (پایدار)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index 62e67a6fee..e9df08a4de 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -128,8 +128,8 @@
Ignore certaines invalidations de cache côté CPU lors des mises à jour mémoire, réduisant l\'utilisation du CPU et améliorant ses performances. Peut causer des bugs ou plantages sur certains jeux.
Activer l\'émulation de la MMU hôte
Cette optimisation accélère les accès mémoire par le programme invité. L\'activer entraîne que les lectures/écritures mémoire de l\'invité sont effectuées directement en mémoire et utilisent la MMU de l\'hôte. Désactiver cela force tous les accès mémoire à utiliser l\'émulation logicielle de la MMU.
- Niveau DMA
- Contrôle la précision du DMA. Une précision plus élevée peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez-la sur Défaut.
+ Précision DMA
+ Contrôle la précision du DMA. Une précision sûre peut résoudre les problèmes dans certains jeux, mais peut aussi affecter les performances dans certains cas. Si vous n\'êtes pas sûr, laissez ce paramètre sur Par défaut.
4 Go (Recommandé)
6 Go (Dangereux)
@@ -506,8 +506,6 @@
RTC personnalisé
Vous permet de définir une horloge en temps réel personnalisée distincte de l\'heure actuelle de votre système.
Définir l\'horloge RTC personnalisée
- Désactiver la vérification NCA
- Désactive la vérification d\'intégrité des archives de contenu NCA. Cela peut améliorer la vitesse de chargement mais risque une corruption des données ou que des fichiers invalides ne soient pas détectés. Est nécessaire pour faire fonctionner les jeux et mises à jour nécessitant un firmware 20+.
Générer
@@ -920,9 +918,8 @@
Défaut
- Normal
- Élevé
- Extrême
+ Dangereux (rapide)
+ Sûr (stable)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml
index ec5b526ce0..4e1624a556 100644
--- a/src/android/app/src/main/res/values-he/strings.xml
+++ b/src/android/app/src/main/res/values-he/strings.xml
@@ -129,8 +129,8 @@
מדלג על איפוסי מטמון מסוימים במהלך עדכוני זיכרון, מפחית שימוש במעבד ומשפר ביצועים. עלול לגרום לתקלות או קריסות בחלק מהמשחקים.
הפעל אמולציית MMU מארח
אופטימיזציה זו מאיצה את גישת הזיכרון על ידי התוכנית האורחת. הפעלתה גורמת לכך שפעולות קריאה/כתיבה לזיכרון האורח מתבצעות ישירות לזיכרון ומשתמשות ב-MMU של המארח. השבתת זאת מאלצת את כל גישות הזיכרון להשתמש באמולציית MMU תוכנתית.
- רמת DMA
- שולטת בדיוק הדיוק של DMA. דיוק גבוה יותר יכול לתקן בעיות בחלק מהמשחקים, אך הוא עלול גם להשפיע על הביצועים במקרים מסוימים. אם אינך בטוח, השאר ברירת מחדל.
+ דיוק DMA
+ שולט בדיוק הדיוק של DMA. דיוק בטוח יכול לתקן בעיות בחלק מהמשחקים, אך הוא עלול גם להשפיע על הביצועים במקרים מסוימים. אם אינך בטוח, השאר זאת על ברירת מחדל.
4GB (מומלץ)
6GB (לא בטוח)
@@ -505,8 +505,6 @@
RTC מותאם אישית
מאפשר לך לקבוע שעון זמן אמת נפרד משעון המערכת שלך.
קבע RTC מותאם אישית
- השבת אימות NCA
- משבית את אימות השלמות של ארכיוני התוכן של NCA. זה עשוי לשפר את מהירות הטעינה אך מסתכן בשחיקת נתונים או שמא קבצים לא חוקיים יעברו ללא זיהוי. זה הכרחי כדי לגרום למשחקים ועדכונים הדורשים firmware 20+ לעבוד.
יצירה
@@ -802,9 +800,8 @@
ברירת מחדל
- רגיל
- גבוה
- קיצוני
+ לא בטוח (מהיר)
+ בטוח (יציב)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml
index b45692f147..061ac07388 100644
--- a/src/android/app/src/main/res/values-hu/strings.xml
+++ b/src/android/app/src/main/res/values-hu/strings.xml
@@ -128,8 +128,8 @@
Kihagy néhány CPU-oldali gyorsítótár-érvénytelenítést memóriafrissítések közben, csökkentve a CPU használatát és javítva a teljesítményt. Néhány játékban hibákat vagy összeomlást okozhat.
Gazda MMU emuláció engedélyezése
Ez az optimalizáció gyorsítja a vendégprogram memória-hozzáférését. Engedélyezése esetén a vendég memóriaolvasási/írási műveletei közvetlenül a memóriában történnek, és kihasználják a gazda MMU-ját. Letiltás esetén minden memória-hozzáférés a szoftveres MMU emulációt használja.
- DMA szint
- Szabályozza a DMA pontosságát. A magasabb pontosság megoldhat néhány játék problémáit, de bizonyos esetekben befolyásolhatja a teljesítményt. Ha bizonytalan, hagyja Alapértelmezett beállításnál.
+ DMA pontosság
+ Szabályozza a DMA pontosságát. A biztonságos pontosság megoldhat néhány játék problémáit, de bizonyos esetekben befolyásolhatja a teljesítményt. Ha bizonytalan, hagyja Alapértelmezett beállításon.
4GB (Ajánlott)
6GB (Nem biztonságos)
@@ -501,8 +501,6 @@
Egyéni RTC
Megadhatsz egy valós idejű órát, amely eltér a rendszer által használt órától.
Egyéni RTC beállítása
- NCA ellenőrzés letiltása
- Letiltja az NCA tartalomarchívumok integritás-ellenőrzését. Ez javíthatja a betöltési sebességet, de az adatsérülés vagy az érvénytelen fájlok észrevétlen maradásának kockázatával jár. Elengedhetetlen a 20+ firmware-et igénylő játékok és frissítések működtetéséhez.
Generálás
@@ -909,9 +907,8 @@
Alapértelmezett
- Normál
- Magas
- Extrém
+ Nem biztonságos (gyors)
+ Biztonságos (stabil)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml
index 1817fc654a..6e3b64953f 100644
--- a/src/android/app/src/main/res/values-id/strings.xml
+++ b/src/android/app/src/main/res/values-id/strings.xml
@@ -128,8 +128,8 @@
Melewati beberapa pembatalan cache sisi CPU selama pembaruan memori, mengurangi penggunaan CPU dan meningkatkan kinerjanya. Mungkin menyebabkan gangguan atau crash pada beberapa game.
Aktifkan Emulasi MMU Host
Optimasi ini mempercepat akses memori oleh program tamu. Mengaktifkannya menyebabkan pembacaan/penulisan memori tamu dilakukan langsung ke memori dan memanfaatkan MMU Host. Menonaktifkan ini memaksa semua akses memori menggunakan Emulasi MMU Perangkat Lunak.
- Level DMA
- Mengontrol akurasi presisi DMA. Presisi yang lebih tinggi dapat memperbaiki masalah di beberapa game, tetapi juga dapat memengaruhi performa dalam beberapa kasus. Jika tidak yakin, biarkan di Bawaan.
+ Akurasi DMA
+ Mengontrol keakuratan presisi DMA. Presisi aman dapat memperbaiki masalah di beberapa game, tetapi juga dapat memengaruhi kinerja dalam beberapa kasus. Jika tidak yakin, biarkan ini pada Bawaan.
4GB (Direkomendasikan)
6GB (Tidak Aman)
@@ -502,8 +502,6 @@
RTC Kustom
Memungkinkan Anda untuk mengatur jam waktu nyata kustom yang terpisah dari waktu sistem saat ini Anda.
Setel RTC Kustom
- Nonaktifkan Verifikasi NCA
- Menonaktifkan verifikasi integritas arsip konten NCA. Ini dapat meningkatkan kecepatan pemuatan tetapi berisiko kerusakan data atau file yang tidak valid tidak terdeteksi. Diperlukan untuk membuat game dan pembaruan yang membutuhkan firmware 20+ bekerja.
Hasilkan
@@ -864,9 +862,8 @@
Bawaan
- Normal
- Tinggi
- Ekstrem
+ Tidak Aman (cepat)
+ Aman (stabil)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index ff3706dd40..38a82b3c11 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -128,8 +128,8 @@
Salta alcuni invalidamenti della cache lato CPU durante gli aggiornamenti di memoria, riducendo l\'uso della CPU e migliorandone le prestazioni. Potrebbe causare glitch o crash in alcuni giochi.
Abilita emulazione MMU host
Questa ottimizzazione accelera gli accessi alla memoria da parte del programma guest. Abilitandola, le letture/scritture della memoria guest vengono eseguite direttamente in memoria e sfruttano la MMU host. Disabilitandola, tutti gli accessi alla memoria sono costretti a utilizzare l\'emulazione software della MMU.
- Livello DMA
- Controlla la precisione del DMA. Una precisione più alta può risolvere problemi in alcuni giochi, ma in alcuni casi può influire sulle prestazioni. Se non sei sicuro, lascia su Predefinito.
+ Precisione DMA
+ Controlla la precisione del DMA. La precisione sicura può risolvere problemi in alcuni giochi, ma in alcuni casi può anche influire sulle prestazioni. In caso di dubbi, lascia questo su Predefinito.
4GB (Consigliato)
6GB (Non sicuro)
@@ -505,8 +505,6 @@
RTC Personalizzato
Ti permette di impostare un orologio in tempo reale personalizzato, completamente separato da quello di sistema.
Imposta un orologio in tempo reale personalizzato
- Disabilita verifica NCA
- Disabilita la verifica dell\'integrità degli archivi di contenuto NCA. Può migliorare la velocità di caricamento ma rischia il danneggiamento dei dati o che file non validi passino inosservati. Necessario per far funzionare giochi e aggiornamenti che richiedono il firmware 20+.
Genera
@@ -833,9 +831,8 @@
Predefinito
- Normale
- Alto
- Estremo
+ Non sicuro (veloce)
+ Sicuro (stabile)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index a6c1ebf8ab..179601f182 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -128,8 +128,8 @@
メモリ更新時のCPU側キャッシュ無効化をスキップし、CPU使用率を減らして性能を向上させます。一部のゲームで不具合やクラッシュが発生する可能性があります。
ホストMMUエミュレーションを有効化
この最適化により、ゲストプログラムによるメモリアクセスが高速化されます。有効にすると、ゲストのメモリ読み書きが直接メモリ内で実行され、ホストのMMUを利用します。無効にすると、すべてのメモリアクセスでソフトウェアMMUエミュレーションが使用されます。
- DMAレベル
- DMAの精度を制御します。精度を高くすると一部のゲームの問題が修正される場合がありますが、場合によってはパフォーマンスに影響を与える可能性もあります。不明な場合は、デフォルトのままにしてください。
+ DMA精度
+ DMAの精度を制御します。安全な精度は一部のゲームの問題を修正できる場合がありますが、場合によってはパフォーマンスに影響を与える可能性もあります。不明な場合は、これをデフォルトのままにしてください。
4GB (推奨)
6GB (安全でない)
@@ -491,8 +491,6 @@
カスタム RTC
現在のシステム時間とは別に、任意のリアルタイムクロックを設定できます。
カスタムRTCを設定
- NCA検証を無効化
- NCAコンテンツアーカイブの整合性検証を無効にします。読み込み速度が向上する可能性がありますが、データ破損や不正なファイルが検出されないリスクがあります。ファームウェア20以上が必要なゲームや更新を動作させるために必要です。
生成
@@ -792,9 +790,8 @@
デフォルト
- 標準
- 高
- 最高
+ 安全でない(高速)
+ 安全(安定)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index 01e2c5f4c0..6f4dd42af2 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -128,8 +128,8 @@
메모리 업데이트 시 일부 CPU 측 캐시 무효화를 건너뛰어 CPU 사용량을 줄이고 성능을 향상시킵니다. 일부 게임에서 오류 또는 충돌을 일으킬 수 있습니다.
호스트 MMU 에뮬레이션 사용
이 최적화는 게스트 프로그램의 메모리 접근 속도를 높입니다. 활성화하면 게스트의 메모리 읽기/쓰기가 메모리에서 직접 수행되고 호스트의 MMU를 활용합니다. 비활성화하면 모든 메모리 접근에 소프트웨어 MMU 에뮬레이션을 사용하게 됩니다.
- DMA 수준
- DMA 정밀도를 제어합니다. 높은 정밀도는 일부 게임의 문제를 해결할 수 있지만 경우에 따라 성능에 영향을 미칠 수도 있습니다. 확실하지 않다면 기본값으로 두세요.
+ DMA 정확도
+ DMA 정밀도 정확도를 제어합니다. 안전한 정밀도는 일부 게임의 문제를 해결할 수 있지만 경우에 따라 성능에 영향을 미칠 수도 있습니다. 확실하지 않은 경우 기본값으로 두십시오.
4GB (권장)
6GB (안전하지 않음)
@@ -501,8 +501,6 @@
사용자 지정 RTC
현재 시스템 시간과 별도로 사용자 지정 RTC를 설정할 수 있습니다.
사용자 지정 RTC 설정
- NCA 검증 비활성화
- NCA 콘텐츠 아카이브의 무결성 검증을 비활성화합니다. 로딩 속도를 향상시킬 수 있지만 데이터 손상이나 유효하지 않은 파일이 미검증될 위험이 있습니다. 펌웨어 20+가 필요한 게임 및 업데이트를 실행하려면 필요합니다.
생성
@@ -863,9 +861,8 @@
기본값
- 보통
- 높음
- 극단적
+ 안전하지 않음(빠름)
+ 안전함(안정적)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index 90c0dbf05e..7f0cffc7c4 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -128,8 +128,8 @@
Hopper over enkelte CPU-side cache-invalideringer under minneoppdateringer, reduserer CPU-bruk og forbedrer ytelsen. Kan forårsake feil eller krasj i noen spill.
Aktiver verts-MMU-emulering
Denne optimaliseringen fremskynder minnetilgang av gjesteprogrammet. Hvis aktivert, utføres gjestens minnelesing/skriving direkte i minnet og bruker vertens MMU. Deaktivering tvinger alle minnetilganger til å bruke programvarebasert MMU-emulering.
- DMA-nivå
- Styrer DMA-presisjonsnøyaktigheten. Høyere presisjon kan fikse problemer i noen spill, men kan også påvirke ytelsen i noen tilfeller. Hvis du er usikker, la den stå på Standard.
+ DMA-nøyaktighet
+ Kontrollerer DMA-presisjonsnøyaktigheten. Sikker presisjon kan fikse problemer i noen spill, men kan også påvirke ytelsen i noen tilfeller. Hvis du er usikker, la dette stå på Standard.
4GB (Anbefalt)
6GB (Usikkert)
@@ -482,8 +482,6 @@
Tilpasset Sannhetstidsklokke
Gjør det mulig å stille inn en egendefinert sanntidsklokke separat fra den gjeldende systemtiden.
Angi tilpasset RTC
- Deaktiver NCA-verifisering
- Deaktiverer integritetsverifisering av NCA-innholdsarkiv. Dette kan forbedre lastehastigheten, men medfører risiko for datakorrupsjon eller at ugyldige filer ikke oppdages. Er nødvendig for å få spill og oppdateringer som trenger firmware 20+ til å fungere.
Generer
@@ -773,9 +771,8 @@
Standard
- Normal
- Høy
- Ekstrem
+ Usikker (rask)
+ Sikker (stabil)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index 080064edaf..de9b8f47fc 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -128,8 +128,8 @@
Pomija niektóre unieważnienia pamięci podręcznej po stronie CPU podczas aktualizacji pamięci, zmniejszając użycie CPU i poprawiając jego wydajność. Może powodować błędy lub awarie w niektórych grach.
Włącz emulację MMU hosta
Ta optymalizacja przyspiesza dostęp do pamięci przez program gościa. Włączenie powoduje, że odczyty/zapisy pamięci gościa są wykonywane bezpośrednio w pamięci i wykorzystują MMU hosta. Wyłączenie wymusza użycie programowej emulacji MMU dla wszystkich dostępów do pamięci.
- Poziom DMA
- Kontroluje dokładność precyzji DMA. Wyższy poziom może naprawić problemy w niektórych grach, ale może również wpłynąć na wydajność. Jeśli nie jesteś pewien, pozostaw wartość «Domyślny».
+ Dokładność DMA
+ Kontroluje dokładność precyzji DMA. Bezpieczna precyzja może naprawić problemy w niektórych grach, ale w niektórych przypadkach może również wpłynąć na wydajność. Jeśli nie jesteś pewien, pozostaw wartość Domyślną.
4GB (Zalecane)
6GB (Niebezpieczne)
@@ -482,8 +482,6 @@
Niestandardowy RTC
Ta opcja pozwala na wybranie własnych ustawień czasu używanych w czasie emulacji, innych niż czas systemu Android.
Ustaw niestandardowy czas RTC
- Wyłącz weryfikację NCA
- Wyłącza weryfikację integralności archiwów zawartości NCA. Może to poprawić szybkość ładowania, ale niesie ryzyko uszkodzenia danych lub niezauważenia nieprawidłowych plików. Konieczne, aby działały gry i aktualizacje wymagające firmware\'u 20+.
Generuj
@@ -770,9 +768,8 @@
預設
- 普通
- 高
- 極高
+ Niezabezpieczone (szybkie)
+ Bezpieczne (stabilne)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index 3dc1f83e6e..eec3fdf715 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -128,8 +128,8 @@
Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo o uso da CPU e melhorando seu desempenho. Pode causar falhas ou travamentos em alguns jogos.
Ativar Emulação de MMU do Host
Esta otimização acelera os acessos à memória pelo programa convidado. Ativar isso faz com que as leituras/gravações de memória do convidado sejam feitas diretamente na memória e utilizem a MMU do Host. Desativar isso força todos os acessos à memória a usarem a Emulação de MMU por Software.
- Nível DMA
- Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode impactar o desempenho em alguns casos. Se não tiver certeza, deixe em Padrão.
+ Precisão de DMA
+ Controla a precisão do DMA. A precisão segura pode corrigir problemas em alguns jogos, mas também pode afetar o desempenho em alguns casos. Se não tiver certeza, deixe isso como Padrão.
4GB (Recomendado)
6GB (Inseguro)
@@ -506,8 +506,6 @@
Data e hora personalizadas
Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.
Definir um relógio em tempo real personalizado
- Desativar verificação NCA
- Desativa a verificação de integridade de arquivos de conteúdo NCA. Pode melhorar a velocidade de carregamento, mas arrisica corromper dados ou que arquivos inválidos passem despercebidos. É necessário para fazer jogos e atualizações que exigem firmware 20+ funcionarem.
Gerar
@@ -921,9 +919,8 @@ uma tentativa de mapeamento automático
Padrão
- Normal
- Alto
- Extremo
+ Inseguro (rápido)
+ Seguro (estável)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index feb4950fcb..d45bf4bfc9 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -128,8 +128,8 @@
Ignora algumas invalidações de cache do lado da CPU durante atualizações de memória, reduzindo a utilização da CPU e melhorando o desempenho. Pode causar falhas ou crashes em alguns jogos.
Ativar Emulação de MMU do Anfitrião
Esta otimização acelera os acessos à memória pelo programa convidado. Ativar faz com que as leituras/escritas de memória do convidado sejam efetuadas diretamente na memória e utilizem a MMU do Anfitrião. Desativar força todos os acessos à memória a usar a Emulação de MMU por Software.
- Nível DMA
- Controla a precisão do DMA. Maior precisão pode corrigir problemas em alguns jogos, mas também pode afetar o desempenho nalguns casos. Se não tiver a certeza, deixe em Predefinido.
+ Precisão da DMA
+ Controla a precisão da DMA. A precisão segura pode resolver problemas em alguns jogos, mas também pode afetar o desempenho nalguns casos. Se não tiver a certeza, deixe esta opção em Predefinido.
4GB (Recomendado)
6GB (Inseguro)
@@ -506,8 +506,6 @@
RTC personalizado
Permite a você configurar um relógio em tempo real separado do relógio do seu dispositivo.
Defina um relógio em tempo real personalizado
- Desativar verificação NCA
- Desativa a verificação de integridade dos arquivos de conteúdo NCA. Pode melhorar a velocidade de carregamento, mas arrisca a corrupção de dados ou que ficheiros inválidos passem despercebidos. É necessário para que jogos e atualizações que necessitam de firmware 20+ funcionem.
Gerar
@@ -921,9 +919,8 @@ uma tentativa de mapeamento automático
Predefinido
- Normal
- Alto
- Extremo
+ Inseguro (rápido)
+ Seguro (estável)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index d56c94f10b..2f7714257f 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -128,8 +128,8 @@
Пропускает некоторые инвалидации кэша на стороне ЦП при обновлениях памяти, уменьшая нагрузку на процессор и повышая производительность. Может вызывать сбои в некоторых играх.
Включить эмуляцию MMU хоста
Эта оптимизация ускоряет доступ к памяти гостевой программой. При включении операции чтения/записи памяти гостя выполняются напрямую в памяти с использованием MMU хоста. Отключение заставляет все обращения к памяти использовать программную эмуляцию MMU.
- Уровень DMA
- Управляет точностью DMA. Более высокий уровень может исправить проблемы в некоторых играх, но также может повлиять на производительность. Если не уверены, оставьте значение «По умолчанию».
+ Точность DMA
+ Управляет точностью DMA. Безопасная точность может исправить проблемы в некоторых играх, но в некоторых случаях также может повлиять на производительность. Если не уверены, оставьте значение По умолчанию.
4 ГБ (Рекомендуется)
6 ГБ (Небезопасно)
@@ -508,8 +508,6 @@
Пользовательский RTC
Позволяет установить пользовательские часы реального времени отдельно от текущего системного времени.
Установить пользовательский RTC
- Отключить проверку NCA
- Отключает проверку целостности архивов содержимого NCA. Может улучшить скорость загрузки, но есть риск повреждения данных или того, что недействительные файлы останутся незамеченными. Необходимо для работы игр и обновлений, требующих прошивку 20+.
Создать
@@ -922,9 +920,8 @@
По умолчанию
- Нормальный
- Высокий
- Экстремальный
+ Небезопасно (быстро)
+ Безопасно (стабильно)
0.25X (180p/270p)
@@ -958,7 +955,7 @@
Авто
Альбомная (сенсор)
- Пейзаж
+ Альбомная
Обратная альбомная
Портретная (сенсор)
Портрет
diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml
index 5b444f6187..e261772fc4 100644
--- a/src/android/app/src/main/res/values-sr/strings.xml
+++ b/src/android/app/src/main/res/values-sr/strings.xml
@@ -121,8 +121,8 @@
Preskače određena poništavanja keša na strani CPU-a tokom ažuriranja memorije, smanjujući opterećenje procesora i poboljšavajući performanse. Može izazvati greške u nekim igrama.
Омогући емулацију MMU домаћина
Ова оптимизација убрзава приступ меморији од стране гостујућег програма. Укључивање изазива да се читања/уписа меморије госта обављају директно у меморији и користе MMU домаћина. Искључивање присиљава све приступе меморији да користе софтверску емулацију MMU.
- DMA ниво
- Контролише тачност DMA прецизности. Виши ниво може да поправи проблеме у неким играма, али може и да утиче на перформансе. Ако нисте сигурни, оставите на «Подразумевано».
+ DMA тачност
+ Управља прецизношћу DMA-а. Сигурна прецизност може да исправи проблеме у неким играма, али у неким случајевима може да утиче и на перформансе. Ако нисте сигурни, оставите ово на Подразумевано.
Схадер Бацкенд
@@ -457,8 +457,6 @@
Цустом РТЦ
Омогућава вам да поставите прилагођени сат у реалном времену одвојено од тренутног времена система.
Подесите прилагођени РТЦ
- Искључи верификацију НЦА
- Искључује верификацију интегритета НЦА архива садржаја. Ово може побољшати брзину учитавања, али ризикује оштећење података или да неважећи фајлови прођу незапажено. Неопходно је да би игре и ажурирања која захтевају firmware 20+ радили.
Генериши
@@ -917,9 +915,8 @@
Подразумевано
- Нормално
- Високо
- Екстремно
+ Небезбедно (брзо)
+ Безбедно (стабилно)
АСТЦ метода декодирања
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 4ae61340dc..2222402a25 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -128,8 +128,8 @@
Пропускає деякі інвалідації кешу на стороні CPU під час оновлення пам\'яті, зменшуючи навантаження на процесор і покращуючи продуктивність. Може спричинити збої в деяких іграх.
Увімкнути емуляцію MMU хоста
Ця оптимізація пришвидшує доступ до пам\'яті гостьовою програмою. Увімкнення призводить до того, що читання/запис пам\'яті гостя виконуються безпосередньо в пам\'яті та використовують MMU хоста. Вимкнення змушує всі звернення до пам\'яті використовувати програмну емуляцію MMU.
- Рівень DMA
- Керує точністю DMA. Вищий рівень може виправити проблеми в деяких іграх, але також може вплинути на продуктивність. Якщо не впевнені, залиште значення «Типово».
+ Точність DMA
+ Керує точністю DMA. Безпечна точність може виправити проблеми в деяких іграх, але в деяких випадках також може вплинути на продуктивність. Якщо не впевнені, залиште це значення за замовчуванням.
4 ГБ (Рекомендовано)
6 ГБ (Небезпечно)
@@ -495,8 +495,6 @@
Свій RTC
Дозволяє встановити власний час (Real-time clock, або RTC), відмінний від системного.
Встановити RTC
- Вимкнути перевірку NCA
- Вимкає перевірку цілісності архівів вмісту NCA. Може покращити швидкість завантаження, але ризикує пошкодженням даних або тим, що недійсні файли залишаться непоміченими. Необхідно для роботи ігор та оновлень, які вимагають прошивки 20+.
Створити
@@ -811,9 +809,8 @@
Типово
- Нормальний
- Високий
- Екстремальний
+ Небезпечно (швидко)
+ Безпечно (стабільно)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml
index dd64fbca55..784b2dec14 100644
--- a/src/android/app/src/main/res/values-vi/strings.xml
+++ b/src/android/app/src/main/res/values-vi/strings.xml
@@ -128,8 +128,8 @@
Bỏ qua một số lần vô hiệu hóa bộ nhớ đệm phía CPU trong khi cập nhật bộ nhớ, giảm mức sử dụng CPU và cải thiện hiệu suất. Có thể gây ra lỗi hoặc treo máy trong một số trò chơi.
Bật giả lập MMU Máy chủ
Tối ưu hóa này tăng tốc độ truy cập bộ nhớ của chương trình khách. Bật nó lên khiến các thao tác đọc/ghi bộ nhớ khách được thực hiện trực tiếp vào bộ nhớ và sử dụng MMU của Máy chủ. Tắt tính năng này buộc tất cả quyền truy cập bộ nhớ phải sử dụng Giả lập MMU Phần mềm.
- Cấp độ DMA
- Điều khiển độ chính xác của DMA. Độ chính xác cao hơn có thể sửa lỗi trong một số trò chơi, nhưng cũng có thể ảnh hưởng đến hiệu suất trong một số trường hợp. Nếu không chắc chắn, hãy để ở Mặc định.
+ Độ chính xác DMA
+ Điều khiển độ chính xác của DMA. Độ chính xác an toàn có thể khắc phục sự cố trong một số trò chơi, nhưng trong một số trường hợp cũng có thể ảnh hưởng đến hiệu suất. Nếu không chắc chắn, hãy để giá trị này ở Mặc định.
4GB (Được đề xuất)
6GB (Không an toàn)
@@ -482,8 +482,6 @@
RTC tuỳ chỉnh
Cho phép bạn thiết lập một đồng hồ thời gian thực tùy chỉnh riêng biệt so với thời gian hệ thống hiện tại.
Thiết lập RTC tùy chỉnh
- Tắt xác minh NCA
- Tắt xác minh tính toàn vẹn của kho lưu trữ nội dung NCA. Có thể cải thiện tốc độ tải nhưng có nguy cơ hỏng dữ liệu hoặc các tệp không hợp lệ không bị phát hiện. Cần thiết để các trò chơi và bản cập nhật yêu cầu firmware 20+ hoạt động.
Tạo
@@ -776,9 +774,8 @@
Mặc định
- Bình thường
- Cao
- Cực cao
+ Không an toàn (nhanh)
+ An toàn (ổn định)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index a12c00063f..bfdc3af3d3 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -127,8 +127,8 @@
在内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。
启用主机 MMU 模拟
此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。
- DMA 级别
- 控制 DMA 精度。更高的精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。
+ DMA 精度
+ 控制 DMA 精度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如果不确定,请保留为“默认”。
4GB (推荐)
6GB (不安全)
@@ -500,8 +500,6 @@
自定义系统时间
此选项允许您设置与目前系统时间相独立的自定义系统时钟。
设置自定义系统时间
-禁用NCA验证
-禁用NCA内容存档的完整性验证。可能会提高加载速度,但存在数据损坏或无效文件未被检测到的风险。对于需要固件20+的游戏和更新是必需的。
生成
@@ -914,9 +912,8 @@
默认
- 普通
- 高
- 极高
+ 不安全(快速)
+ 安全(稳定)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index a125553102..e64aaa9a54 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -120,8 +120,8 @@
在記憶體更新期間跳過某些CPU端快取無效化,減少CPU使用率並提高其性能。可能會導致某些遊戲出現故障或崩潰。
啟用主機 MMU 模擬
此最佳化可加速來賓程式的記憶體存取。啟用後,來賓記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。
- DMA 級別
- 控制 DMA 精確度。更高的精確度可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為「預設」。
+ DMA 精度
+ 控制 DMA 精度。安全精度可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為「預設」。
4GB (推薦)
@@ -505,8 +505,6 @@
自訂 RTC
允許您設定與您的目前系統時間相互獨立的自訂即時時鐘。
設定自訂 RTC
- 停用NCA驗證
- 停用NCA內容存檔的完整性驗證。可能會提高載入速度,但存在資料損毀或無效檔案未被偵測到的風險。對於需要韌體20+的遊戲和更新是必需的。
生成
@@ -919,9 +917,8 @@
預設
- 普通
- 高
- 極高
+ 不安全(快速)
+ 安全(穩定)
0.25X (180p/270p)
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 2f0392675d..08ca53ad81 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -454,15 +454,13 @@
- @string/dma_accuracy_default
- - @string/dma_accuracy_normal
- - @string/dma_accuracy_high
- - @string/dma_accuracy_extreme
+ - @string/dma_accuracy_unsafe
+ - @string/dma_accuracy_safe
- 0
- 1
- 2
- - 3
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 2c7923d5a3..2cef5903cb 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -115,8 +115,8 @@
Use Boost (1700MHz) to run at the Switch\'s highest native clock, or Fast (2000MHz) to run at 2x clock.
Memory Layout
(EXPERIMENTAL) Change the emulated memory layout. This setting will not increase performance, but may help with games utilizing high resolutions via mods. Do not use on phones with 8GB of RAM or less. Only works on the Dynarmic (JIT) backend.
- DMA Level
- Controls the DMA precision accuracy. Higher precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave it at Default.
+ DMA Accuracy
+ Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.
Shader Backend
@@ -266,6 +266,7 @@
Alphabetical
List
Grid
+ Compact Grid
Carousel
Screenshot for %1$s
Folder
@@ -474,8 +475,6 @@
Custom RTC
Allows you to set a custom real-time clock separate from your current system time.
Set custom RTC
- Disable NCA Verification
- Disables integrity verification of NCA content archives. This may improve loading speed but risks data corruption or invalid files going undetected. Some games that require firmware versions 20+ may need this as well.
Generate
@@ -784,9 +783,6 @@
Game Requires Firmware
dump and install firmware, or press "OK" to launch anyways.]]>
- NCA Verification Disabled
- This is required to run new games and updates, but may cause instability or crashes if NCA files are corrupt, modified, or tampered with. If unsure, re-enable verification in Advanced Settings -> System, and use firmware versions of 19.0.1 or below.
-
Searching for game...
Game not found for Title ID: %1$s
@@ -944,9 +940,8 @@
Default
- Normal
- High
- Extreme
+ Unsafe (fast)
+ Safe (stable)
ASTC Decoding Method
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index 1b7532b6b9..2e36d59569 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -598,12 +598,17 @@ public:
bool ClearBackingRegion(size_t physical_offset, size_t length) {
#ifdef __linux__
- // Set MADV_REMOVE on backing map to destroy it instantly.
- // This also deletes the area from the backing file.
- int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
- ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
-
- return true;
+ // Only incur syscall cost IF memset would be slower (theshold = 16MiB)
+ // TODO(lizzie): Smarter way to dynamically get this threshold (broadwell != raptor lake) for example
+ if (length >= 2097152UL * 8) {
+ // Set MADV_REMOVE on backing map to destroy it instantly.
+ // This also deletes the area from the backing file.
+ int ret = madvise(backing_base + physical_offset, length, MADV_REMOVE);
+ ASSERT_MSG(ret == 0, "madvise failed: {}", strerror(errno));
+ return true;
+ } else {
+ return false;
+ }
#else
return false;
#endif
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index d4f16f4853..b41f4c75f5 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -154,11 +154,19 @@ bool IsGPULevelHigh() {
values.current_gpu_accuracy == GpuAccuracy::High;
}
+bool IsDMALevelDefault() {
+ return values.dma_accuracy.GetValue() == DmaAccuracy::Default;
+}
+
+bool IsDMALevelSafe() {
+ return values.dma_accuracy.GetValue() == DmaAccuracy::Safe;
+}
+
bool IsFastmemEnabled() {
if (values.cpu_debug_mode) {
return static_cast(values.cpuopt_fastmem);
}
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Unsafe) {
+ if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe) {
return static_cast(values.cpuopt_unsafe_host_mmu);
}
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__)
diff --git a/src/common/settings.h b/src/common/settings.h
index 9d448a2b38..8605445837 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -443,7 +443,7 @@ struct Values {
SwitchableSetting dma_accuracy{linkage,
DmaAccuracy::Default,
DmaAccuracy::Default,
- DmaAccuracy::Extreme,
+ DmaAccuracy::Safe,
"dma_accuracy",
Category::RendererAdvanced,
Specialization::Default,
@@ -626,10 +626,6 @@ struct Values {
true, true, &rng_seed_enabled};
Setting device_name{
linkage, "Eden", "device_name", Category::System, Specialization::Default, true, true};
- SwitchableSetting disable_nca_verification{linkage, true, "disable_nca_verification",
- Category::System, Specialization::Default};
- Setting hide_nca_verification_popup{
- linkage, false, "hide_nca_verification_popup", Category::System, Specialization::Default};
Setting current_user{linkage, 0, "current_user", Category::System};
@@ -787,6 +783,9 @@ void UpdateGPUAccuracy();
bool IsGPULevelExtreme();
bool IsGPULevelHigh();
+bool IsDMALevelDefault();
+bool IsDMALevelSafe();
+
bool IsFastmemEnabled();
void SetNceEnabled(bool is_64bit);
bool IsNceEnabled();
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index 52b4a128f7..41133a7819 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -136,7 +136,7 @@ ENUM(ShaderBackend, Glsl, Glasm, SpirV);
ENUM(GpuAccuracy, Normal, High, Extreme);
-ENUM(DmaAccuracy, Default, Normal, High, Extreme);
+ENUM(DmaAccuracy, Default, Unsafe, Safe);
ENUM(CpuBackend, Dynarmic, Nce);
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 1e8e4ec07a..89c97eb1aa 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1200,7 +1200,7 @@ else()
target_link_libraries(core PUBLIC Boost::headers)
endif()
-target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API mbedtls mbedcrypto)
+target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto MbedTLS::mbedtls)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 2c2c54a1ad..d2035d0fe0 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -305,7 +305,7 @@ std::shared_ptr ArmDynarmic32::MakeJit(Common::PageTable* pa
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- // Paranoid mode for debugging optimizations
+ // Paranoia mode for debugging optimizations
if (Settings::values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Paranoid) {
config.unsafe_optimizations = false;
config.optimizations = Dynarmic::no_optimizations;
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index c2fd587a73..0a2a4e464c 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -16,7 +16,7 @@ struct CipherContext;
enum class Mode {
CTR = 11,
ECB = 2,
- XTS = 70,
+ XTS = 74,
};
enum class Op {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 04b75d5e8f..6d0b32ba70 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -539,7 +539,7 @@ static std::array MGF1(const std::array& seed) {
while (out.size() < target_size) {
out.resize(out.size() + 0x20);
seed_exp[in_size + 3] = static_cast(i);
- mbedtls_sha256_ret(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
+ mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
++i;
}
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 4b45e72c43..905b8139ef 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -178,7 +178,7 @@ std::array FindKeyFromHex(const std::vector& binary,
std::array temp{};
for (size_t i = 0; i < binary.size() - key_size; ++i) {
- mbedtls_sha256_ret(binary.data() + i, key_size, temp.data(), 0);
+ mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
if (temp != hash)
continue;
@@ -206,7 +206,7 @@ static std::array FindEncryptedMasterKeyFromHex(const std::vector<
AESCipher cipher(key, Mode::ECB);
for (size_t i = 0; i < binary.size() - 0x10; ++i) {
cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
- mbedtls_sha256_ret(dec_temp.data(), dec_temp.size(), temp.data(), 0);
+ mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
for (size_t k = 0; k < out.size(); ++k) {
if (temp == master_key_hashes[k]) {
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6652523589..b961cdb096 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -34,12 +34,9 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
}
reader = std::make_shared();
- if (Result rc =
- reader->Initialize(file, GetCryptoConfiguration(), GetNcaCompressionConfiguration());
- R_FAILED(rc)) {
+ if (Result rc = reader->Initialize(file, GetCryptoConfiguration(), GetNcaCompressionConfiguration()); R_FAILED(rc)) {
if (rc != ResultInvalidNcaSignature) {
- LOG_ERROR(Loader, "File reader errored out during header read: {:#x}",
- rc.GetInnerValue());
+ LOG_ERROR(Loader, "File reader errored out during header read: {:#x}", rc.GetInnerValue());
}
status = Loader::ResultStatus::ErrorBadNCAHeader;
return;
@@ -84,10 +81,8 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca)
std::vector filesystems(fs_count);
for (s32 i = 0; i < fs_count; i++) {
NcaFsHeaderReader header_reader;
- const Result rc = fs.OpenStorage(&filesystems[i], &header_reader, i);
- if (R_FAILED(rc)) {
- LOG_ERROR(Loader, "File reader errored out during read of section {}: {:#x}", i,
- rc.GetInnerValue());
+ if (Result rc = fs.OpenStorage(&filesystems[i], &header_reader, i); R_FAILED(rc)) {
+ LOG_DEBUG(Loader, "File reader errored out during read of section {}: {:#x}", i, rc.GetInnerValue());
status = Loader::ResultStatus::ErrorBadNCAHeader;
return;
}
diff --git a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
index 615a624f4f..71ba458cef 100644
--- a/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
+++ b/src/core/file_sys/fssystem/fssystem_bucket_tree.cpp
@@ -238,9 +238,7 @@ void BucketTree::Initialize(size_t node_size, s64 end_offset) {
ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
ASSERT(Common::IsPowerOfTwo(node_size));
- if (!Settings::values.disable_nca_verification.GetValue()) {
- ASSERT(end_offset > 0);
- }
+ ASSERT(end_offset > 0);
ASSERT(!this->IsInitialized());
m_node_size = node_size;
diff --git a/src/core/file_sys/fssystem/fssystem_integrity_verification_storage.cpp b/src/core/file_sys/fssystem/fssystem_integrity_verification_storage.cpp
index 57cdc19248..c1bad3ec36 100644
--- a/src/core/file_sys/fssystem/fssystem_integrity_verification_storage.cpp
+++ b/src/core/file_sys/fssystem/fssystem_integrity_verification_storage.cpp
@@ -4,23 +4,18 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
#include "common/alignment.h"
+#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
namespace FileSys {
-constexpr inline u32 ILog2(u32 val)
-{
+constexpr inline u32 ILog2(u32 val) {
ASSERT(val > 0);
return static_cast((sizeof(u32) * 8) - 1 - std::countl_zero(val));
}
-void IntegrityVerificationStorage::Initialize(VirtualFile hs,
- VirtualFile ds,
- s64 verif_block_size,
- s64 upper_layer_verif_block_size,
- bool is_real_data)
-{
+void IntegrityVerificationStorage::Initialize(VirtualFile hs, VirtualFile ds, s64 verif_block_size,
+ s64 upper_layer_verif_block_size, bool is_real_data) {
// Validate preconditions.
ASSERT(verif_block_size >= HashSize);
@@ -40,28 +35,22 @@ void IntegrityVerificationStorage::Initialize(VirtualFile hs,
ASSERT(m_upper_layer_verification_block_size == 1ll << m_upper_layer_verification_block_order);
// Validate sizes.
- if (m_data_storage != nullptr) {
+ {
s64 hash_size = m_hash_storage->GetSize();
s64 data_size = m_data_storage->GetSize();
ASSERT(((hash_size / HashSize) * m_verification_block_size) >= data_size);
- } else {
- LOG_ERROR(Loader,
- "Failed to initialize integrity verification store. Game, update, or DLC may not "
- "work.");
}
// Set data.
m_is_real_data = is_real_data;
}
-void IntegrityVerificationStorage::Finalize()
-{
+void IntegrityVerificationStorage::Finalize() {
m_hash_storage = VirtualFile();
m_data_storage = VirtualFile();
}
-size_t IntegrityVerificationStorage::Read(u8* buffer, size_t size, size_t offset) const
-{
+size_t IntegrityVerificationStorage::Read(u8* buffer, size_t size, size_t offset) const {
// Succeed if zero size.
if (size == 0) {
return size;
@@ -70,13 +59,7 @@ size_t IntegrityVerificationStorage::Read(u8* buffer, size_t size, size_t offset
// Validate arguments.
ASSERT(buffer != nullptr);
- if (m_data_storage == nullptr) {
- LOG_ERROR(Loader,
- "Integrity verification store failed read operation. Game, update or DLC may not "
- "work.");
- return 0;
- }
-
+ // Validate the offset.
s64 data_size = m_data_storage->GetSize();
ASSERT(offset <= static_cast(data_size));
@@ -104,8 +87,7 @@ size_t IntegrityVerificationStorage::Read(u8* buffer, size_t size, size_t offset
return m_data_storage->Read(buffer, read_size, offset);
}
-size_t IntegrityVerificationStorage::GetSize() const
-{
+size_t IntegrityVerificationStorage::GetSize() const {
return m_data_storage->GetSize();
}
diff --git a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
index 1bc7039318..37fb71e9e3 100644
--- a/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
+++ b/src/core/file_sys/fssystem/fssystem_nca_file_system_driver.cpp
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -1051,8 +1051,8 @@ Result NcaFileSystemDriver::CreatePatchMetaStorage(
ASSERT(out_aes_ctr_ex_meta != nullptr);
ASSERT(out_indirect_meta != nullptr);
ASSERT(base_storage != nullptr);
- ASSERT(patch_info.HasAesCtrExTable());
- ASSERT(patch_info.HasIndirectTable());
+ //ASSERT(patch_info.HasAesCtrExTable());
+ //ASSERT(patch_info.HasIndirectTable());
ASSERT(Common::IsAligned(patch_info.aes_ctr_ex_size, NcaHeader::XtsBlockSize));
// Validate patch info extents.
@@ -1296,91 +1296,65 @@ Result NcaFileSystemDriver::CreateIntegrityVerificationStorageImpl(
ASSERT(base_storage != nullptr);
ASSERT(layer_info_offset >= 0);
- if (!Settings::values.disable_nca_verification.GetValue()) {
- // Define storage types.
- using VerificationStorage = HierarchicalIntegrityVerificationStorage;
- using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
+ // Define storage types.
+ using VerificationStorage = HierarchicalIntegrityVerificationStorage;
+ using StorageInfo = VerificationStorage::HierarchicalStorageInformation;
- // Validate the meta info.
- HierarchicalIntegrityVerificationInformation level_hash_info;
- std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info),
- sizeof(level_hash_info));
+ // Validate the meta info.
+ HierarchicalIntegrityVerificationInformation level_hash_info;
+ std::memcpy(std::addressof(level_hash_info),
+ std::addressof(meta_info.level_hash_info),
+ sizeof(level_hash_info));
- R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
- R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
+ R_UNLESS(IntegrityMinLayerCount <= level_hash_info.max_layers,
+ ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
+ R_UNLESS(level_hash_info.max_layers <= IntegrityMaxLayerCount,
+ ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
- // Get the base storage size.
- s64 base_storage_size = base_storage->GetSize();
+ // Get the base storage size.
+ s64 base_storage_size = base_storage->GetSize();
- // Create storage info.
- StorageInfo storage_info;
- for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) {
- const auto& layer_info = level_hash_info.info[i];
- R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
- ResultNcaBaseStorageOutOfRangeD);
-
- storage_info[i + 1] = std::make_shared(
- base_storage, layer_info.size, layer_info_offset + layer_info.offset);
- }
-
- // Set the last layer info.
- const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
- const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
- R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
+ // Create storage info.
+ StorageInfo storage_info;
+ for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) {
+ const auto& layer_info = level_hash_info.info[i];
+ R_UNLESS(layer_info_offset + layer_info.offset + layer_info.size <= base_storage_size,
ResultNcaBaseStorageOutOfRangeD);
- if (layer_info_offset > 0) {
- R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
- ResultRomNcaInvalidIntegrityLayerInfoOffset);
- }
- storage_info.SetDataStorage(std::make_shared(
- std::move(base_storage), layer_info.size, last_layer_info_offset));
- // Make the integrity romfs storage.
- auto integrity_storage = std::make_shared();
- R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
-
- // Initialize the integrity storage.
- R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info,
- max_data_cache_entries, max_hash_cache_entries,
- buffer_level));
-
- // Set the output.
- *out = std::move(integrity_storage);
- R_SUCCEED();
- } else {
- // Read IVFC layout
- HierarchicalIntegrityVerificationInformation lhi{};
- std::memcpy(std::addressof(lhi), std::addressof(meta_info.level_hash_info), sizeof(lhi));
-
- R_UNLESS(IntegrityMinLayerCount <= lhi.max_layers,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
- R_UNLESS(lhi.max_layers <= IntegrityMaxLayerCount,
- ResultInvalidNcaHierarchicalIntegrityVerificationLayerCount);
-
- const auto& data_li = lhi.info[lhi.max_layers - 2];
-
- const s64 base_size = base_storage->GetSize();
-
- // Compute the data layer window
- const s64 data_off = (layer_info_offset > 0) ? 0LL : data_li.offset.Get();
- R_UNLESS(data_off + data_li.size <= base_size, ResultNcaBaseStorageOutOfRangeD);
- if (layer_info_offset > 0) {
- R_UNLESS(data_off + data_li.size <= layer_info_offset,
- ResultRomNcaInvalidIntegrityLayerInfoOffset);
- }
-
- // TODO: Passthrough (temporary compatibility: integrity disabled)
- auto data_view = std::make_shared(base_storage, data_li.size, data_off);
- R_UNLESS(data_view != nullptr, ResultAllocationMemoryFailedAllocateShared);
-
- auto passthrough = std::make_shared(std::move(data_view));
- R_UNLESS(passthrough != nullptr, ResultAllocationMemoryFailedAllocateShared);
-
- *out = std::move(passthrough);
- R_SUCCEED();
+ storage_info[i + 1] = std::make_shared(base_storage,
+ layer_info.size,
+ layer_info_offset + layer_info.offset);
}
+
+ // Set the last layer info.
+ const auto& layer_info = level_hash_info.info[level_hash_info.max_layers - 2];
+ const s64 last_layer_info_offset = layer_info_offset > 0 ? 0LL : layer_info.offset.Get();
+ R_UNLESS(last_layer_info_offset + layer_info.size <= base_storage_size,
+ ResultNcaBaseStorageOutOfRangeD);
+ if (layer_info_offset > 0) {
+ R_UNLESS(last_layer_info_offset + layer_info.size <= layer_info_offset,
+ ResultRomNcaInvalidIntegrityLayerInfoOffset);
+ }
+ storage_info[level_hash_info.max_layers - 1]
+ = std::make_shared(std::move(base_storage),
+ layer_info.size,
+ last_layer_info_offset);
+
+ // Make the integrity romfs storage.
+ auto integrity_storage = std::make_shared();
+ R_UNLESS(integrity_storage != nullptr, ResultAllocationMemoryFailedAllocateShared);
+
+ // Initialize the integrity storage.
+ R_TRY(integrity_storage->Initialize(level_hash_info,
+ meta_info.master_hash,
+ storage_info,
+ max_data_cache_entries,
+ max_hash_cache_entries,
+ buffer_level));
+
+ // Set the output.
+ *out = std::move(integrity_storage);
+ R_SUCCEED();
}
Result NcaFileSystemDriver::CreateRegionSwitchStorage(VirtualFile* out,
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index cb2089e9b3..45390500cc 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -64,7 +64,7 @@ static std::string GetRelativePathFromNcaID(const std::array& nca_id, bo
}
Core::Crypto::SHA256Hash hash{};
- mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
+ mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
const auto format_str =
fmt::runtime(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca");
@@ -146,7 +146,7 @@ bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
}
Core::Crypto::SHA256Hash hash{};
- mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
@@ -170,7 +170,7 @@ bool PlaceholderCache::Delete(const NcaID& id) const {
}
Core::Crypto::SHA256Hash hash{};
- mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0);
+ mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
@@ -665,7 +665,7 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
const OptionalHeader opt_header{0, 0};
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
- mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
+ mbedtls_sha256(data.data(), data.size(), c_rec.hash.data(), 0);
std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
if (!RawInstallYuzuMeta(new_cnmt)) {
@@ -776,7 +776,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
id = *override_id;
} else {
const auto& data = in->ReadBytes(0x100000);
- mbedtls_sha256_ret(data.data(), data.size(), hash.data(), 0);
+ mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
memcpy(id.data(), hash.data(), 16);
}
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 106922e04f..edf51e74de 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -126,6 +129,10 @@ std::string SaveDataFactory::GetFullPath(ProgramId program_id, VirtualDir dir,
std::string out = GetSaveDataSpaceIdPath(space);
+ LOG_INFO(Common_Filesystem, "Save ID: {:016X}", save_id);
+ LOG_INFO(Common_Filesystem, "User ID[1]: {:016X}", user_id[1]);
+ LOG_INFO(Common_Filesystem, "User ID[0]: {:016X}", user_id[0]);
+
switch (type) {
case SaveDataType::System:
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index fd5342021c..c1912b2bda 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -68,7 +68,7 @@ NAX::NAX(VirtualFile file_, std::array nca_id)
: header(std::make_unique()),
file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
Core::Crypto::SHA256Hash hash{};
- mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
+ mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexToString(nca_id, false)));
}
diff --git a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
index fea373a607..31693cfc33 100644
--- a/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
+++ b/src/core/hle/service/bcat/delivery_cache_directory_service.cpp
@@ -15,7 +15,7 @@ namespace Service::BCAT {
static BcatDigest DigestFile(const FileSys::VirtualFile& file) {
BcatDigest out{};
const auto bytes = file->ReadAllBytes();
- mbedtls_md5_ret(bytes.data(), bytes.size(), out.data());
+ mbedtls_md5(bytes.data(), bytes.size(), out.data());
return out;
}
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index a9b0f9d2f3..2913d25819 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -97,18 +100,18 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
slots[slot].needs_cleanup_on_release = false;
slots[slot].buffer_state = BufferState::Acquired;
+ // Mark tracked buffer history records as acquired
+ for (auto& buffer_history_record : core->buffer_history) {
+ if (buffer_history_record.frame_number == core->frame_counter) {
+ buffer_history_record.state = BufferState::Acquired;
+ break;
+ }
+ }
+
// TODO: for now, avoid resetting the fence, so that when we next return this
// slot to the producer, it will wait for the fence to pass. We should fix this
// by properly waiting for the fence in the BufferItemConsumer.
// slots[slot].fence = Fence::NoFence();
-
- const auto target_frame_number = slots[slot].frame_number;
- for (size_t i = 0; i < core->history.size(); i++) {
- if (core->history[i].frame_number == target_frame_number) {
- core->history[i].state = BufferState::Acquired;
- break;
- }
- }
}
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index 27ac930f96..6120d8eae1 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -10,12 +13,19 @@
namespace Service::android {
-BufferQueueCore::BufferQueueCore() {
- history.resize(8);
-};
-
+BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::~BufferQueueCore() = default;
+void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) {
+ buffer_history_pos = (buffer_history_pos + 1) % BUFFER_HISTORY_SIZE;
+ buffer_history[buffer_history_pos] = BufferHistoryInfo{
+ .frame_number = frame_number,
+ .queue_time = queue_time,
+ .presentation_time = presentation_time,
+ .state = state,
+ };
+}
+
void BufferQueueCore::SignalDequeueCondition() {
dequeue_possible.store(true);
dequeue_condition.notify_all();
@@ -47,7 +57,7 @@ s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
- auto max_buffer_count = (std::max)(default_max_buffer_count, min_buffer_count);
+ auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
if (override_max_buffer_count != 0) {
ASSERT(override_max_buffer_count >= min_buffer_count);
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h
index 341634352b..ed7d4b4069 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -15,6 +18,7 @@
#include "core/hle/service/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvnflinger/buffer_slot.h"
#include "core/hle/service/nvnflinger/pixel_format.h"
#include "core/hle/service/nvnflinger/status.h"
#include "core/hle/service/nvnflinger/window.h"
@@ -23,22 +27,19 @@ namespace Service::android {
#ifdef _MSC_VER
#pragma pack(push, 1)
+struct BufferHistoryInfo {
+#elif defined(__GNUC__) || defined(__clang__)
+struct __attribute__((packed)) BufferHistoryInfo {
#endif
-struct BufferInfo {
u64 frame_number;
s64 queue_time;
- s64 presentation_time{};
- BufferState state{BufferState::Free};
-}
-#if defined(__GNUC__) || defined(__clang__)
-__attribute__((packed))
-#endif
-;
+ s64 presentation_time;
+ BufferState state;
+};
#ifdef _MSC_VER
#pragma pack(pop)
#endif
-static_assert(sizeof(BufferInfo) == 0x1C,
- "BufferInfo is an invalid size");
+static_assert(sizeof(BufferHistoryInfo) == 0x1C, "BufferHistoryInfo must be 28 bytes");
class IConsumerListener;
class IProducerListener;
@@ -49,10 +50,13 @@ class BufferQueueCore final {
public:
static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
+ static constexpr u32 BUFFER_HISTORY_SIZE = 8;
BufferQueueCore();
~BufferQueueCore();
+ void PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state);
+
private:
void SignalDequeueCondition();
bool WaitForDequeueCondition(std::unique_lock& lk);
@@ -88,11 +92,11 @@ private:
const s32 max_acquired_buffer_count{}; // This is always zero on HOS
bool buffer_has_been_queued{};
u64 frame_counter{};
+ std::array buffer_history{};
+ u32 buffer_history_pos{BUFFER_HISTORY_SIZE-1};
u32 transform_hint{};
bool is_allocating{};
mutable std::condition_variable_any is_allocating_condition;
-
- std::vector history{8};
};
} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index f9e1dba965..bc3076d20b 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -530,11 +533,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item.is_droppable = core->dequeue_buffer_cannot_block || async;
item.swap_interval = swap_interval;
- position = (position + 1) % 8;
- core->history[position] = {.frame_number = core->frame_counter,
- .queue_time = slots[slot].queue_time,
- .state = BufferState::Queued};
-
sticky_transform = sticky_transform_;
if (core->queue.empty()) {
@@ -551,6 +549,15 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
// mark it as freed
if (core->StillTracking(*front)) {
slots[front->slot].buffer_state = BufferState::Free;
+
+ // Mark tracked buffer history records as free
+ for (auto& buffer_history_record : core->buffer_history) {
+ if (buffer_history_record.frame_number == front->frame_number) {
+ buffer_history_record.state = BufferState::Free;
+ break;
+ }
+ }
+
// Reset the frame number of the freed buffer so that it is the first in line to
// be dequeued again
slots[front->slot].frame_number = 0;
@@ -564,6 +571,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
}
}
+ core->PushHistory(core->frame_counter, slots[slot].queue_time, slots[slot].presentation_time, BufferState::Queued);
core->buffer_has_been_queued = true;
core->SignalDequeueCondition();
output->Inflate(core->default_width, core->default_height, core->transform_hint,
@@ -938,33 +946,46 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data,
break;
}
case TransactionId::GetBufferHistory: {
- LOG_WARNING(Service_Nvnflinger, "called, transaction=GetBufferHistory");
+ LOG_DEBUG(Service_Nvnflinger, "called, transaction=GetBufferHistory");
- std::scoped_lock lock{core->mutex};
-
- auto buffer_history_count = (std::min)(parcel_in.Read(), (s32)core->history.size());
-
- if (buffer_history_count <= 0) {
+ const s32 request = parcel_in.Read();
+ if (request <= 0) {
parcel_out.Write(Status::BadValue);
parcel_out.Write(0);
- status = Status::None;
break;
}
- auto info = new BufferInfo[buffer_history_count];
- auto pos = position;
- for (int i = 0; i < buffer_history_count; i++) {
- info[i] = core->history[(pos - i) % core->history.size()];
- LOG_WARNING(Service_Nvnflinger, "frame_number={}, state={}",
- core->history[(pos - i) % core->history.size()].frame_number,
- (u32)core->history[(pos - i) % core->history.size()].state);
- pos--;
+ constexpr u32 history_max = BufferQueueCore::BUFFER_HISTORY_SIZE;
+ std::array buffer_history_snapshot{};
+ s32 valid_index{};
+ {
+ std::scoped_lock lk(core->mutex);
+
+ const u32 current_history_pos = core->buffer_history_pos;
+ u32 index_reversed{};
+ for (u32 i = 0; i < history_max; ++i) {
+ // Wrap values backwards e.g. 7, 6, 5, etc. in the range of 0-7
+ index_reversed = (current_history_pos + history_max - i) % history_max;
+ const auto& current_history_buffer = core->buffer_history[index_reversed];
+
+ // Here we use the frame number as a terminator.
+ // Because a buffer without frame_number is not considered complete
+ if (current_history_buffer.frame_number == 0) {
+ break;
+ }
+
+ buffer_history_snapshot[valid_index] = current_history_buffer;
+ ++valid_index;
+ }
}
+ const s32 limit = std::min(request, valid_index);
parcel_out.Write(Status::NoError);
- parcel_out.Write(buffer_history_count);
- parcel_out.WriteFlattenedObject(info);
- status = Status::None;
+ parcel_out.Write(limit);
+ for (s32 i = 0; i < limit; ++i) {
+ parcel_out.Write(buffer_history_snapshot[i]);
+ }
+
break;
}
default:
@@ -972,9 +993,7 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data,
break;
}
- if (status != Status::None) {
- parcel_out.Write(status);
- }
+ parcel_out.Write(status);
const auto serialized = parcel_out.Serialize();
std::memcpy(parcel_reply.data(), serialized.data(),
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index 28195cd3c5..6610e0853a 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index 5b5cbb6fbd..d348b331cb 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -15,7 +18,7 @@ namespace Service::android {
class GraphicBuffer;
-enum class BufferState : s32 {
+enum class BufferState : u32 {
Free = 0,
Dequeued = 1,
Queued = 2,
diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp
index 3d3ad2d62c..600d0ec4fd 100644
--- a/src/core/hle/service/ro/ro.cpp
+++ b/src/core/hle/service/ro/ro.cpp
@@ -178,7 +178,7 @@ struct ProcessContext {
std::vector nro_data(size);
m_process->GetMemory().ReadBlock(base_address, nro_data.data(), size);
- mbedtls_sha256_ret(nro_data.data(), size, hash.data(), 0);
+ mbedtls_sha256(nro_data.data(), size, hash.data(), 0);
}
for (size_t i = 0; i < MaxNrrInfos; i++) {
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 9a82dae144..0a9d9dc6f8 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -150,7 +150,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function
// Initialize sha256 verification context.
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
- mbedtls_sha256_starts_ret(&ctx, 0);
+ mbedtls_sha256_starts(&ctx, 0);
// Ensure we maintain a clean state on exit.
SCOPE_EXIT {
@@ -168,7 +168,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function
const size_t read_size = file->Read(buffer.data(), intended_read_size, processed_size);
// Update the hash function with the buffer contents.
- mbedtls_sha256_update_ret(&ctx, buffer.data(), read_size);
+ mbedtls_sha256_update(&ctx, buffer.data(), read_size);
// Update counters.
processed_size += read_size;
@@ -181,7 +181,7 @@ ResultStatus AppLoader_NCA::VerifyIntegrity(std::function
// Finalize context and compute the output hash.
std::array output_hash;
- mbedtls_sha256_finish_ret(&ctx, output_hash.data());
+ mbedtls_sha256_finish(&ctx, output_hash.data());
// Compare to expected.
if (std::memcmp(input_hash.data(), output_hash.data(), NcaSha256HalfHashLength) != 0) {
diff --git a/src/dedicated_room/CMakeLists.txt b/src/dedicated_room/CMakeLists.txt
index e5934c941a..5166329156 100644
--- a/src/dedicated_room/CMakeLists.txt
+++ b/src/dedicated_room/CMakeLists.txt
@@ -20,7 +20,7 @@ if (ENABLE_WEB_SERVICE)
target_link_libraries(yuzu-room PRIVATE web_service)
endif()
-target_link_libraries(yuzu-room PRIVATE mbedtls mbedcrypto)
+target_link_libraries(yuzu-room PRIVATE MbedTLS::mbedcrypto MbedTLS::mbedtls)
if (MSVC)
target_link_libraries(yuzu-room PRIVATE getopt)
endif()
diff --git a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp
index 7695df57d2..d0653eceab 100644
--- a/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp
+++ b/src/dynarmic/src/dynarmic/backend/exception_handler_posix.cpp
@@ -6,8 +6,13 @@
* SPDX-License-Identifier: 0BSD
*/
-#include "dynarmic/backend/exception_handler.h"
-
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#ifdef __APPLE__
# include
# include
@@ -21,17 +26,10 @@
# endif
#endif
-#include
-#include
-#include
-#include
-#include
-#include
+#include
-#include "dynarmic/common/assert.h"
-#include
+#include "dynarmic/backend/exception_handler.h"
#include "dynarmic/common/common_types.h"
-
#if defined(MCL_ARCHITECTURE_X86_64)
# include "dynarmic/backend/x64/block_of_code.h"
#elif defined(MCL_ARCHITECTURE_ARM64)
@@ -43,42 +41,80 @@
#else
# error "Invalid architecture"
#endif
+#include
namespace Dynarmic::Backend {
namespace {
struct CodeBlockInfo {
- u64 code_begin, code_end;
+ u64 size;
std::function cb;
};
class SigHandler {
-public:
- SigHandler();
- ~SigHandler();
-
- void AddCodeBlock(CodeBlockInfo info);
- void RemoveCodeBlock(u64 host_pc);
-
- bool SupportsFastmem() const { return supports_fast_mem; }
-
-private:
- auto FindCodeBlockInfo(u64 host_pc) {
- return std::find_if(code_block_infos.begin(), code_block_infos.end(), [&](const auto& x) { return x.code_begin <= host_pc && x.code_end > host_pc; });
+ auto FindCodeBlockInfo(u64 offset) noexcept {
+ return std::find_if(code_block_infos.begin(), code_block_infos.end(), [&](auto const& e) {
+ return e.first <= offset && e.first + e.second.size > offset;
+ });
}
+ static void SigAction(int sig, siginfo_t* info, void* raw_context);
bool supports_fast_mem = true;
-
void* signal_stack_memory = nullptr;
-
- std::vector code_block_infos;
- std::mutex code_block_infos_mutex;
-
+ ankerl::unordered_dense::map code_block_infos;
+ std::shared_mutex code_block_infos_mutex;
struct sigaction old_sa_segv;
struct sigaction old_sa_bus;
+ std::size_t signal_stack_size;
+public:
+ SigHandler() noexcept {
+ signal_stack_size = std::max(SIGSTKSZ, 2 * 1024 * 1024);
+ signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- static void SigAction(int sig, siginfo_t* info, void* raw_context);
+ stack_t signal_stack{};
+ signal_stack.ss_sp = signal_stack_memory;
+ signal_stack.ss_size = signal_stack_size;
+ signal_stack.ss_flags = 0;
+ if (sigaltstack(&signal_stack, nullptr) != 0) {
+ fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
+ supports_fast_mem = false;
+ return;
+ }
+
+ struct sigaction sa{};
+ sa.sa_handler = nullptr;
+ sa.sa_sigaction = &SigHandler::SigAction;
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) {
+ fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n");
+ supports_fast_mem = false;
+ return;
+ }
+#ifdef __APPLE__
+ if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
+ fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
+ supports_fast_mem = false;
+ return;
+ }
+#endif
+ }
+
+ ~SigHandler() noexcept {
+ munmap(signal_stack_memory, signal_stack_size);
+ }
+
+ void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
+ std::unique_lock guard(code_block_infos_mutex);
+ code_block_infos.insert_or_assign(offset, cbi);
+ }
+ void RemoveCodeBlock(u64 offset) noexcept {
+ std::unique_lock guard(code_block_infos_mutex);
+ code_block_infos.erase(offset);
+ }
+
+ bool SupportsFastmem() const noexcept { return supports_fast_mem; }
};
std::mutex handler_lock;
@@ -91,64 +127,8 @@ void RegisterHandler() {
}
}
-SigHandler::SigHandler() {
- const size_t signal_stack_size = std::max(SIGSTKSZ, 2 * 1024 * 1024);
-
- signal_stack_memory = std::malloc(signal_stack_size);
-
- stack_t signal_stack;
- signal_stack.ss_sp = signal_stack_memory;
- signal_stack.ss_size = signal_stack_size;
- signal_stack.ss_flags = 0;
- if (sigaltstack(&signal_stack, nullptr) != 0) {
- fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
- supports_fast_mem = false;
- return;
- }
-
- struct sigaction sa;
- sa.sa_handler = nullptr;
- sa.sa_sigaction = &SigHandler::SigAction;
- sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
- sigemptyset(&sa.sa_mask);
- if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) {
- fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n");
- supports_fast_mem = false;
- return;
- }
-#ifdef __APPLE__
- if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
- fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
- supports_fast_mem = false;
- return;
- }
-#endif
-}
-
-SigHandler::~SigHandler() {
- std::free(signal_stack_memory);
-}
-
-void SigHandler::AddCodeBlock(CodeBlockInfo cbi) {
- std::lock_guard guard(code_block_infos_mutex);
- if (auto iter = FindCodeBlockInfo(cbi.code_begin); iter != code_block_infos.end()) {
- code_block_infos.erase(iter);
- }
- code_block_infos.push_back(cbi);
-}
-
-void SigHandler::RemoveCodeBlock(u64 host_pc) {
- std::lock_guard guard(code_block_infos_mutex);
- const auto iter = FindCodeBlockInfo(host_pc);
- if (iter == code_block_infos.end()) {
- return;
- }
- code_block_infos.erase(iter);
-}
-
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
- ASSERT(sig == SIGSEGV || sig == SIGBUS);
-
+ DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
#ifndef MCL_ARCHITECTURE_RISCV
ucontext_t* ucontext = reinterpret_cast(raw_context);
#ifndef __OpenBSD__
@@ -157,7 +137,6 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
#endif
#if defined(MCL_ARCHITECTURE_X86_64)
-
# if defined(__APPLE__)
# define CTX_RIP (mctx->__ss.__rip)
# define CTX_RSP (mctx->__ss.__rsp)
@@ -179,26 +158,18 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
# else
# error "Unknown platform"
# endif
-
{
- std::lock_guard guard(sig_handler->code_block_infos_mutex);
-
- const auto iter = sig_handler->FindCodeBlockInfo(CTX_RIP);
- if (iter != sig_handler->code_block_infos.end()) {
- FakeCall fc = iter->cb(CTX_RIP);
-
+ std::shared_lock guard(sig_handler->code_block_infos_mutex);
+ if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_RIP); iter != sig_handler->code_block_infos.end()) {
+ FakeCall fc = iter->second.cb(CTX_RIP);
CTX_RSP -= sizeof(u64);
*mcl::bit_cast(CTX_RSP) = fc.ret_rip;
CTX_RIP = fc.call_rip;
-
return;
}
}
-
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
-
#elif defined(MCL_ARCHITECTURE_ARM64)
-
# if defined(__APPLE__)
# define CTX_PC (mctx->__ss.__pc)
# define CTX_SP (mctx->__ss.__sp)
@@ -240,30 +211,19 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
# else
# error "Unknown platform"
# endif
-
{
- std::lock_guard guard(sig_handler->code_block_infos_mutex);
-
- const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC);
- if (iter != sig_handler->code_block_infos.end()) {
- FakeCall fc = iter->cb(CTX_PC);
-
+ std::shared_lock guard(sig_handler->code_block_infos_mutex);
+ if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
+ FakeCall fc = iter->second.cb(CTX_PC);
CTX_PC = fc.call_pc;
-
return;
}
}
-
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
-
#elif defined(MCL_ARCHITECTURE_RISCV)
-
ASSERT_FALSE("Unimplemented");
-
#else
-
# error "Invalid architecture"
-
#endif
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
@@ -284,26 +244,26 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
} // anonymous namespace
struct ExceptionHandler::Impl final {
- Impl(u64 code_begin_, u64 code_end_)
- : code_begin(code_begin_)
- , code_end(code_end_) {
+ Impl(u64 offset_, u64 size_)
+ : offset(offset_)
+ , size(size_) {
RegisterHandler();
}
void SetCallback(std::function cb) {
- CodeBlockInfo cbi;
- cbi.code_begin = code_begin;
- cbi.code_end = code_end;
- cbi.cb = cb;
- sig_handler->AddCodeBlock(cbi);
+ sig_handler->AddCodeBlock(offset, CodeBlockInfo{
+ .size = size,
+ .cb = cb
+ });
}
~Impl() {
- sig_handler->RemoveCodeBlock(code_begin);
+ sig_handler->RemoveCodeBlock(offset);
}
private:
- u64 code_begin, code_end;
+ u64 offset;
+ u64 size;
};
ExceptionHandler::ExceptionHandler() = default;
@@ -311,28 +271,22 @@ ExceptionHandler::~ExceptionHandler() = default;
#if defined(MCL_ARCHITECTURE_X86_64)
void ExceptionHandler::Register(X64::BlockOfCode& code) {
- const u64 code_begin = mcl::bit_cast(code.getCode());
- const u64 code_end = code_begin + code.GetTotalCodeSize();
- impl = std::make_unique(code_begin, code_end);
+ impl = std::make_unique(mcl::bit_cast(code.getCode()), code.GetTotalCodeSize());
}
#elif defined(MCL_ARCHITECTURE_ARM64)
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
- const u64 code_begin = mcl::bit_cast(mem.ptr());
- const u64 code_end = code_begin + size;
- impl = std::make_unique(code_begin, code_end);
+ impl = std::make_unique(mcl::bit_cast(mem.ptr()), size);
}
#elif defined(MCL_ARCHITECTURE_RISCV)
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
- const u64 code_begin = mcl::bit_cast(mem.ptr());
- const u64 code_end = code_begin + size;
- impl = std::make_unique(code_begin, code_end);
+ impl = std::make_unique(mcl::bit_cast(mem.ptr()), size);
}
#else
# error "Invalid architecture"
#endif
bool ExceptionHandler::SupportsFastmem() const noexcept {
- return static_cast(impl) && sig_handler->SupportsFastmem();
+ return bool(impl) && sig_handler->SupportsFastmem();
}
void ExceptionHandler::SetFastmemCallback(std::function cb) {
diff --git a/src/frontend_common/firmware_manager.h b/src/frontend_common/firmware_manager.h
index 20f3b41478..23fce59eb3 100644
--- a/src/frontend_common/firmware_manager.h
+++ b/src/frontend_common/firmware_manager.h
@@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "core/core.h"
#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/content_archive.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/filesystem/filesystem.h"
#include
@@ -143,6 +144,8 @@ inline std::pair GetFirmwareVersion
return {firmware_data, result};
}
+
+// TODO(crueter): GET AS STRING
}
-#endif // FIRMWARE_MANAGER_H
+#endif
diff --git a/src/qt_common/CMakeLists.txt b/src/qt_common/CMakeLists.txt
new file mode 100644
index 0000000000..9d292da401
--- /dev/null
+++ b/src/qt_common/CMakeLists.txt
@@ -0,0 +1,47 @@
+# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+find_package(Qt6 REQUIRED COMPONENTS Core)
+find_package(Qt6 REQUIRED COMPONENTS Core)
+
+add_library(qt_common STATIC
+ qt_common.h
+ qt_common.cpp
+
+ uisettings.cpp
+ uisettings.h
+
+ qt_config.cpp
+ qt_config.h
+
+ shared_translation.cpp
+ shared_translation.h
+ qt_path_util.h qt_path_util.cpp
+ qt_game_util.h qt_game_util.cpp
+ qt_frontend_util.h qt_frontend_util.cpp
+ qt_meta.h qt_meta.cpp
+ qt_content_util.h qt_content_util.cpp
+ qt_rom_util.h qt_rom_util.cpp
+ qt_applet_util.h qt_applet_util.cpp
+ qt_progress_dialog.h qt_progress_dialog.cpp
+
+)
+
+create_target_directory_groups(qt_common)
+
+# TODO(crueter)
+if (ENABLE_QT)
+ target_link_libraries(qt_common PRIVATE Qt6::Widgets)
+endif()
+
+add_subdirectory(externals)
+
+target_link_libraries(qt_common PRIVATE core Qt6::Core SimpleIni::SimpleIni QuaZip::QuaZip frozen::frozen)
+target_link_libraries(qt_common PRIVATE Qt6::Core)
+
+if (NOT WIN32)
+ target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
+endif()
diff --git a/src/yuzu/externals/CMakeLists.txt b/src/qt_common/externals/CMakeLists.txt
similarity index 86%
rename from src/yuzu/externals/CMakeLists.txt
rename to src/qt_common/externals/CMakeLists.txt
index 50594a741f..189a52c0a6 100644
--- a/src/yuzu/externals/CMakeLists.txt
+++ b/src/qt_common/externals/CMakeLists.txt
@@ -14,3 +14,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
# QuaZip
AddJsonPackage(quazip)
+
+# frozen
+# TODO(crueter): Qt String Lookup
+AddJsonPackage(frozen)
diff --git a/src/yuzu/externals/cpmfile.json b/src/qt_common/externals/cpmfile.json
similarity index 55%
rename from src/yuzu/externals/cpmfile.json
rename to src/qt_common/externals/cpmfile.json
index e3590d0f7f..0b464b95b2 100644
--- a/src/yuzu/externals/cpmfile.json
+++ b/src/qt_common/externals/cpmfile.json
@@ -8,5 +8,12 @@
"options": [
"QUAZIP_INSTALL OFF"
]
+ },
+ "frozen": {
+ "package": "frozen",
+ "repo": "serge-sans-paille/frozen",
+ "sha": "61dce5ae18",
+ "hash": "1ae3d073e659c1f24b2cdd76379c90d6af9e06bc707d285a4fafce05f7a4c9e592ff208c94a9ae0f0d07620b3c6cec191f126b03d70ad4dfa496a86ed5658a6d",
+ "bundled": true
}
}
diff --git a/src/qt_common/qt_applet_util.cpp b/src/qt_common/qt_applet_util.cpp
new file mode 100644
index 0000000000..1b0189392e
--- /dev/null
+++ b/src/qt_common/qt_applet_util.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_applet_util.h"
diff --git a/src/qt_common/qt_applet_util.h b/src/qt_common/qt_applet_util.h
new file mode 100644
index 0000000000..2b48d16698
--- /dev/null
+++ b/src/qt_common/qt_applet_util.h
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_APPLET_UTIL_H
+#define QT_APPLET_UTIL_H
+
+// TODO
+namespace QtCommon::Applets {
+
+}
+#endif // QT_APPLET_UTIL_H
diff --git a/src/yuzu/qt_common.cpp b/src/qt_common/qt_common.cpp
similarity index 55%
rename from src/yuzu/qt_common.cpp
rename to src/qt_common/qt_common.cpp
index 413402165c..6be241c740 100644
--- a/src/yuzu/qt_common.cpp
+++ b/src/qt_common/qt_common.cpp
@@ -1,12 +1,19 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_common.h"
+#include "common/fs/fs.h"
#include
#include
-#include
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
-#include "yuzu/qt_common.h"
+
+#include
+
+#include
+
+#include
#if !defined(WIN32) && !defined(__APPLE__)
#include
@@ -15,7 +22,19 @@
#endif
namespace QtCommon {
-Core::Frontend::WindowSystemType GetWindowSystemType() {
+
+#ifdef YUZU_QT_WIDGETS
+QWidget* rootObject = nullptr;
+#else
+QObject* rootObject = nullptr;
+#endif
+
+std::unique_ptr system = nullptr;
+std::shared_ptr vfs = nullptr;
+std::unique_ptr provider = nullptr;
+
+Core::Frontend::WindowSystemType GetWindowSystemType()
+{
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
@@ -35,7 +54,8 @@ Core::Frontend::WindowSystemType GetWindowSystemType() {
return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType
-Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
+Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
+{
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
@@ -43,8 +63,8 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
// Our Win32 Qt external doesn't have the private API.
wsi.render_surface = reinterpret_cast(window->winId());
#elif defined(__APPLE__)
- wsi.render_surface = reinterpret_cast(objc_msgSend)(
- reinterpret_cast(window->winId()), sel_registerName("layer"));
+ wsi.render_surface = reinterpret_cast(
+ objc_msgSend)(reinterpret_cast(window->winId()), sel_registerName("layer"));
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
@@ -57,4 +77,46 @@ Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window)
return wsi;
}
+
+const QString tr(const char* str)
+{
+ return QGuiApplication::tr(str);
+}
+
+const QString tr(const std::string& str)
+{
+ return QGuiApplication::tr(str.c_str());
+}
+
+#ifdef YUZU_QT_WIDGETS
+void Init(QWidget* root)
+#else
+void Init(QObject* root)
+#endif
+{
+ system = std::make_unique();
+ rootObject = root;
+ vfs = std::make_unique();
+ provider = std::make_unique();
+}
+
+std::filesystem::path GetEdenCommand() {
+ std::filesystem::path command;
+
+ QString appimage = QString::fromLocal8Bit(getenv("APPIMAGE"));
+ if (!appimage.isEmpty()) {
+ command = std::filesystem::path{appimage.toStdString()};
+ } else {
+ const QStringList args = QGuiApplication::arguments();
+ command = args[0].toStdString();
+ }
+
+ // If relative path, make it an absolute path
+ if (command.c_str()[0] == '.') {
+ command = Common::FS::GetCurrentDir() / command;
+ }
+
+ return command;
+}
+
} // namespace QtCommon
diff --git a/src/qt_common/qt_common.h b/src/qt_common/qt_common.h
new file mode 100644
index 0000000000..a2700427ab
--- /dev/null
+++ b/src/qt_common/qt_common.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_COMMON_H
+#define QT_COMMON_H
+
+#include
+#include "core/core.h"
+#include "core/file_sys/registered_cache.h"
+#include
+#include
+
+#include
+
+namespace QtCommon {
+
+#ifdef YUZU_QT_WIDGETS
+extern QWidget *rootObject;
+#else
+extern QObject *rootObject;
+#endif
+
+extern std::unique_ptr system;
+extern std::shared_ptr vfs;
+extern std::unique_ptr provider;
+
+typedef std::function QtProgressCallback;
+
+Core::Frontend::WindowSystemType GetWindowSystemType();
+
+Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow *window);
+
+#ifdef YUZU_QT_WIDGETS
+void Init(QWidget *root);
+#else
+void Init(QObject *root);
+#endif
+
+const QString tr(const char *str);
+const QString tr(const std::string &str);
+
+std::filesystem::path GetEdenCommand();
+} // namespace QtCommon
+#endif
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/qt_common/qt_config.cpp
similarity index 99%
rename from src/yuzu/configuration/qt_config.cpp
rename to src/qt_common/qt_config.cpp
index ae5b330e23..f787873cf6 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/qt_common/qt_config.cpp
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
diff --git a/src/yuzu/configuration/qt_config.h b/src/qt_common/qt_config.h
similarity index 94%
rename from src/yuzu/configuration/qt_config.h
rename to src/qt_common/qt_config.h
index dc2dceb4d7..a8c80dd273 100644
--- a/src/yuzu/configuration/qt_config.h
+++ b/src/qt_common/qt_config.h
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
diff --git a/src/qt_common/qt_content_util.cpp b/src/qt_common/qt_content_util.cpp
new file mode 100644
index 0000000000..e4625aa423
--- /dev/null
+++ b/src/qt_common/qt_content_util.cpp
@@ -0,0 +1,313 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_content_util.h"
+#include "common/fs/fs.h"
+#include "frontend_common/content_manager.h"
+#include "frontend_common/firmware_manager.h"
+#include "qt_common/qt_common.h"
+#include "qt_common/qt_progress_dialog.h"
+#include "qt_frontend_util.h"
+
+#include
+
+namespace QtCommon::Content {
+
+bool CheckGameFirmware(u64 program_id, QObject* parent)
+{
+ if (FirmwareManager::GameRequiresFirmware(program_id)
+ && !FirmwareManager::CheckFirmwarePresence(*system)) {
+ auto result = QtCommon::Frontend::ShowMessage(
+ QMessageBox::Warning,
+ "Game Requires Firmware",
+ "The game you are trying to launch requires firmware to boot or to get past the "
+ "opening menu. Please "
+ "dump and install firmware, or press \"OK\" to launch anyways.",
+ QMessageBox::Ok | QMessageBox::Cancel,
+ parent);
+
+ return result == QMessageBox::Ok;
+ }
+
+ return true;
+}
+
+void InstallFirmware(const QString& location, bool recursive)
+{
+ QtCommon::Frontend::QtProgressDialog progress(tr("Installing Firmware..."),
+ tr("Cancel"),
+ 0,
+ 100,
+ rootObject);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+ progress.show();
+
+ // Declare progress callback.
+ auto callback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(static_cast((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ static constexpr const char* failedTitle = "Firmware Install Failed";
+ static constexpr const char* successTitle = "Firmware Install Succeeded";
+ QMessageBox::Icon icon;
+ FirmwareInstallResult result;
+
+ const auto ShowMessage = [&]() {
+ QtCommon::Frontend::ShowMessage(icon,
+ failedTitle,
+ GetFirmwareInstallResultString(result));
+ };
+
+ LOG_INFO(Frontend, "Installing firmware from {}", location.toStdString());
+
+ // Check for a reasonable number of .nca files (don't hardcode them, just see if there's some in
+ // there.)
+ std::filesystem::path firmware_source_path = location.toStdString();
+ if (!Common::FS::IsDir(firmware_source_path)) {
+ return;
+ }
+
+ std::vector out;
+ const Common::FS::DirEntryCallable dir_callback =
+ [&out](const std::filesystem::directory_entry& entry) {
+ if (entry.path().has_extension() && entry.path().extension() == ".nca") {
+ out.emplace_back(entry.path());
+ }
+
+ return true;
+ };
+
+ callback(100, 10);
+
+ if (recursive) {
+ Common::FS::IterateDirEntriesRecursively(firmware_source_path,
+ dir_callback,
+ Common::FS::DirEntryFilter::File);
+ } else {
+ Common::FS::IterateDirEntries(firmware_source_path,
+ dir_callback,
+ Common::FS::DirEntryFilter::File);
+ }
+
+ if (out.size() <= 0) {
+ result = FirmwareInstallResult::NoNCAs;
+ icon = QMessageBox::Warning;
+ ShowMessage();
+ return;
+ }
+
+ // Locate and erase the content of nand/system/Content/registered/*.nca, if any.
+ auto sysnand_content_vdir = system->GetFileSystemController().GetSystemNANDContentDirectory();
+ if (sysnand_content_vdir->IsWritable()
+ && !sysnand_content_vdir->CleanSubdirectoryRecursive("registered")) {
+ result = FirmwareInstallResult::FailedDelete;
+ icon = QMessageBox::Critical;
+ ShowMessage();
+ return;
+ }
+
+ LOG_INFO(Frontend,
+ "Cleaned nand/system/Content/registered folder in preparation for new firmware.");
+
+ callback(100, 20);
+
+ auto firmware_vdir = sysnand_content_vdir->GetDirectoryRelative("registered");
+
+ bool success = true;
+ int i = 0;
+ for (const auto& firmware_src_path : out) {
+ i++;
+ auto firmware_src_vfile = vfs->OpenFile(firmware_src_path.generic_string(),
+ FileSys::OpenMode::Read);
+ auto firmware_dst_vfile = firmware_vdir
+ ->CreateFileRelative(firmware_src_path.filename().string());
+
+ if (!VfsRawCopy(firmware_src_vfile, firmware_dst_vfile)) {
+ LOG_ERROR(Frontend,
+ "Failed to copy firmware file {} to {} in registered folder!",
+ firmware_src_path.generic_string(),
+ firmware_src_path.filename().string());
+ success = false;
+ }
+
+ if (callback(100, 20 + static_cast(((i) / static_cast(out.size())) * 70.0))) {
+ result = FirmwareInstallResult::FailedCorrupted;
+ icon = QMessageBox::Warning;
+ ShowMessage();
+ return;
+ }
+ }
+
+ if (!success) {
+ result = FirmwareInstallResult::FailedCopy;
+ icon = QMessageBox::Critical;
+ ShowMessage();
+ return;
+ }
+
+ // Re-scan VFS for the newly placed firmware files.
+ system->GetFileSystemController().CreateFactories(*vfs);
+
+ auto VerifyFirmwareCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(90 + static_cast((processed_size * 10) / total_size));
+ return progress.wasCanceled();
+ };
+
+ auto results = ContentManager::VerifyInstalledContents(*QtCommon::system,
+ *QtCommon::provider,
+ VerifyFirmwareCallback,
+ true);
+
+ if (results.size() > 0) {
+ const auto failed_names = QString::fromStdString(
+ fmt::format("{}", fmt::join(results, "\n")));
+ progress.close();
+ QtCommon::Frontend::Critical(tr("Firmware integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1")
+ .arg(failed_names));
+ return;
+ }
+
+ progress.close();
+
+ const auto pair = FirmwareManager::GetFirmwareVersion(*system);
+ const auto firmware_data = pair.first;
+ const std::string display_version(firmware_data.display_version.data());
+
+ result = FirmwareInstallResult::Success;
+ QtCommon::Frontend::Information(rootObject,
+ tr(successTitle),
+ tr(GetFirmwareInstallResultString(result))
+ .arg(QString::fromStdString(display_version)));
+}
+
+QString UnzipFirmwareToTmp(const QString& location)
+{
+ namespace fs = std::filesystem;
+ fs::path tmp{fs::temp_directory_path()};
+
+ if (!fs::create_directories(tmp / "eden" / "firmware")) {
+ return "";
+ }
+
+ tmp /= "eden";
+ tmp /= "firmware";
+
+ QString qCacheDir = QString::fromStdString(tmp.string());
+
+ QFile zip(location);
+
+ QStringList result = JlCompress::extractDir(&zip, qCacheDir);
+ if (result.isEmpty()) {
+ return "";
+ }
+
+ return qCacheDir;
+}
+
+// Content //
+void VerifyGameContents(const std::string& game_path)
+{
+ QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."),
+ tr("Cancel"),
+ 0,
+ 100,
+ rootObject);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+
+ const auto callback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(static_cast((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ const auto result = ContentManager::VerifyGameContents(*system, game_path, callback);
+
+ switch (result) {
+ case ContentManager::GameVerificationResult::Success:
+ QtCommon::Frontend::Information(rootObject,
+ tr("Integrity verification succeeded!"),
+ tr("The operation completed successfully."));
+ break;
+ case ContentManager::GameVerificationResult::Failed:
+ QtCommon::Frontend::Critical(rootObject,
+ tr("Integrity verification failed!"),
+ tr("File contents may be corrupt or missing."));
+ break;
+ case ContentManager::GameVerificationResult::NotImplemented:
+ QtCommon::Frontend::Warning(
+ rootObject,
+ tr("Integrity verification couldn't be performed"),
+ tr("Firmware installation cancelled, firmware may be in a bad state or corrupted. "
+ "File contents could not be checked for validity."));
+ }
+}
+
+void InstallKeys()
+{
+ const QString key_source_location
+ = QtCommon::Frontend::GetOpenFileName(tr("Select Dumped Keys Location"),
+ {},
+ QStringLiteral("Decryption Keys (*.keys)"),
+ {},
+ QtCommon::Frontend::Option::ReadOnly);
+
+ if (key_source_location.isEmpty()) {
+ return;
+ }
+
+ FirmwareManager::KeyInstallResult result = FirmwareManager::InstallKeys(key_source_location
+ .toStdString(),
+ "keys");
+
+ system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
+
+ switch (result) {
+ case FirmwareManager::KeyInstallResult::Success:
+ QtCommon::Frontend::Information(tr("Decryption Keys install succeeded"),
+ tr("Decryption Keys were successfully installed"));
+ break;
+ default:
+ QtCommon::Frontend::Critical(tr("Decryption Keys install failed"),
+ tr(FirmwareManager::GetKeyInstallResultString(result)));
+ break;
+ }
+}
+
+void VerifyInstalledContents() {
+ // Initialize a progress dialog.
+ QtCommon::Frontend::QtProgressDialog progress(tr("Verifying integrity..."), tr("Cancel"), 0, 100, QtCommon::rootObject);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.setMinimumDuration(100);
+ progress.setAutoClose(false);
+ progress.setAutoReset(false);
+
+ // Declare progress callback.
+ auto QtProgressCallback = [&](size_t total_size, size_t processed_size) {
+ progress.setValue(static_cast((processed_size * 100) / total_size));
+ return progress.wasCanceled();
+ };
+
+ const std::vector result =
+ ContentManager::VerifyInstalledContents(*QtCommon::system, *QtCommon::provider, QtProgressCallback);
+ progress.close();
+
+ if (result.empty()) {
+ QtCommon::Frontend::Information(tr("Integrity verification succeeded!"),
+ tr("The operation completed successfully."));
+ } else {
+ const auto failed_names =
+ QString::fromStdString(fmt::format("{}", fmt::join(result, "\n")));
+ QtCommon::Frontend::Critical(
+ tr("Integrity verification failed!"),
+ tr("Verification failed for the following files:\n\n%1").arg(failed_names));
+ }
+}
+
+} // namespace QtCommon::Content
diff --git a/src/qt_common/qt_content_util.h b/src/qt_common/qt_content_util.h
new file mode 100644
index 0000000000..b572c1c4a3
--- /dev/null
+++ b/src/qt_common/qt_content_util.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_CONTENT_UTIL_H
+#define QT_CONTENT_UTIL_H
+
+#include
+#include "common/common_types.h"
+
+namespace QtCommon::Content {
+
+//
+bool CheckGameFirmware(u64 program_id, QObject *parent);
+
+static constexpr std::array FIRMWARE_RESULTS
+ = {"Successfully installed firmware version %1",
+ "",
+ "Unable to locate potential firmware NCA files",
+ "Failed to delete one or more firmware files.",
+ "One or more firmware files failed to copy into NAND.",
+ "Firmware installation cancelled, firmware may be in a bad state or corrupted."
+ "Restart Eden or re-install firmware."};
+
+enum class FirmwareInstallResult {
+ Success,
+ NoOp,
+ NoNCAs,
+ FailedDelete,
+ FailedCopy,
+ FailedCorrupted,
+};
+
+inline constexpr const char *GetFirmwareInstallResultString(FirmwareInstallResult result)
+{
+ return FIRMWARE_RESULTS.at(static_cast(result));
+}
+
+void InstallFirmware(const QString &location, bool recursive);
+
+QString UnzipFirmwareToTmp(const QString &location);
+
+// Keys //
+void InstallKeys();
+
+// Content //
+void VerifyGameContents(const std::string &game_path);
+void VerifyInstalledContents();
+}
+#endif // QT_CONTENT_UTIL_H
diff --git a/src/qt_common/qt_frontend_util.cpp b/src/qt_common/qt_frontend_util.cpp
new file mode 100644
index 0000000000..d519669ad5
--- /dev/null
+++ b/src/qt_common/qt_frontend_util.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_frontend_util.h"
+#include "qt_common/qt_common.h"
+
+#ifdef YUZU_QT_WIDGETS
+#include
+#endif
+
+namespace QtCommon::Frontend {
+
+StandardButton ShowMessage(
+ Icon icon, const QString &title, const QString &text, StandardButtons buttons, QObject *parent)
+{
+#ifdef YUZU_QT_WIDGETS
+ QMessageBox *box = new QMessageBox(icon, title, text, buttons, (QWidget *) parent);
+ return static_cast(box->exec());
+#endif
+ // TODO(crueter): If Qt Widgets is disabled...
+ // need a way to reference icon/buttons too
+}
+
+const QString GetOpenFileName(const QString &title,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter,
+ Options options)
+{
+#ifdef YUZU_QT_WIDGETS
+ return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
+#endif
+}
+
+} // namespace QtCommon::Frontend
diff --git a/src/qt_common/qt_frontend_util.h b/src/qt_common/qt_frontend_util.h
new file mode 100644
index 0000000000..f86b9e1357
--- /dev/null
+++ b/src/qt_common/qt_frontend_util.h
@@ -0,0 +1,148 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_FRONTEND_UTIL_H
+#define QT_FRONTEND_UTIL_H
+
+#include
+#include "qt_common/qt_common.h"
+
+#ifdef YUZU_QT_WIDGETS
+#include
+#include
+#include
+#endif
+
+/**
+ * manages common functionality e.g. message boxes and such for Qt/QML
+ */
+namespace QtCommon::Frontend {
+
+Q_NAMESPACE
+
+#ifdef YUZU_QT_WIDGETS
+using Options = QFileDialog::Options;
+using Option = QFileDialog::Option;
+
+using StandardButton = QMessageBox::StandardButton;
+using StandardButtons = QMessageBox::StandardButtons;
+
+using Icon = QMessageBox::Icon;
+#else
+enum Option {
+ ShowDirsOnly = 0x00000001,
+ DontResolveSymlinks = 0x00000002,
+ DontConfirmOverwrite = 0x00000004,
+ DontUseNativeDialog = 0x00000008,
+ ReadOnly = 0x00000010,
+ HideNameFilterDetails = 0x00000020,
+ DontUseCustomDirectoryIcons = 0x00000040
+};
+Q_ENUM_NS(Option)
+Q_DECLARE_FLAGS(Options, Option)
+Q_FLAG_NS(Options)
+
+enum StandardButton {
+ // keep this in sync with QDialogButtonBox::StandardButton and QPlatformDialogHelper::StandardButton
+ NoButton = 0x00000000,
+ Ok = 0x00000400,
+ Save = 0x00000800,
+ SaveAll = 0x00001000,
+ Open = 0x00002000,
+ Yes = 0x00004000,
+ YesToAll = 0x00008000,
+ No = 0x00010000,
+ NoToAll = 0x00020000,
+ Abort = 0x00040000,
+ Retry = 0x00080000,
+ Ignore = 0x00100000,
+ Close = 0x00200000,
+ Cancel = 0x00400000,
+ Discard = 0x00800000,
+ Help = 0x01000000,
+ Apply = 0x02000000,
+ Reset = 0x04000000,
+ RestoreDefaults = 0x08000000,
+
+ FirstButton = Ok, // internal
+ LastButton = RestoreDefaults, // internal
+
+ YesAll = YesToAll, // obsolete
+ NoAll = NoToAll, // obsolete
+
+ Default = 0x00000100, // obsolete
+ Escape = 0x00000200, // obsolete
+ FlagMask = 0x00000300, // obsolete
+ ButtonMask = ~FlagMask // obsolete
+};
+Q_ENUM_NS(StandardButton)
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+typedef StandardButton Button;
+#endif
+Q_DECLARE_FLAGS(StandardButtons, StandardButton)
+Q_FLAG_NS(StandardButtons)
+
+enum Icon {
+ // keep this in sync with QMessageDialogOptions::StandardIcon
+ NoIcon = 0,
+ Information = 1,
+ Warning = 2,
+ Critical = 3,
+ Question = 4
+};
+Q_ENUM_NS(Icon)
+
+#endif
+
+// TODO(crueter) widgets-less impl, choices et al.
+StandardButton ShowMessage(Icon icon,
+ const QString &title,
+ const QString &text,
+ StandardButtons buttons = StandardButton::NoButton,
+ QObject *parent = nullptr);
+
+#define UTIL_OVERRIDES(level) \
+ inline StandardButton level(QObject *parent, \
+ const QString &title, \
+ const QString &text, \
+ StandardButtons buttons = StandardButton::Ok) \
+ { \
+ return ShowMessage(Icon::level, title, text, buttons, parent); \
+ } \
+ inline StandardButton level(QObject *parent, \
+ const char *title, \
+ const char *text, \
+ StandardButtons buttons \
+ = StandardButton::Ok) \
+ { \
+ return ShowMessage(Icon::level, tr(title), tr(text), buttons, parent); \
+ } \
+ inline StandardButton level(const char *title, \
+ const char *text, \
+ StandardButtons buttons \
+ = StandardButton::Ok) \
+ { \
+ return ShowMessage(Icon::level, tr(title), tr(text), buttons, rootObject); \
+ } \
+ inline StandardButton level(const QString title, \
+ const QString &text, \
+ StandardButtons buttons \
+ = StandardButton::Ok) \
+ { \
+ return ShowMessage(Icon::level, title, text, buttons, rootObject); \
+ }
+
+UTIL_OVERRIDES(Information)
+UTIL_OVERRIDES(Warning)
+UTIL_OVERRIDES(Critical)
+UTIL_OVERRIDES(Question)
+
+const QString GetOpenFileName(const QString &title,
+ const QString &dir,
+ const QString &filter,
+ QString *selectedFilter = nullptr,
+ Options options = Options());
+
+} // namespace QtCommon::Frontend
+#endif // QT_FRONTEND_UTIL_H
diff --git a/src/qt_common/qt_game_util.cpp b/src/qt_common/qt_game_util.cpp
new file mode 100644
index 0000000000..5d0b4d8ae7
--- /dev/null
+++ b/src/qt_common/qt_game_util.cpp
@@ -0,0 +1,577 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_game_util.h"
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/hle/service/am/am_types.h"
+#include "frontend_common/content_manager.h"
+#include "qt_common.h"
+#include "qt_common/uisettings.h"
+#include "qt_frontend_util.h"
+#include "yuzu/util/util.h"
+
+#include
+#include
+#include
+
+#ifdef _WIN32
+#include "common/scope_exit.h"
+#include "common/string_util.h"
+#include
+#include
+#else
+#include "fmt/ostream.h"
+#include
+#endif
+
+namespace QtCommon::Game {
+
+bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
+ const std::string& comment,
+ const std::filesystem::path& icon_path,
+ const std::filesystem::path& command,
+ const std::string& arguments,
+ const std::string& categories,
+ const std::string& keywords,
+ const std::string& name)
+try {
+#ifdef _WIN32 // Windows
+ HRESULT hr = CoInitialize(nullptr);
+ if (FAILED(hr)) {
+ LOG_ERROR(Frontend, "CoInitialize failed");
+ return false;
+ }
+ SCOPE_EXIT
+ {
+ CoUninitialize();
+ };
+ IShellLinkW* ps1 = nullptr;
+ IPersistFile* persist_file = nullptr;
+ SCOPE_EXIT
+ {
+ if (persist_file != nullptr) {
+ persist_file->Release();
+ }
+ if (ps1 != nullptr) {
+ ps1->Release();
+ }
+ };
+ HRESULT hres = CoCreateInstance(CLSID_ShellLink,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IShellLinkW,
+ reinterpret_cast(&ps1));
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
+ return false;
+ }
+ hres = ps1->SetPath(command.c_str());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set path");
+ return false;
+ }
+ if (!arguments.empty()) {
+ hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set arguments");
+ return false;
+ }
+ }
+ if (!comment.empty()) {
+ hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set description");
+ return false;
+ }
+ }
+ if (std::filesystem::is_regular_file(icon_path)) {
+ hres = ps1->SetIconLocation(icon_path.c_str(), 0);
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to set icon location");
+ return false;
+ }
+ }
+ hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast(&persist_file));
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
+ return false;
+ }
+ hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
+ if (FAILED(hres)) {
+ LOG_ERROR(Frontend, "Failed to save shortcut");
+ return false;
+ }
+ return true;
+#elif defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__) // Any desktop NIX
+ std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
+ std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
+ if (!shortcut_stream.is_open()) {
+ LOG_ERROR(Frontend, "Failed to create shortcut");
+ return false;
+ }
+ // TODO: Migrate fmt::print to std::print in futures STD C++ 23.
+ fmt::print(shortcut_stream, "[Desktop Entry]\n");
+ fmt::print(shortcut_stream, "Type=Application\n");
+ fmt::print(shortcut_stream, "Version=1.0\n");
+ fmt::print(shortcut_stream, "Name={}\n", name);
+ if (!comment.empty()) {
+ fmt::print(shortcut_stream, "Comment={}\n", comment);
+ }
+ if (std::filesystem::is_regular_file(icon_path)) {
+ fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
+ }
+ fmt::print(shortcut_stream, "TryExec={}\n", command.string());
+ fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
+ if (!categories.empty()) {
+ fmt::print(shortcut_stream, "Categories={}\n", categories);
+ }
+ if (!keywords.empty()) {
+ fmt::print(shortcut_stream, "Keywords={}\n", keywords);
+ }
+ return true;
+#else // Unsupported platform
+ return false;
+#endif
+} catch (const std::exception& e) {
+ LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
+ return false;
+}
+
+bool MakeShortcutIcoPath(const u64 program_id,
+ const std::string_view game_file_name,
+ std::filesystem::path& out_icon_path)
+{
+ // Get path to Yuzu icons directory & icon extension
+ std::string ico_extension = "png";
+#if defined(_WIN32)
+ out_icon_path = Common::FS::GetEdenPath(Common::FS::EdenPath::IconsDir);
+ ico_extension = "ico";
+#elif defined(__linux__) || defined(__FreeBSD__)
+ out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
+#endif
+ // Create icons directory if it doesn't exist
+ if (!Common::FS::CreateDirs(out_icon_path)) {
+ out_icon_path.clear();
+ return false;
+ }
+
+ // Create icon file path
+ out_icon_path /= (program_id == 0 ? fmt::format("eden-{}.{}", game_file_name, ico_extension)
+ : fmt::format("eden-{:016X}.{}", program_id, ico_extension));
+ return true;
+}
+
+void OpenEdenFolder(const Common::FS::EdenPath& path)
+{
+ QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(Common::FS::GetEdenPathString(path))));
+}
+
+void OpenRootDataFolder()
+{
+ OpenEdenFolder(Common::FS::EdenPath::EdenDir);
+}
+
+void OpenNANDFolder()
+{
+ OpenEdenFolder(Common::FS::EdenPath::NANDDir);
+}
+
+void OpenSDMCFolder()
+{
+ OpenEdenFolder(Common::FS::EdenPath::SDMCDir);
+}
+
+void OpenModFolder()
+{
+ OpenEdenFolder(Common::FS::EdenPath::LoadDir);
+}
+
+void OpenLogFolder()
+{
+ OpenEdenFolder(Common::FS::EdenPath::LogDir);
+}
+
+static QString GetGameListErrorRemoving(QtCommon::Game::InstalledEntryType type)
+{
+ switch (type) {
+ case QtCommon::Game::InstalledEntryType::Game:
+ return tr("Error Removing Contents");
+ case QtCommon::Game::InstalledEntryType::Update:
+ return tr("Error Removing Update");
+ case QtCommon::Game::InstalledEntryType::AddOnContent:
+ return tr("Error Removing DLC");
+ default:
+ return QStringLiteral("Error Removing ");
+ }
+}
+
+// Game Content //
+void RemoveBaseContent(u64 program_id, InstalledEntryType type)
+{
+ const auto res = ContentManager::RemoveBaseContent(system->GetFileSystemController(),
+ program_id);
+ if (res) {
+ QtCommon::Frontend::Information(rootObject,
+ "Successfully Removed",
+ "Successfully removed the installed base game.");
+ } else {
+ QtCommon::Frontend::Warning(
+ rootObject,
+ GetGameListErrorRemoving(type),
+ tr("The base game is not installed in the NAND and cannot be removed."));
+ }
+}
+
+void RemoveUpdateContent(u64 program_id, InstalledEntryType type)
+{
+ const auto res = ContentManager::RemoveUpdate(system->GetFileSystemController(), program_id);
+ if (res) {
+ QtCommon::Frontend::Information(rootObject,
+ "Successfully Removed",
+ "Successfully removed the installed update.");
+ } else {
+ QtCommon::Frontend::Warning(rootObject,
+ GetGameListErrorRemoving(type),
+ tr("There is no update installed for this title."));
+ }
+}
+
+void RemoveAddOnContent(u64 program_id, InstalledEntryType type)
+{
+ const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
+ if (count == 0) {
+ QtCommon::Frontend::Warning(rootObject,
+ GetGameListErrorRemoving(type),
+ tr("There are no DLCs installed for this title."));
+ return;
+ }
+
+ QtCommon::Frontend::Information(rootObject,
+ tr("Successfully Removed"),
+ tr("Successfully removed %1 installed DLC.").arg(count));
+}
+
+// Global Content //
+
+void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target)
+{
+ const auto target_file_name = [target] {
+ switch (target) {
+ case GameListRemoveTarget::GlShaderCache:
+ return "opengl.bin";
+ case GameListRemoveTarget::VkShaderCache:
+ return "vulkan.bin";
+ default:
+ return "";
+ }
+ }();
+ const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
+ const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
+ const auto target_file = shader_cache_folder_path / target_file_name;
+
+ if (!Common::FS::Exists(target_file)) {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Transferable Shader Cache"),
+ tr("A shader cache for this title does not exist."));
+ return;
+ }
+ if (Common::FS::RemoveFile(target_file)) {
+ QtCommon::Frontend::Information(rootObject,
+ tr("Successfully Removed"),
+ tr("Successfully removed the transferable shader cache."));
+ } else {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Transferable Shader Cache"),
+ tr("Failed to remove the transferable shader cache."));
+ }
+}
+
+void RemoveVulkanDriverPipelineCache(u64 program_id)
+{
+ static constexpr std::string_view target_file_name = "vulkan_pipelines.bin";
+
+ const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
+ const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id);
+ const auto target_file = shader_cache_folder_path / target_file_name;
+
+ if (!Common::FS::Exists(target_file)) {
+ return;
+ }
+ if (!Common::FS::RemoveFile(target_file)) {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Vulkan Driver Pipeline Cache"),
+ tr("Failed to remove the driver pipeline cache."));
+ }
+}
+
+void RemoveAllTransferableShaderCaches(u64 program_id)
+{
+ const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
+ const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id);
+
+ if (!Common::FS::Exists(program_shader_cache_dir)) {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Transferable Shader Caches"),
+ tr("A shader cache for this title does not exist."));
+ return;
+ }
+ if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) {
+ QtCommon::Frontend::Information(rootObject,
+ tr("Successfully Removed"),
+ tr("Successfully removed the transferable shader caches."));
+ } else {
+ QtCommon::Frontend::Warning(
+ rootObject,
+ tr("Error Removing Transferable Shader Caches"),
+ tr("Failed to remove the transferable shader cache directory."));
+ }
+}
+
+void RemoveCustomConfiguration(u64 program_id, const std::string& game_path)
+{
+ const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path));
+ const auto config_file_name = program_id == 0
+ ? Common::FS::PathToUTF8String(file_path.filename())
+ .append(".ini")
+ : fmt::format("{:016X}.ini", program_id);
+ const auto custom_config_file_path = Common::FS::GetEdenPath(Common::FS::EdenPath::ConfigDir)
+ / "custom" / config_file_name;
+
+ if (!Common::FS::Exists(custom_config_file_path)) {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Custom Configuration"),
+ tr("A custom configuration for this title does not exist."));
+ return;
+ }
+
+ if (Common::FS::RemoveFile(custom_config_file_path)) {
+ QtCommon::Frontend::Information(rootObject,
+ tr("Successfully Removed"),
+ tr("Successfully removed the custom game configuration."));
+ } else {
+ QtCommon::Frontend::Warning(rootObject,
+ tr("Error Removing Custom Configuration"),
+ tr("Failed to remove the custom game configuration."));
+ }
+}
+
+void RemoveCacheStorage(u64 program_id)
+{
+ const auto nand_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
+ auto vfs_nand_dir = vfs->OpenDirectory(Common::FS::PathToUTF8String(nand_dir),
+ FileSys::OpenMode::Read);
+
+ const auto cache_storage_path
+ = FileSys::SaveDataFactory::GetFullPath({},
+ vfs_nand_dir,
+ FileSys::SaveDataSpaceId::User,
+ FileSys::SaveDataType::Cache,
+ 0 /* program_id */,
+ {},
+ 0);
+
+ const auto path = Common::FS::ConcatPathSafe(nand_dir, cache_storage_path);
+
+ // Not an error if it wasn't cleared.
+ Common::FS::RemoveDirRecursively(path);
+}
+
+// Metadata //
+void ResetMetadata()
+{
+ const QString title = tr("Reset Metadata Cache");
+
+ if (!Common::FS::Exists(Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir)
+ / "game_list/")) {
+ QtCommon::Frontend::Warning(rootObject, title, tr("The metadata cache is already empty."));
+ } else if (Common::FS::RemoveDirRecursively(
+ Common::FS::GetEdenPath(Common::FS::EdenPath::CacheDir) / "game_list")) {
+ QtCommon::Frontend::Information(rootObject,
+ title,
+ tr("The operation completed successfully."));
+ UISettings::values.is_game_list_reload_pending.exchange(true);
+ } else {
+ QtCommon::Frontend::Warning(
+ rootObject,
+ title,
+ tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
+ }
+}
+
+// Uhhh //
+
+// Messages in pre-defined message boxes for less code spaghetti
+inline constexpr bool CreateShortcutMessagesGUI(ShortcutMessages imsg, const QString& game_title)
+{
+ int result = 0;
+ QMessageBox::StandardButtons buttons;
+ switch (imsg) {
+ case ShortcutMessages::Fullscreen:
+ buttons = QMessageBox::Yes | QMessageBox::No;
+ result
+ = QtCommon::Frontend::Information(tr("Create Shortcut"),
+ tr("Do you want to launch the game in fullscreen?"),
+ buttons);
+ return result == QMessageBox::Yes;
+ case ShortcutMessages::Success:
+ QtCommon::Frontend::Information(tr("Shortcut Created"),
+ tr("Successfully created a shortcut to %1").arg(game_title));
+ return false;
+ case ShortcutMessages::Volatile:
+ buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
+ result = QtCommon::Frontend::Warning(
+ tr("Shortcut may be Volatile!"),
+ tr("This will create a shortcut to the current AppImage. This may "
+ "not work well if you update. Continue?"),
+ buttons);
+ return result == QMessageBox::Ok;
+ default:
+ buttons = QMessageBox::Ok;
+ QtCommon::Frontend::Critical(tr("Failed to Create Shortcut"),
+ tr("Failed to create a shortcut to %1").arg(game_title),
+ buttons);
+ return false;
+ }
+}
+
+void CreateShortcut(const std::string& game_path,
+ const u64 program_id,
+ const std::string& game_title_,
+ const ShortcutTarget &target,
+ std::string arguments_,
+ const bool needs_title)
+{
+ // Get path to Eden executable
+ std::filesystem::path command = GetEdenCommand();
+
+ // Shortcut path
+ std::filesystem::path shortcut_path = GetShortcutPath(target);
+
+ if (!std::filesystem::exists(shortcut_path)) {
+ CreateShortcutMessagesGUI(ShortcutMessages::Failed,
+ QString::fromStdString(shortcut_path.generic_string()));
+ LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
+ return;
+ }
+
+ const FileSys::PatchManager pm{program_id, QtCommon::system->GetFileSystemController(),
+ QtCommon::system->GetContentProvider()};
+ const auto control = pm.GetControlMetadata();
+ const auto loader =
+ Loader::GetLoader(*QtCommon::system, QtCommon::vfs->OpenFile(game_path, FileSys::OpenMode::Read));
+
+ std::string game_title{game_title_};
+
+ // Delete illegal characters from title
+ if (needs_title) {
+ game_title = fmt::format("{:016X}", program_id);
+ if (control.first != nullptr) {
+ game_title = control.first->GetApplicationName();
+ } else {
+ loader->ReadTitle(game_title);
+ }
+ }
+
+ const std::string illegal_chars = "<>:\"/\\|?*.";
+ for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
+ if (illegal_chars.find(*it) != std::string::npos) {
+ game_title.erase(it.base() - 1);
+ }
+ }
+
+ const QString qgame_title = QString::fromStdString(game_title);
+
+ // Get icon from game file
+ std::vector icon_image_file{};
+ if (control.second != nullptr) {
+ icon_image_file = control.second->ReadAllBytes();
+ } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
+ LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
+ }
+
+ QImage icon_data =
+ QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size()));
+ std::filesystem::path out_icon_path;
+ if (QtCommon::Game::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
+ if (!SaveIconToFile(out_icon_path, icon_data)) {
+ LOG_ERROR(Frontend, "Could not write icon to file");
+ }
+ } else {
+ QtCommon::Frontend::Critical(
+ tr("Create Icon"),
+ tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
+ .arg(QString::fromStdString(out_icon_path.string())));
+ }
+
+#if defined(__unix__) && !defined(__APPLE__) && !defined(__ANDROID__)
+ // Special case for AppImages
+ // Warn once if we are making a shortcut to a volatile AppImage
+ if (command.string().ends_with(".AppImage") && !UISettings::values.shortcut_already_warned) {
+ if (!CreateShortcutMessagesGUI(ShortcutMessages::Volatile, qgame_title)) {
+ return;
+ }
+ UISettings::values.shortcut_already_warned = true;
+ }
+#endif
+
+ // Create shortcut
+ std::string arguments{arguments_};
+ if (CreateShortcutMessagesGUI(ShortcutMessages::Fullscreen, qgame_title)) {
+ arguments = "-f " + arguments;
+ }
+ const std::string comment = fmt::format("Start {:s} with the Eden Emulator", game_title);
+ const std::string categories = "Game;Emulator;Qt;";
+ const std::string keywords = "Switch;Nintendo;";
+
+ if (QtCommon::Game::CreateShortcutLink(shortcut_path, comment, out_icon_path, command,
+ arguments, categories, keywords, game_title)) {
+ CreateShortcutMessagesGUI(ShortcutMessages::Success,
+ qgame_title);
+ return;
+ }
+ CreateShortcutMessagesGUI(ShortcutMessages::Failed,
+ qgame_title);
+}
+
+constexpr std::string GetShortcutPath(ShortcutTarget target) {
+ {
+ std::string shortcut_path{};
+ if (target == ShortcutTarget::Desktop) {
+ shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)
+ .toStdString();
+ } else if (target == ShortcutTarget::Applications) {
+ shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)
+ .toStdString();
+ }
+
+ return shortcut_path;
+ }
+}
+
+void CreateHomeMenuShortcut(ShortcutTarget target) {
+ constexpr u64 QLaunchId = static_cast(Service::AM::AppletProgramId::QLaunch);
+ auto bis_system = QtCommon::system->GetFileSystemController().GetSystemNANDContents();
+ if (!bis_system) {
+ QtCommon::Frontend::Warning(tr("No firmware available"),
+ tr("Please install firmware to use the home menu."));
+ return;
+ }
+
+ auto qlaunch_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
+ if (!qlaunch_nca) {
+ QtCommon::Frontend::Warning(tr("Home Menu Applet"),
+ tr("Home Menu is not available. Please reinstall firmware."));
+ return;
+ }
+
+ auto qlaunch_applet_nca = bis_system->GetEntry(QLaunchId, FileSys::ContentRecordType::Program);
+ const auto game_path = qlaunch_applet_nca->GetFullPath();
+
+ // TODO(crueter): Make this use the Eden icon
+ CreateShortcut(game_path, QLaunchId, "Switch Home Menu", target, "-qlaunch", false);
+}
+
+
+} // namespace QtCommon::Game
diff --git a/src/qt_common/qt_game_util.h b/src/qt_common/qt_game_util.h
new file mode 100644
index 0000000000..0a21208659
--- /dev/null
+++ b/src/qt_common/qt_game_util.h
@@ -0,0 +1,85 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_GAME_UTIL_H
+#define QT_GAME_UTIL_H
+
+#include
+#include
+#include "common/fs/path_util.h"
+
+namespace QtCommon::Game {
+
+enum class InstalledEntryType {
+ Game,
+ Update,
+ AddOnContent,
+};
+
+enum class GameListRemoveTarget {
+ GlShaderCache,
+ VkShaderCache,
+ AllShaderCache,
+ CustomConfiguration,
+ CacheStorage,
+};
+
+enum class ShortcutTarget {
+ Desktop,
+ Applications,
+};
+
+enum class ShortcutMessages{
+ Fullscreen = 0,
+ Success = 1,
+ Volatile = 2,
+ Failed = 3
+};
+
+bool CreateShortcutLink(const std::filesystem::path& shortcut_path,
+ const std::string& comment,
+ const std::filesystem::path& icon_path,
+ const std::filesystem::path& command,
+ const std::string& arguments,
+ const std::string& categories,
+ const std::string& keywords,
+ const std::string& name);
+
+bool MakeShortcutIcoPath(const u64 program_id,
+ const std::string_view game_file_name,
+ std::filesystem::path& out_icon_path);
+
+void OpenEdenFolder(const Common::FS::EdenPath &path);
+void OpenRootDataFolder();
+void OpenNANDFolder();
+void OpenSDMCFolder();
+void OpenModFolder();
+void OpenLogFolder();
+
+void RemoveBaseContent(u64 program_id, InstalledEntryType type);
+void RemoveUpdateContent(u64 program_id, InstalledEntryType type);
+void RemoveAddOnContent(u64 program_id, InstalledEntryType type);
+
+void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target);
+void RemoveVulkanDriverPipelineCache(u64 program_id);
+void RemoveAllTransferableShaderCaches(u64 program_id);
+void RemoveCustomConfiguration(u64 program_id, const std::string& game_path);
+void RemoveCacheStorage(u64 program_id);
+
+// Metadata //
+void ResetMetadata();
+
+// Shortcuts //
+void CreateShortcut(const std::string& game_path,
+ const u64 program_id,
+ const std::string& game_title_,
+ const ShortcutTarget& target,
+ std::string arguments_,
+ const bool needs_title);
+
+constexpr std::string GetShortcutPath(ShortcutTarget target);
+void CreateHomeMenuShortcut(ShortcutTarget target);
+
+}
+
+#endif // QT_GAME_UTIL_H
diff --git a/src/qt_common/qt_meta.cpp b/src/qt_common/qt_meta.cpp
new file mode 100644
index 0000000000..67ae659771
--- /dev/null
+++ b/src/qt_common/qt_meta.cpp
@@ -0,0 +1,75 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_meta.h"
+#include "common/common_types.h"
+#include "core/core.h"
+#include "core/frontend/applets/cabinet.h"
+#include "core/frontend/applets/controller.h"
+#include "core/frontend/applets/profile_select.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/hle/service/am/frontend/applet_web_browser_types.h"
+
+namespace QtCommon::Meta {
+
+void RegisterMetaTypes()
+{
+ // Register integral and floating point types
+ qRegisterMetaType("u8");
+ qRegisterMetaType("u16");
+ qRegisterMetaType("u32");
+ qRegisterMetaType("u64");
+ qRegisterMetaType("u128");
+ qRegisterMetaType("s8");
+ qRegisterMetaType("s16");
+ qRegisterMetaType("s32");
+ qRegisterMetaType("s64");
+ qRegisterMetaType("f32");
+ qRegisterMetaType("f64");
+
+ // Register string types
+ qRegisterMetaType("std::string");
+ qRegisterMetaType("std::wstring");
+ qRegisterMetaType("std::u8string");
+ qRegisterMetaType("std::u16string");
+ qRegisterMetaType("std::u32string");
+ qRegisterMetaType("std::string_view");
+ qRegisterMetaType("std::wstring_view");
+ qRegisterMetaType("std::u8string_view");
+ qRegisterMetaType("std::u16string_view");
+ qRegisterMetaType("std::u32string_view");
+
+ // Register applet types
+
+ // Cabinet Applet
+ qRegisterMetaType("Core::Frontend::CabinetParameters");
+ qRegisterMetaType>(
+ "std::shared_ptr");
+
+ // Controller Applet
+ qRegisterMetaType("Core::Frontend::ControllerParameters");
+
+ // Profile Select Applet
+ qRegisterMetaType(
+ "Core::Frontend::ProfileSelectParameters");
+
+ // Software Keyboard Applet
+ qRegisterMetaType(
+ "Core::Frontend::KeyboardInitializeParameters");
+ qRegisterMetaType(
+ "Core::Frontend::InlineAppearParameters");
+ qRegisterMetaType("Core::Frontend::InlineTextParameters");
+ qRegisterMetaType("Service::AM::Frontend::SwkbdResult");
+ qRegisterMetaType(
+ "Service::AM::Frontend::SwkbdTextCheckResult");
+ qRegisterMetaType(
+ "Service::AM::Frontend::SwkbdReplyType");
+
+ // Web Browser Applet
+ qRegisterMetaType("Service::AM::Frontend::WebExitReason");
+
+ // Register loader types
+ qRegisterMetaType("Core::SystemResultStatus");
+}
+
+}
diff --git a/src/qt_common/qt_meta.h b/src/qt_common/qt_meta.h
new file mode 100644
index 0000000000..c0a37db983
--- /dev/null
+++ b/src/qt_common/qt_meta.h
@@ -0,0 +1,15 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_META_H
+#define QT_META_H
+
+#include
+
+namespace QtCommon::Meta {
+
+//
+void RegisterMetaTypes();
+
+}
+#endif // QT_META_H
diff --git a/src/qt_common/qt_path_util.cpp b/src/qt_common/qt_path_util.cpp
new file mode 100644
index 0000000000..761e6e8405
--- /dev/null
+++ b/src/qt_common/qt_path_util.cpp
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_path_util.h"
+#include
+#include
+#include
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
+#include "qt_common/qt_frontend_util.h"
+#include
+
+namespace QtCommon::Path {
+
+bool OpenShaderCache(u64 program_id, QObject *parent)
+{
+ const auto shader_cache_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir);
+ const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
+ if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
+ QtCommon::Frontend::ShowMessage(QMessageBox::Warning, "Error Opening Shader Cache", "Failed to create or open shader cache for this title, ensure your app data directory has write permissions.", QMessageBox::Ok, parent);
+ }
+
+ const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
+ const auto qt_shader_cache_path = QString::fromStdString(shader_path_string);
+ return QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_path));
+}
+
+}
diff --git a/src/qt_common/qt_path_util.h b/src/qt_common/qt_path_util.h
new file mode 100644
index 0000000000..855b06caa9
--- /dev/null
+++ b/src/qt_common/qt_path_util.h
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_PATH_UTIL_H
+#define QT_PATH_UTIL_H
+
+#include "common/common_types.h"
+#include
+
+namespace QtCommon::Path { bool OpenShaderCache(u64 program_id, QObject *parent); }
+
+#endif // QT_PATH_UTIL_H
diff --git a/src/qt_common/qt_progress_dialog.cpp b/src/qt_common/qt_progress_dialog.cpp
new file mode 100644
index 0000000000..b4bf74c8bd
--- /dev/null
+++ b/src/qt_common/qt_progress_dialog.cpp
@@ -0,0 +1,4 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_progress_dialog.h"
diff --git a/src/qt_common/qt_progress_dialog.h b/src/qt_common/qt_progress_dialog.h
new file mode 100644
index 0000000000..17f6817ffa
--- /dev/null
+++ b/src/qt_common/qt_progress_dialog.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_PROGRESS_DIALOG_H
+#define QT_PROGRESS_DIALOG_H
+
+#include
+
+#ifdef YUZU_QT_WIDGETS
+#include
+#endif
+
+namespace QtCommon::Frontend {
+#ifdef YUZU_QT_WIDGETS
+
+using QtProgressDialog = QProgressDialog;
+
+// TODO(crueter): QML impl
+#else
+class QtProgressDialog
+{
+public:
+ QtProgressDialog(const QString &labelText,
+ const QString &cancelButtonText,
+ int minimum,
+ int maximum,
+ QObject *parent = nullptr,
+ Qt::WindowFlags f = Qt::WindowFlags());
+
+ bool wasCanceled() const;
+ void setWindowModality(Qt::WindowModality modality);
+ void setMinimumDuration(int durationMs);
+ void setAutoClose(bool autoClose);
+ void setAutoReset(bool autoReset);
+
+public slots:
+ void setLabelText(QString &text);
+ void setRange(int min, int max);
+ void setValue(int progress);
+ bool close();
+
+ void show();
+};
+#endif // YUZU_QT_WIDGETS
+
+}
+#endif // QT_PROGRESS_DIALOG_H
diff --git a/src/qt_common/qt_rom_util.cpp b/src/qt_common/qt_rom_util.cpp
new file mode 100644
index 0000000000..08ccb05a97
--- /dev/null
+++ b/src/qt_common/qt_rom_util.cpp
@@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "qt_rom_util.h"
+
+#include
+
+namespace QtCommon::ROM {
+
+bool RomFSRawCopy(size_t total_size,
+ size_t& read_size,
+ QtProgressCallback callback,
+ const FileSys::VirtualDir& src,
+ const FileSys::VirtualDir& dest,
+ bool full)
+{
+ // TODO(crueter)
+ // if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ // return false;
+ // if (dialog.wasCanceled())
+ // return false;
+
+ // std::vector buffer(CopyBufferSize);
+ // auto last_timestamp = std::chrono::steady_clock::now();
+
+ // const auto QtRawCopy = [&](const FileSys::VirtualFile& src_file,
+ // const FileSys::VirtualFile& dest_file) {
+ // if (src_file == nullptr || dest_file == nullptr) {
+ // return false;
+ // }
+ // if (!dest_file->Resize(src_file->GetSize())) {
+ // return false;
+ // }
+
+ // for (std::size_t i = 0; i < src_file->GetSize(); i += buffer.size()) {
+ // if (dialog.wasCanceled()) {
+ // dest_file->Resize(0);
+ // return false;
+ // }
+
+ // using namespace std::literals::chrono_literals;
+ // const auto new_timestamp = std::chrono::steady_clock::now();
+
+ // if ((new_timestamp - last_timestamp) > 33ms) {
+ // last_timestamp = new_timestamp;
+ // dialog.setValue(
+ // static_cast(std::min(read_size, total_size) * 100 / total_size));
+ // QCoreApplication::processEvents();
+ // }
+
+ // const auto read = src_file->Read(buffer.data(), buffer.size(), i);
+ // dest_file->Write(buffer.data(), read, i);
+
+ // read_size += read;
+ // }
+
+ // return true;
+ // };
+
+ // if (full) {
+ // for (const auto& file : src->GetFiles()) {
+ // const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
+ // if (!QtRawCopy(file, out))
+ // return false;
+ // }
+ // }
+
+ // for (const auto& dir : src->GetSubdirectories()) {
+ // const auto out = dest->CreateSubdirectory(dir->GetName());
+ // if (!RomFSRawCopy(total_size, read_size, dialog, dir, out, full))
+ // return false;
+ // }
+
+ // return true;
+ return true;
+}
+
+}
diff --git a/src/qt_common/qt_rom_util.h b/src/qt_common/qt_rom_util.h
new file mode 100644
index 0000000000..f76b09753d
--- /dev/null
+++ b/src/qt_common/qt_rom_util.h
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#ifndef QT_ROM_UTIL_H
+#define QT_ROM_UTIL_H
+
+#include "qt_common/qt_common.h"
+#include
+
+namespace QtCommon::ROM {
+
+bool RomFSRawCopy(size_t total_size,
+ size_t& read_size,
+ QtProgressCallback callback,
+ const FileSys::VirtualDir& src,
+ const FileSys::VirtualDir& dest,
+ bool full);
+
+}
+#endif // QT_ROM_UTIL_H
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/qt_common/shared_translation.cpp
similarity index 89%
rename from src/yuzu/configuration/shared_translation.cpp
rename to src/qt_common/shared_translation.cpp
index 1137145659..cdc05e60e0 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/qt_common/shared_translation.cpp
@@ -7,23 +7,21 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "yuzu/configuration/shared_translation.h"
+#include "shared_translation.h"
#include
-#include
#include "common/settings.h"
#include "common/settings_enums.h"
#include "common/settings_setting.h"
#include "common/time_zone.h"
-#include "yuzu/uisettings.h"
+#include "qt_common/uisettings.h"
#include