mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-18 06:10:35 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0cda02e91e | |||
| f8facda35f | |||
| 39be450fa3 | |||
| 68aaea6085 | |||
| ba608d2b57 | |||
| c22f57bc4e | |||
| 7b97ec4594 | |||
| e63b248e54 | |||
| 97a8470b12 | |||
| 102d254530 | |||
| 7c0e993b5b | |||
| ce14fc91fb | |||
| c27266c4cd | |||
| 02eaf3c5a7 |
@@ -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 }}' \
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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()
|
||||
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+478
-468
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
Vendored
+440
-430
File diff suppressed because it is too large
Load Diff
@@ -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 -> System -> Netzwerk</string>
|
||||
<string name="multiplayer_ip_error">Ungültiges IP-Format</string>
|
||||
<string name="multiplayer_username_error">Muss zwischen 4–20 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 -> 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 <b>Eden</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 <b>prod.keys</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 <b>Spiele</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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QString>
|
||||
#include <QTreeView>
|
||||
|
||||
class GameListModel;
|
||||
|
||||
+10
-1
@@ -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 &Performance Overlay</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_Carousel_View">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Carousel View</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="yuzu.qrc"/>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -410,6 +410,7 @@ private slots:
|
||||
void SetGameListMode(Settings::GameListMode mode);
|
||||
void SetGridView();
|
||||
void SetTreeView();
|
||||
void SetCarouselView();
|
||||
|
||||
void CheckIconSize();
|
||||
void ToggleShowGameName();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user