Compare commits

...

14 Commits

Author SHA1 Message Date
wildcard 0cda02e91e potential fix for totk and smo for amd 2026-06-23 05:31:28 +02:00
lizzie f8facda35f [vk] Prevent OOM due to increased staging buffer on *nix (#4116)
*nix drivers for NVIDIA doesn't seem to like the increased size.
Windows is fine with it however.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4116
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-23 01:48:11 +02:00
crueter 39be450fa3 [desktop] Add basic carousel view (#4112)
Adds a basic carousel view, or essentially a horizontal list a la Android/Qt Quick.

Lacks a lot of niceties like autoscroll, smooth shifts, etc. Will work on those later

Also fixed a bug introduced recently that capped game icon size to 8 at the low end, breaking the None option

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4112
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-06-22 21:04:47 +02:00
MaranBr 68aaea6085 [buffer_cache] Refactor buffer synchronization to use bounding-box download region (#4094)
Implement bounding-box buffer synchronization to minimize downloads and preserve GPU-modified regions during uploads.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4094
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-06-22 17:05:59 +02:00
MaranBr ba608d2b57 [video_core] Add in-flight flush throttling to prevent CPU/GPU desync (#4085)
Prevent the CPU from getting too far ahead of the GPU by limiting pending flushes.

This fixes graphical issues in games that rely on better CPU and GPU synchronization in the Scheduler, such as the flickering bug inside shrines in The Legend of Zelda: Tears of the Kingdom.

This is enabled when the GPU is in Accurate mode.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4085
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-06-22 17:05:18 +02:00
MaranBr c22f57bc4e [video_core] Improve StagingBufferPool stream allocation (#4101)
This fixes (I really hope so) the random graphical corruption issue that can occur in Mario Kart 8 Deluxe and possibly other games as well.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4101
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-06-22 17:04:52 +02:00
Eden CI 7b97ec4594 [dist, android] Update translations from Transifex for Jun 16 (#4102)
Automatic translation update for Jun 16

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4102
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-20 02:16:46 +02:00
crueter e63b248e54 [ci] Fix update-deps workflow (again) (#4110)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4110
2026-06-20 00:58:02 +02:00
lizzie 97a8470b12 [android] fix 'clear per game shader cache' not accounting for new cache (#4109)
fixes android which I didn't account for initially
oops!

Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4109
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-19 23:42:16 +02:00
crueter 102d254530 [desktop] Clean up game list code, fix external watcher crash, and fix macOS flickering (#4106)
- Remove unnecessary icon update code (the UI reloads this stuff anyways); test on Windows please
- Cleaned up a bunch of duplicated/unused code within the game list
- Fix the game list constantly reloading on macOS
  * When you reconstruct the entire directory list on the watcher the directoryChanged signal fires on macOS--seems like a behavioral change that occurred somewhere in the 6.8 release cycle--and it would enter an infinite loop very quickly
  * To fix this, only the differences between the current and old watch list are accounted for on both ends.
  * Since this bug is now fixed, macOS uses Qt 6.11.1 now. Should theoretically improve our situation.
- Fix the external content watcher crashing; the worker would attempt to read files that didn't exist without any bounds since its cache was still pointing to that file.

This supersedes and replaces #4099.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4106
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-19 22:55:29 +02:00
crueter 7c0e993b5b [*] Fix compilation on fmt 12.2.0 (#4107)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4107
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-19 22:50:46 +02:00
BoiledElectricity ce14fc91fb [game_list] fix crash and flashing from the directory watcher (#4099)
So the game list watcher could rebuild the content providers
(CreateFactories) while the populate worker was still scanning them, and
the worker would walk a torn-down RegisteredCache and segfault in
OpenFileOrDirectoryConcat. Fixed by stopping and joining the worker
before rebuilding.

On macOS the watcher also re-armed itself every populate. Re-adding the
same paths makes QFileSystemWatcher re-emit directoryChanged (the
FSEvent comes in async, so the blockSignals guard misses it), so it just
kept refreshing and the list flashed forever. Now it only re-arms when
the watched dirs actually changed.

Also null-guarded OpenFileOrDirectoryConcat so a torn-down cache cant
null-deref there.

Related: https://github.com/eden-emulator/Issue-Reports/issues/336
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4099
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-16 16:20:02 +02:00
omardotdev c27266c4cd [qt] fix configure_debug; use a better method of getting multiple setting values (#4092)
uses `QString::number(Settings::values.serial_battery.GetValue())` instead of `QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));` on two different setting options

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4092
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-06-16 16:16:12 +02:00
crueter 02eaf3c5a7 [cmake, docs] Reorganize CMakeModules directory and add GentooCross toolchain (#4088)
Lightly inspired by my earlier toolchain and writeup from the PowerPC
PR.

Closes #3793

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4088
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-16 16:15:49 +02:00
88 changed files with 12808 additions and 12201 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
}
EOF
curl -X 'POST' \
curl -XPOST \
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
-H 'accept: application/json' \
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
+8 -5
View File
@@ -1,4 +1,4 @@
name: update-deps
name: Update Dependencies
on:
# saturday at noon
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
jobs:
tx-update:
update-deps:
runs-on: source
steps:
- uses: actions/checkout@v4
@@ -24,18 +24,21 @@ jobs:
git remote set-url origin ci:eden-emu/eden.git
DATE=$(date +"%b %d")
TIMESTAMP=$(date +"%s")
echo "DATE=$DATE" >> "$GITHUB_ENV"
echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV"
git switch -c update-deps-$DATE
git switch -c update-deps-$TIMESTAMP
tools/cpmutil.sh package update -ac
git push
- name: Create PR
run: |
set -x
TITLE="[externals] Dependency update for $DATE"
BODY="$(git show -s --format='%b')"
BASE=master
HEAD=update-deps-$DATE
HEAD=update-deps-$TIMESTAMP
cat << EOF > data.json
{
@@ -46,7 +49,7 @@ jobs:
}
EOF
curl -X 'POST' \
curl -XPOST \
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
-H 'accept: application/json' \
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
+2 -6
View File
@@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 3.31)
project(yuzu)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/find")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
set(CPM_SOURCE_CACHE ${CMAKE_SOURCE_DIR}/.cache/cpm)
@@ -588,12 +589,7 @@ endif()
# Qt stuff
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
# Qt 6.8+ is broken on macOS (??)
if (APPLE)
AddQt(Eden-CI/Qt 6.7.3)
else()
AddQt(Eden-CI/Qt 6.11.1)
endif()
AddQt(Eden-CI/Qt 6.11.1)
else()
message(STATUS "Using system Qt")
if (NOT Qt6_DIR)
+68 -56
View File
@@ -128,7 +128,8 @@ function(AddDependentPackages)
message(FATAL_ERROR "Partial dependency installation detected "
"for the following packages:\n${package_names}\n"
"You can solve this in one of two ways:\n"
"1. Install the following packages to your system if available:"
"1. Install or upgrade the following packages "
"to your system if available:"
"\n\t${bundled_names}\n"
"2. Set the following variables to ON:"
"\n\t${system_names}\n"
@@ -241,15 +242,14 @@ function(AddJsonPackage)
# these are overrides that can be generated at runtime,
# so can be defined separately from the json
DOWNLOAD_ONLY
BUNDLED_PACKAGE
FORCE_BUNDLED_PACKAGE)
set(multiValueArgs OPTIONS)
set(options MODULE)
set(optionArgs MODULE_PATH DOWNLOAD_ONLY)
cmake_parse_arguments(JSON "${options}" "${oneValueArgs}" "${multiValueArgs}"
cmake_parse_arguments(JSON "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
list(LENGTH ARGN argnLength)
@@ -278,11 +278,15 @@ function(AddJsonPackage)
parse_object(${object})
if(ci)
if (JSON_MODULE)
set(EXTRA_ARGS MODULE)
endif()
if (JSON_MODULE_PATH)
list(APPEND EXTRA_ARGS MODULE_PATH)
endif()
if (JSON_DOWNLOAD_ONLY)
list(APPEND EXTRA_ARGS DOWNLOAD_ONLY)
endif()
if(ci)
AddCIPackage(
VERSION ${version}
NAME ${name}
@@ -313,40 +317,25 @@ function(AddJsonPackage)
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
SOURCE_SUBDIR "${source_subdir}"
GIT_VERSION ${git_version}
GIT_HOST ${git_host}
GIT_VERSION "${git_version}"
GIT_HOST "${git_host}"
ARTIFACT ${artifact}
TAG ${tag})
ARTIFACT "${artifact}"
TAG "${tag}"
${EXTRA_ARGS})
endif()
# pass stuff to parent scope
Propagate(${package}_ADDED)
Propagate(${package}_SOURCE_DIR)
Propagate(${package}_BINARY_DIR)
Propagate(CMAKE_PREFIX_PATH)
endfunction()
function(AddPackage)
cpm_set_policies()
set(EXTRA_ARGS "")
# TODO(crueter): git clone?
#[[
URL configurations, descending order of precedence:
- URL [+ GIT_URL] -> bare URL fetch
- REPO + TAG + ARTIFACT -> github release artifact
- REPO + TAG -> github release archive
- REPO + SHA -> github commit archive
- REPO + BRANCH -> github branch
Hash configurations, descending order of precedence:
- HASH -> bare sha512sum
- HASH_SUFFIX -> hash grabbed from the URL + this suffix
- HASH_URL -> hash grabbed from a URL
* technically this is unsafe since a hacker can attack that url
NOTE: hash algo defaults to sha512
#]]
set(oneValueArgs
NAME
VERSION
@@ -366,6 +355,7 @@ function(AddPackage)
URL
GIT_URL
SOURCE_SUBDIR
KEY
BUNDLED_PACKAGE
@@ -374,7 +364,9 @@ function(AddPackage)
set(multiValueArgs OPTIONS PATCHES)
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}"
set(optionArgs MODULE_PATH DOWNLOAD_ONLY)
cmake_parse_arguments(PKG_ARGS "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
if(NOT DEFINED PKG_ARGS_NAME)
@@ -558,16 +550,36 @@ function(AddPackage)
VERSION ${PKG_ARGS_VERSION})
endif()
CPMAddPackage(
NAME ${PKG_ARGS_NAME}
URL ${pkg_url}
URL_HASH ${pkg_hash}
CUSTOM_CACHE_KEY ${pkg_key}
DOWNLOAD_ONLY ${PKG_ARGS_DOWNLOAD_ONLY}
FIND_PACKAGE_ARGUMENTS ${PKG_ARGS_FIND_PACKAGE_ARGUMENTS}
if (PKG_ARGS_FIND_PACKAGE_ARGUMENTS)
list(APPEND EXTRA_ARGS
FIND_PACKAGE_ARGUMENTS "${PKG_ARGS_FIND_PACKAGE_ARGUMENTS}")
endif()
if (PKG_ARGS_PATCHES)
list(APPEND EXTRA_ARGS
PATCHES "${PKG_ARGS_PATCHES}")
endif()
if (PKG_ARGS_OPTIONS)
list(APPEND EXTRA_ARGS
OPTIONS "${PKG_ARGS_OPTIONS}")
endif()
if (PKG_ARGS_SOURCE_SUBDIR)
list(APPEND EXTRA_ARGS
SOURCE_SUBDIR "${PKG_ARGS_SOURCE_SUBDIR}")
endif()
if (PKG_ARGS_DOWNLOAD_ONLY OR PKG_ARGS_MODULE_PATH)
list(APPEND EXTRA_ARGS DOWNLOAD_ONLY ON)
endif()
CPMAddPackage(
NAME "${PKG_ARGS_NAME}"
URL "${pkg_url}"
URL_HASH "${pkg_hash}"
CUSTOM_CACHE_KEY "${pkg_key}"
OPTIONS ${PKG_ARGS_OPTIONS}
PATCHES ${PKG_ARGS_PATCHES}
EXCLUDE_FROM_ALL ON
${EXTRA_ARGS}
@@ -607,13 +619,14 @@ function(AddPackage)
endif()
# pass stuff to parent scope
set(${PKG_ARGS_NAME}_ADDED "${${PKG_ARGS_NAME}_ADDED}"
PARENT_SCOPE)
set(${PKG_ARGS_NAME}_SOURCE_DIR "${${PKG_ARGS_NAME}_SOURCE_DIR}"
PARENT_SCOPE)
set(${PKG_ARGS_NAME}_BINARY_DIR "${${PKG_ARGS_NAME}_BINARY_DIR}"
PARENT_SCOPE)
Propagate(${PKG_ARGS_NAME}_ADDED)
Propagate(${PKG_ARGS_NAME}_SOURCE_DIR)
Propagate(${PKG_ARGS_NAME}_BINARY_DIR)
if (PKG_ARGS_MODULE_PATH)
list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
Propagate(CMAKE_PREFIX_PATH)
endif()
endfunction()
# TODO(crueter): we could do an AddMultiArchPackage, multiplatformpackage?
@@ -629,7 +642,7 @@ function(AddCIPackage)
set(multiValueArgs DISABLED_PLATFORMS)
set(optionArgs MODULE)
set(optionArgs MODULE_PATH)
cmake_parse_arguments(PKG_ARGS
"${optionArgs}"
@@ -706,6 +719,10 @@ function(AddCIPackage)
set(ARTIFACT
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
if (PKG_ARGS_MODULE_PATH)
list(APPEND EXTRA_ARGS MODULE_PATH)
endif()
AddPackage(
NAME ${ARTIFACT_PACKAGE}
REPO ${ARTIFACT_REPO}
@@ -716,16 +733,11 @@ function(AddCIPackage)
KEY "${pkgname}-${ARTIFACT_VERSION}"
HASH_SUFFIX sha512sum
FORCE_BUNDLED_PACKAGE ON
DOWNLOAD_ONLY ${PKG_ARGS_MODULE})
${EXTRA_ARGS})
set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE)
set(${ARTIFACT_PACKAGE}_SOURCE_DIR
"${${ARTIFACT_PACKAGE}_SOURCE_DIR}" PARENT_SCOPE)
if (PKG_ARGS_MODULE)
list(PREPEND CMAKE_PREFIX_PATH "${${ARTIFACT_PACKAGE}_SOURCE_DIR}")
Propagate(CMAKE_PREFIX_PATH)
endif()
Propagate(${ARTIFACT_PACKAGE}_SOURCE_DIR)
Propagate(CMAKE_PREFIX_PATH)
else()
find_package(${ARTIFACT_PACKAGE} ${ARTIFACT_MIN_VERSION} REQUIRED)
endif()
@@ -750,7 +762,7 @@ function(AddQt repo version)
DISABLED_PLATFORMS
android-x86_64 android-aarch64
freebsd-amd64 solaris-amd64 openbsd-amd64
MODULE)
MODULE_PATH)
find_package(Qt6 REQUIRED PATHS ${Qt6_SOURCE_DIR} NO_DEFAULT_PATH)
-39
View File
@@ -1,39 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# This file provides the function windows_copy_files.
# This is only valid on Windows.
# Include guard
if(__windows_copy_files)
return()
endif()
set(__windows_copy_files YES)
# Any number of files to copy from SOURCE_DIR to DEST_DIR can be specified after DEST_DIR.
# This copying happens post-build.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
# windows commandline expects the / to be \ so switch them
string(REPLACE "/" "\\\\" SOURCE_DIR ${SOURCE_DIR})
string(REPLACE "/" "\\\\" DEST_DIR ${DEST_DIR})
# /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output
# cmake adds an extra check for command success which doesn't work too well with robocopy
# so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
COMMAND robocopy ${SOURCE_DIR} ${DEST_DIR} ${ARGN} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
)
endfunction()
else()
function(windows_copy_files TARGET SOURCE_DIR DEST_DIR)
add_custom_command(TARGET ${TARGET} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEST_DIR}
COMMAND cp -ra ${SOURCE_DIR}/. ${DEST_DIR}
)
endfunction()
endif()
-30
View File
@@ -1,30 +0,0 @@
# SPDX-FileCopyrightText: 2024 kleidis
[aqt]
concurrency: 2
[mirrors]
trusted_mirrors:
https://download.qt.io
blacklist:
https://qt.mirror.constant.com
https://mirrors.ocf.berkeley.edu
https://mirrors.ustc.edu.cn
https://mirrors.tuna.tsinghua.edu.cn
https://mirrors.geekpie.club
https://mirrors-wan.geekpie.club
https://mirrors.sjtug.sjtu.edu.cn
fallbacks:
https://qtproject.mirror.liquidtelecom.com/
https://mirrors.aliyun.com/qt/
https://ftp.jaist.ac.jp/pub/qtproject/
https://ftp.yz.yamagata-u.ac.jp/pub/qtproject/
https://qt-mirror.dannhauer.de/
https://ftp.fau.de/qtproject/
https://mirror.netcologne.de/qtproject/
https://mirrors.dotsrc.org/qtproject/
https://www.nic.funet.fi/pub/mirrors/download.qt-project.org/
https://master.qt.io/
https://mirrors.ukfast.co.uk/sites/qt.io/
https://ftp2.nluug.nl/languages/qt/
https://ftp1.nluug.nl/languages/qt/
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2019 Citra Emulator Project
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 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
@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
#
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
find_package(PkgConfig QUIET)
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+39
View File
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
set(CROSS_TARGET "" CACHE STRING "Cross-compilation target (aarch64, etc)")
set(CROSS_PLATFORM "unknown-linux-gnu" CACHE STRING "Cross-compilation platform (e.g. unknown-linux-gnu)")
set(CROSS_COMPILER "gcc" CACHE STRING "Cross compiler type (gcc or clang)")
if (NOT CROSS_TARGET)
message(FATAL_ERROR "GentooCross used without a valid CROSS_TARGET")
endif()
set(prefix ${CROSS_TARGET}-${CROSS_PLATFORM})
set(CMAKE_SYSROOT /usr/${prefix})
if (CROSS_COMPILER STREQUAL "gcc")
set(CMAKE_C_COMPILER ${prefix}-gcc)
set(CMAKE_CXX_COMPILER ${prefix}-g++)
elseif (CROSS_COMPILER STREQUAL "clang")
set(CMAKE_C_COMPILER ${prefix}-clang)
set(CMAKE_CXX_COMPILER ${prefix}-clang++)
else()
message(FATAL_ERROR "Unsupported cross compiler type ${CROSS_COMPILER}")
endif()
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
# sanity checks
if (NOT IS_DIRECTORY ${CMAKE_SYSROOT})
message(FATAL_ERROR "Invalid sysroot ${CMAKE_SYSROOT}."
"Double-check your CROSS_TARGET and CROSS_PLATFORM.")
endif()
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+478 -468
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+440 -430
View File
File diff suppressed because it is too large Load Diff
+78
View File
@@ -10,3 +10,81 @@ A painless guide for cross compilation (or to test NCE) from a x86_64 system wit
- Download Debian 13: `wget https://cdimage.debian.org/debian-cd/current/arm64/iso-cd/debian-13.0.0-arm64-netinst.iso`
- Create a system disk: `qemu-img create -f qcow2 debian-13-arm64-ci.qcow2 30G`
- Run the VM: `qemu-system-aarch64 -M virt -m 2G -cpu max -bios /usr/local/share/qemu/edk2-aarch64-code.fd -drive if=none,file=debian-13.0.0-arm64-netinst.iso,format=raw,id=cdrom -device scsi-cd,drive=cdrom -drive if=none,file=debian-13-arm64-ci.qcow2,id=hd0,format=qcow2 -device virtio-blk-device,drive=hd0 -device virtio-gpu-pci -device usb-ehci -device usb-kbd -device intel-hda -device hda-output -nic user,model=virtio-net-pci`
## Gentoo
Gentoo's cross-compilation setup is relatively easy, provided you're already familiar with portage. A [cross toolchain file](../CMakeModules/toolchains/GentooCross.cmake) is provided. Throughout this section, replace `aarch64` with whatever target architecture you desire.
### Crossdev
First, emerge crossdev via `sudo emerge -a sys-devel/crossdev`.
Now, set up the environment depending on the target architecture; e.g.
```sh
sudo crossdev aarch64
```
### QEMU
If you don't have a host Gentoo system of your target architecture, you should install a QEMU user setup for testing. To do so, enable the relevant USE flags for `app-emulation/qemu`:
```txt
app-emulation/qemu static-user qemu_user_targets_aarch64
```
To use cross-emerged shared libraries, you will also need to tell qemu where the sysroot is. You can do this with an alias:
```sh
alias qemu-aarch64="qemu-aarch64 -L /usr/aarch64-unknown-linux-gnu"
```
### Dependencies
Dependencies are the same [as normal Gentoo](./Deps.md#Commands); simply replace the `emerge` command with `emerge-<target>-unknown-linux-gnu` (e.g. `emerge-aarch64-unknown-linux-gnu`). However, there are a few caveats:
#### Enabling GURU
Since Crossdev sysroots are effectively isolated from the system w.r.t Portage, you must manually enable GURU in your sysroot. Run the following as root:
```sh
mkdir -p /usr/aarch64-unknown-linux-gnu/etc/portage/repos.conf
cat << EOF > /usr/aarch64-unknown-linux-gnu/etc/portage/repos.conf/guru.conf
[guru]
location = /var/db/repos/guru
auto-sync = no
priority = 1
EOF
```
#### Package Errata
Crossdev is not perfect, and you may face some challenges with package that are not properly keyworded or have issues on specific architectures. These behaviors are, unfortunately, not well documented, and certain build systems such as Meson--and certain troublesome packages like GTK--are generally unfriendly towards cross-compilation.
Thus, it may be desirable to emerge a minimal set of dependencies and allow Eden's build system to handle the rest for you. At a minimum, you *only* need standard system libraries (Crossdev does this for you) and Qt:
```sh
sudo emerge-aarch64-unknown-linux-gnu dev-qt/qtbase:6 dev-qt/qtcharts:6
```
From here, CPMUtil will take care of everything else. For extra insurance, you may want to set `-DCPMUTIL_FORCE_BUNDLED=ON` in your configure command.
### Building
From here, building is relatively standard. The [cross toolchain file](../CMakeModules/toolchains/GentooCross.cmake) contains a few additional configurations, but generally all you need to do is set `CROSS_TARGET` and `CMAKE_TOOLCHAIN_FILE`. Disabling OpenGL is strongly recommended as well.
```sh
cmake -S . -B build/aarch64 -DCMAKE_TOOLCHAIN_FILE=CMakeModules/toolchains/GentooCross.cmake -GNinja -DCROSS_TARGET=aarch64 -DENABLE_OPENGL=OFF
```
With that done, you can build as normal:
```sh
cmake --build build/aarch64
```
And finally, run the compiled executable with QEMU!
```sh
qemu-aarch64 build/aarch64/bin/eden
```
@@ -440,7 +440,7 @@ class GamePropertiesFragment : Fragment() {
val shaderCacheDir = File(
DirectoryInitialization.userDirectory +
"/shader/" + args.game.settingsName.lowercase()
"/cache/shader/" + args.game.settingsName.lowercase()
)
if (shaderCacheDir.exists()) {
add(
@@ -428,6 +428,9 @@
<string name="cpu_accuracy">دقة وحدة المعالجة المركزية</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">وسائط Homebrew</string>
<string name="program_args_description">سطر الأوامر الوسائط المُمررة إلى Homebrew عند التشغيل (مثل -noglsl).</string>
<!-- System settings strings -->
<string name="device_name">اسم الجهاز</string>
<string name="use_docked_mode">وضع الإرساء</string>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">Diese Software führt Spiele für die Nintendo Switch-Konsole aus. Es sind keine Spiele oder Keys enthalten. Suche bevor du beginnst deine <![CDATA[<b> prod.keys </b>]]>-Datei auf deinem Gerät.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Mehr Erfahren</a>]]></string>
<string name="app_disclaimer">Diese Software führt Spiele für die Nintendo Switch-Konsole aus. Es sind keine Spiele oder Schlüssel enthalten. Suche, bevor du beginnst, deine <![CDATA[<b> prod.keys </b>]]>-Datei auf deinem Gerät.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Mehr Erfahren</a>]]></string>
<string name="notice_notification_channel_name">Hinweise und Fehler</string>
<string name="notice_notification_channel_description">Zeigt Benachrichtigungen an, wenn etwas schief läuft.</string>
<string name="notification_permission_not_granted">Berechtigung für Benachrichtigungen nicht zugelassen!</string>
@@ -16,22 +16,27 @@
<string name="value_too_high">Wert darf höchstens %1$d betragen</string>
<string name="invalid_value">Ungültiger Wert</string>
<string name="using_per_game_config">Verwendung spielspezifischer Konfigurationen</string>
<!-- Input Overlay -->
<string name="show_input_overlay">Eingabe-Overlay anzeigen</string>
<string name="show_input_overlay">Eingabeüberlagerung anzeigen</string>
<string name="show_input_overlay_description">Touch-Bedienelemente während der Emulation anzeigen</string>
<string name="overlay_snap_to_grid">Am Raster ausrichten</string>
<string name="overlay_snap_to_grid_description">Beim Bearbeiten die Overlay-Bedienelemente am Raster ausrichten</string>
<string name="overlay_snap_to_grid_description">Überlagerungs-Steuerelemente beim Bearbeiten an einem Raster ausrichten</string>
<string name="overlay_grid_size">Rastergröße</string>
<string name="overlay_grid_size_description">Rastergröße in Pixeln</string>
<string name="overlay_grid_size_description">Größe der Rasterzellen in Pixeln</string>
<string name="input_overlay_behavior">Verhalten</string>
<string name="overlay_auto_hide">Automatisches Ausblenden des Overlays</string>
<string name="overlay_auto_hide_description">Blende die Touch-Bedienelemente nach Ablauf der angegebenen Inaktivitätszeit automatisch aus</string>
<string name="enable_input_overlay_auto_hide">Automatisches Ausblenden des Overlays aktivieren</string>
<string name="hide_overlay_on_controller_input">Overlay bei Controller-Eingabe ausblenden</string>
<string name="hide_overlay_on_controller_input_description">Blende die Touch-Bedienelemente automatisch aus wenn ein physischer Controller benutzt wird. Das Overlay wird wieder eingeblendet, wenn die Verbindung zum Controller getrennt wird.</string>
<string name="input_overlay_options">Eingabe-Overlay</string>
<string name="overlay_auto_hide">Automatisches Ausblenden der Überlagerung</string>
<string name="overlay_auto_hide_description">Überlagerung der Touch-Bedienelementen automatisch nach der festgelegten Zeit der Inaktivität ausblenden.</string>
<string name="enable_input_overlay_auto_hide">Automatisches Ausblenden der Überlagerung aktivieren</string>
<string name="hide_overlay_on_controller_input">Überlagerung bei Controller-Eingabe ausblenden</string>
<string name="hide_overlay_on_controller_input_description">Touch-Bedienelemente automatisch ausblenden, wenn ein physischer Controller benutzt wird. Die Überlagerung wird wieder eingeblendet, wenn die Verbindung zum Controller getrennt wird.</string>
<string name="invert_confirm_back_controller_buttons">Controller-Tasten für Bestätigen/Zurück vertauschen</string>
<string name="invert_confirm_back_controller_buttons_description">Belegung der Android-Tasten „Bestätigen“ und „Zurück“ so tauschen, dass sie bei der Nutzung der App-Benutzeroberfläche sowohl dem Switch- als auch dem Xbox-Schema entsprechen.</string>
<string name="input_overlay_options">Eingabeüberlagerung</string>
<string name="input_overlay_options_description">Bedienelemente auf dem Bildschirm konfigurieren</string>
<string name="edit_overlay_layout">Overlay-Layout bearbeiten</string>
<string name="edit_overlay_layout">Überlagerungs-Layout bearbeiten</string>
<string name="edit_overlay_layout_description">Position und Skalierung der Bedienelemente auf dem Bildschirm anpassen</string>
@@ -45,12 +50,12 @@
<string name="show_stats_overlay">Leistungsstatistik Overlay anzeigen</string>
<string name="stats_overlay_customization">Anpassung</string>
<string name="stats_overlay_items">Sichtbarkeit</string>
<string name="stats_overlay_options">Leistungs Overlay</string>
<string name="enable_stats_overlay_">Leistungsstatistik Overlay aktivieren</string>
<string name="stats_overlay_options_description">Konfiguriere angezeigte Informationen im Overlay</string>
<string name="show_fps">FPS anzeigen</string>
<string name="stats_overlay_options">Leistungsüberlagerung</string>
<string name="enable_stats_overlay_">Leistungsstatistik-Überlagerung aktivieren</string>
<string name="stats_overlay_options_description">Angezeigte Informationen in der Überlagerung konfigurieren</string>
<string name="show_fps">BpS anzeigen</string>
<string name="show_fps_description">Aktuelle Bilder pro Sekunde</string>
<string name="show_frametime">Frametime anzeigen</string>
<string name="show_frametime">Frame-Zeit anzeigen</string>
<string name="show_app_ram_usage">App-RAM-Nutzung anzeigen</string>
<string name="show_app_ram_usage_description">Zeigt den RAM-Verbrauch des Emulators an</string>
<string name="show_system_ram_usage">System-RAM-Nutzung anzeigen</string>
@@ -59,25 +64,28 @@
<string name="bat_temperature_unit">Batterietemperatur-Einheiten</string>
<string name="show_power_info">Batterieinfo anzeigen</string>
<string name="show_power_info_description">Aktuellen Stromverbrauch und verbleibende Kapazität der Batterie anzeigen</string>
<string name="show_shaders_building">Shader-Bau anzeigen</string>
<string name="show_shaders_building_description">Aktuelle Anzahl der erstellten Shader anzeigen</string>
<string name="overlay_position">Overlay-Position</string>
<string name="overlay_position_description">Position des Overlays auf dem Bildschirm</string>
<string name="show_shaders_building">Schattierer-Erstellung anzeigen</string>
<string name="show_shaders_building_description">Aktuelle Anzahl der erstellten Schattierer anzeigen</string>
<string name="pipeline_worker_cores_description">Lege die Anzahl der Kerne fest, die für die Erstellung von Vulkan-Rohrleitungen verwendet werden sollen. Ein höherer Wert verbessert die Kompilierungsleistung der Rohrleitung, führt jedoch auch zu einem Anstieg der Temperaturen.</string>
<string name="overlay_position">Überlagerungs-Position</string>
<string name="overlay_position_description">Wähle aus, wo die Überlagerung auf dem Bildschirm angezeigt wird</string>
<string name="overlay_position_top_left">Oben links</string>
<string name="overlay_position_top_right">Oben rechts</string>
<string name="overlay_position_bottom_left">Unten links</string>
<string name="overlay_position_bottom_right">Unten rechts</string>
<string name="overlay_position_center_top">Mitte oben</string>
<string name="overlay_position_center_bottom">Mitte unten</string>
<string name="perf_overlay_background">Overlay-Hintergrund</string>
<string name="perf_overlay_background">Überlagerungs-Hintergrund</string>
<string name="perf_overlay_background_description">Hintergrund für bessere Lesbarkeit</string>
<!-- Device Overlay settings -->
<string name="show_soc_overlay">Geräteinfo-Overlay anzeigen</string>
<string name="enable_soc_overlay">Geräte-Overlay aktivieren</string>
<string name="soc_overlay_options">Geräte-Overlay</string>
<string name="soc_overlay_options_description">Konfigurieren Sie, welche Informationen im Geräte-Overlay angezeigt werden</string>
<string name="show_soc_overlay">Geräteinfo-Überlagerung anzeigen</string>
<string name="enable_soc_overlay">Geräteüberlagerung aktivieren</string>
<string name="soc_overlay_options">Geräteüberlagerung</string>
<string name="soc_overlay_options_description">Konfiguriere, welche Informationen in der Geräteüberlagerung angezeigt werden</string>
<string name="show_build_id">Build-ID anzeigen</string>
<string name="show_driver_version">Treiberversion anzeigen</string>
<string name="show_device_model">Gerätemodell anzeigen</string>
<string name="show_gpu_model">GPU-Modell anzeigen</string>
<string name="show_soc_model">SoC-Modell anzeigen</string>
@@ -85,19 +93,19 @@
<!-- Eden\'s Veil -->
<string name="buffer_reorder_disable">Puffer-Neuanordnung deaktivieren</string>
<string name="buffer_reorder_disable_description">Wenn aktiviert, wird die Neuanordnung von gemappten Speicher-Uploads deaktiviert, was die Zuordnung von Uploads zu bestimmten Zeichenvorgängen ermöglicht. Kann in einigen Fällen die Leistung verringern.</string>
<string name="buffer_reorder_disable_description">Wenn aktiviert, wird die Neuanordnung von zugeordneten Speicher-Uploads deaktiviert, wodurch Uploads bestimmten Abrufen zugeordnet werden können. Dies kann in einigen Fällen zu Leistungseinbußen führen.</string>
<string name="use_sync_core">Kern-Geschwindigkeit synchronisieren</string>
<string name="use_sync_core_description">Synchronisiert die Taktrate des Kerns mit der maximalen Geschwindigkeit, um die Leistung zu verbessern, ohne die tatsächliche Spielgeschwindigkeit zu verändern.</string>
<string name="cpuopt_unsafe_host_mmu">Host-MMU-Emulation aktivieren</string>
<string name="cpuopt_unsafe_host_mmu_description">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.</string>
<string name="debug_knobs">Debug-Regler</string>
<string name="debug_knobs">Fehlerbehebungs-Regler</string>
<string name="debug_knobs_description">Nur für Entwicklungszwecke</string>
<string name="debug_knobs_hint">0 bis 65535</string>
<!-- NVDEC Emulation -->
<string name="nvdec_emulation">NVDEC-Emulation</string>
<string name="nvdec_emulation_description">Methode zur Videodekodierung</string>
<string name="nvdec_emulation_description">Wähle aus, wie die Videodekodierung (NVDEC) während Zwischensequenzen und Intros gehandhabt wird.</string>
<string name="nvdec_emulation_none">Keine</string>
<!-- Optimize SPIRV output -->
@@ -126,7 +134,7 @@
<string name="multiplayer_wrong_password">Falsches Passwort</string>
<string name="multiplayer_could_not_connect">Verbindung fehlgeschlagen</string>
<string name="multiplayer_room_is_full">Raum ist voll</string>
<string name="multiplayer_host_banned">Sie sind von diesem Raum gebannt</string>
<string name="multiplayer_host_banned">Du bist von diesem Raum gebannt</string>
<string name="multiplayer_permission_denied">Zugriff verweigert</string>
<string name="multiplayer_no_such_user">Benutzer existiert nicht</string>
<string name="multiplayer_already_in_room">Bereits im Raum</string>
@@ -139,7 +147,7 @@
<string name="multiplayer_room_joined">Raum beigetreten</string>
<string name="multiplayer_room_moderator">Raum-Moderator</string>
<string name="multiplayer_member_join">%1$s ist beigetreten</string>
<string name="multiplayer_member_leave">%1$s hat verlassen</string>
<string name="multiplayer_member_leave">%1$s ist gegangen</string>
<string name="multiplayer_member_kicked">%1$s wurde entfernt</string>
<string name="multiplayer_member_banned">%1$s wurde gebannt</string>
<string name="multiplayer_address_unbanned">Adresse entbannt</string>
@@ -157,11 +165,11 @@
<string name="send_message">Nachricht senden</string>
<string name="multiplayer_moderation">Moderation</string>
<string name="multiplayer_moderation_title">Bannliste</string>
<string name="multiplayer_no_bans">Keine gebannten Benutzer</string>
<string name="multiplayer_unban_title">Entbannen</string>
<string name="multiplayer_no_bans">Keine gebannten Nutzer</string>
<string name="multiplayer_unban_title">Nutzer entbannen</string>
<string name="multiplayer_unban">Entbannen</string>
<string name="multiplayer_unban_message">%1$s wirklich entbannen?</string>
<string name="multiplayer_ban">Benutzer bannen</string>
<string name="multiplayer_ban">Nutzer bannen</string>
<string name="multiplayer_room_browser">Öffentliche Räume</string>
<string name="multiplayer_no_rooms_found">Keine öffentlichen Räume gefunden</string>
<string name="multiplayer_password_required">Passwort erforderlich</string>
@@ -172,15 +180,15 @@
<string name="multiplayer_hide_full_rooms">Volle Räume ausblenden</string>
<string name="multiplayer_hide_empty_rooms">Leere Räume ausblenden</string>
<string name="multiplayer_tap_refresh_to_check_again">Zum Aktualisieren tippen</string>
<string name="multiplayer_search_public_lobbies">Räume suchen…</string>
<string name="multiplayer_search_public_lobbies">Lobbys suchen…</string>
<string name="multiplayer_preferred_game_name">Bevorzugtes Spiel</string>
<string name="multiplayer_lobby_type">Lobby-Typ</string>
<string name="multiplayer_room_name_error">Muss 3-20 Zeichen lang sein</string>
<string name="multiplayer_required">Erforderlich</string>
<string name="multiplayer_token_required">Web-Token erforderlich, gehen Sie zu Erweiterte Einstellungen -> System -> Netzwerk</string>
<string name="multiplayer_token_required">Web-Token erforderlich, gehe zu Erweiterte Einstellungen -&gt; System -&gt; Netzwerk</string>
<string name="multiplayer_ip_error">Ungültiges IP-Format</string>
<string name="multiplayer_username_error">Muss zwischen 420 Zeichen lang sein und nur alphanumerische Zeichen, Punkte, Bindestriche, Unterstriche und Leerzeichen enthalten</string>
<string name="multiplayer_nickname_invalid">Ungültiger Benutzername, stellen Sie sicher, dass er in System Netzwerk korrekt eingestellt ist</string>
<string name="multiplayer_nickname_invalid">Ungültiger Benutzername; stelle sicher, dass er unter System -&gt; Netzwerk korrekt konfiguriert ist.</string>
<string name="multiplayer_token_error">Muss 48 Zeichen lang sein und nur Kleinbuchstaben a-z enthalten</string>
<string name="multiplayer_port_error">Muss zwischen 1-65535 liegen</string>
<string name="cancel">Abbrechen</string>
@@ -188,20 +196,20 @@
<string name="refresh">Aktualisieren</string>
<string name="room_list">Raumliste</string>
<string name="multiplayer_public_visibility">Öffentlich</string>
<string name="multiplayer_unlisted_visibility">Unlisted</string>
<string name="multiplayer_unlisted_visibility">Nicht gelistet</string>
<!-- Setup strings -->
<string name="welcome">Willkommen!</string>
<string name="welcome_description">Erfahre wie man &lt;b>Eden&lt;/b> einrichtet und beginne mit der Emulation.</string>
<string name="welcome_description">Erfahre, wie du Eden einrichtest, und tauche in die Welt der Emulation ein.</string>
<string name="get_started">Erste Schritte</string>
<string name="keys">Schlüssel</string>
<string name="keys_description">Wähle deine &lt;b>prod.keys&lt;/b> Datei mit dem Button unten aus.</string>
<string name="keys_description">Wähle deine <b>prod.keys</b> -Datei über die untenstehende Schaltfläche aus.</string>
<string name="firmware">Firmware</string>
<string name="firmware_description">Wähle deine <b>firmware.zip</b>-Datei mit dem Button unten aus</string>
<string name="firmware_description">Wähle deine <b>firmware.zip</b>-Datei über die untenstehende Schaltfläche aus.</string>
<string name="games">Spiele</string>
<string name="games_description">Wähle mit dem Knopf unten den &lt;b>Spiele&lt;/b>-Ordner aus.</string>
<string name="games_description">Wähle deinen <b>Spiele</b>-Ordner mit der untenstehenden Schaltfläche aus.</string>
<string name="done">Fertig</string>
<string name="done_description">Du bist start klar.\nViel Spaß mit deinen Spielen!</string>
<string name="done_description">Alles bereit.\nViel Spaß beim Spielen!</string>
<string name="next">Weiter</string>
<string name="back">Zurück</string>
<string name="add_games">Spiele hinzufügen</string>
@@ -214,23 +222,23 @@
<string name="view_grid">Raster</string>
<string name="view_grid_compact">Kompaktes Gitter</string>
<string name="view_carousel">Karussell</string>
<string name="game_image_desc">Scrennshot für %1$s</string>
<string name="game_image_desc">Bildschirmfoto für %1$s</string>
<string name="folder">Ordner</string>
<string name="dont_show_again">Nicht mehr anzeigen</string>
<string name="add_directory_success">Neues Spieleverzeichnis erfolgreich hinzugefügt.</string>
<string name="enable_update_checks">Auf Updates überprüfen</string>
<string name="enable_update_checks_description">Beim Start auf Updates überprüfen und optional das neue Update herunterladen und installieren</string>
<string name="update_available">Update verfügbar</string>
<string name="update_available_description">Eine Version ist verfügbar: %1$s\n\nWillst du sie herunterladen\?</string>
<string name="downloading_update">Update wird heruntergeladen</string>
<string name="update_download_failed">Herunterladen des Updates fehlgeschlagen</string>
<string name="update_installed_successfully">Update erfolgreich installiert</string>
<string name="update_install_failed">Installieren des Updates fehlgeschlagen: %1$s</string>
<string name="enable_update_checks">Auf Aktualisierungen prüfen</string>
<string name="enable_update_checks_description">Beim Start auf Aktualisierungen prüfen und optional die neue Aktualisierung herunterladen und installieren</string>
<string name="update_available">Aktualisierung verfügbar</string>
<string name="update_available_description">Eine neue Version ist verfügbar: %1$s\n\nWillst du sie herunterladen\?</string>
<string name="downloading_update">Aktualisierung wird heruntergeladen</string>
<string name="update_download_failed">Herunterladen der Aktualisierung fehlgeschlagen</string>
<string name="update_installed_successfully">Aktualisierung erfolgreich installiert</string>
<string name="update_install_failed">Installieren der Aktualisierung fehlgeschlagen: %1$s</string>
<string name="home_search">Suche</string>
<string name="home_settings">Einstellungen</string>
<string name="empty_gamelist">Es wurden keine Dateien gefunden oder es wurde noch kein Spielverzeichnis ausgewählt.</string>
<string name="manage_game_folders">Spiele-Ordner verwalten</string>
<string name="select_games_folder_description">Erlaubt Eden die Spieleliste zu füllen</string>
<string name="manage_game_folders">Spielordner verwalten</string>
<string name="select_games_folder_description">Ermöglicht es Eden, die Spieleliste zu füllen.</string>
<string name="add_games_warning">Auswahl des Spieleverzeichnisses überspringen?</string>
<string name="add_games_warning_description">Spiele werden in der Spieleliste nicht angezeigt, wenn kein Ordner ausgewählt ist.</string>
<string name="add_games_warning_help">https://yuzu-mirror.github.io/help/quickstart/#dumping-games</string>
@@ -241,33 +249,33 @@
<string name="install_prod_keys_warning">Hinzufügen der Schlüssel überspringen?</string>
<string name="install_prod_keys_warning_description">Für die Emulation von Spielen sind gültige Schlüssel erforderlich. Wenn du fortfährst, funktionieren nur Homebrew-Anwendungen.</string>
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="install_firmware_warning">Firmware nicht hinzufügen?</string>
<string name="install_firmware_warning">Das Hinzufügen der Firmware überspringen\?</string>
<string name="emulator_data">Emulatordaten einrichten</string>
<string name="emulator_data_description">Für die Funktion des Emulators werden Schlüssel benötigt, und Firmware ist empfohlen und für die Verwendung des QLaunch-Applets erforderlich.</string>
<string name="permissions">Berechtigung erteilen</string>
<string name="permissions_description">Optionale Berechtigungen erteilen um bestimmte Features des Emulators zu nutzen</string>
<string name="permissions">Berechtigungen erteilen</string>
<string name="permissions_description">Erteile optionale Berechtigungen zur Nutzung bestimmter Funktionen des Emulators.</string>
<string name="install_firmware_warning_description">Viele Spiele benötigen Zugriff auf die Firmware, um richtig zu funktionieren.</string>
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="notifications">Benachrichtigungen</string>
<string name="notifications_description">Erteile mit dem Knopf unten die Berechtigung, Benachrichtigungen zu senden.</string>
<string name="notifications_description">Erteile die Berechtigung für Benachrichtigungen über die untenstehende Schaltfläche.</string>
<string name="permission_denied">Zugriff verweigert</string>
<string name="permission_denied_description">Du hast diese Berechtigung zu oft verweigert und musst sie nun manuell in den Systemeinstellungen erteilen.</string>
<string name="about">Über</string>
<string name="about_description">Build-Version, Credits und mehr</string>
<string name="about_description">Build-Version, Mitwirkende und mehr</string>
<string name="system_information">Systeminformationen</string>
<string name="system_information_description">Detaillierte Systeminformationen anzeigen</string>
<string name="device_manufacturer">Hersteller</string>
<string name="device_model">Modell</string>
<string name="product">Produkt</string>
<string name="android_version">Android-Version</string>
<string name="android_security_patch">Sicherheitspatch</string>
<string name="android_security_patch">Sicherheits-Patch</string>
<string name="build_id">Build-ID</string>
<string name="general_information">Allgemeine Informationen</string>
<string name="hardware">Hardware</string>
<string name="supported_abis">Unterstützte ABIs</string>
<string name="cpu_info">CPU-Informationen</string>
<string name="gpu_information">GPU-Informationen</string>
<string name="vulkan_driver_version">Vulkan Treiberversion</string>
<string name="vulkan_driver_version">Vulkan-Treiberversion</string>
<string name="error_getting_emulator_info">Fehler beim Abrufen der Emulatorinformationen</string>
<string name="memory_info">Speicherinformation</string>
<string name="total_memory">Gesamtspeicher</string>
@@ -278,7 +286,7 @@
<string name="warning_skip">Überspringen</string>
<string name="warning_cancel">Abbrechen</string>
<string name="install_amiibo_keys">Amiibo-Schlüssel installieren</string>
<string name="install_amiibo_keys_description">Benötigt um Amiibos im Spiel zu verwenden</string>
<string name="install_amiibo_keys_description">Benötigt, um Amiibos im Spiel zu verwenden</string>
<string name="gpu_driver_fetcher">GPU-Treiber-Hersteller</string>
<string name="gpu_driver_manager">GPU-Treiber Verwaltung</string>
<string name="install_gpu_driver_description">Alternative Treiber für eventuell bessere Leistung oder Genauigkeit installieren</string>
@@ -289,36 +297,39 @@
<string name="open_user_folder">Eden-Ordner öffnen</string>
<string name="open_user_folder_description">Eden\'s interne Dateien verwalten</string>
<string name="app_settings_description">Verhalten und Aussehen der App verändern</string>
<string name="no_file_manager">Kein Dateimanager gefunden</string>
<string name="no_file_manager">Kein Datei-Manager gefunden</string>
<string name="notification_no_directory_link">Eden-Verzeichnis konnte nicht geöffnet werden</string>
<string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers.</string>
<string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Datei-Managers.</string>
<string name="manage_save_data">Speicherdaten verwalten</string>
<string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string>
<string name="import_save_warning">Speicherdaten importieren</string>
<string name="import_save_warning_description">Das überschreibt alle existierenden Speicherdaten für dieses Spiel mit der ausgewählten Datei.
Wirklich fortfahren?</string>
<string name="save_files_importing">Importiere Speicherdaten...</string>
<string name="save_files_exporting">Exportiere Speicherdaten...</string>
<string name="save_files_importing">Speicherstände werden importiert...</string>
<string name="save_files_exporting">Speicherstände werden exportiert...</string>
<string name="save_file_imported_success">Erfolgreich importiert</string>
<string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string>
<string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string>
<string name="install_firmware">Firmware installieren</string>
<string name="install_firmware_description">Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Booten einiger Spiele benötigt</string>
<string name="install_firmware_description">Die Firmware muss in einem ZIP-Archiv vorliegen und wird zum Starten einiger Spiele benötigt</string>
<string name="firmware_installing">Firmware wird installiert</string>
<string name="firmware_installed_failure">Bei der Firmware installation ist etwas fehlgeschlagen.</string>
<string name="firmware_installed_failure_description">Stellen Sie sicher, dass sich die Firmware-NCA-Dateien im Stammverzeichnis der ZIP-Datei befinden, und versuchen Sie es erneut.</string>
<string name="firmware_uninstalled_failure">Die Deinstallation der Firmware ist fehlgeschlagen</string>
<string name="share_log">Debug-Logs teilen</string>
<string name="share_log_description">Debug-Logs an Eden zur Untersuchung absenden</string>
<string name="share_log_missing">Keine Log-Datei gefunden</string>
<string name="firmware_installed_failure">Firmware-Installation fehlgeschlagen</string>
<string name="firmware_installed_failure_description">Stelle sicher, dass sich die Firmware-NCA-Dateien im Stammverzeichnis der ZIP-Datei befinden, und versuche es erneut.</string>
<string name="firmware_uninstalled_failure">Deinstallation der Firmware fehlgeschlagen</string>
<string name="share_log">Fehlerbehebungs-Protokolle teilen</string>
<string name="share_log_description">Fehlerbehebungs-Protokolle an Eden zur Fehlerbehebung absenden</string>
<string name="share_log_missing">Keine Protokolldatei gefunden</string>
<string name="share_gpu_log">GPU-Protokolle teilen</string>
<string name="share_gpu_log_description">Teile Edens GPU-Protokolldatei, um Grafikprobleme zu beheben</string>
<string name="share_gpu_log_missing">Keine GPU-Protokolldatei gefunden</string>
<string name="install_game_content">Spiel installieren</string>
<string name="install_game_content_description">Spiel-Updates oder DLCs installieren</string>
<string name="installing_game_content">Installiere...</string>
<string name="install_game_content_description">Spielaktualisierungen oder DLCs installieren</string>
<string name="installing_game_content">Inhalt wird installiert...</string>
<string name="install_game_content_failure">Fehler beim Installieren der Datei(en) auf NAND</string>
<string name="install_game_content_failure_description">Bitte stellen Sie sicher, dass der/die Inhalt(e) gültig sind und dass die Datei prod.keys installiert ist.</string>
<string name="install_game_content_failure_description">Bitte stelle sicher, dass der/die Inhalt(e) gültig sind und dass die Datei prod.keys installiert ist.</string>
<string name="install_game_content_failure_base">Um mögliche Konflikte zu vermeiden, ist die Installation von Basisspielen nicht gestattet.</string>
<string name="install_game_content_failed_count">%1$d Installationsfehler</string>
<string name="install_game_content_success">Game content(s) installed successfully</string>
<string name="install_game_content_success">Spielinhalt(e) erfolgreich installiert</string>
<string name="install_game_content_success_install">%1$d erfolgreich installiert</string>
<string name="install_game_content_success_overwrite">%1$d erfolgreich überschrieben</string>
<string name="install_game_content_help_link">https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates</string>
@@ -326,20 +337,20 @@ Wirklich fortfahren?</string>
<string name="custom_driver_not_supported_description">Das Laden von benutzerdefinierten Treibern wird für dieses Gerät momentan nicht unterstützt.\nSchau später einfach nochmal nach, ob die Unterstützung hinzugefügt wurde!</string>
<string name="manage_yuzu_data">Eden-Daten verwalten</string>
<string name="manage_yuzu_data_description">Importieren/Exportieren Sie Firmware, Schlüssel, Benutzerdaten und mehr!</string>
<string name="game_folders">Spiele-Ordner</string>
<string name="game_folders">Spielordner</string>
<string name="deep_scan">Tiefer Scan</string>
<string name="add_game_folder">Spiele-Ordner hinzufügen</string>
<string name="add_game_folder">Spielordner hinzufügen</string>
<string name="folder_already_added">Ordner bereits vorhanden</string>
<string name="game_folder_properties">Ordner-Eigenschaften</string>
<plurals name="saves_import_failed">
<item quantity="one">%d Spielstand konnte nicht importiert werden</item>
<item quantity="other">%d Spielstände konnten nicht importiert werden</item>
<item quantity="other">%d-Spielstände konnten nicht importiert werden</item>
</plurals>
<plurals name="saves_import_success">
<item quantity="one">%d Spielstand erfolgreich importiert</item>
<item quantity="other">%d Spielstände erfolgreich importiert</item>
<item quantity="other">%d -Spielstände erfolgreich importiert</item>
</plurals>
<string name="no_save_data_found">Keine Spielstände gefunden</string>
<string name="no_save_data_found">Keine Speicherdaten gefunden</string>
<string name="verify_installed_content">Inhalt überprüfen</string>
<string name="verify_installed_content_description">Überprüft installierte Inhalte auf Fehler</string>
@@ -356,14 +367,15 @@ Wirklich fortfahren?</string>
<string name="keys_failed">Schlüsselinstallation fehlgeschlagen</string>
<string name="keys_install_success">Schlüssel erfolgreich installiert</string>
<string name="error_keys_copy_failed">Ein oder mehrere Schlüssel konnten nicht kopiert werden.</string>
<string name="error_keys_invalid_filename">Stellen Sie sicher, dass Ihre Schlüsseldatei die Erweiterung .keys hat, und versuchen Sie es erneut.</string>
<string name="error_keys_failed_init">Schlüssel konnten nicht initialisiert werden. Stellen Sie sicher, dass Ihre Dumping-Tools auf dem neuesten Stand sind, und dumpen Sie die Schlüssel erneut.</string>
<string name="error_keys_invalid_filename">Stelle sicher, dass deine Schlüsseldatei die Erweiterung .keys hat, und versuche es erneut.</string>
<string name="error_keys_failed_init">Schlüssel konnten nicht initialisiert werden. Stelle sicher, dass deine Auslese-Werkzeuge auf dem neuesten Stand sind, und lies dann die Schlüssel erneut aus.</string>
<!-- Applet launcher strings -->
<string name="qlaunch_applet">Qlaunch</string>
<string name="qlaunch_description">Anwendungen vom Systemstartbildschirm aus starten</string>
<string name="applets">Applet-Launcher</string>
<string name="applets_description">System-Applets mit Firmware starten</string>
<string name="applets_error_firmware">Firmware nicht installiert</string>
<string name="applets_error_applet">Applet nicht verfügbar</string>
<string name="album_applet">Album</string>
<string name="album_applet_description">Bilder im Screenshot-Ordner anzeigen</string>
@@ -422,6 +422,9 @@
<string name="cpu_accuracy">Precisión de la CPU</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">Argumentos Homebrew</string>
<string name="program_args_description">Argumentos en la línea de comandos pasados al homebrew al ser lanzado (p.e. -noglsl).</string>
<!-- System settings strings -->
<string name="device_name">Nombre del dispositivo</string>
<string name="use_docked_mode">Modo sobremesa</string>
@@ -424,6 +424,9 @@
<string name="cpu_accuracy">Точність CPU</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">Параметри запуску Homebrew</string>
<string name="program_args_description">Параметри командного рядка, що передаються Homebrew при запуску (як-от noglsl).</string>
<!-- System settings strings -->
<string name="device_name">Назва пристрою</string>
<string name="use_docked_mode">Режим док-станції</string>
+8
View File
@@ -1,7 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#ifndef _WIN32
#include <cstring>
#endif
#include <utility>
#include <fmt/ranges.h>
+1 -1
View File
@@ -89,7 +89,7 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, u
template <typename... Args>
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::format_string<Args...> format, const Args&... args) {
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format, fmt::make_format_args(args...));
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format.get(), fmt::make_format_args(args...));
}
/// Implements a log message filter which allows different log classes to have different minimum
+1 -1
View File
@@ -158,7 +158,7 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
ENUM(GameListMode, TreeView, GridView);
ENUM(GameListMode, TreeView, GridView, CarouselView);
ENUM(SpeedMode, Standard, Turbo, Slow);
template <typename Type>
+2 -4
View File
@@ -6,15 +6,13 @@
#pragma once
#include <algorithm>
#include <limits>
#include <map>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <fmt/core.h>
#include <fmt/ranges.h>
#include "common/common_types.h"
#include "common/settings_common.h"
#include "common/settings_enums.h"
+3
View File
@@ -503,6 +503,9 @@ NcaID PlaceholderCache::Generate() {
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_dir,
std::string_view path) const {
if (open_dir == nullptr) {
return nullptr;
}
const auto file = open_dir->GetFileRelative(path);
if (file != nullptr) {
return file;
+1 -1
View File
@@ -207,7 +207,7 @@ struct Values {
// Game List
Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
Setting<u32, true> game_icon_size{linkage, 64, 8, 512, "game_icon_size", Category::UiGameList};
Setting<u32, true> game_icon_size{linkage, 64, 0, 512, "game_icon_size", Category::UiGameList};
Setting<u32, true> folder_icon_size{linkage, 48, 8, 512, "folder_icon_size", Category::UiGameList};
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
+20 -33
View File
@@ -48,6 +48,14 @@ static QPixmap GetDefaultIcon(u32 size) {
return icon;
}
static QPixmap ThemeIcon(const char* name) {
const int size = UISettings::values.folder_icon_size.GetValue();
return QIcon::fromTheme(QLatin1String(name))
.pixmap(size, size)
.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
class GameListItem : public QStandardItem {
public:
@@ -157,6 +165,7 @@ public:
return QStringLiteral("%1\n %2").arg(row1, row2);
}
case Settings::GameListMode::GridView:
case Settings::GameListMode::CarouselView:
return row1;
default:
break;
@@ -296,45 +305,33 @@ public:
UISettings::GameDir* game_dir = &directory;
setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
const char* icon_name = nullptr;
switch (dir_type) {
case GameListItemType::SdmcDir:
setData(
QIcon::fromTheme(QStringLiteral("sd_card"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
icon_name = "sd_card";
setData(QObject::tr("Installed SD Titles"), Qt::DisplayRole);
break;
case GameListItemType::UserNandDir:
setData(
QIcon::fromTheme(QStringLiteral("chip"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
icon_name = "chip";
setData(QObject::tr("Installed NAND Titles"), Qt::DisplayRole);
break;
case GameListItemType::SysNandDir:
setData(
QIcon::fromTheme(QStringLiteral("chip"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
icon_name = "chip";
setData(QObject::tr("System Titles"), Qt::DisplayRole);
break;
case GameListItemType::CustomDir: {
const QString path = QString::fromStdString(game_dir->path);
const QString icon_name =
QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder");
setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
icon_name = QFileInfo::exists(path) ? "folder" : "bad_folder";
setData(path, Qt::DisplayRole);
break;
}
default:
break;
}
if (icon_name != nullptr)
setData(ThemeIcon(icon_name), Qt::DecorationRole);
}
int type() const override {
@@ -357,12 +354,7 @@ public:
explicit GameListAddDir() {
setData(type(), TypeRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
setData(QIcon::fromTheme(QStringLiteral("list-add"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
setData(ThemeIcon("list-add"), Qt::DecorationRole);
setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole);
}
@@ -380,12 +372,7 @@ public:
explicit GameListFavorites() {
setData(type(), TypeRole);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
setData(QIcon::fromTheme(QStringLiteral("star"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
setData(ThemeIcon("star"), Qt::DecorationRole);
setData(QObject::tr("Favorites"), Qt::DisplayRole);
}
+13 -60
View File
@@ -19,8 +19,8 @@
#include "qt_common/util/game.h"
#include "qt_common/game_list/game_list_p.h"
#include "qt_common/game_list/worker.h"
#include "qt_common/game_list/model.h"
#include "qt_common/game_list/worker.h"
GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
FileSys::ManualContentProvider* provider_,
@@ -36,6 +36,8 @@ GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
connect(external_watcher, &QFileSystemWatcher::directoryChanged, this,
&GameListModel::RefreshExternalContent);
ResetExternalWatcher();
insertColumns(0, COLUMN_COUNT);
RetranslateUI();
@@ -45,6 +47,8 @@ GameListModel::GameListModel(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
GameListModel::~GameListModel() = default;
void GameListModel::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
emit PopulatingStarted();
current_worker.reset();
removeRows(0, rowCount());
@@ -197,13 +201,17 @@ void GameListModel::LoadCompatibilityList() {
}
}
void GameListModel::Repopulate() {
current_worker.reset();
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
}
void GameListModel::RefreshGameDirectory() {
ResetExternalWatcher();
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
Repopulate();
}
}
@@ -211,8 +219,7 @@ void GameListModel::RefreshExternalContent() {
if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache.");
QtCommon::Game::ResetMetadata(false);
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
Repopulate();
}
}
@@ -227,60 +234,6 @@ void GameListModel::ResetExternalWatcher() {
}
}
void GameListModel::OnUpdateThemedIcons() {
for (int i = 0; i < invisibleRootItem()->rowCount(); i++) {
QStandardItem* child = invisibleRootItem()->child(i);
const int icon_size = UISettings::values.folder_icon_size.GetValue();
switch (child->data(GameListItem::TypeRole).value<GameListItemType>()) {
case GameListItemType::SdmcDir:
child->setData(
QIcon::fromTheme(QStringLiteral("sd_card"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
case GameListItemType::UserNandDir:
case GameListItemType::SysNandDir:
child->setData(
QIcon::fromTheme(QStringLiteral("chip"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
case GameListItemType::CustomDir: {
const UISettings::GameDir& game_dir =
UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()];
const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path))
? QStringLiteral("folder")
: QStringLiteral("bad_folder");
child->setData(
QIcon::fromTheme(icon_name).pixmap(icon_size).scaled(
icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
}
case GameListItemType::AddDir:
child->setData(
QIcon::fromTheme(QStringLiteral("list-add"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
case GameListItemType::Favorites:
child->setData(
QIcon::fromTheme(QStringLiteral("star"))
.pixmap(icon_size)
.scaled(icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation),
Qt::DecorationRole);
break;
default:
break;
}
}
}
void GameListModel::RetranslateUI() {
setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility"));
+3 -2
View File
@@ -3,11 +3,11 @@
#pragma once
#include <memory>
#include <QFileSystemWatcher>
#include <QStandardItemModel>
#include <QStringList>
#include <QVector>
#include <memory>
#include "common/common_types.h"
#include "frontend_common/play_time_manager.h"
@@ -64,7 +64,6 @@ public:
void LoadCompatibilityList();
void OnUpdateThemedIcons();
void RetranslateUI();
QFileSystemWatcher* GetWatcher() const;
@@ -76,6 +75,7 @@ public:
signals:
void ShowList(bool show);
void PopulatingCompleted(const QStringList& watch_list);
void PopulatingStarted();
void SaveConfig();
private:
@@ -83,6 +83,7 @@ private:
void AddFavorite(u64 program_id);
void RemoveFavorite(u64 program_id);
void Repopulate();
bool m_flat = false;
+38 -43
View File
@@ -32,11 +32,11 @@
#include "qt_common/config/uisettings.h"
#include "qt_common/qt_common.h"
#include "yuzu/compatibility_list.h"
#include "qt_common/game_list/game_list_p.h"
#include "yuzu/compatibility_list.h"
#include "qt_common/game_list/worker.h"
#include "qt_common/game_list/model.h"
#include "qt_common/game_list/worker.h"
namespace {
@@ -391,6 +391,25 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
std::vector<u64> program_ids;
loader->ReadProgramIds(program_ids);
const auto addEntry = [this, physical_name,
parent_dir](std::unique_ptr<Loader::AppLoader>& app_loader,
const u64 id) {
std::vector<u8> icon;
[[maybe_unused]] const auto res1 = app_loader->ReadIcon(icon);
std::string name = " ";
[[maybe_unused]] const auto res3 = app_loader->ReadTitle(name);
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
system.GetContentProvider()};
auto entry = MakeGameListEntry(
physical_name, name, Common::FS::GetSize(physical_name), icon, *app_loader,
id, compatibility_list, play_time_manager, patch);
RecordEvent([=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
};
if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
for (const auto id : program_ids) {
@@ -404,38 +423,10 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
continue;
}
std::vector<u8> icon;
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
std::string name = " ";
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
system.GetContentProvider()};
auto entry = MakeGameListEntry(
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
id, compatibility_list, play_time_manager, patch);
RecordEvent(
[=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
addEntry(loader, id);
}
} else {
std::vector<u8> icon;
[[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
std::string name = " ";
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto entry = MakeGameListEntry(
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
program_id, compatibility_list, play_time_manager, patch);
RecordEvent(
[=](GameListModel* model) { model->AddEntry(entry, parent_dir); });
addEntry(loader, program_id);
}
}
} else if (is_dir) {
@@ -466,29 +457,33 @@ void GameListWorker::run() {
break;
}
GameListDir* game_list_dir;
bool scan = false;
if (game_dir.path == std::string("SDMC")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
} else if (game_dir.path == std::string("UserNAND")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
} else if (game_dir.path == std::string("SysNAND")) {
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
DirEntryReady(game_list_dir);
AddTitlesToGameList(game_list_dir);
game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
} else {
const QString qpath = QString::fromStdString(game_dir.path);
if (QDir(qpath).exists()) {
watch_list.append(qpath);
}
auto* const game_list_dir = new GameListDir(game_dir);
DirEntryReady(game_list_dir);
game_list_dir = new GameListDir(game_dir);
scan = true;
}
DirEntryReady(game_list_dir);
if (scan) {
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan,
game_list_dir);
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan,
game_list_dir);
} else {
AddTitlesToGameList(game_list_dir);
}
}
+22 -26
View File
@@ -1619,36 +1619,33 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
template <class P>
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) {
upload_copies.clear();
u64 staging_offset = 0;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
DAddr buffer_start = buffer.CpuAddr();
auto push = [&](u64 start, u64 end) {
if (start >= end) {
return;
}
u64 sz = end - start;
upload_copies.push_back({
.src_offset = staging_offset,
.dst_offset = start - buffer_start,
.size = sz
const DAddr buffer_start = buffer.cpu_addr_cached;
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
upload_copies.push_back(BufferCopy{
.src_offset = total_size_bytes,
.dst_offset = device_addr_out - buffer_start,
.size = range_size,
});
staging_offset += sz;
largest_copy = (std::max)(largest_copy, sz);
};
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 addr, u64 range_size) {
u64 start = addr;
u64 end = addr + range_size;
gpu_modified_ranges.ForEachInRange(start, range_size, [&](u64 gstart, u64 gsize) {
u64 gend = gstart + gsize;
push(start, gstart);
start = (std::max)(start, gend);
});
push(start, end);
total_size_bytes += range_size;
largest_copy = (std::max)(largest_copy, range_size);
});
if (upload_copies.empty()) {
if (total_size_bytes == 0) {
return true;
}
UploadMemory(buffer, staging_offset, largest_copy, std::span(upload_copies));
u64 min_offset = (std::numeric_limits<u64>::max)();
u64 max_offset = 0;
for (const auto& copy : upload_copies) {
min_offset = (std::min)(min_offset, copy.dst_offset);
max_offset = (std::max)(max_offset, copy.dst_offset + copy.size);
}
const DAddr sync_addr = buffer.CpuAddr() + min_offset;
const u64 sync_size = max_offset - min_offset;
DownloadBufferMemory(buffer, sync_addr, sync_size);
const std::span<BufferCopy> copies_span(upload_copies.data(), upload_copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);
any_buffer_uploaded = true;
return false;
}
@@ -1699,7 +1696,6 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
// Apply the staging offset
copy.src_offset += upload_staging.offset;
}
@@ -62,6 +62,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_state3_depth_clamp_enable.Assign(features.has_dynamic_state3_depth_clamp_enable ? 1 : 0);
dynamic_state3_logic_op_enable.Assign(features.has_dynamic_state3_logic_op_enable ? 1 : 0);
dynamic_state3_line_stipple_enable.Assign(features.has_dynamic_state3_line_stipple_enable ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
@@ -208,6 +208,9 @@ struct FixedPipelineState {
BitField<12, 2, u32> tessellation_spacing;
BitField<14, 1, u32> tessellation_clockwise;
BitField<15, 5, u32> patch_control_points_minus_one;
BitField<20, 1, u32> dynamic_state3_depth_clamp_enable;
BitField<21, 1, u32> dynamic_state3_logic_op_enable;
BitField<22, 1, u32> dynamic_state3_line_stipple_enable;
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
@@ -907,10 +907,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
// EDS3 - Enables (composite: per-feature)
if (key.state.extended_dynamic_state_3_enables) {
if (device.SupportsDynamicState3DepthClampEnable()) {
if (key.state.dynamic_state3_depth_clamp_enable) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT);
}
if (device.SupportsDynamicState3LogicOpEnable()) {
if (key.state.dynamic_state3_logic_op_enable) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT);
}
if (device.SupportsDynamicState3LineRasterizationMode()) {
@@ -919,7 +919,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
if (device.SupportsDynamicState3ConservativeRasterizationMode()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT);
}
if (device.SupportsDynamicState3LineStippleEnable()) {
if (key.state.dynamic_state3_line_stipple_enable) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT);
}
if (device.SupportsDynamicState3AlphaToCoverageEnable()) {
@@ -27,6 +27,7 @@
namespace Vulkan {
constexpr u64 MAX_PENDING_FLUSHES = 5;
void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
vk::CommandBuffer upload_cmdbuf) {
@@ -115,7 +116,18 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
Scheduler::~Scheduler() = default;
u64 Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
// When flushing, we only send data to the worker thread; no waiting is necessary.
// Prevent the CPU from getting too far ahead of the GPU by limiting pending flushes.
const bool should_throttle = Settings::IsGPULevelHigh();
if (should_throttle) {
const u64 current_tick = master_semaphore->CurrentTick();
const u64 gap = current_tick > last_submitted_tick ? current_tick - last_submitted_tick : 0;
const u64 step = (std::min)(MAX_PENDING_FLUSHES, gap);
const u64 new_tick = last_submitted_tick + step;
if (new_tick < current_tick) {
last_submitted_tick = new_tick;
master_semaphore->Wait(last_submitted_tick);
}
}
const u64 signal_value = SubmitExecution(signal_semaphore, wait_semaphore);
AllocateNewContext();
return signal_value;
@@ -296,6 +296,8 @@ private:
double last_target_fps{};
u64 max_frame_count{};
u64 frame_counter{};
u64 last_submitted_tick = 0;
};
} // namespace Vulkan
@@ -27,8 +27,17 @@ using namespace Common::Literals;
// Maximum potential alignment of a Vulkan buffer
constexpr VkDeviceSize MAX_ALIGNMENT = 256;
// Stream buffer size in bytes
// *NIX drivers are more sensitive to increased buffers for streaming.
// Windows ones however, can intake bigger buffers and generally do not OOM.
// - GTX 960 on Windows will not OOM with 256mib
// - GT 1030 on ^NIX will OOM with 256mib
#ifdef _WIN32
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 256_MiB;
#else
constexpr VkDeviceSize MAX_STREAM_BUFFER_SIZE = 128_MiB;
#endif
size_t GetStreamBufferSize(const Device& device) {
if (!device.HasDebuggingToolAttached()) {
@@ -46,13 +55,13 @@ size_t GetStreamBufferSize(const Device& device) {
// If rebar is not supported, cut the max heap size to 40%. This will allow 2 captures to be
// loaded at the same time in RenderDoc. If rebar is supported, this shouldn't be an issue
// as the heap will be much larger.
if (size <= 256_MiB) {
if (size <= MAX_STREAM_BUFFER_SIZE) {
size = size * 40 / 100;
}
} else {
size = MAX_STREAM_BUFFER_SIZE;
}
return (std::min)(Common::AlignUp(size, MAX_ALIGNMENT), MAX_STREAM_BUFFER_SIZE);
return Common::AlignUp(size, MAX_ALIGNMENT);
}
} // Anonymous namespace
+4 -1
View File
@@ -239,7 +239,10 @@ add_executable(yuzu
configuration/addon/mod_select_dialog.h configuration/addon/mod_select_dialog.cpp configuration/addon/mod_select_dialog.ui
render/performance_overlay.h render/performance_overlay.cpp render/performance_overlay.ui
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui)
updater/update_dialog.h updater/update_dialog.cpp updater/update_dialog.ui
game/common.h
game/carousel.h game/carousel.cpp
)
set_target_properties(yuzu PROPERTIES OUTPUT_NAME "eden")
+4 -2
View File
@@ -52,9 +52,11 @@ void ConfigureDebug::SetConfiguration() {
ui->extended_logging->setChecked(Settings::values.extended_logging.GetValue());
ui->perform_vulkan_check->setChecked(Settings::values.perform_vulkan_check.GetValue());
ui->serial_battery_edit->setText(
QString::fromStdString(std::to_string(Settings::values.serial_battery.GetValue())));
QString::number(Settings::values.serial_battery.GetValue())
);
ui->serial_board_edit->setText(
QString::fromStdString(std::to_string(Settings::values.serial_unit.GetValue())));
QString::number(Settings::values.serial_unit.GetValue())
);
#ifdef YUZU_USE_QT_WEB_ENGINE
ui->disable_web_applet->setChecked(Settings::values.disable_web_applet.GetValue());
#else
+107
View File
@@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/model.h"
#include "yuzu/game/common.h"
#include "yuzu/game/game_card.h"
#include "yuzu/game/carousel.h"
GameCarousel::GameCarousel(QWidget* parent) : QListView{parent} {
m_gameCard = new GameCard(this);
setItemDelegate(m_gameCard);
setViewMode(QListView::IconMode);
setMovement(QListView::Static);
setUniformItemSizes(true);
setSelectionMode(QAbstractItemView::SingleSelection);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setContextMenuPolicy(Qt::CustomContextMenu);
setSpacing(10);
setWordWrap(true);
setTextElideMode(Qt::ElideRight);
setFlow(QListView::LeftToRight);
setWrapping(false);
}
void GameCarousel::SetModel(GameListModel* model) {
QListView::setModel(model);
UpdateIconSize();
}
void GameCarousel::ApplyFilter(const QString& edit_filter_text, GameListModel* model) {
int row_count = model->rowCount();
for (int i = 0; i < row_count; ++i) {
QStandardItem* item = model->item(i, 0);
if (!item)
continue;
if (Yuzu::FilterMatches(edit_filter_text, item)) {
setRowHidden(i, false);
} else {
setRowHidden(i, true);
}
}
}
void GameCarousel::UpdateIconSize() {
const u32 icon_size = UISettings::values.game_icon_size.GetValue();
int heightMargin = 0;
int widthMargin = 80;
// TODO(crueter): get rid of this nonsense
if (UISettings::values.show_game_name) {
switch (icon_size) {
case 128:
heightMargin = 65;
break;
case 0:
widthMargin = 120;
heightMargin = 120;
break;
case 64:
heightMargin = 77;
break;
case 32:
case 256:
heightMargin = 81;
break;
}
} else {
widthMargin = 24;
heightMargin = 24;
}
const int min_item_width = icon_size + widthMargin;
const int min_item_height = icon_size + heightMargin;
const int grid_height = std::max(min_item_height, viewport()->height());
QSize content_size(min_item_width, min_item_height);
QSize grid_size(min_item_width, grid_height);
if (gridSize() != grid_size) {
setUpdatesEnabled(false);
setGridSize(grid_size);
m_gameCard->setSize(grid_size, content_size, 0, 0);
setUpdatesEnabled(true);
}
}
QModelIndex GameCarousel::indexAt(const QPoint& point) const {
QModelIndex index = QListView::indexAt(point);
if (!index.isValid())
return {};
if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index)))
return {};
return index;
}
+27
View File
@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QListView>
#include <QString>
class GameCard;
class GameListModel;
class QResizeEvent;
class GameCarousel : public QListView {
Q_OBJECT
public:
explicit GameCarousel(QWidget* parent = nullptr);
void SetModel(GameListModel* model);
void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
void UpdateIconSize();
QModelIndex indexAt(const QPoint& point) const override;
private:
GameCard* m_gameCard = nullptr;
};
+36
View File
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QStandardItem>
#include <QStringList>
#include "qt_common/game_list/game_list_p.h"
namespace Yuzu {
inline bool ContainsAllWords(const QString& haystack, const QString& userinput) {
const QStringList userinput_split = userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
return std::all_of(userinput_split.begin(), userinput_split.end(),
[&haystack](const QString& s) { return haystack.contains(s); });
}
inline bool FilterMatches(const QString& filter, const QStandardItem* item) {
if (filter.isEmpty())
return true;
const auto program_id = item->data(GameListItemPath::ProgramIdRole).toULongLong();
const QString file_path = item->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title = item->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_program_id = QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
const QString file_name =
file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + file_title;
return Yuzu::ContainsAllWords(file_name, filter) ||
(file_program_id.size() == 16 && file_program_id.contains(filter));
}
} // namespace Yuzu
+54 -15
View File
@@ -18,27 +18,16 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
constexpr int cardMargin = 8;
constexpr int cardCornerRadius = 10;
const int column = index.row() % m_columns;
const int cell_width = option.rect.width();
const int card_width = cell_width - m_padding;
const int row_width = m_columns * cell_width;
const int total_gap = row_width - cardMargin * 2 - m_columns * card_width;
const int gap = (m_columns > 1) ? (total_gap / (m_columns - 1)) : 0;
const int card_left = option.rect.left() - column * cell_width + cardMargin + column * (card_width + gap) + 4;
const QRect cardRect(card_left, option.rect.top() + 4, card_width - 8,
option.rect.height() - cardMargin);
const QRect cardRect = getCardRect(option, index);
QPalette palette = option.palette;
QColor backgroundColor = palette.window().color();
QColor borderColor = palette.dark().color();
QColor textColor = palette.text().color();
// highlight blue on select
// highlight on select or hover
if (option.state & QStyle::State_Selected) {
backgroundColor = palette.highlight().color();
borderColor = palette.highlight().color().lighter(150);
@@ -60,7 +49,7 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
scaled.scale(icon_size, icon_size, Qt::KeepAspectRatio);
iconRect = {cardRect.left() + (cardRect.width() - scaled.width()) / 2,
cardRect.top() + cardMargin, scaled.width(), scaled.height()};
cardRect.top() + cardMargin - 1, scaled.width(), scaled.height()};
painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
@@ -94,12 +83,62 @@ void GameCard::paint(QPainter* painter, const QStyleOptionViewItem& option,
painter->restore();
}
QRect GameCard::getCardRect(const QStyleOptionViewItem& option, const QModelIndex& index) const {
const int cell_width = option.rect.width();
const int card_width = cell_width - m_padding;
int card_left, card_top, card_height;
if (m_columns >= 1) {
// grid mode
// center everything in-line, such that the leftmost and rightmost cards
// have ~ equal padding to the edge of the viewport
// spacing between each card is larger, but equal to each other
const int column = index.row() % m_columns;
const int row_width = m_columns * cell_width;
const int total_gap = row_width - cardMargin * 2 - m_columns * card_width;
const int gap = (m_columns > 1) ? (total_gap / (m_columns - 1)) : 0;
card_left =
option.rect.left() - column * cell_width + cardMargin + column * (card_width + gap) + 4;
// fill cell vertically
card_top = option.rect.top() + cardMargin;
card_height = option.rect.height() - cardMargin - 1;
} else {
// carousel mode
card_left = option.rect.left() + cardMargin + 4;
// the delegate itself takes up the full height, but the card itself
// gets centered
const int content_height = m_contentSize.height() - cardMargin;
const int cell_height = option.rect.height();
card_height = std::min(content_height, cell_height - cardMargin * 2) - 1;
card_top = option.rect.top() + (cell_height - card_height) / 2;
}
return QRect(card_left, card_top, card_width - cardMargin, card_height);
}
bool GameCard::hitTest(const QPoint& point, const QModelIndex& index, const QWidget* widget,
const QRect& cellRect) const {
QStyleOptionViewItem option;
option.initFrom(widget);
option.rect = cellRect;
return getCardRect(option, index).contains(point);
}
QSize GameCard::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
return m_size;
}
void GameCard::setSize(const QSize& newSize, const int padding, const int columns) {
void GameCard::setSize(const QSize& newSize, const QSize& contentSize, const int padding,
const int columns) {
m_size = newSize;
m_contentSize = contentSize;
m_padding = padding;
m_columns = columns;
}
+9 -1
View File
@@ -17,11 +17,19 @@ public:
void paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const override;
QRect getCardRect(const QStyleOptionViewItem &option, const QModelIndex &index) const;
bool hitTest(const QPoint& point, const QModelIndex& index,
const QWidget* widget, const QRect& cellRect) const;
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
void setSize(const QSize& newSize, const int padding, const int columns);
void setSize(const QSize& newSize, const QSize& contentSize, const int padding, const int columns);
private:
static constexpr int cardMargin = 8;
QSize m_size;
QSize m_contentSize;
int m_padding;
int m_columns;
};
+13 -20
View File
@@ -5,10 +5,10 @@
#include <QScrollerProperties>
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/model.h"
#include "yuzu/game/common.h"
#include "yuzu/game/game_card.h"
#include "yuzu/game/game_grid.h"
#include "qt_common/game_list/game_list_p.h"
#include "qt_common/game_list/model.h"
GameGrid::GameGrid(QWidget* parent) : QListView{parent} {
m_gameCard = new GameCard(this);
@@ -25,8 +25,6 @@ GameGrid::GameGrid(QWidget* parent) : QListView{parent} {
setEditTriggers(QAbstractItemView::NoEditTriggers);
setContextMenuPolicy(Qt::CustomContextMenu);
setGridSize(QSize(140, 160));
m_gameCard->setSize(gridSize(), 0, 4);
setSpacing(10);
setWordWrap(true);
@@ -43,26 +41,12 @@ void GameGrid::SetModel(GameListModel* model) {
void GameGrid::ApplyFilter(const QString& edit_filter_text, GameListModel* model) {
int row_count = model->rowCount();
auto ContainsAllWords = [](const QString& haystack, const QString& userinput) {
const QStringList userinput_split =
userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
return std::all_of(userinput_split.begin(), userinput_split.end(),
[&haystack](const QString& s) { return haystack.contains(s); });
};
for (int i = 0; i < row_count; ++i) {
QStandardItem* item = model->item(i, 0);
if (!item)
continue;
const QString file_path =
item->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title =
item->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_name = file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) +
QLatin1Char{' '} + file_title;
if (edit_filter_text.isEmpty() || ContainsAllWords(file_name, edit_filter_text)) {
if (Yuzu::FilterMatches(edit_filter_text, item)) {
setRowHidden(i, false);
} else {
setRowHidden(i, true);
@@ -111,8 +95,17 @@ void GameGrid::UpdateIconSize() {
setUpdatesEnabled(false);
setGridSize(grid_size);
m_gameCard->setSize(grid_size, stretched_width - min_item_width, columns);
m_gameCard->setSize(grid_size, grid_size, stretched_width - min_item_width, columns);
setUpdatesEnabled(true);
}
}
QModelIndex GameGrid::indexAt(const QPoint& point) const {
QModelIndex index = QListView::indexAt(point);
if (!index.isValid())
return {};
if (m_gameCard && !m_gameCard->hitTest(point, index, this, visualRect(index)))
return {};
return index;
}
+2 -2
View File
@@ -6,8 +6,6 @@
#include <QListView>
#include <QString>
#include "common/common_types.h"
class GameCard;
class GameListModel;
@@ -21,6 +19,8 @@ public:
void ApplyFilter(const QString& edit_filter_text, GameListModel* model);
void UpdateIconSize();
QModelIndex indexAt(const QPoint& point) const override;
private:
GameCard* m_gameCard = nullptr;
};
+109 -67
View File
@@ -20,24 +20,25 @@
#include <QScrollerProperties>
#include <QToolButton>
#include <QVariantAnimation>
#include <qlayoutitem.h>
#include "common/common_types.h"
#include "common/logging.h"
#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/game_list_p.h"
#include "qt_common/game_list/model.h"
#include "qt_common/qt_common.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/game/game_list.h"
#include "qt_common/game_list/game_list_p.h"
#include "yuzu/game/carousel.h"
#include "yuzu/game/game_grid.h"
#include "yuzu/game/game_list.h"
#include "yuzu/game/game_tree.h"
#include "qt_common/game_list/model.h"
#include "yuzu/game/search_field.h"
#include "yuzu/main_window.h"
#include "yuzu/util/controller_navigation.h"
#include "yuzu/game/search_field.h"
GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_,
PlayTime::PlayTimeManager& play_time_manager_, Core::System& system_,
@@ -62,14 +63,15 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
SetupScrollAnimation();
connect(main_window, &MainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(grid_view, &QListView::activated, this, &GameList::ValidateEntry);
connect(grid_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(carousel_view, &QListView::activated, this, &GameList::ValidateEntry);
connect(carousel_view, &QListView::customContextMenuRequested, this, &GameList::PopupContextMenu);
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, this,
[this](Qt::Key key) {
if (system.IsPoweredOn()) {
@@ -87,7 +89,9 @@ GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvid
connect(item_model, &GameListModel::ShowList, this, &GameList::ShowList);
connect(item_model, &GameListModel::SaveConfig, this, &GameList::SaveConfig);
connect(item_model, &GameListModel::PopulatingStarted, this, &GameList::OnPopulate);
// TODO: impl on grid/carousel
connect(tree_view, &GameTree::FilterResultReady, search_field,
[this](int visible, int total) { search_field->setFilterResult(visible, total); });
@@ -103,12 +107,11 @@ GameList::~GameList() {
void GameList::SetupViews() {
tree_view = new GameTree(this);
grid_view = new GameGrid(this);
carousel_view = new GameCarousel(this);
tree_view->SetModel(item_model);
grid_view->SetModel(item_model);
layout->addWidget(tree_view);
layout->addWidget(grid_view);
carousel_view->SetModel(item_model);
}
QString GameList::GetLastFilterResultItem() const {
@@ -137,15 +140,23 @@ void GameList::LoadCompatibilityList() {
item_model->LoadCompatibilityList();
}
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
void GameList::OnPopulate() {
m_currentView->setEnabled(false);
tree_view->UpdateColumnVisibility(item_model);
if (!m_isTreeMode) {
switch (game_list_mode) {
case Settings::GameListMode::TreeView:
tree_view->UpdateColumnVisibility(item_model);
break;
case Settings::GameListMode::GridView:
grid_view->UpdateIconSize();
break;
case Settings::GameListMode::CarouselView:
carousel_view->UpdateIconSize();
break;
}
}
void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
item_model->PopulateAsync(game_dirs);
}
@@ -166,26 +177,46 @@ void GameList::UnloadController() {
}
void GameList::ResetViewMode() {
auto& setting = UISettings::values.game_list_mode;
const auto mode = UISettings::values.game_list_mode.GetValue();
game_list_mode = mode;
if (m_currentView)
layout->removeWidget(m_currentView);
bool newTreeMode = false;
switch (setting.GetValue()) {
switch (mode) {
case Settings::GameListMode::TreeView:
m_currentView = tree_view;
newTreeMode = true;
tree_view->setVisible(true);
grid_view->setVisible(false);
break;
case Settings::GameListMode::GridView:
m_currentView = grid_view;
newTreeMode = false;
grid_view->setVisible(true);
tree_view->setVisible(false);
break;
case Settings::GameListMode::CarouselView:
m_currentView = carousel_view;
newTreeMode = false;
break;
default:
UNREACHABLE();
}
tree_view->setVisible(false);
grid_view->setVisible(false);
carousel_view->setVisible(false);
tree_view->setEnabled(false);
grid_view->setEnabled(false);
carousel_view->setEnabled(false);
m_currentView->setVisible(true);
m_currentView->setEnabled(true);
layout->insertWidget(0, m_currentView);
auto view = m_currentView->viewport();
view->installEventFilter(this);
@@ -196,8 +227,10 @@ void GameList::ResetViewMode() {
auto scroller = QScroller::scroller(view);
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
QScrollerProperties::OvershootAlwaysOff);
scroller->setScrollerProperties(props);
if (m_isTreeMode != newTreeMode) {
@@ -223,10 +256,6 @@ void GameList::OnFilterCloseClicked() {
main_window->filterBarSetChecked(false);
}
void GameList::OnUpdateThemedIcons() {
item_model->OnUpdateThemedIcons();
}
void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
emit ShowList(!item_model->IsEmpty());
@@ -255,32 +284,43 @@ void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
}
}
// Clear out the old directories to watch for changes and add the new ones
// Watcher updates
auto* watcher = item_model->GetWatcher();
auto watch_dirs = watcher->directories();
if (!watch_dirs.isEmpty()) {
watcher->removePaths(watch_dirs);
}
auto current_watch_list = watcher->directories();
constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
constexpr qsizetype LIMIT_WATCH_DIRECTORIES = 5000;
constexpr int SLICE_SIZE = 25;
int len = (std::min)(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
#ifdef __APPLE__
const bool old_signals_blocked = watcher->blockSignals(true);
#endif
QStringList to_remove, to_add;
for (int i = 0; i < len; i += SLICE_SIZE) {
auto chunk = watch_list.mid(i, SLICE_SIZE);
if (!chunk.isEmpty()) {
watcher->addPaths(chunk);
const auto slice = [&](const QStringList& list, std::function<void(const QStringList&)> callback) {
const int len = (std::min)(list.size(), LIMIT_WATCH_DIRECTORIES);
for (int i = 0; i < len; i += SLICE_SIZE) {
auto chunk = list.mid(i, SLICE_SIZE);
if (!chunk.isEmpty()) {
callback(chunk);
}
QCoreApplication::processEvents();
}
};
// remove any paths not in the new watch list
for (const auto& path : std::as_const(current_watch_list)) {
if (!watch_list.contains(path)) {
to_remove.emplaceBack(path);
}
QCoreApplication::processEvents();
}
#ifdef __APPLE__
watcher->blockSignals(old_signals_blocked);
#endif
slice(to_remove, [watcher](const QStringList& chunk) { watcher->removePaths(chunk); });
// add any paths not in the old watch list
for (const auto& path : std::as_const(watch_list)) {
if (!current_watch_list.contains(path)) {
to_add.emplaceBack(path);
}
}
slice(to_add, [watcher](const QStringList& chunk) { watcher->addPaths(chunk); });
m_currentView->setEnabled(true);
@@ -288,11 +328,13 @@ void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
for (int i = 1; i < item_model->rowCount() - 1; ++i) {
children_total += item_model->item(i, 0)->rowCount();
}
search_field->setFilterResult(children_total, children_total);
if (children_total > 0) {
search_field->setFocus();
}
// TODO: carousel/grid impl.
item_model->sort(tree_view->header()->sortIndicatorSection(),
tree_view->header()->sortIndicatorOrder());
@@ -300,21 +342,23 @@ void GameList::OnPopulatingCompleted(const QStringList& watch_list) {
}
void GameList::RefreshGameDirectory() {
item_model->ResetExternalWatcher();
if (!UISettings::values.game_dirs.empty()) {
LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
}
item_model->RefreshGameDirectory();
}
void GameList::RefreshExternalContent() {
if (!UISettings::values.game_dirs.empty()) {
LOG_INFO(Frontend, "External content directory changed. Clearing metadata cache.");
QtCommon::Game::ResetMetadata(false);
QtCommon::system->GetFileSystemController().CreateFactories(*QtCommon::vfs);
PopulateAsync(UISettings::values.game_dirs);
item_model->RefreshExternalContent();
}
void GameList::UpdateIconSizes() {
switch (game_list_mode) {
case Settings::GameListMode::GridView:
grid_view->UpdateIconSize();
break;
case Settings::GameListMode::CarouselView:
carousel_view->UpdateIconSize();
break;
case Settings::GameListMode::TreeView:
break;
}
}
@@ -472,9 +516,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
});
connect(start_game, &QAction::triggered, this,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); });
connect(start_game_global, &QAction::triggered, this, [this, path]() {
emit BootGame(QString::fromStdString(path), StartGameType::Global);
});
connect(start_game_global, &QAction::triggered, this,
[this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); });
connect(open_mod_location, &QAction::triggered, this, [this, program_id, path]() {
emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
});
@@ -613,7 +656,8 @@ void GameList::AddFavoritesPopup(QMenu& context_menu) {
connect(clear, &QAction::triggered, this, [this] {
UISettings::values.favorited_ids.clear();
item_model->invisibleRootItem()->child(0)->removeRows(0, item_model->invisibleRootItem()->child(0)->rowCount());
item_model->invisibleRootItem()->child(0)->removeRows(
0, item_model->invisibleRootItem()->child(0)->rowCount());
tree_view->setRowHidden(0, item_model->invisibleRootItem()->index(), true);
});
}
@@ -708,13 +752,15 @@ bool GameList::eventFilter(QObject* obj, QEvent* event) {
return true;
}
if (obj == carousel_view->viewport() && event->type() == QEvent::Resize) {
carousel_view->UpdateIconSize();
return true;
}
return QWidget::eventFilter(obj, event);
}
GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} {
connect(parent, &MainWindow::UpdateThemedIcons, this,
&GameListPlaceholder::onUpdateThemedIcons);
layout = new QVBoxLayout;
image = new QLabel;
text = new QLabel;
@@ -735,10 +781,6 @@ GameListPlaceholder::GameListPlaceholder(MainWindow* parent) : QWidget{parent} {
GameListPlaceholder::~GameListPlaceholder() = default;
void GameListPlaceholder::onUpdateThemedIcons() {
image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
}
void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
emit GameListPlaceholder::AddDirectory();
}
+8 -5
View File
@@ -15,12 +15,12 @@
#include <QVBoxLayout>
#include <QVector>
#include <QWidget>
#include <qabstractitemview.h>
#include "common/common_types.h"
#include "core/core.h"
#include "frontend_common/play_time_manager.h"
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/model.h"
#include "qt_common/util/game.h"
#include "yuzu/compatibility_list.h"
@@ -38,6 +38,9 @@ class MainWindow;
enum class AmLaunchType;
enum class StartGameType;
class GameCarousel;
class QAbstractItemView;
namespace Core {
class System;
}
@@ -89,6 +92,8 @@ public:
public slots:
void RefreshGameDirectory();
void RefreshExternalContent();
void UpdateIconSizes();
void OnPopulate();
signals:
void BootGame(const QString& game_path, StartGameType type);
@@ -119,7 +124,6 @@ signals:
private slots:
void OnTextChanged(const QString& new_text);
void OnFilterCloseClicked();
void OnUpdateThemedIcons();
void OnPopulatingCompleted(const QStringList& watch_list);
private:
@@ -150,7 +154,9 @@ private:
GameTree* tree_view = nullptr;
GameGrid* grid_view = nullptr;
GameCarousel* carousel_view = nullptr;
GameListModel* item_model = nullptr;
Settings::GameListMode game_list_mode;
ControllerNavigation* controller_navigation = nullptr;
@@ -175,9 +181,6 @@ public:
signals:
void AddDirectory();
private slots:
void onUpdateThemedIcons();
protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
+3 -23
View File
@@ -8,8 +8,9 @@
#include "qt_common/config/uisettings.h"
#include "qt_common/game_list/game_list_p.h"
#include "yuzu/game/game_tree.h"
#include "qt_common/game_list/model.h"
#include "yuzu/game/common.h"
#include "yuzu/game/game_tree.h"
GameTree::GameTree(QWidget* parent) : QTreeView{parent} {
setAlternatingRowColors(true);
@@ -139,28 +140,7 @@ void GameTree::ApplyFilter(const QString& edit_filter_text, GameListModel* model
const QStandardItem* child = folder->child(j, 0);
const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong();
const QString file_path =
child->data(GameListItemPath::FullPathRole).toString().toLower();
const QString file_title =
child->data(GameListItemPath::TitleRole).toString().toLower();
const QString file_program_id =
QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
const QString file_name =
file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} +
file_title;
auto ContainsAllWords = [](const QString& haystack, const QString& userinput) {
const QStringList userinput_split =
userinput.split(QLatin1Char{' '}, Qt::SkipEmptyParts);
return std::all_of(userinput_split.begin(), userinput_split.end(),
[&haystack](const QString& s) { return haystack.contains(s); });
};
if (ContainsAllWords(file_name, edit_filter_text) ||
(file_program_id.size() == 16 && file_program_id.contains(edit_filter_text))) {
if (Yuzu::FilterMatches(edit_filter_text, child)) {
setRowHidden(j, folder_index, false);
++result_count;
} else {
-2
View File
@@ -3,8 +3,6 @@
#pragma once
#include <QHeaderView>
#include <QString>
#include <QTreeView>
class GameListModel;
+10 -1
View File
@@ -45,7 +45,7 @@
<x>0</x>
<y>0</y>
<width>1280</width>
<height>22</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menu_File">
@@ -111,6 +111,7 @@
</property>
<addaction name="action_Tree_View"/>
<addaction name="action_Grid_View"/>
<addaction name="action_Carousel_View"/>
</widget>
<widget class="QMenu" name="menuGame_Icon_Size">
<property name="title">
@@ -620,6 +621,14 @@
<string>Show &amp;Performance Overlay</string>
</property>
</action>
<action name="action_Carousel_View">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>&amp;Carousel View</string>
</property>
</action>
</widget>
<resources>
<include location="yuzu.qrc"/>
+19 -6
View File
@@ -579,9 +579,12 @@ MainWindow::MainWindow(bool has_broken_vulkan)
} else if (should_launch_qlaunch) {
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
} else if (should_launch_hlaunch) {
std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
std::filesystem::path const sd_dir =
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
BootGame(QString::fromStdString(hbl_path), LibraryAppletParameters(0x010000000000100Dull, Service::AM::AppletId::QLaunch));
BootGame(
QString::fromStdString(hbl_path),
LibraryAppletParameters(0x010000000000100Dull, Service::AM::AppletId::QLaunch));
}
}
}
@@ -1526,6 +1529,7 @@ void MainWindow::ConnectMenuEvents() {
connect_menu(ui->action_Grid_View, &MainWindow::SetGridView);
connect_menu(ui->action_Tree_View, &MainWindow::SetTreeView);
connect_menu(ui->action_Carousel_View, &MainWindow::SetCarouselView);
game_size_actions = new QActionGroup(this);
game_size_actions->setExclusive(true);
@@ -1545,6 +1549,8 @@ void MainWindow::ConnectMenuEvents() {
if (checked) {
UISettings::values.game_icon_size.SetValue(size);
CheckIconSize();
game_list->UpdateIconSizes();
game_list->RefreshGameDirectory();
}
});
@@ -3312,9 +3318,10 @@ void MainWindow::ResetWindowSize1080() {
void MainWindow::SetGameListMode(Settings::GameListMode mode) {
ui->action_Grid_View->setChecked(mode == Settings::GameListMode::GridView);
ui->action_Tree_View->setChecked(mode == Settings::GameListMode::TreeView);
ui->action_Carousel_View->setChecked(mode == Settings::GameListMode::CarouselView);
UISettings::values.game_list_mode = mode;
ui->action_Show_Game_Name->setEnabled(mode == Settings::GameListMode::GridView);
ui->action_Show_Game_Name->setEnabled(mode != Settings::GameListMode::TreeView);
CheckIconSize();
game_list->ResetViewMode();
@@ -3328,11 +3335,15 @@ void MainWindow::SetTreeView() {
SetGameListMode(Settings::GameListMode::TreeView);
}
void MainWindow::SetCarouselView() {
SetGameListMode(Settings::GameListMode::CarouselView);
}
void MainWindow::CheckIconSize() {
// When in grid view mode, with text off
// there is no point in having icons turned off..
// When in grid/carousel view mode, with text off
// there is no point in having icons turned off
auto actions = game_size_actions->actions();
if (UISettings::values.game_list_mode.GetValue() == Settings::GameListMode::GridView &&
if (UISettings::values.game_list_mode.GetValue() != Settings::GameListMode::TreeView &&
!UISettings::values.show_game_name.GetValue()) {
u32 newSize = UISettings::values.game_icon_size.GetValue();
if (newSize == 0) {
@@ -3363,6 +3374,7 @@ void MainWindow::ToggleShowGameName() {
CheckIconSize();
game_list->UpdateIconSizes();
game_list->RefreshGameDirectory();
}
@@ -3868,6 +3880,7 @@ void MainWindow::OnGameListRefresh() {
// Resets metadata cache and reloads
QtCommon::Game::ResetMetadata(false);
game_list->RefreshGameDirectory();
game_list->RefreshExternalContent();
SetFirmwareVersion();
}
+1
View File
@@ -410,6 +410,7 @@ private slots:
void SetGameListMode(Settings::GameListMode mode);
void SetGridView();
void SetTreeView();
void SetCarouselView();
void CheckIconSize();
void ToggleShowGameName();
+13 -2
View File
@@ -83,8 +83,13 @@ for pkg in $packages; do
echo "-- Package $PACKAGE"
# TODO(crueter): Support for Forgejo updates w/ forgejo_token
# Use gh-cli to avoid ratelimits lmao
TAGS=$(gh api --method GET "/repos/$REPO/tags")
# Use gh-cli to avoid ratelimits, if available
endpoint="/repos/$REPO/tags"
if command -v gh >/dev/null 2>&1; then
TAGS=$(gh api --method GET "$endpoint")
else
TAGS=$(curl -sfL "https://api.github.com$endpoint")
fi
# filter out some commonly known annoyances
# TODO add more
@@ -105,6 +110,12 @@ for pkg in $packages; do
filter_out beta
filter_out rc
# openssl
if [ "$PACKAGE" = openssl ]; then
filter_out rsaref
filter_in "openssl-"
fi
# Add package-specific overrides here, e.g. here for fmt:
[ "$PACKAGE" != fmt ] || filter_out v0.11