mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5044961cf2 | |||
| a4abca4040 | |||
| 6f9d025ad2 | |||
| ab129cf314 | |||
| 72b3f3a8a5 | |||
| 5fb3ae487c | |||
| 86e02aceba | |||
| 50ec0892c6 | |||
| f4e7fc91ba | |||
| 45c9f9bbb3 | |||
| 8e373eb714 | |||
| 75ebfaa090 | |||
| e3035ae8f2 | |||
| bcd22d9f8b | |||
| 4e7c036c7e | |||
| 19e2dba35a | |||
| 0634b4a278 | |||
| ee428deb1e | |||
| 07bc77c7e7 | |||
| 72973fe582 | |||
| c263b6af6f | |||
| e46576b4c3 | |||
| 89bcb73d47 | |||
| 7d81a724ef | |||
| 634686d59b | |||
| d0af14632e | |||
| c87986b276 | |||
| b600b90d53 | |||
| 2b979024cb | |||
| 5f676a6a55 | |||
| 2ab5b37137 | |||
| f6547fac8c | |||
| 9c685c1449 | |||
| aaf1a597ef | |||
| 850fc372b7 | |||
| bad9d8043b | |||
| 8e8e66f880 | |||
| fec5ac2298 | |||
| 19494bc7ac | |||
| 28a78d76fe | |||
| 6b3f8e1b62 | |||
| aacc40254c | |||
| efd26c3e4d | |||
| a8093c2a3c | |||
| 0f8b35c4cd | |||
| 866881d0e3 | |||
| a56b8d3de8 | |||
| e544cb3cf6 | |||
| ca9f2d43be | |||
| e10f55d9db | |||
| 71e035f83b | |||
| 16f2564478 | |||
| 75fda70db2 | |||
| b9e052b3a7 | |||
| 69aff83ef4 | |||
| e07e269bd7 | |||
| 08232ce642 | |||
| d536a66010 | |||
| 8907523ee4 | |||
| 9b1f6801a5 | |||
| ff03ef657a | |||
| 5acddfde16 | |||
| 913a74dbd3 | |||
| cbb92e75d3 | |||
| bd7d0e0466 | |||
| cce77b8d11 | |||
| c2f4449b1c | |||
| 48ba1f3f24 | |||
| d59fcf01bf | |||
| b8456394f1 | |||
| a467dd1ba6 | |||
| c156f4760f | |||
| 13f11ebf49 | |||
| 6065e9aa09 | |||
| 8ed0ed5828 | |||
| 33067af283 | |||
| d0a054270e | |||
| 2a3507c2b9 | |||
| f71f43561d | |||
| ecbfad4193 | |||
| d76b2b5d26 | |||
| cd9527072d | |||
| ffdaf7369a | |||
| 5113f503d1 | |||
| a5f1c2bcb0 | |||
| 2e432c9d17 |
+2
-3
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
case "$1" in
|
||||
@@ -104,8 +104,7 @@ cmake .. -G Ninja \
|
||||
-DYUZU_USE_QT_MULTIMEDIA=$MULTIMEDIA \
|
||||
-DYUZU_USE_QT_WEB_ENGINE=$WEBENGINE \
|
||||
-DYUZU_USE_FASTER_LD=ON \
|
||||
-DYUZU_ENABLE_LTO=ON \
|
||||
-DDYNARMIC_ENABLE_LTO=ON \
|
||||
-DENABLE_LTO=ON \
|
||||
"${EXTRA_CMAKE_FLAGS[@]}"
|
||||
|
||||
ninja -j${NPROC}
|
||||
|
||||
Regular → Executable
+2
-3
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
if [ "$COMPILER" == "clang" ]
|
||||
@@ -32,9 +32,8 @@ cmake .. -G Ninja \
|
||||
-DYUZU_ROOM_STANDALONE=OFF \
|
||||
-DYUZU_USE_QT_MULTIMEDIA=${USE_MULTIMEDIA:-false} \
|
||||
-DYUZU_USE_QT_WEB_ENGINE=${USE_WEBENGINE:-false} \
|
||||
-DYUZU_ENABLE_LTO=ON \
|
||||
-DENABLE_LTO=ON \
|
||||
-DCMAKE_EXE_LINKER_FLAGS=" /LTCG" \
|
||||
-DDYNARMIC_ENABLE_LTO=ON \
|
||||
-DYUZU_USE_BUNDLED_QT=${BUNDLE_QT:-false} \
|
||||
-DUSE_CCACHE=${CCACHE:-false} \
|
||||
-DENABLE_UPDATE_CHECKER=${DEVEL:-true} \
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- a/libs/context/CMakeLists.txt 2025-09-08 00:42:31.303651800 -0400
|
||||
+++ b/libs/context/CMakeLists.txt 2025-09-08 00:42:40.592184300 -0400
|
||||
@@ -146,7 +146,7 @@
|
||||
set(ASM_LANGUAGE ASM)
|
||||
endif()
|
||||
elseif(BOOST_CONTEXT_ASSEMBLER STREQUAL armasm)
|
||||
- set(ASM_LANGUAGE ASM_ARMASM)
|
||||
+ set(ASM_LANGUAGE ASM_MARMASM)
|
||||
else()
|
||||
set(ASM_LANGUAGE ASM_MASM)
|
||||
endif()
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/libs/context/CMakeLists.txt b/libs/context/CMakeLists.txt
|
||||
index 8210f65..0e59dd7 100644
|
||||
--- a/libs/context/CMakeLists.txt
|
||||
+++ b/libs/context/CMakeLists.txt
|
||||
@@ -186,7 +186,8 @@ if(BOOST_CONTEXT_IMPLEMENTATION STREQUAL "fcontext")
|
||||
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "/safeseh")
|
||||
endif()
|
||||
|
||||
- else() # masm
|
||||
+ # armasm doesn't support most of these options
|
||||
+ elseif(NOT BOOST_CONTEXT_ASSEMBLER STREQUAL armasm) # masm
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set_property(SOURCE ${ASM_SOURCES} APPEND PROPERTY COMPILE_OPTIONS "-x" "assembler-with-cpp")
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -1,52 +1,62 @@
|
||||
From e1a946ffb79022d38351a0623f819a5419965c3e Mon Sep 17 00:00:00 2001
|
||||
From 436fc1978c78edd085d99b33275b24be0ac96aa0 Mon Sep 17 00:00:00 2001
|
||||
From: crueter <crueter@eden-emu.dev>
|
||||
Date: Fri, 24 Oct 2025 23:41:09 -0700
|
||||
Subject: [PATCH] [build] Fix MinGW missing GetAddrInfoExCancel definition
|
||||
Date: Sun, 1 Feb 2026 16:21:10 -0500
|
||||
Subject: [PATCH] Fix build on MinGW
|
||||
|
||||
MinGW does not define GetAddrInfoExCancel in its wstcpi whatever header,
|
||||
so to get around this we can just load it with GetProcAddress et al.
|
||||
MinGW doesn't define GetAddrInfoExCancel.
|
||||
|
||||
Signed-off-by: crueter <crueter@eden-emu.dev>
|
||||
---
|
||||
httplib.h | 14 ++++++++++++--
|
||||
1 file changed, 12 insertions(+), 2 deletions(-)
|
||||
httplib.h | 18 ++++++++++++++++--
|
||||
1 file changed, 16 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/httplib.h b/httplib.h
|
||||
index e15ba44..90a76dc 100644
|
||||
index ec8d2a2..5f9a510 100644
|
||||
--- a/httplib.h
|
||||
+++ b/httplib.h
|
||||
@@ -203,11 +203,13 @@
|
||||
@@ -203,14 +203,17 @@
|
||||
#error Sorry, Visual Studio versions prior to 2015 are not supported
|
||||
#endif
|
||||
|
||||
-#pragma comment(lib, "ws2_32.lib")
|
||||
-
|
||||
#ifndef _SSIZE_T_DEFINED
|
||||
using ssize_t = __int64;
|
||||
#define _SSIZE_T_DEFINED
|
||||
#endif
|
||||
#endif // _MSC_VER
|
||||
|
||||
+#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
+#pragma comment(lib, "ws2_32.lib")
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
#ifndef S_ISREG
|
||||
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
|
||||
#endif // S_ISREG
|
||||
@@ -3557,7 +3559,15 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
|
||||
@@ -4528,7 +4531,17 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
|
||||
auto wait_result =
|
||||
::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
|
||||
if (wait_result == WAIT_TIMEOUT) {
|
||||
+#ifdef __MINGW32__
|
||||
+ typedef INT (WSAAPI *PFN_GETADDRINFOEXCANCEL)(HANDLE *CancelHandle);
|
||||
+ auto wsdll = LoadLibraryW((wchar_t*) "ws2_32.lib");
|
||||
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel = (PFN_GETADDRINFOEXCANCEL) GetProcAddress(wsdll, "GetAddrInfoExCancel");
|
||||
+ typedef INT(WSAAPI * PFN_GETADDRINFOEXCANCEL)(HANDLE * CancelHandle);
|
||||
+ auto wsdll = LoadLibraryW((wchar_t *)"ws2_32.lib");
|
||||
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel =
|
||||
+ (PFN_GETADDRINFOEXCANCEL)GetProcAddress(wsdll, "GetAddrInfoExCancel");
|
||||
+
|
||||
+ if (cancel_handle) { GetAddrInfoExCancel(&cancel_handle); }
|
||||
+#else
|
||||
if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
|
||||
+#endif
|
||||
+
|
||||
::CloseHandle(event);
|
||||
return EAI_AGAIN;
|
||||
}
|
||||
@@ -13952,3 +13965,4 @@ inline SSL_CTX *Client::ssl_context() const {
|
||||
} // namespace httplib
|
||||
|
||||
#endif // CPPHTTPLIB_HTTPLIB_H
|
||||
+
|
||||
--
|
||||
2.51.0
|
||||
2.51.2
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
From b3622608433c183ba868a1dc8dd9cf285eb3b916 Mon Sep 17 00:00:00 2001
|
||||
From: Dario Petrillo <dario.pk1@gmail.com>
|
||||
Date: Thu, 27 Nov 2025 23:12:38 +0100
|
||||
Subject: [PATCH] avoid extra memset when clearing an empty table
|
||||
|
||||
---
|
||||
include/ankerl/unordered_dense.h | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/include/ankerl/unordered_dense.h b/include/ankerl/unordered_dense.h
|
||||
index 0835342..4938212 100644
|
||||
--- a/include/ankerl/unordered_dense.h
|
||||
+++ b/include/ankerl/unordered_dense.h
|
||||
@@ -1490,8 +1490,10 @@ class table : public std::conditional_t<is_map_v<T>, base_table_type_map<T>, bas
|
||||
// modifiers //////////////////////////////////////////////////////////////
|
||||
|
||||
void clear() {
|
||||
- m_values.clear();
|
||||
- clear_buckets();
|
||||
+ if (!empty()) {
|
||||
+ m_values.clear();
|
||||
+ clear_buckets();
|
||||
+ }
|
||||
}
|
||||
|
||||
auto insert(value_type const& value) -> std::pair<iterator, bool> {
|
||||
+17
-7
@@ -32,8 +32,8 @@ if (PLATFORM_OPENBSD)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
|
||||
elseif (PLATFORM_NETBSD)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
|
||||
endif()
|
||||
|
||||
@@ -178,7 +178,9 @@ endif()
|
||||
|
||||
# Disable Warnings as Errors for MSVC
|
||||
if (MSVC AND NOT CXX_CLANG)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
|
||||
# This was dripping into spirv, being overriden, and causing cl flag override warning
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
|
||||
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS_INIT} /W3 /WX-")
|
||||
endif()
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
@@ -244,11 +246,12 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
|
||||
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
|
||||
|
||||
set(DEFAULT_ENABLE_OPENSSL ON)
|
||||
if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN)
|
||||
if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN OR PLATFORM_OPENBSD)
|
||||
# - Windows defaults to the Schannel backend.
|
||||
# - macOS defaults to the SecureTransport backend.
|
||||
# - Android currently has no SSL backend as the NDK doesn't include any SSL
|
||||
# library; a proper 'native' backend would have to go through Java.
|
||||
# - Solaris and OpenBSD have too old backends
|
||||
# But you can force builds for those platforms to use OpenSSL if you have
|
||||
# your own copy of it.
|
||||
set(DEFAULT_ENABLE_OPENSSL OFF)
|
||||
@@ -261,7 +264,7 @@ endif()
|
||||
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
|
||||
|
||||
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
|
||||
if (EXT_DEFAULT OR PLATFORM_SUN)
|
||||
if (EXT_DEFAULT OR PLATFORM_SUN OR PLATFORM_OPENBSD)
|
||||
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
|
||||
endif()
|
||||
|
||||
@@ -573,6 +576,7 @@ add_subdirectory(externals)
|
||||
# pass targets from externals
|
||||
find_package(enet)
|
||||
find_package(MbedTLS)
|
||||
find_package(unordered_dense REQUIRED)
|
||||
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
find_package(xbyak)
|
||||
@@ -587,6 +591,7 @@ if (NOT YUZU_STATIC_ROOM)
|
||||
find_package(sirit)
|
||||
find_package(gamemode)
|
||||
find_package(mcl)
|
||||
find_package(frozen)
|
||||
|
||||
if (ARCHITECTURE_riscv64)
|
||||
find_package(biscuit)
|
||||
@@ -636,7 +641,7 @@ if (ENABLE_QT)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}")
|
||||
endif()
|
||||
|
||||
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Concurrent)
|
||||
find_package(Qt6 CONFIG REQUIRED COMPONENTS Widgets Charts Concurrent)
|
||||
|
||||
if (YUZU_USE_QT_MULTIMEDIA)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Multimedia)
|
||||
@@ -675,7 +680,7 @@ if (ENABLE_QT)
|
||||
## Components ##
|
||||
|
||||
# Best practice is to ask for all components at once, so they are from the same version
|
||||
set(YUZU_QT_COMPONENTS Core Widgets Concurrent)
|
||||
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent)
|
||||
if (PLATFORM_LINUX)
|
||||
list(APPEND YUZU_QT_COMPONENTS DBus)
|
||||
endif()
|
||||
@@ -693,6 +698,11 @@ if (ENABLE_QT)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
# Qt6 sets cxx_std_17 and we need to undo that
|
||||
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
|
||||
|
||||
## Qt Externals ##
|
||||
|
||||
# QuaZip
|
||||
AddJsonPackage(quazip)
|
||||
endif()
|
||||
|
||||
if (NOT YUZU_STATIC_ROOM AND NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
|
||||
|
||||
+196
-125
@@ -41,6 +41,11 @@ function(cpm_utils_message level name message)
|
||||
message(${level} "[CPMUtil] ${name}: ${message}")
|
||||
endfunction()
|
||||
|
||||
# propagate a variable to parent scope
|
||||
macro(Propagate var)
|
||||
set(${var} ${${var}} PARENT_SCOPE)
|
||||
endmacro()
|
||||
|
||||
function(array_to_list array length out)
|
||||
math(EXPR range "${length} - 1")
|
||||
|
||||
@@ -72,6 +77,163 @@ function(get_json_element object out member default)
|
||||
set("${out}" "${outvar}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Determine whether or not a package has a viable system candidate.
|
||||
function(SystemPackageViable JSON_NAME)
|
||||
string(JSON object GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
|
||||
|
||||
parse_object(${object})
|
||||
|
||||
string(REPLACE " " ";" find_args "${find_args}")
|
||||
if (${package}_FORCE_BUNDLED)
|
||||
set(${package}_FOUND OFF)
|
||||
else()
|
||||
find_package(${package} ${version} ${find_args} QUIET NO_POLICY_SCOPE)
|
||||
endif()
|
||||
|
||||
set(${pkg}_VIABLE ${${package}_FOUND} PARENT_SCOPE)
|
||||
set(${pkg}_PACKAGE ${package} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Add several packages such that if one is bundled,
|
||||
# all the rest must also be bundled.
|
||||
function(AddDependentPackages)
|
||||
set(_some_system OFF)
|
||||
set(_some_bundled OFF)
|
||||
|
||||
foreach(pkg ${ARGN})
|
||||
SystemPackageViable(${pkg})
|
||||
|
||||
if (${pkg}_VIABLE)
|
||||
set(_some_system ON)
|
||||
list(APPEND _system_pkgs ${${pkg}_PACKAGE})
|
||||
else()
|
||||
set(_some_bundled ON)
|
||||
list(APPEND _bundled_pkgs ${${pkg}_PACKAGE})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (_some_system AND _some_bundled)
|
||||
foreach(pkg ${ARGN})
|
||||
list(APPEND package_names ${${pkg}_PACKAGE})
|
||||
endforeach()
|
||||
|
||||
string(REPLACE ";" ", " package_names "${package_names}")
|
||||
string(REPLACE ";" ", " bundled_names "${_bundled_pkgs}")
|
||||
foreach(sys ${_system_pkgs})
|
||||
list(APPEND system_names ${sys}_FORCE_BUNDLED)
|
||||
endforeach()
|
||||
|
||||
string(REPLACE ";" ", " system_names "${system_names}")
|
||||
|
||||
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:"
|
||||
"\n\t${bundled_names}\n"
|
||||
"2. Set the following variables to ON:"
|
||||
"\n\t${system_names}\n"
|
||||
"This may also be caused by a version mismatch, "
|
||||
"such as one package being newer than the other.")
|
||||
endif()
|
||||
|
||||
foreach(pkg ${ARGN})
|
||||
AddJsonPackage(${pkg})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# json util
|
||||
macro(parse_object object)
|
||||
get_json_element("${object}" package package ${JSON_NAME})
|
||||
get_json_element("${object}" repo repo "")
|
||||
get_json_element("${object}" ci ci OFF)
|
||||
get_json_element("${object}" version version "")
|
||||
|
||||
if(ci)
|
||||
get_json_element("${object}" name name "${JSON_NAME}")
|
||||
get_json_element("${object}" extension extension "tar.zst")
|
||||
get_json_element("${object}" min_version min_version "")
|
||||
get_json_element("${object}" raw_disabled disabled_platforms "")
|
||||
|
||||
if(raw_disabled)
|
||||
array_to_list("${raw_disabled}"
|
||||
${raw_disabled_LENGTH} disabled_platforms)
|
||||
else()
|
||||
set(disabled_platforms "")
|
||||
endif()
|
||||
else()
|
||||
get_json_element("${object}" hash hash "")
|
||||
get_json_element("${object}" hash_suffix hash_suffix "")
|
||||
get_json_element("${object}" sha sha "")
|
||||
get_json_element("${object}" url url "")
|
||||
get_json_element("${object}" key key "")
|
||||
get_json_element("${object}" tag tag "")
|
||||
get_json_element("${object}" artifact artifact "")
|
||||
get_json_element("${object}" git_version git_version "")
|
||||
get_json_element("${object}" git_host git_host "")
|
||||
get_json_element("${object}" source_subdir source_subdir "")
|
||||
get_json_element("${object}" bundled bundled "unset")
|
||||
get_json_element("${object}" find_args find_args "")
|
||||
get_json_element("${object}" raw_patches patches "")
|
||||
|
||||
# okay here comes the fun part: REPLACEMENTS!
|
||||
# first: tag gets %VERSION% replaced if applicable,
|
||||
# with either git_version (preferred) or version
|
||||
# second: artifact gets %VERSION% and %TAG% replaced
|
||||
# accordingly (same rules for VERSION)
|
||||
|
||||
if(git_version)
|
||||
set(version_replace ${git_version})
|
||||
else()
|
||||
set(version_replace ${version})
|
||||
endif()
|
||||
|
||||
# TODO(crueter): fmt module for cmake
|
||||
if(tag)
|
||||
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
|
||||
endif()
|
||||
|
||||
if(artifact)
|
||||
string(REPLACE "%VERSION%" "${version_replace}"
|
||||
artifact ${artifact})
|
||||
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
|
||||
endif()
|
||||
|
||||
# format patchdir
|
||||
if(raw_patches)
|
||||
math(EXPR range "${raw_patches_LENGTH} - 1")
|
||||
|
||||
foreach(IDX RANGE ${range})
|
||||
string(JSON _patch GET "${raw_patches}" "${IDX}")
|
||||
|
||||
set(full_patch
|
||||
"${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
|
||||
if(NOT EXISTS ${full_patch})
|
||||
cpm_utils_message(FATAL_ERROR ${JSON_NAME}
|
||||
"specifies patch ${full_patch} which does not exist")
|
||||
endif()
|
||||
|
||||
list(APPEND patches "${full_patch}")
|
||||
endforeach()
|
||||
endif()
|
||||
# end format patchdir
|
||||
|
||||
# options
|
||||
get_json_element("${object}" raw_options options "")
|
||||
|
||||
if(raw_options)
|
||||
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
|
||||
endif()
|
||||
|
||||
set(options ${options} ${JSON_OPTIONS})
|
||||
# end options
|
||||
|
||||
# system/bundled
|
||||
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
|
||||
set(bundled ${JSON_BUNDLED_PACKAGE})
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# The preferred usage
|
||||
function(AddJsonPackage)
|
||||
set(oneValueArgs
|
||||
@@ -80,7 +242,8 @@ function(AddJsonPackage)
|
||||
# these are overrides that can be generated at runtime,
|
||||
# so can be defined separately from the json
|
||||
DOWNLOAD_ONLY
|
||||
BUNDLED_PACKAGE)
|
||||
BUNDLED_PACKAGE
|
||||
FORCE_BUNDLED_PACKAGE)
|
||||
|
||||
set(multiValueArgs OPTIONS)
|
||||
|
||||
@@ -111,24 +274,9 @@ function(AddJsonPackage)
|
||||
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
|
||||
endif()
|
||||
|
||||
get_json_element("${object}" package package ${JSON_NAME})
|
||||
get_json_element("${object}" repo repo "")
|
||||
get_json_element("${object}" ci ci OFF)
|
||||
get_json_element("${object}" version version "")
|
||||
parse_object(${object})
|
||||
|
||||
if(ci)
|
||||
get_json_element("${object}" name name "${JSON_NAME}")
|
||||
get_json_element("${object}" extension extension "tar.zst")
|
||||
get_json_element("${object}" min_version min_version "")
|
||||
get_json_element("${object}" raw_disabled disabled_platforms "")
|
||||
|
||||
if(raw_disabled)
|
||||
array_to_list("${raw_disabled}"
|
||||
${raw_disabled_LENGTH} disabled_platforms)
|
||||
else()
|
||||
set(disabled_platforms "")
|
||||
endif()
|
||||
|
||||
AddCIPackage(
|
||||
VERSION ${version}
|
||||
NAME ${name}
|
||||
@@ -138,116 +286,38 @@ function(AddJsonPackage)
|
||||
MIN_VERSION ${min_version}
|
||||
DISABLED_PLATFORMS ${disabled_platforms})
|
||||
|
||||
# pass stuff to parent scope
|
||||
set(${package}_ADDED "${${package}_ADDED}"
|
||||
PARENT_SCOPE)
|
||||
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}"
|
||||
PARENT_SCOPE)
|
||||
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
|
||||
PARENT_SCOPE)
|
||||
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_json_element("${object}" hash hash "")
|
||||
get_json_element("${object}" hash_suffix hash_suffix "")
|
||||
get_json_element("${object}" sha sha "")
|
||||
get_json_element("${object}" url url "")
|
||||
get_json_element("${object}" key key "")
|
||||
get_json_element("${object}" tag tag "")
|
||||
get_json_element("${object}" artifact artifact "")
|
||||
get_json_element("${object}" git_version git_version "")
|
||||
get_json_element("${object}" git_host git_host "")
|
||||
get_json_element("${object}" source_subdir source_subdir "")
|
||||
get_json_element("${object}" bundled bundled "unset")
|
||||
get_json_element("${object}" find_args find_args "")
|
||||
get_json_element("${object}" raw_patches patches "")
|
||||
|
||||
# okay here comes the fun part: REPLACEMENTS!
|
||||
# first: tag gets %VERSION% replaced if applicable,
|
||||
# with either git_version (preferred) or version
|
||||
# second: artifact gets %VERSION% and %TAG% replaced
|
||||
# accordingly (same rules for VERSION)
|
||||
|
||||
if(git_version)
|
||||
set(version_replace ${git_version})
|
||||
else()
|
||||
set(version_replace ${version})
|
||||
if (NOT DEFINED JSON_FORCE_BUNDLED_PACKAGE)
|
||||
set(JSON_FORCE_BUNDLED_PACKAGE OFF)
|
||||
endif()
|
||||
|
||||
AddPackage(
|
||||
NAME "${package}"
|
||||
VERSION "${version}"
|
||||
URL "${url}"
|
||||
HASH "${hash}"
|
||||
HASH_SUFFIX "${hash_suffix}"
|
||||
SHA "${sha}"
|
||||
REPO "${repo}"
|
||||
KEY "${key}"
|
||||
PATCHES "${patches}"
|
||||
OPTIONS "${options}"
|
||||
FIND_PACKAGE_ARGUMENTS "${find_args}"
|
||||
BUNDLED_PACKAGE "${bundled}"
|
||||
FORCE_BUNDLED_PACKAGE "${JSON_FORCE_BUNDLED_PACKAGE}"
|
||||
SOURCE_SUBDIR "${source_subdir}"
|
||||
|
||||
GIT_VERSION ${git_version}
|
||||
GIT_HOST ${git_host}
|
||||
|
||||
ARTIFACT ${artifact}
|
||||
TAG ${tag})
|
||||
endif()
|
||||
|
||||
# TODO(crueter): fmt module for cmake
|
||||
if(tag)
|
||||
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
|
||||
endif()
|
||||
|
||||
if(artifact)
|
||||
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact})
|
||||
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
|
||||
endif()
|
||||
|
||||
# format patchdir
|
||||
if(raw_patches)
|
||||
math(EXPR range "${raw_patches_LENGTH} - 1")
|
||||
|
||||
foreach(IDX RANGE ${range})
|
||||
string(JSON _patch GET "${raw_patches}" "${IDX}")
|
||||
|
||||
set(full_patch
|
||||
"${PROJECT_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
|
||||
if(NOT EXISTS ${full_patch})
|
||||
cpm_utils_message(FATAL_ERROR ${JSON_NAME}
|
||||
"specifies patch ${full_patch} which does not exist")
|
||||
endif()
|
||||
|
||||
list(APPEND patches "${full_patch}")
|
||||
endforeach()
|
||||
endif()
|
||||
# end format patchdir
|
||||
|
||||
# options
|
||||
get_json_element("${object}" raw_options options "")
|
||||
|
||||
if(raw_options)
|
||||
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
|
||||
endif()
|
||||
|
||||
set(options ${options} ${JSON_OPTIONS})
|
||||
# end options
|
||||
|
||||
# system/bundled
|
||||
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
|
||||
set(bundled ${JSON_BUNDLED_PACKAGE})
|
||||
endif()
|
||||
|
||||
AddPackage(
|
||||
NAME "${package}"
|
||||
VERSION "${version}"
|
||||
URL "${url}"
|
||||
HASH "${hash}"
|
||||
HASH_SUFFIX "${hash_suffix}"
|
||||
SHA "${sha}"
|
||||
REPO "${repo}"
|
||||
KEY "${key}"
|
||||
PATCHES "${patches}"
|
||||
OPTIONS "${options}"
|
||||
FIND_PACKAGE_ARGUMENTS "${find_args}"
|
||||
BUNDLED_PACKAGE "${bundled}"
|
||||
SOURCE_SUBDIR "${source_subdir}"
|
||||
|
||||
GIT_VERSION ${git_version}
|
||||
GIT_HOST ${git_host}
|
||||
|
||||
ARTIFACT ${artifact}
|
||||
TAG ${tag})
|
||||
|
||||
# pass stuff to parent scope
|
||||
set(${package}_ADDED "${${package}_ADDED}"
|
||||
PARENT_SCOPE)
|
||||
set(${package}_SOURCE_DIR "${${package}_SOURCE_DIR}"
|
||||
PARENT_SCOPE)
|
||||
set(${package}_BINARY_DIR "${${package}_BINARY_DIR}"
|
||||
PARENT_SCOPE)
|
||||
|
||||
Propagate(${package}_ADDED)
|
||||
Propagate(${package}_SOURCE_DIR)
|
||||
Propagate(${package}_BINARY_DIR)
|
||||
endfunction()
|
||||
|
||||
function(AddPackage)
|
||||
@@ -343,7 +413,7 @@ function(AddPackage)
|
||||
|
||||
if(DEFINED PKG_ARGS_ARTIFACT)
|
||||
set(pkg_url
|
||||
${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT})
|
||||
"${pkg_git_url}/releases/download/${PKG_ARGS_TAG}/${PKG_ARGS_ARTIFACT}")
|
||||
else()
|
||||
set(pkg_url
|
||||
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
|
||||
@@ -625,7 +695,8 @@ function(AddCIPackage)
|
||||
endif()
|
||||
|
||||
if (DEFINED pkgname AND NOT "${pkgname}" IN_LIST DISABLED_PLATFORMS)
|
||||
set(ARTIFACT "${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
|
||||
set(ARTIFACT
|
||||
"${ARTIFACT_NAME}-${pkgname}-${ARTIFACT_VERSION}.${ARTIFACT_EXT}")
|
||||
|
||||
AddPackage(
|
||||
NAME ${ARTIFACT_PACKAGE}
|
||||
|
||||
@@ -8,20 +8,26 @@
|
||||
include(GetSCMRev)
|
||||
|
||||
function(get_timestamp _var)
|
||||
string(TIMESTAMP timestamp UTC)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
string(TIMESTAMP timestamp UTC)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
get_timestamp(BUILD_DATE)
|
||||
|
||||
if (DEFINED GIT_RELEASE)
|
||||
set(BUILD_VERSION "${GIT_TAG}")
|
||||
set(GIT_REFSPEC "${GIT_RELEASE}")
|
||||
set(IS_DEV_BUILD false)
|
||||
set(BUILD_VERSION "${GIT_TAG}")
|
||||
set(GIT_REFSPEC "${GIT_RELEASE}")
|
||||
set(IS_DEV_BUILD false)
|
||||
else()
|
||||
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
|
||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
|
||||
set(IS_DEV_BUILD true)
|
||||
string(SUBSTRING ${GIT_COMMIT} 0 10 BUILD_VERSION)
|
||||
set(BUILD_VERSION "${BUILD_VERSION}-${GIT_REFSPEC}")
|
||||
set(IS_DEV_BUILD true)
|
||||
endif()
|
||||
|
||||
if (NIGHTLY_BUILD)
|
||||
set(IS_NIGHTLY_BUILD true)
|
||||
else()
|
||||
set(IS_NIGHTLY_BUILD false)
|
||||
endif()
|
||||
|
||||
set(GIT_DESC ${BUILD_VERSION})
|
||||
@@ -30,15 +36,18 @@ set(GIT_DESC ${BUILD_VERSION})
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
|
||||
# Auto-updater metadata! Must somewhat mirror GitHub API endpoint
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
|
||||
set(BUILD_AUTO_UPDATE_API "http://api.github.com")
|
||||
|
||||
if (NIGHTLY_BUILD)
|
||||
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
|
||||
set(REPO_NAME "Eden Nightly")
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://github.com")
|
||||
set(BUILD_AUTO_UPDATE_API "api.github.com")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "Eden-CI/Nightly")
|
||||
set(REPO_NAME "Eden Nightly")
|
||||
else()
|
||||
set(BUILD_AUTO_UPDATE_REPO "eden-emulator/Releases")
|
||||
set(REPO_NAME "Eden")
|
||||
set(BUILD_AUTO_UPDATE_WEBSITE "https://git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API "git.eden-emu.dev")
|
||||
set(BUILD_AUTO_UPDATE_API_PATH "/api/v1/repos/")
|
||||
set(BUILD_AUTO_UPDATE_REPO "eden-emu/eden")
|
||||
set(REPO_NAME "Eden")
|
||||
endif()
|
||||
|
||||
set(BUILD_ID ${GIT_REFSPEC})
|
||||
|
||||
+18
-7
@@ -12,14 +12,12 @@
|
||||
"repo": "boostorg/boost",
|
||||
"tag": "boost-%VERSION%",
|
||||
"artifact": "%TAG%-cmake.tar.xz",
|
||||
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9",
|
||||
"git_version": "1.89.0",
|
||||
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
|
||||
"git_version": "1.90.0",
|
||||
"version": "1.57",
|
||||
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
|
||||
"patches": [
|
||||
"0001-clang-cl.patch",
|
||||
"0002-use-marmasm.patch",
|
||||
"0003-armasm-options.patch"
|
||||
"0001-clang-cl.patch"
|
||||
]
|
||||
},
|
||||
"fmt": {
|
||||
@@ -48,9 +46,9 @@
|
||||
"package": "ZLIB",
|
||||
"repo": "madler/zlib",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "8c9642495bafd6fad4ab9fb67f09b268c69ff9af0f4f20cf15dfc18852ff1f312bd8ca41de761b3f8d8e90e77d79f2ccacd3d4c5b19e475ecf09d021fdfe9088",
|
||||
"hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
|
||||
"version": "1.2",
|
||||
"git_version": "1.3.1",
|
||||
"git_version": "1.3.1.2",
|
||||
"options": [
|
||||
"ZLIB_BUILD_SHARED OFF",
|
||||
"ZLIB_INSTALL OFF"
|
||||
@@ -103,5 +101,18 @@
|
||||
"git_version": "1.4.335.0",
|
||||
"artifact": "android-binaries-%VERSION%.zip",
|
||||
"hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
|
||||
},
|
||||
"quazip": {
|
||||
"package": "QuaZip-Qt6",
|
||||
"repo": "stachenov/quazip",
|
||||
"sha": "2e95c9001b",
|
||||
"hash": "609c240c7f029ac26a37d8fbab51bc16284e05e128b78b9b9c0e95d083538c36047a67d682759ac990e4adb0eeb90f04f1ea7fe2253bbda7e7e3bcce32e53dd8",
|
||||
"version": "1.3",
|
||||
"git_version": "1.5",
|
||||
"options": [
|
||||
"QUAZIP_QT_MAJOR_VERSION 6",
|
||||
"QUAZIP_INSTALL OFF",
|
||||
"QUAZIP_ENABLE_QTEXTCODEC OFF"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
fill="none"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="EdenLogoLoveWhiteV3.svg"
|
||||
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="dev.eden_emu.eden.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata1">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Madeline_Dev</dc:title>
|
||||
<dc:identifier>mailto:madelvidel@gmail.com</dc:identifier>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:date>2025</dc:date>
|
||||
<dc:license
|
||||
rdf:resource="https://www.gnu.org/licenses/gpl-3.0.html" />
|
||||
<dc:rights>2025 Eden Emulator Project</dc:rights>
|
||||
<dc:source>https://git.eden-emu.dev</dc:source>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs7"><linearGradient
|
||||
id="swatch14"
|
||||
inkscape:swatch="solid"><stop
|
||||
style="stop-color:#66003b;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop14" /></linearGradient><linearGradient
|
||||
id="linearGradient1"
|
||||
inkscape:collect="always"><stop
|
||||
style="stop-color:#ffffff;stop-opacity:0.50980395;"
|
||||
offset="0"
|
||||
id="stop1" /><stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2" /></linearGradient><linearGradient
|
||||
id="linearGradient11"
|
||||
inkscape:collect="always"><stop
|
||||
style="stop-color:#9523a7;stop-opacity:0.50980395;"
|
||||
offset="0"
|
||||
id="stop11" /><stop
|
||||
style="stop-color:#ffabad;stop-opacity:1;"
|
||||
offset="0.99898213"
|
||||
id="stop20" /><stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="0.99898213"
|
||||
id="stop12" /></linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient11"
|
||||
id="linearGradient12"
|
||||
x1="109.74531"
|
||||
y1="106.54533"
|
||||
x2="431.05463"
|
||||
y2="427.85461"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="reflect"
|
||||
gradientTransform="matrix(1.0945321,0,0,1.0945321,-39.661525,-35.159057)" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1"
|
||||
id="linearGradient2"
|
||||
x1="125.40197"
|
||||
y1="271.834"
|
||||
x2="431.02424"
|
||||
y2="271.834"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0964243,0,0,1.0253208,-40.187969,-20.060025)" /><filter
|
||||
inkscape:label="Light Contour"
|
||||
inkscape:menu="Image Paint and Draw"
|
||||
inkscape:menu-tooltip="Uses vertical specular light to draw lines"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter11"
|
||||
x="-0.01907517"
|
||||
y="-0.054959154"
|
||||
width="1.0379885"
|
||||
height="1.1092314"><feGaussianBlur
|
||||
in="SourceGraphic"
|
||||
stdDeviation="0.38250006"
|
||||
result="result3"
|
||||
id="feGaussianBlur9" /><feComponentTransfer
|
||||
result="result1"
|
||||
in="result3"
|
||||
id="feComponentTransfer9"><feFuncR
|
||||
type="discrete"
|
||||
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
|
||||
id="feFuncR9" /><feFuncG
|
||||
type="discrete"
|
||||
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
|
||||
id="feFuncG9" /><feFuncB
|
||||
type="discrete"
|
||||
tableValues="0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1"
|
||||
id="feFuncB9" /></feComponentTransfer><feGaussianBlur
|
||||
result="result5"
|
||||
stdDeviation="0.01"
|
||||
id="feGaussianBlur10" /><feBlend
|
||||
in2="result5"
|
||||
result="result6"
|
||||
mode="lighten"
|
||||
in="result5"
|
||||
id="feBlend10" /><feColorMatrix
|
||||
in="result6"
|
||||
type="luminanceToAlpha"
|
||||
result="result2"
|
||||
id="feColorMatrix10" /><feSpecularLighting
|
||||
surfaceScale="5"
|
||||
result="result9"
|
||||
specularExponent="20"
|
||||
in="result2"
|
||||
specularConstant="1"
|
||||
id="feSpecularLighting10"><feDistantLight
|
||||
azimuth="180"
|
||||
elevation="90"
|
||||
id="feDistantLight10" /></feSpecularLighting><feComposite
|
||||
in2="result6"
|
||||
operator="arithmetic"
|
||||
in="result9"
|
||||
k1="0.4"
|
||||
k3="0.7"
|
||||
result="result3"
|
||||
id="feComposite10"
|
||||
k2="0"
|
||||
k4="0" /><feBlend
|
||||
in2="result1"
|
||||
in="result3"
|
||||
mode="normal"
|
||||
result="result8"
|
||||
id="feBlend11" /><feComposite
|
||||
in2="SourceGraphic"
|
||||
in="result8"
|
||||
operator="in"
|
||||
result="result7"
|
||||
id="feComposite11" /></filter></defs><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.35"
|
||||
inkscape:cx="254.81481"
|
||||
inkscape:cy="260.37037"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7"
|
||||
showguides="false" /><circle
|
||||
style="fill:url(#linearGradient12);fill-opacity:1;stroke:#e4e4e4;stroke-width:14.0448;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path8"
|
||||
cx="256.2999"
|
||||
cy="257.2999"
|
||||
r="248.67769" /><path
|
||||
id="path15"
|
||||
style="fill:url(#linearGradient2);fill-opacity:1;stroke:#ffffff;stroke-width:16.9642;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.52156866;paint-order:stroke fill markers"
|
||||
d="m 306.72111,24.233031 c 0,0 -11.98397,40.08696 -18.0546,60.431848 -12.96613,9.503601 -21.49377,18.397701 -21.49377,18.397701 0,0 -23.41313,-31.029398 -45.74145,-43.934598 -22.32833,-12.905201 -52.42065,-11.242483 -52.42065,-11.242483 0,0 -12.50052,4e-4 -27.63117,5.537132 -15.13066,5.536732 -21.27107,9.227888 -21.27107,9.227888 0,0 15.35165,-0.410529 37.93799,6.766716 22.58635,7.177243 32.45374,11.484796 32.45374,11.484796 l 31.02752,-6.562453 -8.44161,11.074275 c 0,0 14.80259,8.920284 22.80648,16.917787 8.0039,7.9975 11.73088,12.50812 11.73088,12.50812 0,0 -13.92373,-5.43341 -56.68427,-1.74226 -42.76055,3.69116 -84.86368,56.39265 -84.86368,56.39265 0,0 41.22428,-15.9958 65.5649,-21.32747 24.34062,-5.33166 47.58524,-7.9983 47.58524,-7.9983 0,0 -18.41865,7.3827 -38.15428,38.3474 -19.73564,30.96468 -14.0351,80.18128 -14.0351,80.18128 0,0 31.35774,-59.05848 61.39977,-78.94969 30.04203,-19.89124 36.182,-20.50642 36.182,-20.50642 0,0 -24.1209,48.39514 -33.55015,126.11445 -9.42924,77.71931 30.26089,207.72959 30.26089,207.72959 l 47.80367,-6.97298 c 0,0 -45.61041,-42.24281 -49.55753,-151.33695 -3.94713,-109.09413 16.66479,-174.30453 16.66479,-174.30453 0,0 17.10508,1.23039 42.54213,32.81027 25.43704,31.57987 40.34713,57.41795 40.34713,57.41795 0,0 9.65024,-51.26683 -11.4011,-74.64415 -21.05135,-23.3773 -53.50637,-33.62931 -53.50637,-33.62931 0,0 13.70573,-6.66419 42.10313,-4.71609 28.39738,1.94813 87.30278,27.12897 87.30278,27.12896 0,0 -15.8158,-25.5214 -52.87463,-43.21167 -37.05881,-17.69028 -81.35597,0.29238 -81.35597,0.29238 0,0 4.13589,-15.37277 29.10021,-32.628037 24.96432,-17.25527 56.907,-34.36427 56.907,-34.36427 0,0 -26.3595,1.160335 -55.20025,12.035504 -7.176,2.7059 -14.064,6.299542 -20.36309,10.173104 l 17.40574,-51.051762 z" /><path
|
||||
id="path4-2-6"
|
||||
style="fill:#ffffff;fill-opacity:0.509804;stroke:#ffffff;stroke-width:8.49614;stroke-dasharray:none;stroke-opacity:0.521569"
|
||||
d="m 105.64857,363.95957 a 27.851731,25.309928 0 0 0 -7.115581,35.20471 27.851731,25.309928 0 0 0 5.656921,5.48617 c 7.16052,5.83605 21.58543,14.65841 45.88391,17.847 37.57172,4.93039 41.7105,10.38005 41.71122,10.381 -4.5e-4,-6e-4 -4.13529,-5.45309 4.14459,-39.11953 5.35491,-21.77281 1.40624,-37.16078 -2.05217,-45.31698 a 27.851731,25.309928 0 0 0 -3.63281,-6.75471 27.851731,25.309928 0 0 0 -38.74027,-6.4662 27.851731,25.309928 0 0 0 -7.11556,35.20472 27.851731,25.309928 0 0 0 -38.74025,-6.46618 z" /><path
|
||||
id="path4-2-6-1"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:6.89965;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 351.29524,378.32915 a 20.862787,22.283365 87.929759 0 0 -28.94663,11.7882 20.862787,22.283365 87.929759 0 0 -1.50914,6.04125 c -1.09835,7.13663 -1.16134,20.16231 7.28339,36.72931 13.0577,25.61689 10.90177,30.69793 10.90138,30.69882 2.4e-4,-5.6e-4 2.16,-5.08045 30.49632,-15.43688 18.32576,-6.69765 27.84648,-16.23659 32.287,-22.14109 a 20.862787,22.283365 87.929759 0 0 3.33646,-5.36755 20.862787,22.283365 87.929759 0 0 -12.45108,-27.05014 20.862787,22.283365 87.929759 0 0 -28.94664,11.78822 20.862787,22.283365 87.929759 0 0 -12.45106,-27.05014 z" /><path
|
||||
id="path4-2-6-1-5"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:6.89965;stroke-dasharray:none;stroke-opacity:0.52156866"
|
||||
d="m 83.024352,249.4105 a 22.283365,20.862787 0 0 0 -29.353581,10.73482 22.283365,20.862787 0 0 0 -1.72639,5.98278 c -1.35544,7.0923 -1.88894,20.10721 5.951801,36.96846 12.12378,26.07187 9.7857,31.07171 9.78528,31.07259 2.6e-4,-5.5e-4 2.34212,-4.99911 31.03406,-14.32513 18.555748,-6.03127 28.414858,-15.22005 33.065768,-20.96029 a 22.283365,20.862787 0 0 0 3.52818,-5.24352 22.283365,20.862787 0 0 0 -11.46577,-27.48227 22.283365,20.862787 0 0 0 -29.353588,10.73483 22.283365,20.862787 0 0 0 -11.46576,-27.48227 z" /><path
|
||||
id="path4-2-6-1-9"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:5.01078;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 320.97602,291.36362 a 16.625347,14.748181 0 0 0 -21.90036,7.58859 16.625347,14.748181 0 0 0 -1.28804,4.2293 c -1.01128,5.01364 -1.40932,14.21406 4.44057,26.1335 9.0454,18.43055 7.30098,21.965 7.30067,21.96562 2e-4,-3.9e-4 1.74743,-3.53394 23.15415,-10.12663 13.84423,-4.26358 21.19999,-10.75925 24.66998,-14.8171 a 16.625347,14.748181 0 0 0 2.63233,-3.70671 16.625347,14.748181 0 0 0 -8.55447,-19.42758 16.625347,14.748181 0 0 0 -21.90037,7.58859 16.625347,14.748181 0 0 0 -8.55446,-19.42758 z" /><path
|
||||
id="path4-2-6-1-9-8"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:4.22313;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 416.90426,193.13811 a 12.322476,14.134158 82.30909 0 0 -17.56469,8.83612 12.322476,14.134158 82.30909 0 0 -0.59553,3.65007 c -0.27175,4.26676 0.45709,11.92642 6.76182,21.10665 9.74867,14.19503 8.68847,17.32338 8.68827,17.3239 1.2e-4,-3.3e-4 1.06282,-3.12827 18.32699,-11.08266 11.16516,-5.14424 16.60821,-11.37802 19.06101,-15.14093 a 12.322476,14.134158 82.30909 0 0 1.788,-3.37453 12.322476,14.134158 82.30909 0 0 -9.45059,-15.07737 12.322476,14.134158 82.30909 0 0 -17.5647,8.83612 12.322476,14.134158 82.30909 0 0 -9.45058,-15.07737 z" /><path
|
||||
id="path4-2-6-1-9-8-4"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:3.48342;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 96.169666,99.754795 a 10.296362,11.508677 62.365085 0 0 -7.330189,13.841365 10.296362,11.508677 62.365085 0 0 1.473186,2.6805 c 2.008889,2.93989 6.437543,7.66269 15.415927,10.84633 13.88288,4.92276 14.77391,7.47086 14.77407,7.4713 -1e-4,-2.8e-4 -0.88925,-2.54921 6.69094,-15.67164 4.90232,-8.48655 5.37758,-15.08279 5.10162,-18.6847 a 10.296362,11.508677 62.365085 0 0 -0.5257,-3.042454 10.296362,11.508677 62.365085 0 0 -14.1348,-5.641051 10.296362,11.508677 62.365085 0 0 -7.33021,13.841385 10.296362,11.508677 62.365085 0 0 -14.134844,-5.641035 z" /><path
|
||||
id="path4-2-6-1-9-8-4-9-2"
|
||||
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:3.05139;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 296.64657,233.26347 a 8.8114666,10.319195 80.445391 0 0 -11.58972,8.06989 8.8114666,10.319195 80.445391 0 0 0.0366,2.62543 c 0.35019,3.02392 1.86017,8.32306 7.60386,14.09672 8.8812,8.92759 8.51432,11.23624 8.51426,11.23663 5e-5,-2.5e-4 0.36876,-2.30885 11.85385,-9.72788 7.4277,-4.79799 10.57103,-9.75243 11.86523,-12.65455 a 8.8114666,10.319195 80.445391 0 0 0.86244,-2.55882 8.8114666,10.319195 80.445391 0 0 -8.77837,-9.57865 8.8114666,10.319195 80.445391 0 0 -11.58975,8.06989 8.8114666,10.319195 80.445391 0 0 -8.77842,-9.57866 z" /><path
|
||||
id="path4-2-6-1-9-8-4-9-2-6"
|
||||
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:2.49738;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 403.95446,91.44744 a 7.0950375,8.5844443 82.142743 0 0 -9.64888,6.492875 7.0950375,8.5844443 82.142743 0 0 0.0305,2.112375 c 0.29155,2.43298 1.54866,6.69657 6.33049,11.34195 7.39392,7.18298 7.08848,9.04046 7.08843,9.04078 4e-5,-2.1e-4 0.307,-1.85765 9.86876,-7.82686 6.18383,-3.86038 8.80077,-7.84663 9.87824,-10.18161 a 7.0950375,8.5844443 82.142743 0 0 0.718,-2.05879 7.0950375,8.5844443 82.142743 0 0 -7.30831,-7.706798 7.0950375,8.5844443 82.142743 0 0 -9.64889,6.492875 7.0950375,8.5844443 82.142743 0 0 -7.30834,-7.706797 z" /><path
|
||||
id="path4-2-6-1-9-8-4-9-5"
|
||||
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:7.54176;stroke-dasharray:none;stroke-opacity:0.521569"
|
||||
d="m 410.46352,265.86621 a 24.802737,22.39469 2.0523303 0 0 -30.17074,15.95063 24.802737,22.39469 2.0523303 0 0 -0.74427,6.61415 c -0.12337,7.7333 1.79614,21.62064 13.65297,38.28529 18.33368,25.76802 16.72835,31.4347 16.72811,31.43572 1.4e-4,-6.3e-4 1.60998,-5.66652 31.32518,-20.02007 19.21759,-9.28266 28.27643,-20.56192 32.27921,-27.37361 a 24.802737,22.39469 2.0523303 0 0 2.8654,-6.11021 24.802737,22.39469 2.0523303 0 0 -17.88248,-27.36631 24.802737,22.39469 2.0523303 0 0 -30.17081,15.95066 24.802737,22.39469 2.0523303 0 0 -17.88257,-27.36625 z" /><path
|
||||
id="path4-2-6-1-9-8-5"
|
||||
style="fill:#ffffff;fill-opacity:0.50980395;stroke:#ffffff;stroke-width:3.6405;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 114.43099,189.33754 a 10.333135,12.525309 79.004057 0 0 -14.00848,9.75499 10.333135,12.525309 79.004057 0 0 0.0547,3.08382 c 0.43602,3.54457 2.28588,9.73496 9.26636,16.38295 10.79359,10.27954 10.35819,13.00063 10.3581,13.00111 6e-5,-3e-4 0.4377,-2.72141 14.32197,-11.70923 8.9793,-5.8126 12.76786,-11.70785 14.32433,-15.14812 a 10.333135,12.525309 79.004057 0 0 1.03481,-3.02677 10.333135,12.525309 79.004057 0 0 -10.67161,-11.04688 10.333135,12.525309 79.004057 0 0 -14.00848,9.75501 10.333135,12.525309 79.004057 0 0 -10.6716,-11.04688 z" /><path
|
||||
id="path4-2-6-1-9-8-4-9-2-2"
|
||||
style="fill:#ffffff;fill-opacity:0.5117;stroke:#ffffff;stroke-width:3.62674;stroke-dasharray:none;stroke-opacity:0.519962"
|
||||
d="m 178.56055,273.32338 a 10.656883,12.053184 66.089768 0 0 -7.97821,14.16205 10.656883,12.053184 66.089768 0 0 1.49012,2.79504 c 2.04675,3.07265 6.5954,8.03049 15.94965,11.48124 14.46412,5.33577 15.34612,7.98353 15.34628,7.98398 -8e-5,-2.8e-4 -0.88012,-2.64887 7.3455,-16.0639 5.31973,-8.67576 5.95524,-15.47956 5.74045,-19.20452 a 10.656883,12.053184 66.089768 0 0 -0.48843,-3.15169 10.656883,12.053184 66.089768 0 0 -14.7135,-6.08217 10.656883,12.053184 66.089768 0 0 -7.97826,14.1621 10.656883,12.053184 66.089768 0 0 -14.71355,-6.08215 z" /></svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
+1
@@ -0,0 +1 @@
|
||||
#e48cc9ff
|
||||
+225
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 42 KiB |
+1
-1
@@ -18,7 +18,7 @@
|
||||
src/web_service @AleksandrPopovich
|
||||
src/dynarmic @Lizzie
|
||||
src/core @Lizzie @Maufeat @PavelBARABANOV @MrPurple666 @JPikachu
|
||||
src/core/hle @Maufeat @PavelBARABANOV @SDK-Chan
|
||||
src/core/hle @Maufeat @PavelBARABANOV
|
||||
src/core/arm @Lizzie @MrPurple666
|
||||
src/*_room @AleksandrPopovich
|
||||
src/video_core @CamilleLaVey @MaranBr @Wildcard @weakboson
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# AddDependentPackage
|
||||
|
||||
Use `AddDependentPackage` when you have multiple packages that are required to all be from the system, OR bundled. This is useful in cases where e.g. versions must absolutely match.
|
||||
|
||||
## Versioning
|
||||
|
||||
Versioning must be handled by the package itself.
|
||||
|
||||
## Examples
|
||||
|
||||
### Vulkan
|
||||
|
||||
`cpmfile.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"vulkan-headers": {
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"version": "1.4.317",
|
||||
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
|
||||
"git_version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
|
||||
"git_version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
|
||||
```
|
||||
|
||||
If Vulkan Headers are installed, but NOT Vulkan Utility Libraries, then CPMUtil will throw an error.
|
||||
@@ -31,6 +31,10 @@ The core of CPMUtil is the [`AddPackage`](./AddPackage.md) function. [`AddPackag
|
||||
|
||||
[`AddJsonPackage`](./AddJsonPackage.md) is the recommended method of usage for CPMUtil.
|
||||
|
||||
## AddDependentPackage
|
||||
|
||||
[`AddDependentPackage`](./AddDependentPackage.md) allows you to add multiple packages such that all of them must be from the system OR bundled.
|
||||
|
||||
## AddQt
|
||||
|
||||
[`AddQt`](./AddQt.md) adds a specific version of Qt to your project.
|
||||
|
||||
@@ -85,6 +85,8 @@ If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMak
|
||||
|
||||
## OpenBSD
|
||||
|
||||
System boost doesn't have `context` (as of 7.8); so you may need to specify `-DYUZU_USE_CPM=ON -DBoost_FORCE_BUNDLED=ON`.
|
||||
|
||||
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.
|
||||
|
||||
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up.
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv
|
||||
|
||||
```sh
|
||||
pkg_add -u
|
||||
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake llvm-19.1.7p3 qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.27
|
||||
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl2 libusb1-1.0.29
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#openbsd).
|
||||
|
||||
+5
-5
@@ -82,15 +82,15 @@ You may additionally need the `Qt Extension Pack` extension if building Qt.
|
||||
|
||||
# Build speedup
|
||||
|
||||
If you have an HDD, use ramdisk (build in RAM):
|
||||
If you have an HDD, use ramdisk (build in RAM), approximatedly you need 4GB for a full build with debug symbols:
|
||||
```sh
|
||||
sudo mkdir /tmp/ramdisk
|
||||
sudo chmod 777 /tmp/ramdisk
|
||||
mkdir /tmp/ramdisk
|
||||
chmod 777 /tmp/ramdisk
|
||||
# about 8GB needed
|
||||
sudo mount -t tmpfs -o size=8G myramdisk /tmp/ramdisk
|
||||
mount -t tmpfs -o size=4G myramdisk /tmp/ramdisk
|
||||
cmake -B /tmp/ramdisk
|
||||
cmake --build /tmp/ramdisk -- -j32
|
||||
sudo umount /tmp/ramdisk
|
||||
umount /tmp/ramdisk
|
||||
```
|
||||
|
||||
# Assets and large files
|
||||
|
||||
+1
-4
@@ -6,7 +6,4 @@ Debugging on physical hardware can get tedious and time consuming. Users are emp
|
||||
|
||||
**Standard key prefix**: Allows to redirect the key manager to a file other than `prod.keys` (for example `other` would redirect to `other.keys`). This is useful for testing multiple keysets. Default is `prod`.
|
||||
|
||||
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as it will be writen in-place (that is, it will be filled with the default serial and then overwrite the serial from the beginning).
|
||||
- Battery serial: `YUZU0EMULATOR14022024`
|
||||
- Board serial: `YUZ10000000001`
|
||||
If the user were to set their board serial as `ABC`, then it will be written in-place and the resulting serial would be `ABC10000000001`. There are no underlying checks to ensure correctness of serials other than a hard limit of 16-characters for both.
|
||||
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as only the first digits (excluding the last) will be accoutned for. Region settings will affect the generated serial. The serial corresponds to a non-OLED/Lite console.
|
||||
|
||||
Vendored
+8
-14
@@ -27,11 +27,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_NETBSD OR PLATFORM_DRAGONFLY)
|
||||
AddJsonPackage(xbyak_sun)
|
||||
else()
|
||||
AddJsonPackage(xbyak)
|
||||
endif()
|
||||
AddJsonPackage(xbyak)
|
||||
endif()
|
||||
|
||||
# enet
|
||||
@@ -66,6 +62,9 @@ if (NOT TARGET LLVM::Demangle)
|
||||
add_library(LLVM::Demangle ALIAS demangle)
|
||||
endif()
|
||||
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
if (YUZU_STATIC_ROOM)
|
||||
return()
|
||||
endif()
|
||||
@@ -83,13 +82,11 @@ endif()
|
||||
# mcl
|
||||
AddJsonPackage(mcl)
|
||||
|
||||
# VulkanUtilityHeaders - pulls in headers and utility libs
|
||||
AddJsonPackage(vulkan-utility-headers)
|
||||
# Vulkan stuff
|
||||
AddDependentPackages(vulkan-headers vulkan-utility-libraries)
|
||||
|
||||
# small hack
|
||||
if (NOT VulkanUtilityLibraries_ADDED)
|
||||
find_package(VulkanHeaders 1.3.274 REQUIRED)
|
||||
endif()
|
||||
# frozen
|
||||
AddJsonPackage(frozen)
|
||||
|
||||
# DiscordRPC
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
@@ -246,9 +243,6 @@ if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
|
||||
AddJsonPackage(cpp-jwt)
|
||||
endif()
|
||||
|
||||
# unordered_dense
|
||||
AddJsonPackage(unordered-dense)
|
||||
|
||||
# FFMpeg
|
||||
if (YUZU_USE_EXTERNAL_FFMPEG OR YUZU_USE_BUNDLED_FFMPEG)
|
||||
add_subdirectory(ffmpeg)
|
||||
|
||||
Vendored
+34
-32
@@ -28,8 +28,8 @@
|
||||
"httplib": {
|
||||
"repo": "yhirose/cpp-httplib",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "e7a8877d489c97669a8ee536e1498575be921e558ed947253013fe6b67a49d4569eedd01f543caa70183b92d8ac0e8687d662a70d880954412e387317008a239",
|
||||
"git_version": "0.28.0",
|
||||
"hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
|
||||
"git_version": "0.30.1",
|
||||
"find_args": "MODULE GLOBAL",
|
||||
"patches": [
|
||||
"0001-mingw.patch"
|
||||
@@ -48,23 +48,12 @@
|
||||
"0001-fix-missing-decl.patch"
|
||||
]
|
||||
},
|
||||
"xbyak_sun": {
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b40dade90fb0e46a2bd52934f7ce461e37be931b571e58cbe2203bc08ed5b54c7ff1a29026c74c7f9805e4e3f6c9636deca528e6b4a8093ce7eae145218599f1",
|
||||
"git_version": "7.29",
|
||||
"bundled": true,
|
||||
"skip_updates": true
|
||||
},
|
||||
"xbyak": {
|
||||
"package": "xbyak",
|
||||
"repo": "herumi/xbyak",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "1042090405c426e339506c179d53e91d4d545ce9c9f53d8f797caa092d589f913a9bcb9c8f31c4c60870acb954c556e305fb6732c66bc3c8f1cd924f9172def9",
|
||||
"git_version": "7.22",
|
||||
"bundled": true,
|
||||
"skip_updates": true
|
||||
"hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47",
|
||||
"git_version": "7.33.2"
|
||||
},
|
||||
"oaknut": {
|
||||
"repo": "eden-emulator/oaknut",
|
||||
@@ -91,10 +80,13 @@
|
||||
"unordered-dense": {
|
||||
"package": "unordered_dense",
|
||||
"repo": "martinus/unordered_dense",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "b98b5d4d96f8e0081b184d6c4c1181fae4e41723b54bed4296717d7f417348b48fad0bbcc664cac142b8c8a47e95aa57c1eb1cf6caa855fd782fad3e3ab99e5e",
|
||||
"sha": "7b55cab841",
|
||||
"hash": "d2106f6640f6bfb81755e4b8bfb64982e46ec4a507cacdb38f940123212ccf35a20b43c70c6f01d7bfb8c246d1a16f7845d8052971949cea9def1475e3fa02c8",
|
||||
"find_args": "CONFIG",
|
||||
"git_version": "4.8.1"
|
||||
"bundled": true,
|
||||
"patches": [
|
||||
"0001-avoid-memset-when-clearing-an-empty-table.patch"
|
||||
]
|
||||
},
|
||||
"mbedtls": {
|
||||
"package": "MbedTLS",
|
||||
@@ -118,15 +110,6 @@
|
||||
"git_version": "1.3.18",
|
||||
"find_args": "MODULE"
|
||||
},
|
||||
"vulkan-utility-headers": {
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"repo": "scripts/VulkanUtilityHeaders",
|
||||
"tag": "%VERSION%",
|
||||
"git_version": "1.4.335",
|
||||
"artifact": "VulkanUtilityHeaders.tar.zst",
|
||||
"git_host": "git.crueter.xyz",
|
||||
"hash": "16dac0e6586702580c4279e4cd37ffe3cf909c93eb31b5069da7af36436d47b270a9cbaac953bb66c22ed12ed67ffa096688599267f307dfb62be1bc09f79833"
|
||||
},
|
||||
"spirv-tools": {
|
||||
"package": "SPIRV-Tools",
|
||||
"repo": "KhronosGroup/SPIRV-Tools",
|
||||
@@ -167,16 +150,16 @@
|
||||
"package": "SDL2",
|
||||
"name": "SDL2",
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"version": "2.32.10-a65111bd2d",
|
||||
"version": "2.32.10-cf5dabd6ea",
|
||||
"min_version": "2.26.4"
|
||||
},
|
||||
"catch2": {
|
||||
"package": "Catch2",
|
||||
"repo": "catchorg/Catch2",
|
||||
"tag": "v%VERSION%",
|
||||
"hash": "a95495142f915d6e9c2a23e80fe360343e9097680066a2f9d3037a070ba5f81ee5559a0407cc9e972dc2afae325873f1fc7ea07a64012c0f01aac6e549f03e3f",
|
||||
"hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
|
||||
"version": "3.0.1",
|
||||
"git_version": "3.11.0",
|
||||
"git_version": "3.12.0",
|
||||
"patches": [
|
||||
"0001-solaris-isnan-fix.patch"
|
||||
]
|
||||
@@ -201,7 +184,6 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"tag": "release-%VERSION%",
|
||||
"hash": "d5622d6bb7266f7942a7b8ad43e8a22524893bf0c2ea1af91204838d9b78d32768843f6faa248757427b8404b8c6443776d4afa6b672cd8571a4e0c03a829383",
|
||||
"key": "generic",
|
||||
"bundled": true,
|
||||
"git_version": "2.32.10",
|
||||
"skip_updates": true
|
||||
@@ -211,7 +193,6 @@
|
||||
"repo": "libsdl-org/SDL",
|
||||
"sha": "cc016b0046",
|
||||
"hash": "b8d9873446cdb922387471df9968e078714683046674ef0d0edddf8e25da65a539a3bae83d635496b970237f90b07b36a69f8d7855d450de59311d6d6e8c3dbc",
|
||||
"key": "steamdeck",
|
||||
"bundled": true,
|
||||
"skip_updates": "true"
|
||||
},
|
||||
@@ -281,5 +262,26 @@
|
||||
"tag": "%VERSION%",
|
||||
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
|
||||
"version": "121125"
|
||||
},
|
||||
"vulkan-headers": {
|
||||
"repo": "KhronosGroup/Vulkan-Headers",
|
||||
"package": "VulkanHeaders",
|
||||
"version": "1.4.317",
|
||||
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
|
||||
"git_version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"vulkan-utility-libraries": {
|
||||
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
|
||||
"package": "VulkanUtilityLibraries",
|
||||
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
|
||||
"git_version": "1.4.342",
|
||||
"tag": "v%VERSION%"
|
||||
},
|
||||
"frozen": {
|
||||
"package": "frozen",
|
||||
"repo": "serge-sans-paille/frozen",
|
||||
"sha": "61dce5ae18",
|
||||
"hash": "b8dfe741c82bc178dfc9749d4ab5a130cee718d9ee7b71d9b547cf5f7f23027ed0152ad250012a8546399fcc1e12187efc68d89d6731256c4d2df7d04eef8d5c"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -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: 2023 yuzu Emulator Project
|
||||
@@ -33,7 +33,7 @@ endif()
|
||||
|
||||
if(NOT YUZU_TZDB_PATH STREQUAL "")
|
||||
set(NX_TZDB_BASE_DIR "${YUZU_TZDB_PATH}")
|
||||
elseif (MSVC AND NOT CXX_CLANG AND YUZU_ENABLE_LTO)
|
||||
elseif (MSVC AND NOT CXX_CLANG AND ENABLE_LTO)
|
||||
# TODO(crueter): boot up the windows vm
|
||||
set(NX_TZDB_VERSION "250725")
|
||||
set(NX_TZDB_ARCHIVE "${CPM_SOURCE_CACHE}/nx_tzdb/${NX_TZDB_VERSION}.zip")
|
||||
|
||||
@@ -20,4 +20,4 @@ pkgs.mkShellNoCC {
|
||||
# optional components
|
||||
discord-rpc gamemode
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,24 @@ object NativeLibrary {
|
||||
|
||||
external fun getDebugKnobAt(index: Int): Boolean
|
||||
|
||||
/**
|
||||
* Set the current speed limit to the configured turbo speed.
|
||||
*/
|
||||
external fun setTurboSpeedLimit(enabled: Boolean)
|
||||
|
||||
/**
|
||||
* Set the current speed limit to the configured slow speed.
|
||||
*/
|
||||
external fun setSlowSpeedLimit(enabled: Boolean)
|
||||
|
||||
/**
|
||||
* Set the current speed limit to the configured standard speed.
|
||||
*/
|
||||
external fun setStandardSpeedLimit(enabled: Boolean)
|
||||
|
||||
external fun isTurboMode(): Boolean
|
||||
external fun isSlowMode(): Boolean
|
||||
|
||||
/**
|
||||
* Returns Vulkan driver version / API version / GPU model
|
||||
*/
|
||||
@@ -614,4 +632,23 @@ object NativeLibrary {
|
||||
* Updates the device power state to global variables
|
||||
*/
|
||||
external fun updatePowerState(percentage: Int, isCharging: Boolean, hasBattery: Boolean)
|
||||
|
||||
/**
|
||||
* Profile manager native calls
|
||||
*/
|
||||
external fun getAllUsers(): Array<String>?
|
||||
external fun getUserUsername(uuid: String): String?
|
||||
external fun getUserCount(): Long
|
||||
external fun canCreateUser(): Boolean
|
||||
external fun createUser(uuid: String, username: String): Boolean
|
||||
external fun updateUserUsername(uuid: String, username: String): Boolean
|
||||
external fun removeUser(uuid: String): Boolean
|
||||
external fun getCurrentUser(): String?
|
||||
external fun setCurrentUser(uuid: String): Boolean
|
||||
external fun getUserImagePath(uuid: String): String?
|
||||
external fun saveUserImage(uuid: String, imagePath: String): Boolean
|
||||
external fun reloadProfiles()
|
||||
external fun getFirmwareAvatarCount(): Int
|
||||
external fun getFirmwareAvatarImage(index: Int): ByteArray?
|
||||
external fun getDefaultAccountBackupJpeg(): ByteArray
|
||||
}
|
||||
|
||||
@@ -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: 2023 yuzu Emulator Project
|
||||
@@ -10,6 +10,7 @@ import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
|
||||
import org.yuzu.yuzu_emu.model.Patch
|
||||
import org.yuzu.yuzu_emu.model.PatchType
|
||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||
|
||||
@@ -31,7 +32,12 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
|
||||
binding.addonSwitch.isChecked = model.enabled
|
||||
|
||||
binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
|
||||
model.enabled = checked
|
||||
if (PatchType.from(model.type) == PatchType.Update && checked) {
|
||||
addonViewModel.enableOnlyThisUpdate(model)
|
||||
notifyDataSetChanged()
|
||||
} else {
|
||||
model.enabled = checked
|
||||
}
|
||||
}
|
||||
|
||||
val deleteAction = {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.adapters
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.yuzu.yuzu_emu.databinding.ItemFirmwareAvatarBinding
|
||||
|
||||
class FirmwareAvatarAdapter(
|
||||
private val avatars: List<Bitmap>,
|
||||
private val onAvatarSelected: (Bitmap) -> Unit
|
||||
) : RecyclerView.Adapter<FirmwareAvatarAdapter.AvatarViewHolder>() {
|
||||
|
||||
private var selectedPosition = -1
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AvatarViewHolder {
|
||||
val binding = ItemFirmwareAvatarBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return AvatarViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: AvatarViewHolder, position: Int) {
|
||||
holder.bind(avatars[position], position == selectedPosition)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = avatars.size
|
||||
|
||||
inner class AvatarViewHolder(
|
||||
private val binding: ItemFirmwareAvatarBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(avatar: Bitmap, isSelected: Boolean) {
|
||||
binding.imageAvatar.setImageBitmap(avatar)
|
||||
binding.root.isChecked = isSelected
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
val previousSelected = selectedPosition
|
||||
selectedPosition = bindingAdapterPosition
|
||||
|
||||
if (previousSelected != -1) {
|
||||
notifyItemChanged(previousSelected)
|
||||
}
|
||||
notifyItemChanged(selectedPosition)
|
||||
|
||||
onAvatarSelected(avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -7,8 +10,10 @@ import android.net.Uri
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
|
||||
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
|
||||
import org.yuzu.yuzu_emu.model.DirectoryType
|
||||
import org.yuzu.yuzu_emu.model.GameDir
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
|
||||
@@ -31,6 +36,12 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
|
||||
path.text = Uri.parse(model.uriString).path
|
||||
path.marquee()
|
||||
|
||||
// Set type indicator, shows below folder name, to see if DLC or Games
|
||||
typeIndicator.text = when (model.type) {
|
||||
DirectoryType.GAME -> activity.getString(R.string.games)
|
||||
DirectoryType.EXTERNAL_CONTENT -> activity.getString(R.string.external_content)
|
||||
}
|
||||
|
||||
buttonEdit.setOnClickListener {
|
||||
GameFolderPropertiesDialogFragment.newInstance(model)
|
||||
.show(
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.adapters
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemProfileBinding
|
||||
import org.yuzu.yuzu_emu.model.UserProfile
|
||||
import java.io.File
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
|
||||
class ProfileAdapter(
|
||||
private val onProfileClick: (UserProfile) -> Unit,
|
||||
private val onEditClick: (UserProfile) -> Unit,
|
||||
private val onDeleteClick: (UserProfile) -> Unit
|
||||
) : RecyclerView.Adapter<ProfileAdapter.ProfileViewHolder>() {
|
||||
|
||||
private var currentUserUUID: String = ""
|
||||
|
||||
private val differ = AsyncListDiffer(this, object : DiffUtil.ItemCallback<UserProfile>() {
|
||||
override fun areItemsTheSame(oldItem: UserProfile, newItem: UserProfile): Boolean {
|
||||
return oldItem.uuid == newItem.uuid
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: UserProfile, newItem: UserProfile): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
})
|
||||
|
||||
fun submitList(list: List<UserProfile>) {
|
||||
differ.submitList(list)
|
||||
}
|
||||
|
||||
fun setCurrentUser(uuid: String) {
|
||||
currentUserUUID = uuid
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder {
|
||||
val binding = ListItemProfileBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
false
|
||||
)
|
||||
return ProfileViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
|
||||
holder.bind(differ.currentList[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = differ.currentList.size
|
||||
|
||||
inner class ProfileViewHolder(private val binding: ListItemProfileBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(profile: UserProfile) {
|
||||
binding.textUsername.text = profile.username
|
||||
binding.textUuid.text = formatUUID(profile.uuid)
|
||||
|
||||
val imageFile = File(profile.imagePath)
|
||||
if (imageFile.exists()) {
|
||||
val bitmap = BitmapFactory.decodeFile(profile.imagePath)
|
||||
binding.imageAvatar.setImageBitmap(bitmap)
|
||||
} else {
|
||||
val jpegData = NativeLibrary.getDefaultAccountBackupJpeg()
|
||||
val bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.size)
|
||||
binding.imageAvatar.setImageBitmap(bitmap)
|
||||
}
|
||||
|
||||
if (profile.uuid == currentUserUUID) {
|
||||
binding.checkContainer.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.checkContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
onProfileClick(profile)
|
||||
}
|
||||
|
||||
binding.buttonEdit.setOnClickListener {
|
||||
onEditClick(profile)
|
||||
}
|
||||
|
||||
binding.buttonDelete.setOnClickListener {
|
||||
onDeleteClick(profile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatUUID(uuid: String): String {
|
||||
if (uuid.length != 32) return uuid
|
||||
return buildString {
|
||||
append(uuid.substring(0, 8))
|
||||
append("-")
|
||||
append(uuid.substring(8, 12))
|
||||
append("-")
|
||||
append(uuid.substring(12, 16))
|
||||
append("-")
|
||||
append(uuid.substring(16, 20))
|
||||
append("-")
|
||||
append(uuid.substring(20, 32))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import android.widget.RadioGroup
|
||||
import android.widget.TextView
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.materialswitch.MaterialSwitch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
@@ -22,14 +23,6 @@ import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||
|
||||
class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
// Kinda a crappy workaround to get a title from setting keys
|
||||
// Idk how to do this witthout hardcoding every single one
|
||||
private fun getSettingTitle(settingKey: String): String {
|
||||
return settingKey.replace("_", " ").split(" ")
|
||||
.joinToString(" ") { it.replaceFirstChar { c -> c.uppercase() } }
|
||||
|
||||
}
|
||||
|
||||
private fun saveSettings() {
|
||||
if (emulationFragment.shouldUseCustom) {
|
||||
NativeConfig.savePerGameConfig()
|
||||
@@ -60,6 +53,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
// settings
|
||||
|
||||
fun addIntSetting(
|
||||
name: Int,
|
||||
container: ViewGroup,
|
||||
setting: IntSetting,
|
||||
namesArrayId: Int,
|
||||
@@ -73,7 +67,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon)
|
||||
val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group)
|
||||
|
||||
titleView.text = getSettingTitle(setting.key)
|
||||
titleView.text = YuzuApplication.appContext.getString(name)
|
||||
|
||||
val names = emulationFragment.resources.getStringArray(namesArrayId)
|
||||
val values = emulationFragment.resources.getIntArray(valuesArrayId)
|
||||
@@ -115,6 +109,8 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
}
|
||||
|
||||
fun addBooleanSetting(
|
||||
name: Int,
|
||||
|
||||
container: ViewGroup,
|
||||
setting: BooleanSetting
|
||||
) {
|
||||
@@ -125,7 +121,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
val titleView = itemView.findViewById<TextView>(R.id.switch_title)
|
||||
val switchView = itemView.findViewById<com.google.android.material.materialswitch.MaterialSwitch>(R.id.setting_switch)
|
||||
|
||||
titleView.text = getSettingTitle(setting.key)
|
||||
titleView.text = YuzuApplication.appContext.getString(name)
|
||||
switchContainer.visibility = View.VISIBLE
|
||||
switchView.isChecked = setting.getBoolean()
|
||||
|
||||
@@ -140,7 +136,41 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addCustomToggle(
|
||||
name: Int,
|
||||
isChecked: Boolean,
|
||||
isEnabled: Boolean,
|
||||
|
||||
container: ViewGroup,
|
||||
callback: (Boolean) -> Unit
|
||||
): MaterialSwitch? {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
||||
|
||||
val switchContainer = itemView.findViewById<ViewGroup>(R.id.switch_container)
|
||||
val titleView = itemView.findViewById<TextView>(R.id.switch_title)
|
||||
val switchView = itemView.findViewById<MaterialSwitch>(R.id.setting_switch)
|
||||
|
||||
titleView.text = YuzuApplication.appContext.getString(name)
|
||||
switchContainer.visibility = View.VISIBLE
|
||||
|
||||
switchView.isChecked = isChecked
|
||||
|
||||
switchView.setOnCheckedChangeListener { _, checked ->
|
||||
callback(checked)
|
||||
saveSettings()
|
||||
}
|
||||
|
||||
switchContainer.setOnClickListener {
|
||||
switchView.toggle()
|
||||
}
|
||||
container.addView(itemView)
|
||||
|
||||
return switchView
|
||||
}
|
||||
|
||||
fun addSliderSetting(
|
||||
name: Int,
|
||||
container: ViewGroup,
|
||||
setting: AbstractSetting,
|
||||
minValue: Int = 0,
|
||||
@@ -156,7 +186,7 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
val slider = itemView.findViewById<com.google.android.material.slider.Slider>(R.id.setting_slider)
|
||||
|
||||
|
||||
titleView.text = getSettingTitle(setting.key)
|
||||
titleView.text = YuzuApplication.appContext.getString(name)
|
||||
sliderContainer.visibility = View.VISIBLE
|
||||
|
||||
slider.valueFrom = minValue.toFloat()
|
||||
|
||||
+9
-3
@@ -24,16 +24,23 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
|
||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"),
|
||||
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
|
||||
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
|
||||
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
|
||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||
BLACK_BACKGROUNDS("black_backgrounds"),
|
||||
|
||||
ENABLE_FOLDER_BUTTON("enable_folder_button"),
|
||||
ENABLE_QLAUNCH_BUTTON("enable_qlaunch_button"),
|
||||
|
||||
ENABLE_UPDATE_CHECKS("enable_update_checks"),
|
||||
JOYSTICK_REL_CENTER("joystick_rel_center"),
|
||||
DPAD_SLIDE("dpad_slide"),
|
||||
@@ -67,8 +74,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
SHOW_SHADERS_BUILDING("show_shaders_building"),
|
||||
|
||||
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||
USE_LRU_CACHE("use_lru_cache"),
|
||||
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
|
||||
ENABLE_OVERLAY("enable_overlay"),
|
||||
|
||||
@@ -77,8 +82,9 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
GPU_LOG_VULKAN_CALLS("gpu_log_vulkan_calls"),
|
||||
GPU_LOG_SHADER_DUMPS("gpu_log_shader_dumps"),
|
||||
GPU_LOG_MEMORY_TRACKING("gpu_log_memory_tracking"),
|
||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug");
|
||||
GPU_LOG_DRIVER_DEBUG("gpu_log_driver_debug"),
|
||||
|
||||
ENABLE_QUICK_SETTINGS("enable_quick_settings");
|
||||
|
||||
// external fun isFrameSkippingEnabled(): Boolean
|
||||
external fun isFrameInterpolationEnabled(): Boolean
|
||||
|
||||
@@ -29,6 +29,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
|
||||
RENDERER_DYNA_STATE("dyna_state"),
|
||||
DMA_ACCURACY("dma_accuracy"),
|
||||
FRAME_PACING_MODE("frame_pacing_mode"),
|
||||
AUDIO_OUTPUT_ENGINE("output_engine"),
|
||||
MAX_ANISOTROPY("max_anisotropy"),
|
||||
THEME("theme"),
|
||||
@@ -65,6 +66,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
LOGIN_SHARE_APPLET("login_share_applet_mode"),
|
||||
WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"),
|
||||
MY_PAGE_APPLET("my_page_applet_mode"),
|
||||
SPLAY_APPLET("splay_applet_mode"),
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
|
||||
OVERLAY_GRID_SIZE("overlay_grid_size"),
|
||||
DEBUG_KNOBS("debug_knobs"),
|
||||
|
||||
+7
-1
@@ -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
|
||||
|
||||
@@ -6,7 +9,10 @@ package org.yuzu.yuzu_emu.features.settings.model
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
enum class ShortSetting(override val key: String) : AbstractShortSetting {
|
||||
RENDERER_SPEED_LIMIT("speed_limit");
|
||||
RENDERER_SPEED_LIMIT("speed_limit"),
|
||||
RENDERER_TURBO_SPEED_LIMIT("turbo_speed_limit"),
|
||||
RENDERER_SLOW_SPEED_LIMIT("slow_speed_limit"),
|
||||
;
|
||||
|
||||
override fun getShort(needsGlobal: Boolean): Short = NativeConfig.getShort(key, needsGlobal)
|
||||
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.ArrayRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
|
||||
class GpuUnswizzleSetting(
|
||||
@StringRes titleId: Int = 0,
|
||||
titleString: String = "",
|
||||
@StringRes descriptionId: Int = 0,
|
||||
descriptionString: String = "",
|
||||
@ArrayRes val textureSizeChoicesId: Int,
|
||||
@ArrayRes val textureSizeValuesId: Int,
|
||||
@ArrayRes val streamSizeChoicesId: Int,
|
||||
@ArrayRes val streamSizeValuesId: Int,
|
||||
@ArrayRes val chunkSizeChoicesId: Int,
|
||||
@ArrayRes val chunkSizeValuesId: Int
|
||||
) : SettingsItem(
|
||||
object : AbstractSetting {
|
||||
override val key: String = SettingsItem.GPU_UNSWIZZLE_COMBINED
|
||||
override val defaultValue: Any = false
|
||||
override val isSaveable = true
|
||||
override val isRuntimeModifiable = true
|
||||
override val isSwitchable = true
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = "combined"
|
||||
override fun reset() {
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.reset()
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.reset()
|
||||
}
|
||||
},
|
||||
titleId,
|
||||
titleString,
|
||||
descriptionId,
|
||||
descriptionString
|
||||
) {
|
||||
override val type = SettingsItem.TYPE_GPU_UNSWIZZLE
|
||||
|
||||
// Check if GPU unswizzle is enabled via the dedicated boolean setting
|
||||
fun isEnabled(needsGlobal: Boolean = false): Boolean =
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.getBoolean(needsGlobal)
|
||||
|
||||
fun setEnabled(value: Boolean) =
|
||||
BooleanSetting.GPU_UNSWIZZLE_ENABLED.setBoolean(value)
|
||||
|
||||
fun enable() = setEnabled(true)
|
||||
|
||||
fun disable() = setEnabled(false)
|
||||
|
||||
fun getTextureSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setTextureSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.setInt(value)
|
||||
|
||||
fun getStreamSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setStreamSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.setInt(value)
|
||||
|
||||
fun getChunkSize(needsGlobal: Boolean = false): Int =
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.getInt(needsGlobal)
|
||||
|
||||
fun setChunkSize(value: Int) =
|
||||
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value)
|
||||
|
||||
fun reset() = setting.reset()
|
||||
}
|
||||
+90
-8
@@ -60,6 +60,11 @@ abstract class SettingsItem(
|
||||
return NativeInput.getStyleIndex(0) != NpadStyleIndex.Handheld
|
||||
}
|
||||
|
||||
// Can't edit enable_qlaunch_button if firmware is not available
|
||||
if (setting.key == BooleanSetting.ENABLE_QLAUNCH_BUTTON.key) {
|
||||
return NativeLibrary.isFirmwareAvailable()
|
||||
}
|
||||
|
||||
// Can't edit settings that aren't saveable in per-game config even if they are switchable
|
||||
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
|
||||
return false
|
||||
@@ -99,8 +104,10 @@ abstract class SettingsItem(
|
||||
const val TYPE_SPINBOX = 12
|
||||
const val TYPE_LAUNCHABLE = 13
|
||||
const val TYPE_PATH = 14
|
||||
const val TYPE_GPU_UNSWIZZLE = 15
|
||||
|
||||
const val FASTMEM_COMBINED = "fastmem_combined"
|
||||
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
|
||||
|
||||
val emptySetting = object : AbstractSetting {
|
||||
override val key: String = ""
|
||||
@@ -118,13 +125,6 @@ abstract class SettingsItem(
|
||||
// List of all general
|
||||
val settingsItems = HashMap<String, SettingsItem>().apply {
|
||||
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_LRU_CACHE,
|
||||
titleId = R.string.use_lru_cache,
|
||||
descriptionId = R.string.use_lru_cache_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
|
||||
@@ -143,12 +143,25 @@ abstract class SettingsItem(
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
|
||||
BooleanSetting.RENDERER_PROVOKING_VERTEX,
|
||||
titleId = R.string.provoking_vertex,
|
||||
descriptionId = R.string.provoking_vertex_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
|
||||
titleId = R.string.vertex_input_dynamic_state,
|
||||
descriptionId = R.string.vertex_input_dynamic_state_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_DESCRIPTOR_INDEXING,
|
||||
titleId = R.string.descriptor_indexing,
|
||||
descriptionId = R.string.descriptor_indexing_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SliderSetting(
|
||||
IntSetting.RENDERER_SAMPLE_SHADING,
|
||||
@@ -167,6 +180,26 @@ abstract class SettingsItem(
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SliderSetting(
|
||||
ShortSetting.RENDERER_TURBO_SPEED_LIMIT,
|
||||
titleId = R.string.turbo_speed_limit,
|
||||
descriptionId = R.string.turbo_speed_limit_description,
|
||||
min = 1,
|
||||
max = 400,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SliderSetting(
|
||||
ShortSetting.RENDERER_SLOW_SPEED_LIMIT,
|
||||
titleId = R.string.slow_speed_limit,
|
||||
descriptionId = R.string.slow_speed_limit_description,
|
||||
min = 1,
|
||||
max = 400,
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.CPU_BACKEND,
|
||||
@@ -630,6 +663,15 @@ abstract class SettingsItem(
|
||||
valuesId = R.array.dmaAccuracyValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.FRAME_PACING_MODE,
|
||||
titleId = R.string.frame_pacing_mode,
|
||||
descriptionId = R.string.frame_pacing_mode_description,
|
||||
choicesId = R.array.framePacingModeNames,
|
||||
valuesId = R.array.framePacingModeValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS,
|
||||
@@ -673,6 +715,18 @@ abstract class SettingsItem(
|
||||
valuesId = R.array.gpuSwizzleChunkValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
GpuUnswizzleSetting(
|
||||
titleId = R.string.gpu_unswizzle_settings,
|
||||
descriptionId = R.string.gpu_unswizzle_settings_description,
|
||||
textureSizeChoicesId = R.array.gpuTextureSizeSwizzleEntries,
|
||||
textureSizeValuesId = R.array.gpuTextureSizeSwizzleValues,
|
||||
streamSizeChoicesId = R.array.gpuSwizzleEntries,
|
||||
streamSizeValuesId = R.array.gpuSwizzleValues,
|
||||
chunkSizeChoicesId = R.array.gpuSwizzleChunkEntries,
|
||||
chunkSizeValuesId = R.array.gpuSwizzleChunkValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.FAST_CPU_TIME,
|
||||
@@ -727,6 +781,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.renderer_reactive_flushing_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_BUFFER_HISTORY,
|
||||
titleId = R.string.enable_buffer_history,
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.SYNC_MEMORY_OPERATIONS,
|
||||
@@ -781,6 +842,27 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_update_checks_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_QUICK_SETTINGS,
|
||||
titleId = R.string.enable_quick_settings,
|
||||
descriptionId = R.string.enable_quick_settings_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_FOLDER_BUTTON,
|
||||
titleId = R.string.enable_folder_button,
|
||||
descriptionId = R.string.enable_folder_button_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_QLAUNCH_BUTTON,
|
||||
titleId = R.string.enable_qlaunch_button,
|
||||
descriptionId = R.string.enable_qlaunch_button_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.APP_LANGUAGE,
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogGpuUnswizzleBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
|
||||
|
||||
class GpuUnswizzleDialogFragment : DialogFragment() {
|
||||
private var position = 0
|
||||
private val settingsViewModel: SettingsViewModel by activityViewModels()
|
||||
private lateinit var binding: DialogGpuUnswizzleBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
position = requireArguments().getInt(POSITION)
|
||||
|
||||
if (settingsViewModel.clickedItem == null) dismiss()
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogGpuUnswizzleBinding.inflate(LayoutInflater.from(requireContext()))
|
||||
val item = settingsViewModel.clickedItem as GpuUnswizzleSetting
|
||||
|
||||
// Setup texture size dropdown
|
||||
val textureSizeEntries = resources.getStringArray(item.textureSizeChoicesId)
|
||||
val textureSizeValues = resources.getIntArray(item.textureSizeValuesId)
|
||||
val textureSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
textureSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownTextureSize.setAdapter(textureSizeAdapter)
|
||||
|
||||
// Setup stream size dropdown
|
||||
val streamSizeEntries = resources.getStringArray(item.streamSizeChoicesId)
|
||||
val streamSizeValues = resources.getIntArray(item.streamSizeValuesId)
|
||||
val streamSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
streamSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownStreamSize.setAdapter(streamSizeAdapter)
|
||||
|
||||
// Setup chunk size dropdown
|
||||
val chunkSizeEntries = resources.getStringArray(item.chunkSizeChoicesId)
|
||||
val chunkSizeValues = resources.getIntArray(item.chunkSizeValuesId)
|
||||
val chunkSizeAdapter = ArrayAdapter(
|
||||
requireContext(),
|
||||
android.R.layout.simple_dropdown_item_1line,
|
||||
chunkSizeEntries.toMutableList()
|
||||
)
|
||||
binding.dropdownChunkSize.setAdapter(chunkSizeAdapter)
|
||||
|
||||
// Load current values
|
||||
val isEnabled = item.isEnabled()
|
||||
binding.switchEnable.isChecked = isEnabled
|
||||
|
||||
if (isEnabled) {
|
||||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
|
||||
if (textureSizeIndex >= 0) {
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
|
||||
}
|
||||
|
||||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
|
||||
if (streamSizeIndex >= 0) {
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
|
||||
}
|
||||
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
|
||||
if (chunkSizeIndex >= 0) {
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
|
||||
}
|
||||
} else {
|
||||
// Set default/recommended values when disabling
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[3], false)
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[3], false)
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[3], false)
|
||||
}
|
||||
|
||||
// Clear adapter filters after setText to fix rotation bug
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
|
||||
// Enable/disable dropdowns based on switch state
|
||||
updateDropdownsState(isEnabled)
|
||||
binding.switchEnable.setOnCheckedChangeListener { _, checked ->
|
||||
updateDropdownsState(checked)
|
||||
}
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(item.title)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
|
||||
// Setup button listeners
|
||||
binding.btnDefault.setOnClickListener {
|
||||
// Reset to defaults
|
||||
item.reset()
|
||||
// Refresh values with adapters reset
|
||||
val textureSizeIndex = textureSizeValues.indexOf(item.getTextureSize())
|
||||
if (textureSizeIndex >= 0) {
|
||||
binding.dropdownTextureSize.setText(textureSizeEntries[textureSizeIndex], false)
|
||||
}
|
||||
val streamSizeIndex = streamSizeValues.indexOf(item.getStreamSize())
|
||||
if (streamSizeIndex >= 0) {
|
||||
binding.dropdownStreamSize.setText(streamSizeEntries[streamSizeIndex], false)
|
||||
}
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(item.getChunkSize())
|
||||
if (chunkSizeIndex >= 0) {
|
||||
binding.dropdownChunkSize.setText(chunkSizeEntries[chunkSizeIndex], false)
|
||||
}
|
||||
// Clear filters
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
|
||||
settingsViewModel.setAdapterItemChanged(position)
|
||||
settingsViewModel.setShouldReloadSettingsList(true)
|
||||
}
|
||||
|
||||
binding.btnCancel.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
binding.btnOk.setOnClickListener {
|
||||
if (binding.switchEnable.isChecked) {
|
||||
item.enable()
|
||||
// Save the selected values
|
||||
val selectedTextureIndex = textureSizeEntries.indexOf(
|
||||
binding.dropdownTextureSize.text.toString()
|
||||
)
|
||||
if (selectedTextureIndex >= 0) {
|
||||
item.setTextureSize(textureSizeValues[selectedTextureIndex])
|
||||
}
|
||||
|
||||
val selectedStreamIndex = streamSizeEntries.indexOf(
|
||||
binding.dropdownStreamSize.text.toString()
|
||||
)
|
||||
if (selectedStreamIndex >= 0) {
|
||||
item.setStreamSize(streamSizeValues[selectedStreamIndex])
|
||||
}
|
||||
|
||||
val selectedChunkIndex = chunkSizeEntries.indexOf(
|
||||
binding.dropdownChunkSize.text.toString()
|
||||
)
|
||||
if (selectedChunkIndex >= 0) {
|
||||
item.setChunkSize(chunkSizeValues[selectedChunkIndex])
|
||||
}
|
||||
} else {
|
||||
// Disable GPU unswizzle
|
||||
item.disable()
|
||||
}
|
||||
|
||||
settingsViewModel.setAdapterItemChanged(position)
|
||||
settingsViewModel.setShouldReloadSettingsList(true)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
// Ensure filters are cleared after dialog is shown
|
||||
binding.root.post {
|
||||
textureSizeAdapter.filter.filter(null)
|
||||
streamSizeAdapter.filter.filter(null)
|
||||
chunkSizeAdapter.filter.filter(null)
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun updateDropdownsState(enabled: Boolean) {
|
||||
binding.layoutTextureSize.isEnabled = enabled
|
||||
binding.dropdownTextureSize.isEnabled = enabled
|
||||
binding.layoutStreamSize.isEnabled = enabled
|
||||
binding.dropdownStreamSize.isEnabled = enabled
|
||||
binding.layoutChunkSize.isEnabled = enabled
|
||||
binding.dropdownChunkSize.isEnabled = enabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "GpuUnswizzleDialogFragment"
|
||||
const val POSITION = "Position"
|
||||
|
||||
fun newInstance(
|
||||
settingsViewModel: SettingsViewModel,
|
||||
item: GpuUnswizzleSetting,
|
||||
position: Int
|
||||
): GpuUnswizzleDialogFragment {
|
||||
val dialog = GpuUnswizzleDialogFragment()
|
||||
val args = Bundle()
|
||||
args.putInt(POSITION, position)
|
||||
dialog.arguments = args
|
||||
settingsViewModel.clickedItem = item
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
@@ -101,6 +101,11 @@ class SettingsAdapter(
|
||||
SettingsItem.TYPE_PATH -> {
|
||||
PathViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_GPU_UNSWIZZLE -> {
|
||||
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||
}
|
||||
@@ -474,6 +479,14 @@ class SettingsAdapter(
|
||||
settingsViewModel.setShouldShowPathResetDialog(true)
|
||||
}
|
||||
|
||||
fun onGpuUnswizzleClick(item: GpuUnswizzleSetting, position: Int) {
|
||||
GpuUnswizzleDialogFragment.newInstance(
|
||||
settingsViewModel,
|
||||
item,
|
||||
position
|
||||
).show(fragment.childFragmentManager, GpuUnswizzleDialogFragment.TAG)
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
|
||||
override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
|
||||
return oldItem.setting.key == newItem.setting.key
|
||||
|
||||
+16
-4
@@ -227,6 +227,8 @@ class SettingsFragmentPresenter(
|
||||
add(StringSetting.DEVICE_NAME.key)
|
||||
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_TURBO_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_SLOW_SPEED_LIMIT.key)
|
||||
add(BooleanSetting.USE_DOCKED_MODE.key)
|
||||
add(IntSetting.REGION_INDEX.key)
|
||||
add(IntSetting.LANGUAGE_INDEX.key)
|
||||
@@ -235,7 +237,6 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(HeaderSetting(R.string.cpu))
|
||||
add(IntSetting.FAST_CPU_TIME.key)
|
||||
add(BooleanSetting.USE_LRU_CACHE.key)
|
||||
add(BooleanSetting.CORE_SYNC_CORE_SPEED.key)
|
||||
|
||||
add(IntSetting.MEMORY_LAYOUT.key)
|
||||
@@ -266,6 +267,7 @@ class SettingsFragmentPresenter(
|
||||
|
||||
add(IntSetting.RENDERER_ACCURACY.key)
|
||||
add(IntSetting.DMA_ACCURACY.key)
|
||||
add(IntSetting.FRAME_PACING_MODE.key)
|
||||
add(IntSetting.MAX_ANISOTROPY.key)
|
||||
add(IntSetting.RENDERER_VRAM_USAGE_MODE.key)
|
||||
add(IntSetting.RENDERER_ASTC_DECODE_METHOD.key)
|
||||
@@ -275,6 +277,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -282,14 +285,14 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||
add(IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.key)
|
||||
add(IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.key)
|
||||
add(IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.key)
|
||||
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
|
||||
|
||||
add(HeaderSetting(R.string.extensions))
|
||||
|
||||
add(IntSetting.RENDERER_DYNA_STATE.key)
|
||||
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
|
||||
add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key)
|
||||
add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key)
|
||||
add(IntSetting.RENDERER_SAMPLE_SHADING.key)
|
||||
|
||||
add(HeaderSetting(R.string.display))
|
||||
@@ -1072,6 +1075,8 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
|
||||
}
|
||||
|
||||
add(BooleanSetting.ENABLE_QUICK_SETTINGS.key)
|
||||
|
||||
add(HeaderSetting(R.string.theme_and_color))
|
||||
|
||||
|
||||
@@ -1189,6 +1194,13 @@ class SettingsFragmentPresenter(
|
||||
descriptionId = R.string.use_black_backgrounds_description
|
||||
)
|
||||
)
|
||||
|
||||
add(HeaderSetting(R.string.buttons))
|
||||
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
|
||||
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)
|
||||
if (!NativeLibrary.isFirmwareAvailable()) {
|
||||
BooleanSetting.ENABLE_QLAUNCH_BUTTON.setBoolean(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.GpuUnswizzleSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
|
||||
class GpuUnswizzleViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
|
||||
SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: GpuUnswizzleSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as GpuUnswizzleSetting
|
||||
binding.textSettingName.text = setting.title
|
||||
binding.textSettingDescription.setVisible(item.description.isNotEmpty())
|
||||
binding.textSettingDescription.text = item.description
|
||||
|
||||
binding.textSettingValue.setVisible(true)
|
||||
val resMgr = binding.root.context.resources
|
||||
|
||||
if (setting.isEnabled()) {
|
||||
// Show a summary of current settings
|
||||
val textureSizeEntries = resMgr.getStringArray(setting.textureSizeChoicesId)
|
||||
val textureSizeValues = resMgr.getIntArray(setting.textureSizeValuesId)
|
||||
val textureSizeIndex = textureSizeValues.indexOf(setting.getTextureSize())
|
||||
val textureSizeLabel = if (textureSizeIndex >= 0) textureSizeEntries[textureSizeIndex] else "?"
|
||||
|
||||
val streamSizeEntries = resMgr.getStringArray(setting.streamSizeChoicesId)
|
||||
val streamSizeValues = resMgr.getIntArray(setting.streamSizeValuesId)
|
||||
val streamSizeIndex = streamSizeValues.indexOf(setting.getStreamSize())
|
||||
val streamSizeLabel = if (streamSizeIndex >= 0) streamSizeEntries[streamSizeIndex] else "?"
|
||||
|
||||
val chunkSizeEntries = resMgr.getStringArray(setting.chunkSizeChoicesId)
|
||||
val chunkSizeValues = resMgr.getIntArray(setting.chunkSizeValuesId)
|
||||
val chunkSizeIndex = chunkSizeValues.indexOf(setting.getChunkSize())
|
||||
val chunkSizeLabel = if (chunkSizeIndex >= 0) chunkSizeEntries[chunkSizeIndex] else "?"
|
||||
|
||||
binding.textSettingValue.text = "$textureSizeLabel ⋅ $streamSizeLabel ⋅ $chunkSizeLabel"
|
||||
} else {
|
||||
binding.textSettingValue.text = resMgr.getString(R.string.gpu_unswizzle_disabled)
|
||||
}
|
||||
|
||||
binding.buttonClear.setVisible(setting.clearable)
|
||||
binding.buttonClear.setOnClickListener {
|
||||
adapter.onClearClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
setStyle(setting.isEditable, binding)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {
|
||||
if (!setting.isEditable) {
|
||||
return
|
||||
}
|
||||
|
||||
adapter.onGpuUnswizzleClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean {
|
||||
if (setting.isEditable) {
|
||||
return adapter.onLongClick(setting, bindingAdapterPosition)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.FirmwareAvatarAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentEditUserDialogBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.ProfileUtils
|
||||
import org.yuzu.yuzu_emu.model.UserProfile
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import androidx.core.graphics.scale
|
||||
import androidx.core.graphics.createBitmap
|
||||
|
||||
class EditUserDialogFragment : Fragment() {
|
||||
private var _binding: FragmentEditUserDialogBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
|
||||
private var currentUUID: String = ""
|
||||
private var isEditMode = false
|
||||
private var selectedImageUri: Uri? = null
|
||||
private var selectedFirmwareAvatar: Bitmap? = null
|
||||
private var hasCustomImage = false
|
||||
private var revertedToDefault = false
|
||||
|
||||
companion object {
|
||||
private const val ARG_UUID = "uuid"
|
||||
private const val ARG_USERNAME = "username"
|
||||
|
||||
fun newInstance(profile: UserProfile?): EditUserDialogFragment {
|
||||
val fragment = EditUserDialogFragment()
|
||||
profile?.let {
|
||||
val args = Bundle()
|
||||
args.putString(ARG_UUID, it.uuid)
|
||||
args.putString(ARG_USERNAME, it.username)
|
||||
fragment.arguments = args
|
||||
}
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
private val imagePickerLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
result.data?.data?.let { uri ->
|
||||
selectedImageUri = uri
|
||||
loadImage(uri)
|
||||
hasCustomImage = true
|
||||
binding.buttonRevertImage.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentEditUserDialogBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
val existingUUID = arguments?.getString(ARG_UUID)
|
||||
val existingUsername = arguments?.getString(ARG_USERNAME)
|
||||
|
||||
if (existingUUID != null && existingUsername != null) {
|
||||
isEditMode = true
|
||||
currentUUID = existingUUID
|
||||
binding.toolbarNewUser.title = getString(R.string.profile_edit_user)
|
||||
binding.editUsername.setText(existingUsername)
|
||||
binding.textUuid.text = formatUUID(existingUUID)
|
||||
binding.buttonGenerateUuid.visibility = View.GONE
|
||||
|
||||
val imagePath = NativeLibrary.getUserImagePath(existingUUID)
|
||||
val imageFile = File(imagePath)
|
||||
if (imageFile.exists()) {
|
||||
val bitmap = BitmapFactory.decodeFile(imagePath)
|
||||
binding.imageUserAvatar.setImageBitmap(bitmap)
|
||||
hasCustomImage = true
|
||||
binding.buttonRevertImage.visibility = View.VISIBLE
|
||||
} else {
|
||||
loadDefaultAvatar()
|
||||
}
|
||||
} else {
|
||||
isEditMode = false
|
||||
currentUUID = ProfileUtils.generateRandomUUID()
|
||||
binding.toolbarNewUser.title = getString(R.string.profile_new_user)
|
||||
binding.textUuid.text = formatUUID(currentUUID)
|
||||
loadDefaultAvatar()
|
||||
}
|
||||
|
||||
binding.toolbarNewUser.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
binding.editUsername.doAfterTextChanged {
|
||||
validateInput()
|
||||
}
|
||||
|
||||
binding.buttonGenerateUuid.setOnClickListener {
|
||||
currentUUID = ProfileUtils.generateRandomUUID()
|
||||
binding.textUuid.text = formatUUID(currentUUID)
|
||||
}
|
||||
|
||||
binding.buttonSelectImage.setOnClickListener {
|
||||
selectImage()
|
||||
}
|
||||
|
||||
binding.buttonRevertImage.setOnClickListener {
|
||||
revertToDefaultImage()
|
||||
}
|
||||
|
||||
if (NativeLibrary.isFirmwareAvailable()) {
|
||||
binding.buttonFirmwareAvatars.visibility = View.VISIBLE
|
||||
binding.buttonFirmwareAvatars.setOnClickListener {
|
||||
showFirmwareAvatarPicker()
|
||||
}
|
||||
}
|
||||
|
||||
binding.buttonSave.setOnClickListener {
|
||||
saveUser()
|
||||
}
|
||||
|
||||
binding.buttonCancel.setOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
validateInput()
|
||||
setInsets()
|
||||
}
|
||||
|
||||
private fun showFirmwareAvatarPicker() {
|
||||
val dialogView = LayoutInflater.from(requireContext())
|
||||
.inflate(R.layout.dialog_firmware_avatar_picker, null)
|
||||
|
||||
val gridAvatars = dialogView.findViewById<RecyclerView>(R.id.grid_avatars)
|
||||
val progressLoading = dialogView.findViewById<View>(R.id.progress_loading)
|
||||
val textEmpty = dialogView.findViewById<View>(R.id.text_empty)
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.profile_firmware_avatars)
|
||||
.setView(dialogView)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
|
||||
dialog.show()
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val avatars = withContext(Dispatchers.IO) {
|
||||
loadFirmwareAvatars()
|
||||
}
|
||||
|
||||
if (avatars.isEmpty()) {
|
||||
progressLoading.visibility = View.GONE
|
||||
textEmpty.visibility = View.VISIBLE
|
||||
} else {
|
||||
progressLoading.visibility = View.GONE
|
||||
gridAvatars.visibility = View.VISIBLE
|
||||
|
||||
val adapter = FirmwareAvatarAdapter(avatars) { selectedAvatar ->
|
||||
val scaledBitmap = selectedAvatar.scale(256, 256)
|
||||
binding.imageUserAvatar.setImageBitmap(scaledBitmap)
|
||||
selectedFirmwareAvatar = scaledBitmap
|
||||
hasCustomImage = true
|
||||
binding.buttonRevertImage.visibility = View.VISIBLE
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
gridAvatars.apply {
|
||||
layoutManager = GridLayoutManager(requireContext(), 4)
|
||||
this.adapter = adapter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFirmwareAvatars(): List<Bitmap> {
|
||||
val avatars = mutableListOf<Bitmap>()
|
||||
val count = NativeLibrary.getFirmwareAvatarCount()
|
||||
|
||||
for (i in 0 until count) {
|
||||
try {
|
||||
val imageData = NativeLibrary.getFirmwareAvatarImage(i) ?: continue
|
||||
|
||||
val argbData = IntArray(256 * 256)
|
||||
for (pixel in 0 until 256 * 256) {
|
||||
val offset = pixel * 4
|
||||
val r = imageData[offset].toInt() and 0xFF
|
||||
val g = imageData[offset + 1].toInt() and 0xFF
|
||||
val b = imageData[offset + 2].toInt() and 0xFF
|
||||
val a = imageData[offset + 3].toInt() and 0xFF
|
||||
argbData[pixel] = (a shl 24) or (r shl 16) or (g shl 8) or b
|
||||
}
|
||||
|
||||
val bitmap = Bitmap.createBitmap(argbData, 256, 256, Bitmap.Config.ARGB_8888)
|
||||
avatars.add(bitmap)
|
||||
} catch (e: Exception) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return avatars
|
||||
}
|
||||
|
||||
private fun formatUUID(uuid: String): String {
|
||||
if (uuid.length != 32) return uuid
|
||||
return buildString {
|
||||
append(uuid.substring(0, 8))
|
||||
append("-")
|
||||
append(uuid.substring(8, 12))
|
||||
append("-")
|
||||
append(uuid.substring(12, 16))
|
||||
append("-")
|
||||
append(uuid.substring(16, 20))
|
||||
append("-")
|
||||
append(uuid.substring(20, 32))
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateInput() {
|
||||
val username = binding.editUsername.text.toString()
|
||||
val isValid = username.isNotEmpty() && username.length <= 32
|
||||
binding.buttonSave.isEnabled = isValid
|
||||
}
|
||||
|
||||
private fun selectImage() {
|
||||
val intent = Intent(Intent.ACTION_PICK).apply {
|
||||
type = "image/*"
|
||||
}
|
||||
imagePickerLauncher.launch(intent)
|
||||
}
|
||||
|
||||
private fun loadImage(uri: Uri) {
|
||||
try {
|
||||
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
val source = ImageDecoder.createSource(requireContext().contentResolver, uri)
|
||||
ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
|
||||
decoder.setTargetSampleSize(1)
|
||||
}
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
MediaStore.Images.Media.getBitmap(requireContext().contentResolver, uri)
|
||||
}
|
||||
|
||||
val croppedBitmap = centerCropBitmap(bitmap, 256, 256)
|
||||
binding.imageUserAvatar.setImageBitmap(croppedBitmap)
|
||||
} catch (e: Exception) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(getString(R.string.profile_image_load_error, e.message))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadDefaultAvatar() {
|
||||
val jpegData = NativeLibrary.getDefaultAccountBackupJpeg()
|
||||
val bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.size)
|
||||
binding.imageUserAvatar.setImageBitmap(bitmap)
|
||||
|
||||
hasCustomImage = false
|
||||
binding.buttonRevertImage.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun revertToDefaultImage() {
|
||||
selectedImageUri = null
|
||||
selectedFirmwareAvatar = null
|
||||
revertedToDefault = true
|
||||
loadDefaultAvatar()
|
||||
}
|
||||
|
||||
private fun saveUser() {
|
||||
val username = binding.editUsername.text.toString()
|
||||
|
||||
if (isEditMode) {
|
||||
if (NativeLibrary.updateUserUsername(currentUUID, username)) {
|
||||
saveImageIfNeeded()
|
||||
findNavController().popBackStack()
|
||||
} else {
|
||||
showError(getString(R.string.profile_update_failed))
|
||||
}
|
||||
} else {
|
||||
if (NativeLibrary.createUser(currentUUID, username)) {
|
||||
saveImageIfNeeded()
|
||||
findNavController().popBackStack()
|
||||
} else {
|
||||
showError(getString(R.string.profile_create_failed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveImageIfNeeded() {
|
||||
if (revertedToDefault && isEditMode) {
|
||||
val imagePath = NativeLibrary.getUserImagePath(currentUUID)
|
||||
if (imagePath != null) {
|
||||
val imageFile = File(imagePath)
|
||||
if (imageFile.exists()) {
|
||||
imageFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (!hasCustomImage) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
val bitmapToSave: Bitmap? = when {
|
||||
selectedFirmwareAvatar != null -> selectedFirmwareAvatar
|
||||
selectedImageUri != null -> {
|
||||
val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
val source = ImageDecoder.createSource(
|
||||
requireContext().contentResolver,
|
||||
selectedImageUri!!
|
||||
)
|
||||
ImageDecoder.decodeBitmap(source)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
MediaStore.Images.Media.getBitmap(
|
||||
requireContext().contentResolver,
|
||||
selectedImageUri
|
||||
)
|
||||
}
|
||||
centerCropBitmap(bitmap, 256, 256)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (bitmapToSave == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val tempFile = File(requireContext().cacheDir, "temp_avatar_${currentUUID}.jpg")
|
||||
FileOutputStream(tempFile).use { out ->
|
||||
bitmapToSave.compress(Bitmap.CompressFormat.JPEG, 100, out)
|
||||
}
|
||||
|
||||
NativeLibrary.saveUserImage(currentUUID, tempFile.absolutePath)
|
||||
|
||||
tempFile.delete()
|
||||
} catch (e: Exception) {
|
||||
showError(getString(R.string.profile_image_save_error, e.message))
|
||||
}
|
||||
}
|
||||
|
||||
private fun centerCropBitmap(source: Bitmap, targetWidth: Int, targetHeight: Int): Bitmap {
|
||||
val sourceWidth = source.width
|
||||
val sourceHeight = source.height
|
||||
|
||||
val scale = maxOf(
|
||||
targetWidth.toFloat() / sourceWidth,
|
||||
targetHeight.toFloat() / sourceHeight
|
||||
)
|
||||
|
||||
val scaledWidth = (sourceWidth * scale).toInt()
|
||||
val scaledHeight = (sourceHeight * scale).toInt()
|
||||
|
||||
val scaledBitmap = source.scale(scaledWidth, scaledHeight)
|
||||
|
||||
val x = (scaledWidth - targetWidth) / 2
|
||||
val y = (scaledHeight - targetHeight) / 2
|
||||
|
||||
return Bitmap.createBitmap(scaledBitmap, x, y, targetWidth, targetHeight)
|
||||
}
|
||||
|
||||
private fun showError(message: String) {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInset = barInsets.left + cutoutInsets.left
|
||||
val topInset = cutoutInsets.top
|
||||
val rightInset = barInsets.right + cutoutInsets.right
|
||||
val bottomInset = barInsets.bottom + cutoutInsets.bottom
|
||||
|
||||
binding.appbar.updatePadding(
|
||||
left = leftInset,
|
||||
top = topInset,
|
||||
right = rightInset
|
||||
)
|
||||
|
||||
binding.scrollContent.updatePadding(
|
||||
left = leftInset,
|
||||
right = rightInset
|
||||
)
|
||||
|
||||
binding.buttonContainer.updatePadding(
|
||||
left = leftInset,
|
||||
right = rightInset,
|
||||
bottom = bottomInset
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import androidx.window.layout.WindowInfoTracker
|
||||
import androidx.window.layout.WindowLayoutInfo
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.materialswitch.MaterialSwitch
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -690,8 +691,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
})
|
||||
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
|
||||
if (!BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()) {
|
||||
binding.drawerLayout.setDrawerLockMode(
|
||||
DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
|
||||
binding.quickSettingsSheet
|
||||
)
|
||||
}
|
||||
|
||||
updateGameTitle()
|
||||
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_quick_settings)?.isVisible =
|
||||
BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean()
|
||||
|
||||
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
|
||||
val lockMode = IntSetting.LOCK_DRAWER.getInt()
|
||||
val titleId = if (lockMode == DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
|
||||
@@ -749,10 +760,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_quick_settings -> {
|
||||
openQuickSettingsMenu()
|
||||
true
|
||||
}
|
||||
if (BooleanSetting.ENABLE_QUICK_SETTINGS.getBoolean())
|
||||
R.id.menu_quick_settings else 0 -> {
|
||||
openQuickSettingsMenu()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.menu_settings_per_game -> {
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
@@ -1044,12 +1056,50 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
quickSettings.addPerGameConfigStatusIndicator(container)
|
||||
}
|
||||
|
||||
quickSettings.addBooleanSetting(
|
||||
container,
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
|
||||
)
|
||||
lateinit var slowSpeed: MaterialSwitch
|
||||
lateinit var turboSpeed: MaterialSwitch
|
||||
|
||||
turboSpeed = quickSettings.addCustomToggle(
|
||||
R.string.turbo_speed_limit,
|
||||
NativeLibrary.isTurboMode(),
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
|
||||
container
|
||||
) { enabled ->
|
||||
if (enabled)
|
||||
slowSpeed.isChecked = false
|
||||
NativeLibrary.setTurboSpeedLimit(enabled)
|
||||
}!!
|
||||
|
||||
slowSpeed = quickSettings.addCustomToggle(
|
||||
R.string.slow_speed_limit,
|
||||
NativeLibrary.isSlowMode(),
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
|
||||
container
|
||||
) { enabled ->
|
||||
if (enabled)
|
||||
turboSpeed.isChecked = false
|
||||
NativeLibrary.setSlowSpeedLimit(enabled)
|
||||
}!!
|
||||
|
||||
quickSettings.addCustomToggle(
|
||||
R.string.frame_limit_enable,
|
||||
BooleanSetting.RENDERER_USE_SPEED_LIMIT.getBoolean(false),
|
||||
true,
|
||||
container
|
||||
) { enabled ->
|
||||
if (!enabled) {
|
||||
turboSpeed.isChecked = false
|
||||
slowSpeed.isChecked = false
|
||||
}
|
||||
|
||||
turboSpeed.isEnabled = enabled
|
||||
slowSpeed.isEnabled = enabled
|
||||
|
||||
NativeLibrary.setStandardSpeedLimit(enabled)
|
||||
}!!
|
||||
|
||||
quickSettings.addSliderSetting(
|
||||
R.string.frame_limit_slider,
|
||||
container,
|
||||
ShortSetting.RENDERER_SPEED_LIMIT,
|
||||
minValue = 0,
|
||||
@@ -1058,6 +1108,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
)
|
||||
|
||||
quickSettings.addBooleanSetting(
|
||||
R.string.use_docked_mode,
|
||||
container,
|
||||
BooleanSetting.USE_DOCKED_MODE,
|
||||
)
|
||||
@@ -1065,6 +1116,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
quickSettings.addDivider(container)
|
||||
|
||||
quickSettings.addIntSetting(
|
||||
R.string.renderer_accuracy,
|
||||
container,
|
||||
IntSetting.RENDERER_ACCURACY,
|
||||
R.array.rendererAccuracyNames,
|
||||
@@ -1073,6 +1125,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
|
||||
quickSettings.addIntSetting(
|
||||
R.string.renderer_scaling_filter,
|
||||
container,
|
||||
IntSetting.RENDERER_SCALING_FILTER,
|
||||
R.array.rendererScalingFilterNames,
|
||||
@@ -1080,6 +1133,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
)
|
||||
|
||||
quickSettings.addSliderSetting(
|
||||
R.string.fsr_sharpness,
|
||||
container,
|
||||
IntSetting.FSR_SHARPENING_SLIDER,
|
||||
minValue = 0,
|
||||
@@ -1088,6 +1142,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
)
|
||||
|
||||
quickSettings.addIntSetting(
|
||||
R.string.renderer_anti_aliasing,
|
||||
container,
|
||||
IntSetting.RENDERER_ANTI_ALIASING,
|
||||
R.array.rendererAntiAliasingNames,
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -110,7 +110,7 @@ class FreedrenoSettingsFragment : Fragment() {
|
||||
val commonVars = listOf(
|
||||
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
|
||||
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
|
||||
"TU_BREADCRUMBS"
|
||||
"TU_BREADCRUMBS", "FD_DEV_FEATURES"
|
||||
)
|
||||
|
||||
for (varName in commonVars) {
|
||||
|
||||
+19
-8
@@ -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
|
||||
|
||||
@@ -6,11 +9,13 @@ package org.yuzu.yuzu_emu.fragments
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogFolderPropertiesBinding
|
||||
import org.yuzu.yuzu_emu.model.DirectoryType
|
||||
import org.yuzu.yuzu_emu.model.GameDir
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
@@ -25,14 +30,18 @@ class GameFolderPropertiesDialogFragment : DialogFragment() {
|
||||
val binding = DialogFolderPropertiesBinding.inflate(layoutInflater)
|
||||
val gameDir = requireArguments().parcelable<GameDir>(GAME_DIR)!!
|
||||
|
||||
// Restore checkbox state
|
||||
binding.deepScanSwitch.isChecked =
|
||||
savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
|
||||
// Hide deepScan for external content, do automatically
|
||||
if (gameDir.type == DirectoryType.EXTERNAL_CONTENT) {
|
||||
binding.deepScanSwitch.visibility = View.GONE
|
||||
} else {
|
||||
// Restore checkbox state for game dirs
|
||||
binding.deepScanSwitch.isChecked =
|
||||
savedInstanceState?.getBoolean(DEEP_SCAN) ?: gameDir.deepScan
|
||||
|
||||
// Ensure that we can get the checkbox state even if the view is destroyed
|
||||
deepScan = binding.deepScanSwitch.isChecked
|
||||
binding.deepScanSwitch.setOnClickListener {
|
||||
deepScan = binding.deepScanSwitch.isChecked
|
||||
binding.deepScanSwitch.setOnClickListener {
|
||||
deepScan = binding.deepScanSwitch.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
@@ -41,8 +50,10 @@ class GameFolderPropertiesDialogFragment : DialogFragment() {
|
||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
|
||||
val folderIndex = gamesViewModel.folders.value.indexOf(gameDir)
|
||||
if (folderIndex != -1) {
|
||||
gamesViewModel.folders.value[folderIndex].deepScan =
|
||||
binding.deepScanSwitch.isChecked
|
||||
if (gameDir.type == DirectoryType.GAME) {
|
||||
gamesViewModel.folders.value[folderIndex].deepScan =
|
||||
binding.deepScanSwitch.isChecked
|
||||
}
|
||||
gamesViewModel.updateGameDirs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -15,11 +15,14 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.FolderAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
|
||||
import org.yuzu.yuzu_emu.model.DirectoryType
|
||||
import org.yuzu.yuzu_emu.model.GameDir
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
@@ -73,7 +76,25 @@ class GameFoldersFragment : Fragment() {
|
||||
|
||||
val mainActivity = requireActivity() as MainActivity
|
||||
binding.buttonAdd.setOnClickListener {
|
||||
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
// Show a model to choose between Game and External Content
|
||||
val options = arrayOf(
|
||||
getString(R.string.games),
|
||||
getString(R.string.external_content)
|
||||
)
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.add_folders)
|
||||
.setItems(options) { _, which ->
|
||||
when (which) {
|
||||
0 -> { // Game Folder
|
||||
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
}
|
||||
1 -> { // External Content Folder
|
||||
mainActivity.getExternalContentDirectory.launch(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
setInsets()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.Manifest
|
||||
@@ -114,6 +117,17 @@ class HomeSettingsFragment : Fragment() {
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.profile_manager,
|
||||
R.string.profile_manager_description,
|
||||
R.drawable.ic_account_circle,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_profileManagerFragment)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.gpu_driver_manager,
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.ProfileAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.UserProfile
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
|
||||
class ProfileManagerFragment : Fragment() {
|
||||
private var _binding: FragmentProfileManagerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private lateinit var profileAdapter: ProfileAdapter
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentProfileManagerBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarProfiles.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
setupRecyclerView()
|
||||
loadProfiles()
|
||||
|
||||
binding.buttonAddUser.setOnClickListener {
|
||||
if (NativeLibrary.canCreateUser()) {
|
||||
findNavController().navigate(R.id.action_profileManagerFragment_to_newUserDialog)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.profile_max_users_title)
|
||||
.setMessage(R.string.profile_max_users_message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
setInsets()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
loadProfiles()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
profileAdapter = ProfileAdapter(
|
||||
onProfileClick = { profile -> selectProfile(profile) },
|
||||
onEditClick = { profile -> editProfile(profile) },
|
||||
onDeleteClick = { profile -> confirmDeleteProfile(profile) }
|
||||
)
|
||||
binding.listProfiles.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = profileAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadProfiles() {
|
||||
val profiles = mutableListOf<UserProfile>()
|
||||
val userUUIDs = NativeLibrary.getAllUsers() ?: emptyArray()
|
||||
val currentUserUUID = NativeLibrary.getCurrentUser()
|
||||
|
||||
for (uuid in userUUIDs) {
|
||||
if (uuid.isNotEmpty()) {
|
||||
val username = NativeLibrary.getUserUsername(uuid)
|
||||
if (!username.isNullOrEmpty()) {
|
||||
val imagePath = NativeLibrary.getUserImagePath(uuid) ?: ""
|
||||
profiles.add(UserProfile(uuid, username, imagePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profileAdapter.submitList(profiles)
|
||||
profileAdapter.setCurrentUser(currentUserUUID ?: "")
|
||||
|
||||
binding.buttonAddUser.isEnabled = NativeLibrary.canCreateUser()
|
||||
}
|
||||
|
||||
private fun selectProfile(profile: UserProfile) {
|
||||
if (NativeLibrary.setCurrentUser(profile.uuid)) {
|
||||
loadProfiles()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun editProfile(profile: UserProfile) {
|
||||
val bundle = Bundle().apply {
|
||||
putString("uuid", profile.uuid)
|
||||
putString("username", profile.username)
|
||||
}
|
||||
findNavController().navigate(R.id.action_profileManagerFragment_to_newUserDialog, bundle)
|
||||
}
|
||||
|
||||
private fun confirmDeleteProfile(profile: UserProfile) {
|
||||
val currentUser = NativeLibrary.getCurrentUser()
|
||||
val isCurrentUser = profile.uuid == currentUser
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.profile_delete_confirm_title)
|
||||
.setMessage(
|
||||
if (isCurrentUser) {
|
||||
getString(R.string.profile_delete_current_user_message, profile.username)
|
||||
} else {
|
||||
getString(R.string.profile_delete_confirm_message, profile.username)
|
||||
}
|
||||
)
|
||||
.setPositiveButton(R.string.profile_delete) { _, _ ->
|
||||
deleteProfile(profile)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun deleteProfile(profile: UserProfile) {
|
||||
val currentUser = NativeLibrary.getCurrentUser()
|
||||
if (!currentUser.isNullOrEmpty() && profile.uuid == currentUser) {
|
||||
val users = NativeLibrary.getAllUsers() ?: emptyArray()
|
||||
for (uuid in users) {
|
||||
if (uuid.isNotEmpty() && uuid != profile.uuid) {
|
||||
NativeLibrary.setCurrentUser(uuid)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NativeLibrary.removeUser(profile.uuid)) {
|
||||
loadProfiles()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val fabLayoutParams = binding.buttonAddUser.layoutParams as ViewGroup.MarginLayoutParams
|
||||
fabLayoutParams.leftMargin = leftInsets + 24
|
||||
fabLayoutParams.rightMargin = rightInsets + 24
|
||||
fabLayoutParams.bottomMargin = barInsets.bottom + 24
|
||||
binding.buttonAddUser.layoutParams = fabLayoutParams
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
NativeConfig.saveGlobalConfig()
|
||||
_binding = null
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -48,16 +51,68 @@ class AddonViewModel : ViewModel() {
|
||||
?: emptyArray()
|
||||
).toMutableList()
|
||||
patchList.sortBy { it.name }
|
||||
|
||||
// Ensure only one update is enabled
|
||||
ensureSingleUpdateEnabled(patchList)
|
||||
|
||||
removeDuplicates(patchList)
|
||||
|
||||
_patchList.value = patchList
|
||||
isRefreshing.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureSingleUpdateEnabled(patchList: MutableList<Patch>) {
|
||||
val updates = patchList.filter { PatchType.from(it.type) == PatchType.Update }
|
||||
if (updates.size <= 1) {
|
||||
return
|
||||
}
|
||||
|
||||
val enabledUpdates = updates.filter { it.enabled }
|
||||
|
||||
if (enabledUpdates.size > 1) {
|
||||
val nandOrSdmcEnabled = enabledUpdates.find {
|
||||
it.name.contains("(NAND)") || it.name.contains("(SDMC)")
|
||||
}
|
||||
|
||||
val updateToKeep = nandOrSdmcEnabled ?: enabledUpdates.first()
|
||||
|
||||
for (patch in patchList) {
|
||||
if (PatchType.from(patch.type) == PatchType.Update) {
|
||||
patch.enabled = (patch === updateToKeep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeDuplicates(patchList: MutableList<Patch>) {
|
||||
val seen = mutableSetOf<String>()
|
||||
val iterator = patchList.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val patch = iterator.next()
|
||||
val key = "${patch.name}|${patch.version}|${patch.type}"
|
||||
if (seen.contains(key)) {
|
||||
iterator.remove()
|
||||
} else {
|
||||
seen.add(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setAddonToDelete(patch: Patch?) {
|
||||
_addonToDelete.value = patch
|
||||
}
|
||||
|
||||
fun enableOnlyThisUpdate(selectedPatch: Patch) {
|
||||
val currentList = _patchList.value
|
||||
for (patch in currentList) {
|
||||
if (PatchType.from(patch.type) == PatchType.Update) {
|
||||
patch.enabled = (patch === selectedPatch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteAddon(patch: Patch) {
|
||||
when (PatchType.from(patch.type)) {
|
||||
PatchType.Update -> NativeLibrary.removeUpdate(patch.programId)
|
||||
@@ -72,13 +127,27 @@ class AddonViewModel : ViewModel() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if there are multiple update versions
|
||||
val updates = _patchList.value.filter { PatchType.from(it.type) == PatchType.Update }
|
||||
val hasMultipleUpdates = updates.size > 1
|
||||
|
||||
NativeConfig.setDisabledAddons(
|
||||
game!!.programId,
|
||||
_patchList.value.mapNotNull {
|
||||
if (it.enabled) {
|
||||
null
|
||||
} else {
|
||||
it.name
|
||||
if (PatchType.from(it.type) == PatchType.Update) {
|
||||
if (it.name.contains("(NAND)") || it.name.contains("(SDMC)")) {
|
||||
it.name
|
||||
} else if (hasMultipleUpdates) {
|
||||
"Update@${it.numericVersion}"
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
}.toTypedArray()
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,7 +41,8 @@ enum class AppletInfo(val appletId: Int, val entryId: Long = 0) {
|
||||
OfflineWeb(0x17),
|
||||
LoginShare(0x18),
|
||||
WebAuth(0x19),
|
||||
MyPage(0x1A)
|
||||
MyPage(0x1A),
|
||||
Splay(0x64)
|
||||
}
|
||||
|
||||
// Matches enum in Service::NFP::CabinetMode with extra metadata
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,5 +12,14 @@ import kotlinx.parcelize.Parcelize
|
||||
@Parcelize
|
||||
data class GameDir(
|
||||
val uriString: String,
|
||||
var deepScan: Boolean
|
||||
) : Parcelable
|
||||
var deepScan: Boolean,
|
||||
val type: DirectoryType = DirectoryType.GAME
|
||||
) : Parcelable {
|
||||
// Needed for JNI backward compatability
|
||||
constructor(uriString: String, deepScan: Boolean) : this(uriString, deepScan, DirectoryType.GAME)
|
||||
}
|
||||
|
||||
enum class DirectoryType {
|
||||
GAME,
|
||||
EXTERNAL_CONTENT
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.model
|
||||
@@ -56,7 +56,7 @@ class GamesViewModel : ViewModel() {
|
||||
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
||||
NativeLibrary.reloadKeys()
|
||||
|
||||
getGameDirs()
|
||||
getGameDirsAndExternalContent()
|
||||
reloadGames(directoriesChanged = false, firstStartup = true)
|
||||
}
|
||||
|
||||
@@ -144,11 +144,19 @@ class GamesViewModel : ViewModel() {
|
||||
fun addFolder(gameDir: GameDir, savedFromGameFragment: Boolean) =
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
NativeConfig.addGameDir(gameDir)
|
||||
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||
|
||||
getGameDirs(!isFirstTimeSetup)
|
||||
when (gameDir.type) {
|
||||
DirectoryType.GAME -> {
|
||||
NativeConfig.addGameDir(gameDir)
|
||||
val isFirstTimeSetup = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
.getBoolean(org.yuzu.yuzu_emu.features.settings.model.Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||
getGameDirsAndExternalContent(!isFirstTimeSetup)
|
||||
}
|
||||
DirectoryType.EXTERNAL_CONTENT -> {
|
||||
addExternalContentDir(gameDir.uriString)
|
||||
NativeConfig.saveGlobalConfig()
|
||||
getGameDirsAndExternalContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (savedFromGameFragment) {
|
||||
@@ -168,8 +176,15 @@ class GamesViewModel : ViewModel() {
|
||||
val removedDirIndex = gameDirs.indexOf(gameDir)
|
||||
if (removedDirIndex != -1) {
|
||||
gameDirs.removeAt(removedDirIndex)
|
||||
NativeConfig.setGameDirs(gameDirs.toTypedArray())
|
||||
getGameDirs()
|
||||
when (gameDir.type) {
|
||||
DirectoryType.GAME -> {
|
||||
NativeConfig.setGameDirs(gameDirs.filter { it.type == DirectoryType.GAME }.toTypedArray())
|
||||
}
|
||||
DirectoryType.EXTERNAL_CONTENT -> {
|
||||
removeExternalContentDir(gameDir.uriString)
|
||||
}
|
||||
}
|
||||
getGameDirsAndExternalContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,15 +192,16 @@ class GamesViewModel : ViewModel() {
|
||||
fun updateGameDirs() =
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
NativeConfig.setGameDirs(_folders.value.toTypedArray())
|
||||
getGameDirs()
|
||||
val gameDirs = _folders.value.filter { it.type == DirectoryType.GAME }
|
||||
NativeConfig.setGameDirs(gameDirs.toTypedArray())
|
||||
getGameDirsAndExternalContent()
|
||||
}
|
||||
}
|
||||
|
||||
fun onOpenGameFoldersFragment() =
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
getGameDirs()
|
||||
getGameDirsAndExternalContent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,16 +209,36 @@ class GamesViewModel : ViewModel() {
|
||||
NativeConfig.saveGlobalConfig()
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
getGameDirs(true)
|
||||
getGameDirsAndExternalContent(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGameDirs(reloadList: Boolean = false) {
|
||||
val gameDirs = NativeConfig.getGameDirs()
|
||||
_folders.value = gameDirs.toMutableList()
|
||||
private fun getGameDirsAndExternalContent(reloadList: Boolean = false) {
|
||||
val gameDirs = NativeConfig.getGameDirs().toMutableList()
|
||||
val externalContentDirs = NativeConfig.getExternalContentDirs().map {
|
||||
GameDir(it, false, DirectoryType.EXTERNAL_CONTENT)
|
||||
}
|
||||
gameDirs.addAll(externalContentDirs)
|
||||
_folders.value = gameDirs
|
||||
if (reloadList) {
|
||||
reloadGames(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addExternalContentDir(path: String) {
|
||||
val currentDirs = NativeConfig.getExternalContentDirs().toMutableList()
|
||||
if (!currentDirs.contains(path)) {
|
||||
currentDirs.add(path)
|
||||
NativeConfig.setExternalContentDirs(currentDirs.toTypedArray())
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeExternalContentDir(path: String) {
|
||||
val currentDirs = NativeConfig.getExternalContentDirs().toMutableList()
|
||||
currentDirs.remove(path)
|
||||
NativeConfig.setExternalContentDirs(currentDirs.toTypedArray())
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,5 +15,6 @@ data class Patch(
|
||||
val version: String,
|
||||
val type: Int,
|
||||
val programId: String,
|
||||
val titleId: String
|
||||
val titleId: String,
|
||||
val numericVersion: Long = 0
|
||||
)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class UserProfile(
|
||||
val uuid: String,
|
||||
val username: String,
|
||||
val imagePath: String = ""
|
||||
) : Parcelable
|
||||
|
||||
object ProfileUtils {
|
||||
fun generateRandomUUID(): String {
|
||||
val uuid = java.util.UUID.randomUUID()
|
||||
return uuid.toString().replace("-", "")
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.ui
|
||||
@@ -13,6 +13,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
@@ -27,10 +28,14 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.adapters.GameAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.model.AppletInfo
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
@@ -173,10 +178,16 @@ class GamesFragment : Fragment() {
|
||||
|
||||
setupTopView()
|
||||
|
||||
updateButtonsVisibility()
|
||||
|
||||
binding.addDirectory.setOnClickListener {
|
||||
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
|
||||
}
|
||||
|
||||
binding.launchQlaunch?.setOnClickListener {
|
||||
launchQLaunch()
|
||||
}
|
||||
|
||||
setInsets()
|
||||
}
|
||||
|
||||
@@ -445,6 +456,47 @@ class GamesFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchQLaunch() {
|
||||
try {
|
||||
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.QLaunch.entryId)
|
||||
if (appletPath.isEmpty()) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.applets_error_applet,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
NativeLibrary.setCurrentAppletId(AppletInfo.QLaunch.appletId)
|
||||
|
||||
val qlaunchGame = Game(
|
||||
title = getString(R.string.qlaunch_applet),
|
||||
path = appletPath
|
||||
)
|
||||
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(qlaunchGame)
|
||||
findNavController().navigate(action)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Failed to launch QLaunch: ${e.message}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateButtonsVisibility() {
|
||||
val showQLaunch = BooleanSetting.ENABLE_QLAUNCH_BUTTON.getBoolean()
|
||||
val showFolder = BooleanSetting.ENABLE_FOLDER_BUTTON.getBoolean()
|
||||
val isFirmwareAvailable = NativeLibrary.isFirmwareAvailable()
|
||||
|
||||
val shouldShowQLaunch = showQLaunch && isFirmwareAvailable
|
||||
binding.launchQlaunch.visibility = if (shouldShowQLaunch) View.VISIBLE else View.GONE
|
||||
|
||||
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
@@ -498,6 +550,13 @@ class GamesFragment : Fragment() {
|
||||
mlpFab.rightMargin = rightInset + fabPadding
|
||||
binding.addDirectory.layoutParams = mlpFab
|
||||
|
||||
binding.launchQlaunch?.let { qlaunchButton ->
|
||||
val mlpQLaunch = qlaunchButton.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpQLaunch.leftMargin = leftInset + fabPadding
|
||||
mlpQLaunch.bottomMargin = barInsets.bottom + fabPadding
|
||||
qlaunchButton.layoutParams = mlpQLaunch
|
||||
}
|
||||
|
||||
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
|
||||
@@ -63,6 +63,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
|
||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
@@ -199,8 +200,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
var artifact = tag
|
||||
// Nightly builds have a slightly different format
|
||||
if (NativeLibrary.isNightlyBuild()) {
|
||||
val splitTag = tag.split('.')
|
||||
artifact = splitTag.subList(1, splitTag.size - 1).joinToString(".")
|
||||
artifact = tag.substringAfter('.', tag)
|
||||
}
|
||||
downloadAndInstallUpdate(tag, artifact)
|
||||
}
|
||||
@@ -389,6 +389,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
val getExternalContentDirectory =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
|
||||
if (result != null) {
|
||||
processExternalContentDir(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun processGamesDir(result: Uri, calledFromGameFragment: Boolean = false) {
|
||||
contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
@@ -410,6 +417,27 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
.show(supportFragmentManager, AddGameFolderDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun processExternalContentDir(result: Uri) {
|
||||
contentResolver.takePersistableUriPermission(
|
||||
result,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
|
||||
val uriString = result.toString()
|
||||
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
|
||||
if (folder != null) {
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
R.string.folder_already_added,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
val externalContentDir = org.yuzu.yuzu_emu.model.GameDir(uriString, false, org.yuzu.yuzu_emu.model.DirectoryType.EXTERNAL_CONTENT)
|
||||
gamesViewModel.addFolder(externalContentDir, savedFromGameFragment = false)
|
||||
}
|
||||
|
||||
val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result != null) {
|
||||
processKey(result, "keys")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
@@ -25,6 +25,7 @@ object DirectoryInitialization {
|
||||
initializeInternalStorage()
|
||||
NativeLibrary.initializeSystem(false)
|
||||
NativeConfig.initializeGlobalConfig()
|
||||
NativeLibrary.reloadProfiles()
|
||||
migrateSettings()
|
||||
areDirectoriesReady = true
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// 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
|
||||
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import android.content.SharedPreferences
|
||||
@@ -49,6 +49,17 @@ object GameHelper {
|
||||
// Remove previous filesystem provider information so we can get up to date version info
|
||||
NativeLibrary.clearFilesystemProvider()
|
||||
|
||||
// Scan External Content directories and register all NSP/XCI files
|
||||
val externalContentDirs = NativeConfig.getExternalContentDirs()
|
||||
for (externalDir in externalContentDirs) {
|
||||
if (externalDir.isNotEmpty()) {
|
||||
val externalDirUri = externalDir.toUri()
|
||||
if (FileUtil.isTreeUriValid(externalDirUri)) {
|
||||
scanExternalContentRecursive(FileUtil.listFiles(externalDirUri), 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val badDirs = mutableListOf<Int>()
|
||||
gameDirs.forEachIndexed { index: Int, gameDir: GameDir ->
|
||||
val gameDirUri = gameDir.uriString.toUri()
|
||||
@@ -88,6 +99,33 @@ object GameHelper {
|
||||
return games.toList()
|
||||
}
|
||||
|
||||
// File extensions considered as external content, buuut should
|
||||
// be done better imo.
|
||||
private val externalContentExtensions = setOf("nsp", "xci")
|
||||
|
||||
private fun scanExternalContentRecursive(
|
||||
files: Array<MinimalDocumentFile>,
|
||||
depth: Int
|
||||
) {
|
||||
if (depth <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
files.forEach {
|
||||
if (it.isDirectory) {
|
||||
scanExternalContentRecursive(
|
||||
FileUtil.listFiles(it.uri),
|
||||
depth - 1
|
||||
)
|
||||
} else {
|
||||
val extension = FileUtil.getExtension(it.uri).lowercase()
|
||||
if (externalContentExtensions.contains(extension)) {
|
||||
NativeLibrary.addFileToFilesystemProvider(it.uri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addGamesRecursive(
|
||||
games: MutableList<Game>,
|
||||
files: Array<MinimalDocumentFile>,
|
||||
|
||||
@@ -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: 2023 yuzu Emulator Project
|
||||
@@ -204,4 +204,12 @@ object NativeConfig {
|
||||
external fun getSdmcDir(): String
|
||||
@Synchronized
|
||||
external fun setSdmcDir(path: String)
|
||||
|
||||
/**
|
||||
* External Content Provider
|
||||
*/
|
||||
@Synchronized
|
||||
external fun getExternalContentDirs(): Array<String>
|
||||
@Synchronized
|
||||
external fun setExternalContentDirs(dirs: Array<String>)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
@@ -144,6 +144,15 @@ object FreedrenoPresets {
|
||||
)
|
||||
)
|
||||
|
||||
val DEV_FEATURES_UBWC_HINT = FreedrenoPreset(
|
||||
name = "Dev - UBWC Flag Hint",
|
||||
description = "Enable TP UBWC flag hint for development",
|
||||
icon = "ic_dev_features",
|
||||
variables = mapOf(
|
||||
"FD_DEV_FEATURES" to "enable_tp_ubwc_flag_hint=1"
|
||||
)
|
||||
)
|
||||
|
||||
val PERFORMANCE_DEFAULT = FreedrenoPreset(
|
||||
name = "Performance - Default",
|
||||
description = "Clear all debug options for performance",
|
||||
@@ -159,6 +168,7 @@ object FreedrenoPresets {
|
||||
CAPTURE_FRAMES,
|
||||
SHADER_DEBUG,
|
||||
GPU_HANG_TRACE,
|
||||
DEV_FEATURES_UBWC_HINT,
|
||||
PERFORMANCE_DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <common/fs/path_util.h>
|
||||
#include <common/logging/log.h>
|
||||
#include <common/settings.h>
|
||||
#include <input_common/main.h>
|
||||
#include "android_config.h"
|
||||
#include "android_settings.h"
|
||||
@@ -69,6 +70,18 @@ void AndroidConfig::ReadPathValues() {
|
||||
}
|
||||
EndArray();
|
||||
|
||||
// Read external content directories
|
||||
Settings::values.external_content_dirs.clear();
|
||||
const int external_dirs_size = BeginArray(std::string("external_content_dirs"));
|
||||
for (int i = 0; i < external_dirs_size; ++i) {
|
||||
SetArrayIndex(i);
|
||||
std::string dir_path = ReadStringSetting(std::string("path"));
|
||||
if (!dir_path.empty()) {
|
||||
Settings::values.external_content_dirs.push_back(dir_path);
|
||||
}
|
||||
}
|
||||
EndArray();
|
||||
|
||||
const auto nand_dir_setting = ReadStringSetting(std::string("nand_directory"));
|
||||
if (!nand_dir_setting.empty()) {
|
||||
Common::FS::SetEdenPath(Common::FS::EdenPath::NANDDir, nand_dir_setting);
|
||||
@@ -241,6 +254,14 @@ void AndroidConfig::SavePathValues() {
|
||||
}
|
||||
EndArray();
|
||||
|
||||
// Save external content directories
|
||||
BeginArray(std::string("external_content_dirs"));
|
||||
for (size_t i = 0; i < Settings::values.external_content_dirs.size(); ++i) {
|
||||
SetArrayIndex(i);
|
||||
WriteStringSetting(std::string("path"), Settings::values.external_content_dirs[i]);
|
||||
}
|
||||
EndArray();
|
||||
|
||||
// Save custom NAND directory
|
||||
const auto nand_path = Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir);
|
||||
WriteStringSetting(std::string("nand_directory"), nand_path,
|
||||
|
||||
@@ -54,10 +54,6 @@ namespace AndroidSettings {
|
||||
Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
|
||||
Settings::Category::GpuDriver};
|
||||
|
||||
// LRU Cache
|
||||
Settings::SwitchableSetting<bool> use_lru_cache{linkage, true, "use_lru_cache",
|
||||
Settings::Category::System};
|
||||
|
||||
Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
|
||||
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
|
||||
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
|
||||
@@ -65,6 +61,10 @@ namespace AndroidSettings {
|
||||
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
|
||||
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
|
||||
Settings::Category::Android};
|
||||
Settings::Setting<bool> enable_folder_button{linkage, true, "enable_folder_button",
|
||||
Settings::Category::Android};
|
||||
Settings::Setting<bool> enable_qlaunch_button{linkage, false, "enable_qlaunch_button",
|
||||
Settings::Category::Android};
|
||||
|
||||
// Input/performance overlay settings
|
||||
std::vector<OverlayControlData> overlay_control_data;
|
||||
@@ -198,9 +198,14 @@ namespace AndroidSettings {
|
||||
Settings::Specialization::Default, true, true,
|
||||
&show_soc_overlay};
|
||||
|
||||
// MISC
|
||||
Settings::Setting<bool> dont_show_driver_shader_warning{linkage, false,
|
||||
"dont_show_driver_shader_warning",
|
||||
Settings::Category::Android, Settings::Specialization::Default, true, true};
|
||||
Settings::Setting<bool> enable_quick_settings{linkage, true,
|
||||
"enable_quick_settings",
|
||||
Settings::Category::Android, Settings::Specialization::Default, true,
|
||||
false};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@ struct RomMetadata {
|
||||
bool isHomebrew;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
|
||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
const auto file =
|
||||
|
||||
@@ -49,12 +49,17 @@
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "frontend_common/play_time_manager.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/core.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/fs_filesystem.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/submission_package.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/file_sys/vfs/vfs_real.h"
|
||||
@@ -210,6 +215,109 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto extension = Common::ToLower(filepath.substr(filepath.find_last_of('.') + 1));
|
||||
|
||||
if (extension == "nsp") {
|
||||
auto nsp = std::make_shared<FileSys::NSP>(file);
|
||||
if (nsp->GetStatus() == Loader::ResultStatus::Success) {
|
||||
std::map<u64, u32> nsp_versions;
|
||||
std::map<u64, std::string> nsp_version_strings;
|
||||
|
||||
for (const auto& [title_id, nca_map] : nsp->GetNCAs()) {
|
||||
for (const auto& [type_pair, nca] : nca_map) {
|
||||
const auto& [title_type, content_type] = type_pair;
|
||||
|
||||
if (content_type == FileSys::ContentRecordType::Meta) {
|
||||
const auto meta_nca = std::make_shared<FileSys::NCA>(nca->GetBaseFile());
|
||||
if (meta_nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
const auto section0 = meta_nca->GetSubdirectories();
|
||||
if (!section0.empty()) {
|
||||
for (const auto& meta_file : section0[0]->GetFiles()) {
|
||||
if (meta_file->GetExtension() == "cnmt") {
|
||||
FileSys::CNMT cnmt(meta_file);
|
||||
nsp_versions[cnmt.GetTitleID()] = cnmt.GetTitleVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content_type == FileSys::ContentRecordType::Control &&
|
||||
title_type == FileSys::TitleType::Update) {
|
||||
auto romfs = nca->GetRomFS();
|
||||
if (romfs) {
|
||||
auto extracted = FileSys::ExtractRomFS(romfs);
|
||||
if (extracted) {
|
||||
auto nacp_file = extracted->GetFile("control.nacp");
|
||||
if (!nacp_file) {
|
||||
nacp_file = extracted->GetFile("Control.nacp");
|
||||
}
|
||||
if (nacp_file) {
|
||||
FileSys::NACP nacp(nacp_file);
|
||||
auto ver_str = nacp.GetVersionString();
|
||||
if (!ver_str.empty()) {
|
||||
nsp_version_strings[title_id] = ver_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [title_id, nca_map] : nsp->GetNCAs()) {
|
||||
for (const auto& [type_pair, nca] : nca_map) {
|
||||
const auto& [title_type, content_type] = type_pair;
|
||||
|
||||
if (title_type == FileSys::TitleType::Update) {
|
||||
u32 version = 0;
|
||||
auto ver_it = nsp_versions.find(title_id);
|
||||
if (ver_it != nsp_versions.end()) {
|
||||
version = ver_it->second;
|
||||
}
|
||||
|
||||
std::string version_string;
|
||||
auto str_it = nsp_version_strings.find(title_id);
|
||||
if (str_it != nsp_version_strings.end()) {
|
||||
version_string = str_it->second;
|
||||
}
|
||||
|
||||
m_manual_provider->AddEntryWithVersion(
|
||||
title_type, content_type, title_id, version, version_string,
|
||||
nca->GetBaseFile());
|
||||
|
||||
LOG_DEBUG(Frontend, "Added NSP update entry - TitleID: {:016X}, Version: {}, VersionStr: {}",
|
||||
title_id, version, version_string);
|
||||
} else {
|
||||
// Use regular AddEntry for non-updates
|
||||
m_manual_provider->AddEntry(title_type, content_type, title_id,
|
||||
nca->GetBaseFile());
|
||||
LOG_DEBUG(Frontend, "Added NSP entry - TitleID: {:016X}, TitleType: {}, ContentType: {}",
|
||||
title_id, static_cast<int>(title_type), static_cast<int>(content_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle XCI files
|
||||
if (extension == "xci") {
|
||||
FileSys::XCI xci{file};
|
||||
if (xci.GetStatus() == Loader::ResultStatus::Success) {
|
||||
const auto nsp = xci.GetSecurePartitionNSP();
|
||||
if (nsp) {
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(m_system, file);
|
||||
if (!loader) {
|
||||
return;
|
||||
@@ -226,17 +334,6 @@ void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath)
|
||||
m_manual_provider->AddEntry(FileSys::TitleType::Application,
|
||||
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
|
||||
program_id, file);
|
||||
} else if (res2 == Loader::ResultStatus::Success &&
|
||||
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
|
||||
const auto nsp = file_type == Loader::FileType::NSP
|
||||
? std::make_shared<FileSys::NSP>(file)
|
||||
: FileSys::XCI{file}.GetSecurePartitionNSP();
|
||||
for (const auto& title : nsp->GetNCAs()) {
|
||||
for (const auto& entry : title.second) {
|
||||
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
|
||||
entry.second->GetBaseFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1136,6 +1233,39 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_getDebugKnobAt(JNIEnv* env, jobje
|
||||
return static_cast<jboolean>(Settings::getDebugKnobAt(static_cast<u8>(index)));
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setTurboSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||
if (enabled) {
|
||||
Settings::values.use_speed_limit.SetValue(true);
|
||||
Settings::SetSpeedMode(Settings::SpeedMode::Turbo);
|
||||
} else {
|
||||
Settings::SetSpeedMode(Settings::SpeedMode::Standard);
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setSlowSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||
if (enabled) {
|
||||
Settings::values.use_speed_limit.SetValue(true);
|
||||
Settings::SetSpeedMode(Settings::SpeedMode::Slow);
|
||||
} else {
|
||||
Settings::SetSpeedMode(Settings::SpeedMode::Standard);
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setStandardSpeedLimit(JNIEnv *env, jobject jobj, jboolean enabled) {
|
||||
Settings::values.use_speed_limit.SetValue(enabled);
|
||||
if (enabled) {
|
||||
Settings::SetSpeedMode(Settings::SpeedMode::Standard);
|
||||
}
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isTurboMode(JNIEnv *env, jobject jobj) {
|
||||
return Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Turbo;
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isSlowMode(JNIEnv *env, jobject jobj) {
|
||||
return Settings::values.current_speed_mode.GetValue() == Settings::SpeedMode::Slow;
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run(JNIEnv* env, jobject jobj, jstring j_path,
|
||||
jint j_program_index,
|
||||
jboolean j_frontend_initiated) {
|
||||
@@ -1331,7 +1461,8 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
|
||||
Common::Android::ToJString(env, patch.name),
|
||||
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
|
||||
Common::Android::ToJString(env, std::to_string(patch.program_id)),
|
||||
Common::Android::ToJString(env, std::to_string(patch.title_id)));
|
||||
Common::Android::ToJString(env, std::to_string(patch.title_id)),
|
||||
static_cast<jlong>(patch.numeric_version));
|
||||
env->SetObjectArrayElement(jpatchArray, i, jpatch);
|
||||
++i;
|
||||
}
|
||||
@@ -1615,22 +1746,14 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isNightlyBuild(
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_UPDATE_CHECKER
|
||||
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate(
|
||||
JNIEnv* env,
|
||||
jobject obj) {
|
||||
const bool is_prerelease = ((strstr(Common::g_build_version, "pre-alpha") != nullptr) ||
|
||||
(strstr(Common::g_build_version, "alpha") != nullptr) ||
|
||||
(strstr(Common::g_build_version, "beta") != nullptr) ||
|
||||
(strstr(Common::g_build_version, "rc") != nullptr));
|
||||
const std::optional<UpdateChecker::Update> release =
|
||||
UpdateChecker::GetLatestRelease(is_prerelease);
|
||||
|
||||
if (!release || release->tag == Common::g_build_version) {
|
||||
return nullptr;
|
||||
}
|
||||
std::optional<UpdateChecker::Update> release = UpdateChecker::GetUpdate();
|
||||
if (!release) return nullptr;
|
||||
|
||||
const std::string tag = release->tag;
|
||||
const std::string name = release->name;
|
||||
@@ -1686,7 +1809,7 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||
#endif
|
||||
}
|
||||
|
||||
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant);
|
||||
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", artifact_str, variant);
|
||||
const std::string url = fmt::format("{}/{}/releases/download/{}/{}",
|
||||
std::string{Common::g_build_auto_update_website},
|
||||
std::string{Common::g_build_auto_update_repo},
|
||||
@@ -1694,6 +1817,7 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||
apk_filename);
|
||||
|
||||
env->ReleaseStringUTFChars(tag, version_str);
|
||||
env->ReleaseStringUTFChars(artifact, artifact_str);
|
||||
env->ReleaseStringUTFChars(packageId, package_id_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
@@ -1705,4 +1829,388 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
|
||||
return env->NewStringUTF(Common::g_build_version);
|
||||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getAllUsers(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
|
||||
manager.ResetUserSaveFile();
|
||||
|
||||
if (manager.GetUserCount() == 0) {
|
||||
manager.CreateNewUser(Common::UUID::MakeRandom(), "Eden");
|
||||
manager.WriteUserSaveFile();
|
||||
}
|
||||
|
||||
const auto& users = manager.GetAllUsers();
|
||||
|
||||
jclass string_class = env->FindClass("java/lang/String");
|
||||
if (!string_class) {
|
||||
return env->NewObjectArray(0, env->FindClass("java/lang/Object"), nullptr);
|
||||
}
|
||||
|
||||
jsize valid_count = 0;
|
||||
for (const auto& user : users) {
|
||||
if (user.IsValid()) {
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
|
||||
jobjectArray result = env->NewObjectArray(valid_count, string_class, nullptr);
|
||||
if (!result) {
|
||||
return env->NewObjectArray(0, string_class, nullptr);
|
||||
}
|
||||
|
||||
// fill array sequentially with only valid users
|
||||
jsize array_index = 0;
|
||||
for (const auto& user : users) {
|
||||
if (user.IsValid()) {
|
||||
jstring uuid_str = env->NewStringUTF(user.FormattedString().c_str());
|
||||
if (uuid_str) {
|
||||
env->SetObjectArrayElement(result, array_index++, uuid_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserUsername(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
Service::Account::ProfileBase profile{};
|
||||
if (!manager.GetProfileBase(uuid, profile)) {
|
||||
jstring result = env->NewStringUTF("");
|
||||
return result ? result : env->NewStringUTF("");
|
||||
}
|
||||
|
||||
const auto text = Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(profile.username.data()), profile.username.size());
|
||||
jstring result = env->NewStringUTF(text.c_str());
|
||||
return result ? result : env->NewStringUTF("");
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserCount(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
return static_cast<jlong>(manager.GetUserCount());
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_canCreateUser(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
return manager.CanSystemRegisterUser();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_createUser(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid,
|
||||
jstring jusername) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto username = Common::Android::GetJString(env, jusername);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
const auto result = manager.CreateNewUser(uuid, username);
|
||||
if (result.IsSuccess()) {
|
||||
manager.WriteUserSaveFile();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_updateUserUsername(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid,
|
||||
jstring jusername) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto username = Common::Android::GetJString(env, jusername);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
Service::Account::ProfileBase profile{};
|
||||
if (!manager.GetProfileBase(uuid, profile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fill(profile.username.begin(), profile.username.end(), '\0');
|
||||
std::copy(username.begin(), username.end(), profile.username.begin());
|
||||
|
||||
if (manager.SetProfileBase(uuid, profile)) {
|
||||
manager.WriteUserSaveFile();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUser(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
const auto user_index = manager.GetUserIndex(uuid);
|
||||
if (!user_index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Settings::values.current_user.GetValue() == static_cast<s32>(*user_index)) {
|
||||
Settings::values.current_user = 0;
|
||||
}
|
||||
|
||||
if (manager.RemoveUser(uuid)) {
|
||||
manager.WriteUserSaveFile();
|
||||
manager.ResetUserSaveFile();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getCurrentUser(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto user_id = manager.GetUser(Settings::values.current_user.GetValue());
|
||||
if (!user_id) {
|
||||
jstring result = env->NewStringUTF("");
|
||||
return result ? result : env->NewStringUTF("");
|
||||
}
|
||||
jstring result = env->NewStringUTF(user_id->FormattedString().c_str());
|
||||
return result ? result : env->NewStringUTF("");
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentUser(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
const auto index = manager.GetUserIndex(uuid);
|
||||
if (index) {
|
||||
Settings::values.current_user = static_cast<s32>(*index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserImagePath(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid) {
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
|
||||
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) /
|
||||
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
|
||||
|
||||
jstring result = Common::Android::ToJString(env, Common::FS::PathToUTF8String(path));
|
||||
return result ? result : env->NewStringUTF("");
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_saveUserImage(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jstring juuid,
|
||||
jstring jimagePath) {
|
||||
const auto uuid_string = Common::Android::GetJString(env, juuid);
|
||||
const auto uuid = Common::UUID{uuid_string};
|
||||
const auto image_source = Common::Android::GetJString(env, jimagePath);
|
||||
|
||||
const auto dest_path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) /
|
||||
fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString());
|
||||
|
||||
const auto dest_dir = dest_path.parent_path();
|
||||
if (!Common::FS::CreateDirs(dest_dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
std::filesystem::copy_file(image_source, dest_path,
|
||||
std::filesystem::copy_options::overwrite_existing);
|
||||
return true;
|
||||
} catch (const std::filesystem::filesystem_error& e) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to copy image file: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadProfiles(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
auto& manager = EmulationSession::GetInstance().System().GetProfileManager();
|
||||
manager.ResetUserSaveFile();
|
||||
|
||||
// create a default user if non exist
|
||||
if (manager.GetUserCount() == 0) {
|
||||
manager.CreateNewUser(Common::UUID::MakeRandom(), "Eden");
|
||||
manager.WriteUserSaveFile();
|
||||
}
|
||||
|
||||
LOG_INFO(Service_ACC, "Profile manager reloaded, user count: {}", manager.GetUserCount());
|
||||
}
|
||||
|
||||
// for firmware avatar images
|
||||
static std::vector<uint8_t> DecompressYaz0(const FileSys::VirtualFile& file) {
|
||||
if (!file) {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
uint32_t magic{};
|
||||
file->ReadObject(&magic, 0);
|
||||
if (magic != Common::MakeMagic('Y', 'a', 'z', '0')) {
|
||||
return std::vector<uint8_t>();
|
||||
}
|
||||
|
||||
uint32_t decoded_length{};
|
||||
file->ReadObject(&decoded_length, 4);
|
||||
decoded_length = Common::swap32(decoded_length);
|
||||
|
||||
std::size_t input_size = file->GetSize() - 16;
|
||||
std::vector<uint8_t> input(input_size);
|
||||
file->ReadBytes(input.data(), input_size, 16);
|
||||
|
||||
uint32_t input_offset{};
|
||||
uint32_t output_offset{};
|
||||
std::vector<uint8_t> output(decoded_length);
|
||||
|
||||
uint16_t mask{};
|
||||
uint8_t header{};
|
||||
|
||||
while (output_offset < decoded_length) {
|
||||
if ((mask >>= 1) == 0) {
|
||||
if (input_offset >= input.size()) break;
|
||||
header = input[input_offset++];
|
||||
mask = 0x80;
|
||||
}
|
||||
|
||||
if ((header & mask) != 0) {
|
||||
if (output_offset >= output.size() || input_offset >= input.size()) {
|
||||
break;
|
||||
}
|
||||
output[output_offset++] = input[input_offset++];
|
||||
} else {
|
||||
if (input_offset + 1 >= input.size()) break;
|
||||
uint8_t byte1 = input[input_offset++];
|
||||
uint8_t byte2 = input[input_offset++];
|
||||
|
||||
uint32_t dist = ((byte1 & 0xF) << 8) | byte2;
|
||||
uint32_t position = output_offset - (dist + 1);
|
||||
|
||||
uint32_t length = byte1 >> 4;
|
||||
if (length == 0) {
|
||||
if (input_offset >= input.size()) break;
|
||||
length = static_cast<uint32_t>(input[input_offset++]) + 0x12;
|
||||
} else {
|
||||
length += 2;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length && output_offset < decoded_length; ++i) {
|
||||
output[output_offset++] = output[position++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static FileSys::VirtualDir GetFirmwareAvatarDirectory() {
|
||||
constexpr u64 AvatarImageDataId = 0x010000000000080AULL;
|
||||
|
||||
auto* bis_system = EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
|
||||
if (!bis_system) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto nca = bis_system->GetEntry(AvatarImageDataId, FileSys::ContentRecordType::Data);
|
||||
if (!nca) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto romfs = nca->GetRomFS();
|
||||
if (!romfs) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto extracted = FileSys::ExtractRomFS(romfs);
|
||||
if (!extracted) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return extracted->GetSubdirectory("chara");
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getFirmwareAvatarCount(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
const auto chara_dir = GetFirmwareAvatarDirectory();
|
||||
if (!chara_dir) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (const auto& item : chara_dir->GetFiles()) {
|
||||
if (item->GetExtension() == "szs") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getFirmwareAvatarImage(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj,
|
||||
jint index) {
|
||||
const auto chara_dir = GetFirmwareAvatarDirectory();
|
||||
if (!chara_dir) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int current_index = 0;
|
||||
for (const auto& item : chara_dir->GetFiles()) {
|
||||
if (item->GetExtension() != "szs") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_index == index) {
|
||||
auto image_data = DecompressYaz0(item);
|
||||
if (image_data.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
jbyteArray result = env->NewByteArray(image_data.size());
|
||||
if (result) {
|
||||
env->SetByteArrayRegion(result, 0, image_data.size(),
|
||||
reinterpret_cast<const jbyte*>(image_data.data()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
current_index++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getDefaultAccountBackupJpeg(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
jbyteArray result = env->NewByteArray(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
|
||||
if (result) {
|
||||
env->SetByteArrayRegion(result, 0, Core::Constants::ACCOUNT_BACKUP_JPEG.size(),
|
||||
reinterpret_cast<const jbyte*>(Core::Constants::ACCOUNT_BACKUP_JPEG.data()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
#include <common/fs/path_util.h>
|
||||
|
||||
#include "android_config.h"
|
||||
#include "android_settings.h"
|
||||
#include "common/android/android_common.h"
|
||||
#include "common/android/id_cache.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "frontend_common/config.h"
|
||||
#include "frontend_common/settings_generator.h"
|
||||
#include "native.h"
|
||||
|
||||
std::unique_ptr<AndroidConfig> global_config;
|
||||
@@ -37,6 +38,7 @@ extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
global_config = std::make_unique<AndroidConfig>();
|
||||
FrontendCommon::GenerateSettings();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
@@ -581,4 +583,26 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setSdmcDir(JNIEnv* env, jobject
|
||||
Common::FS::SetEdenPath(Common::FS::EdenPath::SDMCDir, path);
|
||||
}
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getExternalContentDirs(JNIEnv* env,
|
||||
jobject obj) {
|
||||
const auto& dirs = Settings::values.external_content_dirs;
|
||||
jobjectArray jdirsArray =
|
||||
env->NewObjectArray(dirs.size(), Common::Android::GetStringClass(),
|
||||
Common::Android::ToJString(env, ""));
|
||||
for (size_t i = 0; i < dirs.size(); ++i) {
|
||||
env->SetObjectArrayElement(jdirsArray, i, Common::Android::ToJString(env, dirs[i]));
|
||||
}
|
||||
return jdirsArray;
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setExternalContentDirs(JNIEnv* env, jobject obj,
|
||||
jobjectArray jdirs) {
|
||||
Settings::values.external_content_dirs.clear();
|
||||
const int size = env->GetArrayLength(jdirs);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
auto jdir = static_cast<jstring>(env->GetObjectArrayElement(jdirs, i));
|
||||
Settings::values.external_content_dirs.push_back(Common::Android::GetJString(env, jdir));
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
@@ -258,6 +258,11 @@ Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoEnv(
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "[Freedreno] Set {}={}", var_name, value);
|
||||
|
||||
if (var_name == "FD_DEV_FEATURES") {
|
||||
LOG_INFO(Frontend, "[Freedreno] FD_DEV_FEATURES enabled: {}", value);
|
||||
}
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -18,7 +21,7 @@
|
||||
#include "input_common/drivers/virtual_gamepad.h"
|
||||
#include "native.h"
|
||||
|
||||
std::unordered_map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
ankerl::unordered_dense::map<std::string, std::unique_ptr<AndroidConfig>> map_profiles;
|
||||
|
||||
bool IsHandheldOnly() {
|
||||
const auto npad_style_set =
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3s-3,-1.34 -3,-3S10.34,5 12,5zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22c0.03,-1.99 4,-3.08 6,-3.08c1.99,0 5.97,1.09 6,3.08C16.71,17.92 14.5,19.2 12,19.2z" />
|
||||
</vector>
|
||||
@@ -220,6 +220,23 @@
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:rippleColor="#99FFFFFF"
|
||||
/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/launch_qlaunch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="Launch QLaunch"
|
||||
android:text="@string/qlaunch_applet"
|
||||
app:icon="@drawable/ic_home"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:rippleColor="#99FFFFFF"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -11,7 +11,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_gravity="center_vertical">
|
||||
|
||||
@@ -23,12 +23,25 @@
|
||||
android:layout_gravity="center_vertical|start"
|
||||
android:requiresFadingEdge="horizontal"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/type_indicator"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/select_gpu_driver_default" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/type_indicator"
|
||||
style="@style/TextAppearance.Material3.LabelSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/button_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/path"
|
||||
tools:text="Games" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_layout"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/grid_avatars"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_empty"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/profile_firmware_avatars_unavailable"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:gravity="center"
|
||||
android:padding="16dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switch_enable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/gpu_unswizzle_enable" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_texture_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:hint="@string/gpu_unswizzle_texture_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_texture_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_stream_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:hint="@string/gpu_unswizzle_stream_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_stream_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_chunk_size"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/gpu_unswizzle_chunk_size">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_chunk_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="end"
|
||||
android:spacing="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_default"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/gpu_unswizzle_default_button" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_cancel"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_ok"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@android:string/ok" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,226 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_new_user"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/profile_new_user"
|
||||
app:navigationIcon="@drawable/ic_back"
|
||||
app:titleCentered="false" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scroll_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="88dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="24dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="128dp"
|
||||
android:layout_height="128dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="24dp"
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
app:cardCornerRadius="64dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_user_avatar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/profile_avatar"
|
||||
tools:src="@drawable/ic_account_circle" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_select_image"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/profile_select_image"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
app:icon="@drawable/ic_add"
|
||||
android:layout_marginEnd="4dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_firmware_avatars"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/profile_firmware_avatars"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
app:icon="@drawable/ic_account_circle"
|
||||
android:layout_marginStart="4dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_revert_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/profile_revert_image"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:hint="@string/profile_username">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edit_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLength="32"
|
||||
android:maxLines="1"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
app:cardCornerRadius="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/profile_uuid"
|
||||
android:textAppearance="?attr/textAppearanceLabelMedium"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_uuid"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:fontFamily="monospace"
|
||||
android:textIsSelectable="true"
|
||||
tools:text="12345678-1234-1234-1234-123456789012" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_generate_uuid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/profile_generate"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
app:icon="@drawable/ic_refresh"
|
||||
app:iconGravity="textStart" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/profile_uuid_description"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
app:cardCornerRadius="0dp"
|
||||
app:cardElevation="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/button_container"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@android:string/cancel"
|
||||
style="@style/Widget.Material3.Button.TonalButton" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_save"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/save"
|
||||
style="@style/Widget.Material3.Button" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -214,6 +214,22 @@
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/launch_qlaunch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="Launch QLaunch"
|
||||
android:text="@string/qlaunch_applet"
|
||||
app:icon="@drawable/ic_home"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:rippleColor="#99FFFFFF"
|
||||
/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/add_directory"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -227,6 +243,7 @@
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:backgroundTint="?attr/colorPrimary"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:rippleColor="#99FFFFFF"
|
||||
/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_profiles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:title="@string/profile_manager"
|
||||
app:navigationIcon="@drawable/ic_back"
|
||||
app:titleCentered="false" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_profiles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="96dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/list_item_profile" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/button_add_user"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="24dp"
|
||||
android:text="@string/profile_add_user"
|
||||
android:contentDescription="@string/profile_add_user"
|
||||
app:icon="@drawable/ic_add"
|
||||
app:iconGravity="start"
|
||||
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:checkable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_avatar"
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="72dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/profile_avatar" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -0,0 +1,125 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="6dp"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
app:cardCornerRadius="16dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/avatar_container"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
app:cardCornerRadius="28dp"
|
||||
app:cardElevation="1dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_avatar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@drawable/ic_account_circle"
|
||||
android:contentDescription="@string/profile_avatar" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/check_container"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
app:cardBackgroundColor="?attr/colorPrimary"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="2dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/avatar_container"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar_container"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon_check"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/ic_check"
|
||||
app:tint="?attr/colorOnPrimary"
|
||||
android:contentDescription="@string/profile_current_user" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/text_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintStart_toEndOf="@id/avatar_container"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_edit"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar_container"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar_container">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
tools:text="User Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_uuid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="middle"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
tools:text="12345678-1234-1234-1234-123456789012" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_edit"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:icon="@drawable/ic_edit"
|
||||
app:iconTint="?attr/colorOnSurfaceVariant"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_delete"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:contentDescription="@string/profile_edit" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_delete"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
style="@style/Widget.Material3.Button.IconButton"
|
||||
app:icon="@drawable/ic_delete"
|
||||
app:iconTint="?attr/colorError"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:contentDescription="@string/profile_delete" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -36,6 +36,9 @@
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_gameFoldersFragment"
|
||||
app:destination="@id/gameFoldersFragment" />
|
||||
<action
|
||||
android:id="@+id/action_homeSettingsFragment_to_profileManagerFragment"
|
||||
app:destination="@id/profileManagerFragment" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
@@ -186,5 +189,17 @@
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/profileManagerFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.ProfileManagerFragment"
|
||||
android:label="ProfileManagerFragment" >
|
||||
<action
|
||||
android:id="@+id/action_profileManagerFragment_to_newUserDialog"
|
||||
app:destination="@id/newUserDialogFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/newUserDialogFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.EditUserDialogFragment"
|
||||
android:label="NewUserDialogFragment" />
|
||||
|
||||
</navigation>
|
||||
|
||||
@@ -94,9 +94,6 @@
|
||||
|
||||
<string name="use_sync_core">مزامنة سرعة النواة</string>
|
||||
<string name="use_sync_core_description">قم بمزامنة سرعة النواة مع النسبة المئوية للسرعة القصوى لتحسين الأداء دون تغيير السرعة الفعلية للعبة.</string>
|
||||
<string name="use_lru_cache">تمكين ذاكرة التخزين المؤقتة LRU</string>
|
||||
<string name="use_lru_cache_description">قم بتمكين أو تعطيل ذاكرة التخزين المؤقتة الأقل استخدامًا (LRU)، مما يزيد من الأداء عن طريق توفير استخدام معالج وحدة المعالجة المركزية. قد تواجه بعض الألعاب مشكلات مع هذا الإعداد، لذا قم بتعطيله إذا لم يتم تشغيل اللعبة أو تعطلت بشكل عشوائي.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">تمكين محاكاة MMU المضيف</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">يعمل هذا التحسين على تسريع وصول الذاكرة بواسطة البرنامج الضيف. يؤدي تمكينه إلى إجراء عمليات قراءة/كتابة ذاكرة الضيف مباشرة في الذاكرة والاستفادة من MMU المضيف. يؤدي تعطيل هذا إلى إجبار جميع عمليات الوصول إلى الذاكرة على استخدام محاكاة MMU البرمجية.</string>
|
||||
<string name="debug_knobs">مقابض تصحيح الأخطاء</string>
|
||||
@@ -511,7 +508,10 @@
|
||||
<string name="disabled">معطل</string>
|
||||
<string name="vertex_input_dynamic_state">حالة ديناميكية لإدخال الرأس</string>
|
||||
<string name="vertex_input_dynamic_state_description">يتيح ميزة الحالة الديناميكية لإدخال الرأس لتحسين الجودة والأداء.</string>
|
||||
|
||||
<string name="provoking_vertex">الرأس المثير</string>
|
||||
<string name="provoking_vertex_description">يحسن الإضاءة ومعالجة الرؤوس في بعض الألعاب. مدعوم فقط على وحدات معالجة الرسومات Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">فهرسة الوصف</string>
|
||||
<string name="descriptor_indexing_description">يحسن معالجة النسيج والمخزن المؤقت، بالإضافة إلى طبقة الترجمة Maxwell. مدعوم من بعض وحدات معالجة الرسومات Vulkan 1.1 وجميع وحدات معالجة الرسومات Vulkan 1.2+.</string>
|
||||
<string name="sample_shading_fraction">تظليل العينة</string>
|
||||
<string name="sample_shading_fraction_description">يسمح هذا الخيار بتنفيذ مُظلل الأجزاء لكل عينة في جزء متعدد العينات بدلاً من تنفيذه مرة واحدة لكل جزء. يُحسّن هذا من جودة الرسومات على حساب بعض الأداء.</string>
|
||||
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">مزامنة سرعة النواة</string>
|
||||
<string name="use_sync_core_description">خێرایی تیکەکانی ناوک ڕێکبخە لەگەڵ ڕێژەی خێرایی بەرزترین بۆ باشترکردنی کارایی بەبێ گۆڕینی خێرایی ڕاستەقینەی یارییەکە.</string>
|
||||
<string name="use_lru_cache">تمكين ذاكرة التخزين المؤقت LRU</string>
|
||||
<string name="use_lru_cache_description">چالاک یان ناچالاککردنی کاشەی LRU، کارایی باشتر دەکات بە هەڵگرتنی بەکارهێنانی پرۆسەی CPU. هەندێک یاری کێشەی لەگەڵ هەیە، بەتایبەتی TotK 1.2.1، بۆیە بیخەوێنە ئەگەر یاریەکە نەگەڕێت یان بە هەڕەمەکی بشکێت.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">چالاککردنی میمیکردنی MMU میواندە</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">ئەم باشکردنە خێرایی دەستکەوتنی بیرگە لەلایەن پرۆگرامی میوانەکە زیاد دەکات. چالاککردنی وای لێدەکات کە خوێندنەوە/نووسینەکانی بیرگەی میوانەکە ڕاستەوخۆ لە بیرگە ئەنجام بدرێت و میمیکردنی MMU میواندە بەکاربهێنێت. ناچالاککردنی ئەمە هەموو دەستکەوتنەکانی بیرگە ڕەت دەکاتەوە لە بەکارهێنانی میمیکردنی MMU نەرمەکاڵا.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -368,7 +365,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">کۆمپایلکردنی شادەرەکان بەشێوەی ناڕێک. ئەمە ڕەنگە کەمکردنەوەی ستەتەر بکات بەڵام لەوانەیە هەڵەش دروست بکات.</string>
|
||||
<string name="dyna_state">الحالة الديناميكية الممتدة</string>
|
||||
<string name="disabled">ناچالاک</string>
|
||||
|
||||
<string name="provoking_vertex">الرأس المثير</string>
|
||||
<string name="provoking_vertex_description">يحسن الإضاءة ومعالجة الرؤوس في بعض الألعاب. مدعوم فقط على وحدات معالجة الرسومات التي تدعم فولكان 1.0+.</string>
|
||||
<string name="descriptor_indexing">فهرسة الواصفات</string>
|
||||
<string name="descriptor_indexing_description">يحسن معالجة القوام والمخازن المؤقتة، بالإضافة إلى طبقة ترجمة ماكسويل. مدعوم من قبل بعض وحدات معالجة الرسومات التي تدعم فولكان 1.1 وجميع وحدات معالجة الرسومات التي تدعم فولكان 1.2+.</string>
|
||||
<string name="display">پیشاندان</string>
|
||||
|
||||
<string name="renderer_screen_layout">ڕێکخستنی شاشە</string>
|
||||
|
||||
@@ -90,9 +90,6 @@
|
||||
|
||||
<string name="use_sync_core">Synchronizovat rychlost jádra</string>
|
||||
<string name="use_sync_core_description">Synchronizuje rychlost jádra s nastaveným limitem rychlosti, aby se zlepšil výkon bez zrychlení samotné hry.</string>
|
||||
<string name="use_lru_cache">Zapnout mezipaměť LRU</string>
|
||||
<string name="use_lru_cache_description">Zapne, nebo vypne mezipaměť LRU (Least Recently Used). Ta zvyšuje výkon tím, že šetří využití procesoru. Některé hry s tímto mohou mít problém, takže pokud se hra nespustí, nebo náhodně padá, tuto volbu vypněte.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Povolit emulaci hostitelské MMU</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Tato optimalizace zrychluje hostovanému programu přístupy do paměti. Zapnutím této funkce se čtení i zápisy do paměti provádějí přímo a využívají hostitelskou MMU. Při vypnutí musí všechny přístupy do paměti použít softwarovou emulaci MMU. </string>
|
||||
<string name="debug_knobs">Ladící parametry</string>
|
||||
@@ -493,7 +490,10 @@
|
||||
<string name="disabled">Vypnuto</string>
|
||||
<string name="vertex_input_dynamic_state">Dynamický stav vstupu vrcholů (Vertex Input)</string>
|
||||
<string name="vertex_input_dynamic_state_description">Aktivuje funkci dynamického stavu vstupu vrcholů (Vertex Input Dynamic State) pro lepší kvalitu a výkon.</string>
|
||||
|
||||
<string name="provoking_vertex">Určující vrchol</string>
|
||||
<string name="provoking_vertex_description">Zlepšuje osvětlení a zpracování vrcholů v některých hrách. Podporováno pouze na GPU s API Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indexování deskriptorů</string>
|
||||
<string name="descriptor_indexing_description">Zlepšuje zpracování textur a bufferů, stejně jako překladovou vrstvu architektury Maxwell. Podporováno některými GPU s API Vulkan 1.1 a všemi GPU s API Vulkan 1.2+.</string>
|
||||
<string name="display">Zobrazení</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientace</string>
|
||||
|
||||
@@ -90,9 +90,6 @@
|
||||
|
||||
<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="use_lru_cache">LRU-Cache aktivieren</string>
|
||||
<string name="use_lru_cache_description">Aktivieren oder deaktivieren Sie den LRU-Cache, um die Leistung durch Einsparung von CPU-Prozessorauslastung zu verbessern. Einige Spiele haben Probleme damit, insbesondere TotK 1.2.1, deaktivieren Sie es also, wenn das Spiel nicht startet oder zufällig abstürzt.</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>
|
||||
@@ -491,7 +488,10 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
|
||||
<string name="disabled">Deaktiviert</string>
|
||||
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
|
||||
<string name="vertex_input_dynamic_state_description">Aktiviert die Funktion \"Vertex Input Dynamic State\" für bessere Qualität und Leistung.</string>
|
||||
|
||||
<string name="provoking_vertex">Provokanter Vertex</string>
|
||||
<string name="provoking_vertex_description">Verbessert die Beleuchtung und die Vertex-Verarbeitung in einigen Spielen. Wird nur von GPUs mit Vulkan 1.0+ unterstützt.</string>
|
||||
<string name="descriptor_indexing">Deskriptor-Indizierung</string>
|
||||
<string name="descriptor_indexing_description">Verbessert die Textur- und Puffer-Verarbeitung sowie die Maxwell-Übersetzungsschicht. Wird von einigen Vulkan 1.1-GPUs und allen Vulkan 1.2+-GPUs unterstützt.</string>
|
||||
<string name="sample_shading_fraction">Sample Shading</string>
|
||||
<string name="sample_shading_fraction_description">Ermöglicht es dem Fragment-Shader, in einem Multisample-Fragment pro Sample anstatt einmal pro Fragment ausgeführt zu werden. Verbessert die Grafikqualität auf Kosten der Leistung.</string>
|
||||
|
||||
|
||||
@@ -75,9 +75,6 @@
|
||||
|
||||
<string name="use_sync_core">Sincronizar la velocidad del núcleo</string>
|
||||
<string name="use_sync_core_description">Sincroniza la velocidad del núcleo con el porcentaje máximo de velocidad para mejorar el rendimiento sin alterar la velocidad real del juego.</string>
|
||||
<string name="use_lru_cache">Habilitar caché LRU</string>
|
||||
<string name="use_lru_cache_description">Habilite o deshabilite la caché menos utilizada recientemente (LRU), aumentando el rendimiento al ahorrar el uso del proceso de la CPU. Algunos juegos pueden ver problemas con esta configuración, así que desactívela si el juego no arranca o se bloquea aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Habilitar emulación de MMU del anfitrión</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta optimización acelera el acceso a la memoria del programa invitado. Al habilitarla, las lecturas y escrituras de la memoria del invitado se realizan directamente en la memoria y utilizan la MMU del anfitrión. Al deshabilitarla, todos los accesos a la memoria utilizan el software de emulación de la MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -425,7 +422,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Compila los shaders de forma asíncrona. Esto puede reducir los tirones, pero también puede introducir errores gráficos.</string>
|
||||
<string name="dyna_state">Estado dinámico extendido</string>
|
||||
<string name="disabled">Desactivado</string>
|
||||
|
||||
<string name="provoking_vertex">Vértice provocante</string>
|
||||
<string name="provoking_vertex_description">Mejora la iluminación y el manejo de vértices en ciertos juegos. Solo es compatible con las GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indexación del descriptor</string>
|
||||
<string name="descriptor_indexing_description">Mejora la textura y el manejo del búfer, así como la capa de traducción Maxwell. Compatible con algunas GPU Vulkan 1.1 y todas las GPU Vulkan 1.2+.</string>
|
||||
<string name="display">Pantalla</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientación</string>
|
||||
|
||||
@@ -60,8 +60,6 @@
|
||||
<string name="sync_memory_operations_description">اطمینان از سازگاری دادهها بین عملیات محاسباتی و حافظه. این گزینه ممکن است مشکلات برخی بازیها را رفع کند، اما در برخی موارد ممکن است عملکرد را کاهش دهد. به نظر میرسد بازیهای با Unreal Engine 4 بیشترین تأثیر را داشته باشند.</string>
|
||||
<string name="buffer_reorder_disable">غیرفعال کردن مرتبسازی مجدد بافر</string>
|
||||
<string name="buffer_reorder_disable_description">در صورت انتخاب، مرتبسازی مجدد آپلودهای حافظه نگاشتشده غیرفعال میشود که امکان ارتباط آپلودها با ترسیمات خاص را فراهم میکند. ممکن است در برخی موارد عملکرد را کاهش دهد.</string>
|
||||
<string name="use_lru_cache">فعالسازی حافظه نهان LRU</string>
|
||||
<string name="use_lru_cache_description">حافظه پنهان LRU را فعال یا غیرفعال کنید تا با کاهش استفاده از پردازنده، عملکرد بهبود یابد. برخی بازیها مانند TotK 1.2.1 با این ویژگی مشکل دارند، در صورت عدم راهاندازی یا قطعی تصادفی بازی، آن را غیرفعال کنید.</string>
|
||||
<string name="dyna_state">حالت پویای گسترده</string>
|
||||
<string name="dyna_state_description">تعداد قابلیتهایی که میتوان در حالت Extended Dynamic State استفاده کرد را کنترل میکند. اعداد بالاتر قابلیتهای بیشتری را فعال کرده و میتوانند عملکرد را افزایش دهند، اما ممکن است با برخی درایورها و فروشندگان مشکلاتی ایجاد کنند. مقدار پیشفرض بسته به سیستم و قابلیتهای سختافزاری شما ممکن است متفاوت باشد. این مقدار را میتوان تغییر داد تا زمانی که پایداری و کیفیت بصری بهتری حاصل شود.</string>
|
||||
<string name="disabled">غیرفعال</string>
|
||||
@@ -73,7 +71,10 @@
|
||||
<string name="uninstall_firmware_description">حذف فرمور آن را از دستگاه پاک میکند و ممکن است بر سازگاری بازیها تأثیر بگذارد</string>
|
||||
<string name="firmware_uninstalling">در حال حذف فرمور</string>
|
||||
<string name="firmware_uninstalled_success">فرمور با موفقیت حذف شد</string>
|
||||
|
||||
<string name="provoking_vertex">رأس تحریککننده</string>
|
||||
<string name="provoking_vertex_description">نورپردازی و مدیریت رأس را در برخی بازیها بهبود میبخشد. فقط در کارتهای گرافیک با پشتیبانی از ولکان 1.0+ قابل استفاده است</string>
|
||||
<string name="descriptor_indexing">نمایهسازی توصیفگر</string>
|
||||
<string name="descriptor_indexing_description">مدیریت بافتها و بافرها و همچنین لایه ترجمه ماکسول را بهبود میبخشد. توسط برخی کارتهای گرافیک ولکان 1.1 و تمام کارتهای گرافیک ولکان 1.2+ پشتیبانی میشود</string>
|
||||
<string name="fast_gpu_time">فاکتور اورکلاک GPU</string>
|
||||
<string name="fast_gpu_time_description">برای حداکثر عملکرد از 128 و برای حداکثر وفاداری گرافیکی از 512 استفاده کنید.</string>
|
||||
<string name="fast_gpu_medium">متوسط (256)</string>
|
||||
|
||||
@@ -75,9 +75,6 @@
|
||||
|
||||
<string name="use_sync_core">Synchroniser la vitesse du cœur</string>
|
||||
<string name="use_sync_core_description">Synchronise la vitesse du cœur avec le pourcentage de vitesse maximal pour améliorer les performances sans modifier la vitesse réelle du jeu.</string>
|
||||
<string name="use_lru_cache">Activer le cache LRU</string>
|
||||
<string name="use_lru_cache_description">Active ou désactive le cache LRU (Least Recently Used) pour améliorer les performances en réduisant l’utilisation du processeur. Certains jeux peuvent rencontrer des problèmes avec ce réglage ; désactivez-le si le jeu ne démarre pas ou plante de manière aléatoire.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Activer l\'émulation de la MMU hôte</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Cette optimisation accélère les accès mémoire par le programme invité. L\'activer entraîne que les lectures/écritures mémoire de l\'invité sont effectuées directement en mémoire et utilisent la MMU de l\'hôte. Désactiver cela force tous les accès mémoire à utiliser l\'émulation logicielle de la MMU.</string>
|
||||
<string name="debug_knobs">Boutons de débogage</string>
|
||||
@@ -441,7 +438,10 @@
|
||||
<string name="disabled">Désactivé</string>
|
||||
<string name="vertex_input_dynamic_state">État dynamique d\'entrée de sommet</string>
|
||||
<string name="vertex_input_dynamic_state_description">Active la fonctionnalité d\'état dynamique des entrées de sommets pour une meilleure qualité et de meilleures performances.</string>
|
||||
|
||||
<string name="provoking_vertex">Provoque des Vertex</string>
|
||||
<string name="provoking_vertex_description">Améliore l`éclairage et la gestion des vertex dans certains jeux. Pris en charge uniquement par les GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indexation des descripteurs</string>
|
||||
<string name="descriptor_indexing_description">Améliore la gestion des textures et des tampons, ainsi que la couche de traduction Maxwell. Pris en charge par certains GPU Vulkan 1.1 et tous les GPU Vulkan 1.2+.</string>
|
||||
<string name="display">Affichage</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientation</string>
|
||||
|
||||
@@ -62,9 +62,6 @@
|
||||
|
||||
<string name="use_sync_core">סנכרון מהירות ליבה</string>
|
||||
<string name="use_sync_core_description">סנכרן את מהירות הליבה לאחוז המהירות המרבי כדי לשפר ביצועים מבלי לשנות את מהירות המשחק בפועל.</string>
|
||||
<string name="use_lru_cache">הפעלת מטמון LRU</string>
|
||||
<string name="use_lru_cache_description">הפעל או השבת מטמון LRU לשיפור ביצועים על ידי חיסכון בשימוש במעבד. לחלק מהמשחקים כמו TotK 1.2.1 יש בעיות - השבת אם המשחק לא עולה או קורס באקראי.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">הפעל אמולציית MMU מארח</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">אופטימיזציה זו מאיצה את גישת הזיכרון על ידי התוכנית האורחת. הפעלתה גורמת לכך שפעולות קריאה/כתיבה לזיכרון האורח מתבצעות ישירות לזיכרון ומשתמשות ב-MMU של המארח. השבתת זאת מאלצת את כל גישות הזיכרון להשתמש באמולציית MMU תוכנתית.</string>
|
||||
<string name="debug_knobs_description">לשימוש בפיתוח בלבד.</string>
|
||||
@@ -399,7 +396,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">מקמפל שיידרים באופן אסינכרוני. זה עשוי להפחית קפיצות אך גם עלול לגרום לתקלות גרפיות.</string>
|
||||
<string name="dyna_state">מצב דינמי מורחב</string>
|
||||
<string name="disabled">מושבת</string>
|
||||
|
||||
<string name="provoking_vertex">קודקוד מעורר</string>
|
||||
<string name="provoking_vertex_description">משפר תאורה וטיפול בקודקודים במשחקים מסוימים. נתמך רק בכרטיסי מסך עם Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">אינדוקס תיאורים</string>
|
||||
<string name="descriptor_indexing_description">משפר טיפול במרקמים ומאגרים, כמו גם בשכבת התרגום של Maxwell. נתמך בחלק מכרטיסי ה-Vulkan 1.1 ובכל כרטיסי ה-Vulkan 1.2+.</string>
|
||||
<string name="display">תצוגה</string>
|
||||
|
||||
<string name="renderer_screen_layout">כיוון</string>
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">Magsebesség szinkronizálása</string>
|
||||
<string name="use_sync_core_description">A mag sebességének szinkronizálása a maximális sebesség százalékával a teljesítmény javítása érdekében a játék tényleges sebességének megváltoztatása nélkül.</string>
|
||||
<string name="use_lru_cache">LRU gyorsítótár engedélyezése</string>
|
||||
<string name="use_lru_cache_description">Engedélyezi vagy letiltja az LRU gyorsítótárat, növelve a teljesítményt a CPU használat csökkentésével. Néhány játéknak (különösen a TotK 1.2.1-nek) problémája lehet vele - tiltsa le, ha a játék nem indul el vagy véletlenszerűen összeomlik.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Gazda MMU emuláció engedélyezése</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ez az optimalizáció gyorsítja a vendégprogram memória-hozzáférését. Engedélyezése esetén a vendég memóriaolvasási/írási műveletei közvetlenül a memóriában történnek, és kihasználják a gazda MMU-ját. Letiltás esetén minden memória-hozzáférés a szoftveres MMU emulációt használja.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -387,8 +384,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">A shaderek aszinkron fordítása. Csökkentheti a belassulásokat, de hibákat is okozhat.</string>
|
||||
<string name="dyna_state">Kiterjesztett Dinamikus Állapot</string>
|
||||
<string name="disabled">Letiltva</string>
|
||||
|
||||
|
||||
<string name="provoking_vertex">Provokáló csúcs</string>
|
||||
<string name="provoking_vertex_description">Javítja a világítást és a csúcskezelést bizonyos játékokban. Csak Vulkan 1.0+ GPU-kon támogatott.</string>
|
||||
<string name="descriptor_indexing">Deskriptor Indexelés</string>
|
||||
<string name="descriptor_indexing_description">Javítja a textúrák és pufferek kezelését, valamint a Maxwell fordítási réteget. Néhány Vulkan 1.1 GPU és minden Vulkan 1.2+ GPU támogatja.</string>
|
||||
<string name="display">Kijelző</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientáció</string>
|
||||
|
||||
@@ -77,9 +77,6 @@
|
||||
|
||||
<string name="use_sync_core">Sinkronisasi Kecepatan Inti</string>
|
||||
<string name="use_sync_core_description">Sinkronkan kecepatan inti dengan persentase kecepatan maksimum untuk meningkatkan performa tanpa mengubah kecepatan sebenarnya dari permainan.</string>
|
||||
<string name="use_lru_cache">Aktifkan LRU Cache</string>
|
||||
<string name="use_lru_cache_description">Aktifkan atau nonaktifkan cache LRU untuk meningkatkan performa dengan mengurangi penggunaan proses CPU. Beberapa game seperti TotK 1.2.1 memiliki masalah - nonaktifkan jika game tidak mau berjalan atau crash acak.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Aktifkan Emulasi MMU Host</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Optimasi ini mempercepat akses memori oleh program tamu. Mengaktifkannya menyebabkan pembacaan/penulisan memori tamu dilakukan langsung ke memori dan memanfaatkan MMU Host. Menonaktifkan ini memaksa semua akses memori menggunakan Emulasi MMU Perangkat Lunak.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -419,7 +416,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Kompilasi shader secara asinkron. Ini dapat mengurangi lag tetapi juga dapat menyebabkan glitch.</string>
|
||||
<string name="dyna_state">Status Dinamis Ekstensi</string>
|
||||
<string name="disabled">Dinonaktifkan</string>
|
||||
|
||||
<string name="provoking_vertex">Vertex Provokasi</string>
|
||||
<string name="provoking_vertex_description">Meningkatkan pencahayaan dan penanganan vertex di beberapa game. Hanya didukung di GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Pengindeks Deskriptor</string>
|
||||
<string name="descriptor_indexing_description">Meningkatkan penanganan tekstur dan buffer, serta lapisan terjemahan Maxwell. Didukung oleh beberapa GPU Vulkan 1.1 dan semua GPU Vulkan 1.2+.</string>
|
||||
<string name="display">Tampilan</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientasi</string>
|
||||
|
||||
@@ -75,9 +75,6 @@
|
||||
|
||||
<string name="use_sync_core">Sincronizza velocità core</string>
|
||||
<string name="use_sync_core_description">Sincronizza la velocità del core con la percentuale massima di velocità per migliorare le prestazioni senza alterare la velocità effettiva del gioco.</string>
|
||||
<string name="use_lru_cache">Abilita cache LRU</string>
|
||||
<string name="use_lru_cache_description">Abilita o disabilita la cache LRU per migliorare le prestazioni riducendo l\'uso della CPU. Alcuni giochi come TotK 1.2.1 hanno problemi - disabilitalo se il gioco non si avvia o crasha casualmente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Abilita l\'emulazione della MMU nell\'host</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Questa ottimizzazione accelera gli accessi alla memoria da parte del programma guest. Abilitandola, le letture/scritture della memoria guest vengono eseguite direttamente in memoria e sfruttano la MMU host. Disabilitandola, tutti gli accessi alla memoria sono costretti a utilizzare l\'emulazione software della MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -426,7 +423,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Compila gli shader in modo asincrono. Può ridurre gli scatti, ma può anche causare glitch grafici.</string>
|
||||
<string name="dyna_state">Stato dinamico esteso</string>
|
||||
<string name="disabled">Disabilitato</string>
|
||||
|
||||
<string name="provoking_vertex">Vertice provocante</string>
|
||||
<string name="provoking_vertex_description">Migliora illuminazione e gestione dei vertici in alcuni giochi. Supportato solo su GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indicizzazione descrittori</string>
|
||||
<string name="descriptor_indexing_description">Migliora la gestione di texture e buffer, nonché il livello di traduzione Maxwell. Supportato da alcune GPU Vulkan 1.1 e tutte le GPU Vulkan 1.2+.</string>
|
||||
<string name="display">Schermo</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientamento</string>
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">コア速度の同期</string>
|
||||
<string name="use_sync_core_description">コアの速度を最大速度パーセンテージに同期させ、ゲームの実際の速度を変えずにパフォーマンスを向上させます。</string>
|
||||
<string name="use_lru_cache">LRUキャッシュを有効化</string>
|
||||
<string name="use_lru_cache_description">LRUキャッシュを有効/無効にし、CPUプロセスの使用を節約してパフォーマンスを向上させます。TotK 1.2.1など一部のゲームで問題が発生する可能性があるため、ゲームが起動しない場合やランダムにクラッシュする場合は無効にしてください。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">ホストMMUエミュレーションを有効化</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">この最適化により、ゲストプログラムによるメモリアクセスが高速化されます。有効にすると、ゲストのメモリ読み書きが直接メモリ内で実行され、ホストのMMUを利用します。無効にすると、すべてのメモリアクセスでソフトウェアMMUエミュレーションが使用されます。</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -385,7 +382,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">シェーダーを非同期でコンパイルします。これによりカクつきが減る可能性がありますが、グラフィックの不具合が発生する場合もあります。</string>
|
||||
<string name="dyna_state">拡張ダイナミックステート</string>
|
||||
<string name="disabled">無効</string>
|
||||
|
||||
<string name="provoking_vertex">プロボーキング頂点</string>
|
||||
<string name="provoking_vertex_description">特定のゲームで照明と頂点処理を改善します。Vulkan 1.0+ GPUでのみサポートされています。</string>
|
||||
<string name="descriptor_indexing">ディスクリプタインデキシング</string>
|
||||
<string name="descriptor_indexing_description">テクスチャとバッファの処理、およびMaxwell翻訳レイヤーを改善します。一部のVulkan 1.1 GPUとすべてのVulkan 1.2+ GPUでサポートされています。</string>
|
||||
<string name="display">ディスプレイ</string>
|
||||
|
||||
<string name="renderer_screen_layout">画面の向き</string>
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">코어 속도 동기화</string>
|
||||
<string name="use_sync_core_description">코어 틱 속도를 최대 속도 백분율과 동기화하여 게임의 실제 속도를 변경하지 않고 성능을 향상시킵니다.</string>
|
||||
<string name="use_lru_cache">LRU 캐시 사용</string>
|
||||
<string name="use_lru_cache_description">LRU 캐시를 활성화 또는 비활성화하여 CPU 프로세스 사용을 절약하고 성능을 향상시킵니다. TotK 1.2.1을 포함한 일부 게임에서 문제가 발생할 수 있으므로 게임이 부팅되지 않거나 무작위로 충돌하는 경우 비활성화하세요.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">호스트 MMU 에뮬레이션 사용</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">이 최적화는 게스트 프로그램의 메모리 접근 속도를 높입니다. 활성화하면 게스트의 메모리 읽기/쓰기가 메모리에서 직접 수행되고 호스트의 MMU를 활용합니다. 비활성화하면 모든 메모리 접근에 소프트웨어 MMU 에뮬레이션을 사용하게 됩니다.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -385,7 +382,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">셰이더를 비동기적으로 컴파일합니다. 이로 인해 끊김 현상이 줄어들 수 있지만 그래픽 오류가 발생할 수도 있습니다.</string>
|
||||
<string name="dyna_state">확장 동적 상태</string>
|
||||
<string name="disabled">비활성화됨</string>
|
||||
|
||||
<string name="provoking_vertex">프로보킹 버텍스</string>
|
||||
<string name="provoking_vertex_description">일부 게임에서 조명과 버텍스 처리를 개선합니다. Vulkan 1.0+ GPU에서만 지원됩니다.</string>
|
||||
<string name="descriptor_indexing">디스크립터 인덱싱</string>
|
||||
<string name="descriptor_indexing_description">텍스처 및 버퍼 처리와 Maxwell 변환 레이어를 개선합니다. 일부 Vulkan 1.1 GPU 및 모든 Vulkan 1.2+ GPU에서 지원됩니다.</string>
|
||||
<string name="display">디스플레이</string>
|
||||
|
||||
<string name="renderer_screen_layout">화면 방향</string>
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">Synkroniser kjernespeed</string>
|
||||
<string name="use_sync_core_description">Synkroniser kjernens hastighet med maksimal hastighetsprosent for å forbedre ytelsen uten å endre spillets faktiske hastighet.</string>
|
||||
<string name="use_lru_cache">Aktiver LRU-mellomlager</string>
|
||||
<string name="use_lru_cache_description">Aktiver eller deaktiver LRU-mellomlager for å forbedre ytelsen ved å spare CPU-prosessorbruk. Noen spill som TotK 1.2.1 har problemer med dette - deaktiver hvis spillet ikke starter eller krasjer tilfeldig.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Aktiver verts-MMU-emulering</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Denne optimaliseringen fremskynder minnetilgang av gjesteprogrammet. Hvis aktivert, utføres gjestens minnelesing/skriving direkte i minnet og bruker vertens MMU. Deaktivering tvinger alle minnetilganger til å bruke programvarebasert MMU-emulering.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -368,7 +365,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Kompilerer shadere asynkront. Dette kan redusere rykk, men kan også forårsake grafiske feil.</string>
|
||||
<string name="dyna_state">Utvidet dynamisk tilstand</string>
|
||||
<string name="disabled">Deaktivert</string>
|
||||
|
||||
<string name="provoking_vertex">Provoserende vertex</string>
|
||||
<string name="provoking_vertex_description">Forbedrer belysning og vertexhåndtering i enkelte spill. Støttes kun på Vulkan 1.0+ GPU-er.</string>
|
||||
<string name="descriptor_indexing">Beskrivelsesindeksering</string>
|
||||
<string name="descriptor_indexing_description">Forbedrer tekstur- og bufferhåndtering, samt Maxwell-oversettelseslaget. Støttes av noen Vulkan 1.1 GPU-er og alle Vulkan 1.2+ GPU-er.</string>
|
||||
<string name="display">Skjerm</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientering</string>
|
||||
|
||||
@@ -90,9 +90,6 @@
|
||||
|
||||
<string name="use_sync_core">Synchronizuj prędkość rdzenia</string>
|
||||
<string name="use_sync_core_description">Synchronizuje prędkość rdzenia z maksymalnym procentem prędkości, aby poprawić wydajność bez zmiany rzeczywistej prędkości gry.</string>
|
||||
<string name="use_lru_cache">Włącz pamięć podręczną LRU</string>
|
||||
<string name="use_lru_cache_description">Włącz lub wyłącz pamięć podręczną LRU, aby poprawić wydajność poprzez zmniejszenie użycia procesora. Niektóre gry, takie jak TotK 1.2.1, mogą mieć problemy - wyłącz, jeśli gra się nie uruchamia lub losowo zawiesza.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Włącz emulację MMU hosta</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ta optymalizacja przyspiesza dostęp do pamięci przez program gościa. Włączenie powoduje, że odczyty/zapisy pamięci gościa są wykonywane bezpośrednio w pamięci i wykorzystują MMU hosta. Wyłączenie wymusza użycie programowej emulacji MMU dla wszystkich dostępów do pamięci.</string>
|
||||
<string name="debug_knobs">Przełączniki debugowania</string>
|
||||
@@ -493,7 +490,10 @@
|
||||
<string name="disabled">Wyłączone</string>
|
||||
<string name="vertex_input_dynamic_state">Dynamiczny stan wejścia wierzchołków</string>
|
||||
<string name="vertex_input_dynamic_state_description">Włącza funkcję dynamicznego stanu wejścia wierzchołków, poprawiając jakość i wydajność.</string>
|
||||
|
||||
<string name="provoking_vertex">Wierzchołek prowokujący</string>
|
||||
<string name="provoking_vertex_description">Poprawia oświetlenie i obsługę wierzchołków w niektórych grach. Obsługiwane tylko przez GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indeksowanie deskryptorów</string>
|
||||
<string name="descriptor_indexing_description">Poprawia obsługę tekstur i buforów oraz warstwę tłumaczenia Maxwell. Obsługiwane przez niektóre GPU Vulkan 1.1 i wszystkie GPU Vulkan 1.2+.</string>
|
||||
<string name="sample_shading_fraction">Cieniowanie próbkowane</string>
|
||||
<string name="sample_shading_fraction_description">Pozwala uruchamiać shader fragmentów dla każdej próbki w wielopróbkowanym fragmencie zamiast raz na fragment. Poprawia jakość grafiki kosztem części wydajności.</string>
|
||||
|
||||
|
||||
@@ -84,9 +84,6 @@
|
||||
|
||||
<string name="use_sync_core">Sincronizar Velocidade do Núcleo</string>
|
||||
<string name="use_sync_core_description">Aumenta a velocidade do ciclo do núcleo até o máximo para melhorar o desempenho, mantendo a velocidade real do jogo inalterada.</string>
|
||||
<string name="use_lru_cache">Ativar Cache LRU</string>
|
||||
<string name="use_lru_cache_description">O cache Least Recently Used (LRU, ou Itens Menos Usados) aumenta o desempenho, economizando o uso do processador. Alguns jogos podem apresentar problemas com esta configuração. Desative-a caso o jogo não inicie ou trave aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação da MMU do Sistema</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera o acesso à memória pelo programa. Ao ativá-la, leituras e gravações são feitas diretamente na memória, usando a MMU (Unidade de Gerenciamento de Memória) do sistema. Ao desativar, todos os acessos passam a utilizar a Emulação de MMU por Software.</string>
|
||||
<string name="debug_knobs">Controles de Depuração</string>
|
||||
@@ -476,7 +473,10 @@
|
||||
<string name="disabled">Desativado</string>
|
||||
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
|
||||
<string name="vertex_input_dynamic_state_description">Ativa o recurso de vertex input dynamic state para melhor qualidade e desempenho.</string>
|
||||
|
||||
<string name="provoking_vertex">Provoking Vertex</string>
|
||||
<string name="provoking_vertex_description">Vértice Provocante: Melhora a iluminação e o processamento de vértices em certos jogos. Suportado apenas em GPUs com Vulkan 1.0 ou superior.</string>
|
||||
<string name="descriptor_indexing">Descriptor Indexing</string>
|
||||
<string name="descriptor_indexing_description">Indexação de Descritores: Melhora o processamento de texturas e buffers, assim como a camada de tradução Maxwell. Suportado por algumas GPUs Vulkan 1.1 e todas as GPUs Vulkan 1.2 ou superiores.</string>
|
||||
<string name="display">Tela</string>
|
||||
|
||||
<string name="renderer_screen_layout">Orientação</string>
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">Sincronizar velocidade do núcleo</string>
|
||||
<string name="use_sync_core_description">Sincroniza a velocidade do núcleo com a percentagem máxima de velocidade para melhorar o desempenho sem alterar a velocidade real do jogo.</string>
|
||||
<string name="use_lru_cache">Ativar cache LRU</string>
|
||||
<string name="use_lru_cache_description">Ative ou desative a cache LRU para melhorar desempenho poupando uso do processador. Alguns jogos como TotK 1.2.1 têm problemas - desative se o jogo não iniciar ou falhar aleatoriamente.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Ativar Emulação de MMU do Anfitrião</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Esta otimização acelera os acessos à memória pelo programa convidado. Ativar faz com que as leituras/escritas de memória do convidado sejam efetuadas diretamente na memória e utilizem a MMU do Anfitrião. Desativar força todos os acessos à memória a usar a Emulação de MMU por Software.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -391,7 +388,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Compila shaders de forma assíncrona. Isto pode reduzir engasgadelas, mas também pode introduzir falhas gráficas.</string>
|
||||
<string name="dyna_state">Estado Dinâmico Estendido</string>
|
||||
<string name="disabled">Desativado</string>
|
||||
|
||||
<string name="provoking_vertex">Vértice provocante</string>
|
||||
<string name="provoking_vertex_description">Melhora a iluminação e o tratamento de vértices em certos jogos. Suportado apenas em GPUs Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Indexação de descritores</string>
|
||||
<string name="descriptor_indexing_description">Melhora o tratamento de texturas e buffers, assim como a camada de tradução Maxwell. Suportado por algumas GPUs Vulkan 1.1 e todas Vulkan 1.2+.</string>
|
||||
<string name="display">Ecrã</string>
|
||||
|
||||
<string name="renderer_screen_layout">Oriantação</string>
|
||||
|
||||
@@ -92,9 +92,6 @@
|
||||
|
||||
<string name="use_sync_core">Синхронизация скорости ядра</string>
|
||||
<string name="use_sync_core_description">Синхронизирует скорость ядра с максимальным процентом скорости для улучшения производительности без изменения фактической скорости игры.</string>
|
||||
<string name="use_lru_cache">Включить LRU-кеш</string>
|
||||
<string name="use_lru_cache_description">Включите или отключите кэш LRU (наименее недавно использованный), повышая производительность за счёт снижения нагрузки на ЦП. Некоторые игры могут работать с ним некорректно (например, TotK 1.2.1), поэтому отключите, если игра не запускается или случайно вылетает.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Включить эмуляцию MMU хоста</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Эта оптимизация ускоряет доступ к памяти гостевой программой. При включении операции чтения/записи памяти гостя выполняются напрямую в памяти с использованием MMU хоста. Отключение заставляет все обращения к памяти использовать программную эмуляцию MMU.</string>
|
||||
<string name="debug_knobs">Настройки отладки</string>
|
||||
@@ -503,7 +500,10 @@
|
||||
<string name="disabled">Отключено</string>
|
||||
<string name="vertex_input_dynamic_state">Динамическое состояние ввода вершин</string>
|
||||
<string name="vertex_input_dynamic_state_description">Включает функцию динамического состояния ввода вершин для повышения качества и производительности</string>
|
||||
|
||||
<string name="provoking_vertex">Определяющая вершина</string>
|
||||
<string name="provoking_vertex_description">Улучшает освещение и обработку вершин в некоторых играх. Поддерживается только ГПУ с Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Индексирование дескрипторов</string>
|
||||
<string name="descriptor_indexing_description">Улучшает обработку текстур и буферов, а также слой перевода Maxwell. Поддерживается некоторыми ГПУ Vulkan 1.1 и всеми ГПУ Vulkan 1.2+.</string>
|
||||
<string name="sample_shading_fraction">Сэмпловое затенение</string>
|
||||
<string name="sample_shading_fraction_description">Позволяет шейдеру фрагментов выполняться для каждого сэмпла в многосэмпловом фрагменте, а не один раз на фрагмент. Улучшает качество графики ценой некоторого падения производительности.</string>
|
||||
|
||||
|
||||
@@ -56,9 +56,6 @@
|
||||
|
||||
<string name="use_sync_core">Синхронизујте брзину језгре</string>
|
||||
<string name="use_sync_core_description">Синхронизујте језгро Тицк Брзина брзине максималне брзине да бисте побољшали перформансе без измене стварне брзине игре.</string>
|
||||
<string name="use_lru_cache">Омогући ЛРУ кеш</string>
|
||||
<string name="use_lru_cache_description">Омогућите или онемогућите најмање недавно коришћене (ЛРУ) кеш меморије, све веће перформансе штедећи употребу процеса ЦПУ-а. Неке игре са њом имају проблеме, посебно тотк 1.2.1, тако да онемогућите ако се игра не покрене или се руши насумично.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Омогући емулацију MMU домаћина</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ова оптимизација убрзава приступ меморији од стране гостујућег програма. Укључивање изазива да се читања/уписа меморије госта обављају директно у меморији и користе MMU домаћина. Искључивање присиљава све приступе меморији да користе софтверску емулацију MMU.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -390,7 +387,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Саставља схадера асинхроно. Ово може умањити затезаче, али могу такође да уведе пропусте.</string>
|
||||
<string name="dyna_state">Проширена динамичка држава</string>
|
||||
<string name="disabled">Искључено</string>
|
||||
|
||||
<string name="provoking_vertex">Провоцирајући врх</string>
|
||||
<string name="provoking_vertex_description">Побољшава осветљење и вертификат руковања у одређеним играма. Подржан само на Вулкану 1.0+ ГПУ-у.</string>
|
||||
<string name="descriptor_indexing">Индексирање дескриптора</string>
|
||||
<string name="descriptor_indexing_description">Побољшава текстуру и руковање међуспремника, као и преводилачки слој Маквелл. Подржани од стране неких Вулкана 1.1 ГПУ-а и сви Вулкан 1.2+ ГПУ.</string>
|
||||
<string name="display">Приказ</string>
|
||||
|
||||
<string name="renderer_screen_layout">Оријентација</string>
|
||||
|
||||
@@ -94,9 +94,6 @@
|
||||
|
||||
<string name="use_sync_core">Синхронізувати швидкість ядра</string>
|
||||
<string name="use_sync_core_description">Синхронізує швидкість ядра з максимальним відсотком швидкості для покращення продуктивності без зміни реальної швидкості гри.</string>
|
||||
<string name="use_lru_cache">Увімкнути LRU-кеш</string>
|
||||
<string name="use_lru_cache_description">Увімкніть або вимкніть кеш LRU (Least Recently Used) для покращення продуктивності шляхом зменшення навантаження на CPU. Деякі ігри (зокрема TotK 1.2.1) можуть працювати некоректно - вимкніть, якщо гра не запускається або раптово вилітає.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Увімкнути емуляцію MMU хоста</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Ця оптимізація пришвидшує доступ до пам\'яті гостьовою програмою. Увімкнення призводить до того, що читання/запис пам\'яті гостя виконуються безпосередньо в пам\'яті та використовують MMU хоста. Вимкнення змушує всі звернення до пам\'яті використовувати програмну емуляцію MMU.</string>
|
||||
<string name="debug_knobs">Зневаджувальні регулятори</string>
|
||||
@@ -507,7 +504,10 @@
|
||||
<string name="disabled">Вимкнено</string>
|
||||
<string name="vertex_input_dynamic_state">Динамічний стан введення вершин</string>
|
||||
<string name="vertex_input_dynamic_state_description">Вмикає можливість динамічного стану введення вершин для кращих якості й продуктивності.</string>
|
||||
|
||||
<string name="provoking_vertex">Провокативна вершина</string>
|
||||
<string name="provoking_vertex_description">Покращує освітлення та взаємодію з вершинами у деяких іграх. Лише для ГП з підтримкою Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Індексація дескрипторів</string>
|
||||
<string name="descriptor_indexing_description">Покращує обробку текстур та буферів, а також шар перекладу Maxwell. Підтримується деякими GPU Vulkan 1.1 та всіма GPU Vulkan 1.2+.</string>
|
||||
<string name="sample_shading_fraction">Простий шейдинг</string>
|
||||
<string name="sample_shading_fraction_description">Дозволяє виконувати фрагмент шейдера для кожного зразка в багатозразковому фрагменті замість одного разу для кожного фрагмента. Покращує якість графікі ціною втрати продуктивності.</string>
|
||||
|
||||
|
||||
@@ -58,9 +58,6 @@
|
||||
|
||||
<string name="use_sync_core">Đồng bộ tốc độ lõi</string>
|
||||
<string name="use_sync_core_description">Đồng bộ tốc độ lõi với tỷ lệ phần trăm tốc độ tối đa để cải thiện hiệu suất mà không làm thay đổi tốc độ thực tế của trò chơi.</string>
|
||||
<string name="use_lru_cache">Bật bộ nhớ đệm LRU</string>
|
||||
<string name="use_lru_cache_description">Bật hoặc tắt bộ nhớ đệm LRU để cải thiện hiệu suất bằng cách tiết kiệm quy trình sử dụng CPU. Một số trò chơi như TotK 1.2.1 có vấn đề - hãy tắt nếu trò chơi không khởi động hoặc bị treo ngẫu nhiên.</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">Bật giả lập MMU Máy chủ</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">Tối ưu hóa này tăng tốc độ truy cập bộ nhớ của chương trình khách. Bật nó lên khiến các thao tác đọc/ghi bộ nhớ khách được thực hiện trực tiếp vào bộ nhớ và sử dụng MMU của Máy chủ. Tắt tính năng này buộc tất cả quyền truy cập bộ nhớ phải sử dụng Giả lập MMU Phần mềm.</string>
|
||||
<!-- NVDEC Emulation -->
|
||||
@@ -366,7 +363,10 @@
|
||||
<string name="renderer_asynchronous_shaders_description">Biên dịch shader không đồng bộ. Điều này có thể giảm giật lag nhưng cũng có thể gây ra lỗi đồ họa.</string>
|
||||
<string name="dyna_state">Trạng thái động mở rộng</string>
|
||||
<string name="disabled">Đã tắt</string>
|
||||
|
||||
<string name="provoking_vertex">Đỉnh kích hoạt</string>
|
||||
<string name="provoking_vertex_description">Cải thiện ánh sáng và xử lý đỉnh trong một số trò chơi. Chỉ được hỗ trợ trên GPU Vulkan 1.0+.</string>
|
||||
<string name="descriptor_indexing">Lập chỉ mục bộ mô tả</string>
|
||||
<string name="descriptor_indexing_description">Cải thiện xử lý kết cấu và bộ đệm, cũng như lớp dịch Maxwell. Được hỗ trợ bởi một số GPU Vulkan 1.1 và tất cả GPU Vulkan 1.2+.</string>
|
||||
<string name="display">Hiển thị</string>
|
||||
|
||||
<string name="renderer_screen_layout">Hướng màn hình</string>
|
||||
|
||||
@@ -94,9 +94,6 @@
|
||||
|
||||
<string name="use_sync_core">同步核心速度</string>
|
||||
<string name="use_sync_core_description">将核心速度与最大速度百分比同步,在不改变游戏实际速度的情况下提高性能。</string>
|
||||
<string name="use_lru_cache">启用LRU缓存</string>
|
||||
<string name="use_lru_cache_description">启用或禁用LRU缓存,通过节省CPU进程使用来提高性能。某些游戏可能存在问题,特别是TotK 1.2.1,如果游戏无法启动或随机崩溃,请禁用此选项。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">启用主机 MMU 模拟</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">此优化可加速来宾程序的内存访问。启用后,来宾内存读取/写入将直接在内存中执行并利用主机的 MMU。禁用此功能将强制所有内存访问使用软件 MMU 模拟。</string>
|
||||
<string name="debug_knobs">调试开关</string>
|
||||
@@ -501,7 +498,10 @@
|
||||
<string name="disabled">已禁用</string>
|
||||
<string name="vertex_input_dynamic_state">顶点输入动态状态</string>
|
||||
<string name="vertex_input_dynamic_state_description">开启顶点输入动态状态功能来获得更好的质量和性能。</string>
|
||||
|
||||
<string name="provoking_vertex">引发顶点</string>
|
||||
<string name="provoking_vertex_description">改善某些游戏中的光照和顶点处理。仅支持Vulkan 1.0+ GPU。</string>
|
||||
<string name="descriptor_indexing">描述符索引</string>
|
||||
<string name="descriptor_indexing_description">改进纹理和缓冲区处理以及Maxwell转换层。部分Vulkan 1.1 GPU和所有Vulkan 1.2+ GPU支持。</string>
|
||||
<string name="sample_shading_fraction">采样着色</string>
|
||||
<string name="sample_shading_fraction_description">允许片段着色器在多采样片段中每个样本执行一次,而不是每个片段执行一次。以提高性能为代价改善图形质量。</string>
|
||||
|
||||
|
||||
@@ -90,9 +90,6 @@
|
||||
|
||||
<string name="use_sync_core">同步核心速度</string>
|
||||
<string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能。</string>
|
||||
<string name="use_lru_cache">啟用LRU快取</string>
|
||||
<string name="use_lru_cache_description">啟用或停用LRU快取,透過節省CPU進程使用率來提高效能。某些遊戲可能存在問題,特別是薩爾達傳說:王國之淚 v1.2.1(更新),如果遊戲無法啟動或隨機崩潰,請停用此選項。</string>
|
||||
|
||||
<string name="cpuopt_unsafe_host_mmu">啟用主機 MMU 模擬</string>
|
||||
<string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速來賓程式的記憶體存取。啟用後,來賓記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。</string>
|
||||
<string name="debug_knobs">偵錯開關</string>
|
||||
@@ -472,7 +469,10 @@
|
||||
<string name="disabled">已停用</string>
|
||||
<string name="vertex_input_dynamic_state">頂點輸入動態狀態</string>
|
||||
<string name="vertex_input_dynamic_state_description">啟用頂點輸入動態狀態以取得更佳的品質及性能</string>
|
||||
|
||||
<string name="provoking_vertex">引發頂點</string>
|
||||
<string name="provoking_vertex_description">改善某些遊戲中的光照和頂點處理。僅支援Vulkan 1.0+ GPU。</string>
|
||||
<string name="descriptor_indexing">描述符索引</string>
|
||||
<string name="descriptor_indexing_description">改進紋理和緩衝區處理以及Maxwell轉換層。部分Vulkan 1.1 GPU和所有Vulkan 1.2+ GPU支援。</string>
|
||||
<string name="display">顯示</string>
|
||||
|
||||
<string name="renderer_screen_layout">方向</string>
|
||||
|
||||
@@ -530,6 +530,21 @@
|
||||
<item>2</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="framePacingModeNames">
|
||||
<item>@string/frame_pacing_mode_target_Auto</item>
|
||||
<item>@string/frame_pacing_mode_target_30</item>
|
||||
<item>@string/frame_pacing_mode_target_60</item>
|
||||
<item>@string/frame_pacing_mode_target_90</item>
|
||||
<item>@string/frame_pacing_mode_target_120</item>
|
||||
</string-array>
|
||||
<integer-array name="framePacingModeValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="appletEntries">
|
||||
<item>@string/applet_hle</item>
|
||||
<item>@string/applet_lle</item>
|
||||
@@ -611,7 +626,7 @@
|
||||
</integer-array>
|
||||
|
||||
<string-array name="dynaStateEntries">
|
||||
<item>Core</item>
|
||||
<item>@string/disabled</item>
|
||||
<item>ExtendedDynamicState 1</item>
|
||||
<item>ExtendedDynamicState 2</item>
|
||||
<item>ExtendedDynamicState 3</item>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user