Compare commits

..

2 Commits

Author SHA1 Message Date
lizzie 6ae680fc96 add format 2025-12-30 20:53:52 +00:00
lizzie 792ea02969 [vk] macro-ify PixelSurface and SurfaceFormat lists 2025-12-30 20:09:57 +00:00
273 changed files with 18246 additions and 23513 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
fetch-depth: 0
- name: Find Unused Strings
run: ./tools/unused-strings.sh
run: ./tools/find-unused-strings.sh
- name: Find Stale Translations
run: ./tools/stale-translations.sh
+1 -1
View File
@@ -13,7 +13,7 @@ index 50f2a29..c60d9dc 100644
#define MBEDTLS_HAVE_NEON_INTRINSICS
-#elif defined(MBEDTLS_PLATFORM_IS_WINDOWS_ON_ARM64)
-#include <arm64_neon.h>
+#elif (defined(__ANDROID__) && defined(__ARM_FP)) || defined(__ARM_NEON)
+#elif defined(__ANDROID__) || defined(__ARM_NEON)
+#include <arm_neon.h>
#define MBEDTLS_HAVE_NEON_INTRINSICS
#endif
+80 -82
View File
@@ -1,9 +1,7 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-License-Identifier: GPL-3.0-or-later
set(CPM_SOURCE_CACHE "${PROJECT_SOURCE_DIR}/.cache/cpm" CACHE STRING "" FORCE)
if(MSVC OR ANDROID)
if (MSVC OR ANDROID)
set(BUNDLED_DEFAULT ON)
else()
set(BUNDLED_DEFAULT OFF)
@@ -21,7 +19,7 @@ include(CPM)
# cpmfile parsing
set(CPMUTIL_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cpmfile.json")
if(EXISTS ${CPMUTIL_JSON_FILE})
if (EXISTS ${CPMUTIL_JSON_FILE})
file(READ ${CPMUTIL_JSON_FILE} CPMFILE_CONTENT)
else()
message(WARNING "[CPMUtil] cpmfile ${CPMUTIL_JSON_FILE} does not exist, AddJsonPackage will be a no-op")
@@ -47,14 +45,14 @@ endfunction()
function(get_json_element object out member default)
string(JSON out_type ERROR_VARIABLE err TYPE "${object}" ${member})
if(err)
if (err)
set("${out}" "${default}" PARENT_SCOPE)
return()
endif()
string(JSON outvar GET "${object}" ${member})
if(out_type STREQUAL "ARRAY")
if (out_type STREQUAL "ARRAY")
string(JSON _len LENGTH "${object}" ${member})
# array_to_list("${outvar}" ${_len} outvar)
set("${out}_LENGTH" "${_len}" PARENT_SCOPE)
@@ -76,7 +74,7 @@ function(AddJsonPackage)
set(multiValueArgs OPTIONS)
cmake_parse_arguments(JSON "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
"${ARGN}")
list(LENGTH ARGN argnLength)
@@ -85,18 +83,18 @@ function(AddJsonPackage)
set(JSON_NAME "${ARGV0}")
endif()
if(NOT DEFINED CPMFILE_CONTENT)
if (NOT DEFINED CPMFILE_CONTENT)
cpm_utils_message(WARNING ${name} "No cpmfile, AddJsonPackage is a no-op")
return()
endif()
if(NOT DEFINED JSON_NAME)
if (NOT DEFINED JSON_NAME)
cpm_utils_message(FATAL_ERROR "json package" "No name specified")
endif()
string(JSON object ERROR_VARIABLE err GET "${CPMFILE_CONTENT}" "${JSON_NAME}")
if(err)
if (err)
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "Not found in cpmfile")
endif()
@@ -105,13 +103,13 @@ function(AddJsonPackage)
get_json_element("${object}" ci ci OFF)
get_json_element("${object}" version version "")
if(ci)
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)
if (raw_disabled)
array_to_list("${raw_disabled}" ${raw_disabled_LENGTH} disabled_platforms)
else()
set(disabled_platforms "")
@@ -156,31 +154,31 @@ function(AddJsonPackage)
# 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)
if (git_version)
set(version_replace ${git_version})
else()
set(version_replace ${version})
endif()
# TODO(crueter): fmt module for cmake
if(tag)
if (tag)
string(REPLACE "%VERSION%" "${version_replace}" tag ${tag})
endif()
if(artifact)
if (artifact)
string(REPLACE "%VERSION%" "${version_replace}" artifact ${artifact})
string(REPLACE "%TAG%" "${tag}" artifact ${artifact})
endif()
# format patchdir
if(raw_patches)
if (raw_patches)
math(EXPR range "${raw_patches_LENGTH} - 1")
foreach(IDX RANGE ${range})
string(JSON _patch GET "${raw_patches}" "${IDX}")
set(full_patch "${CMAKE_SOURCE_DIR}/.patch/${JSON_NAME}/${_patch}")
if(NOT EXISTS ${full_patch})
if (NOT EXISTS ${full_patch})
cpm_utils_message(FATAL_ERROR ${JSON_NAME} "specifies patch ${full_patch} which does not exist")
endif()
@@ -192,7 +190,7 @@ function(AddJsonPackage)
# options
get_json_element("${object}" raw_options options "")
if(raw_options)
if (raw_options)
array_to_list("${raw_options}" ${raw_options_LENGTH} options)
endif()
@@ -200,7 +198,7 @@ function(AddJsonPackage)
# end options
# system/bundled
if(bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
if (bundled STREQUAL "unset" AND DEFINED JSON_BUNDLED_PACKAGE)
set(bundled ${JSON_BUNDLED_PACKAGE})
endif()
@@ -286,37 +284,37 @@ function(AddPackage)
set(multiValueArgs OPTIONS PATCHES)
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}"
"${ARGN}")
"${ARGN}")
if(NOT DEFINED PKG_ARGS_NAME)
if (NOT DEFINED PKG_ARGS_NAME)
cpm_utils_message(FATAL_ERROR "package" "No package name defined")
endif()
option(${PKG_ARGS_NAME}_FORCE_SYSTEM "Force the system package for ${PKG_ARGS_NAME}")
option(${PKG_ARGS_NAME}_FORCE_BUNDLED "Force the bundled package for ${PKG_ARGS_NAME}")
if(NOT DEFINED PKG_ARGS_GIT_HOST)
if (NOT DEFINED PKG_ARGS_GIT_HOST)
set(git_host github.com)
else()
set(git_host ${PKG_ARGS_GIT_HOST})
endif()
if(DEFINED PKG_ARGS_URL)
if (DEFINED PKG_ARGS_URL)
set(pkg_url ${PKG_ARGS_URL})
if(DEFINED PKG_ARGS_REPO)
if (DEFINED PKG_ARGS_REPO)
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
else()
if(DEFINED PKG_ARGS_GIT_URL)
if (DEFINED PKG_ARGS_GIT_URL)
set(pkg_git_url ${PKG_ARGS_GIT_URL})
else()
set(pkg_git_url ${pkg_url})
endif()
endif()
elseif(DEFINED PKG_ARGS_REPO)
elseif (DEFINED PKG_ARGS_REPO)
set(pkg_git_url https://${git_host}/${PKG_ARGS_REPO})
if(DEFINED PKG_ARGS_TAG)
if (DEFINED PKG_ARGS_TAG)
set(pkg_key ${PKG_ARGS_TAG})
if(DEFINED PKG_ARGS_ARTIFACT)
@@ -326,14 +324,14 @@ function(AddPackage)
set(pkg_url
${pkg_git_url}/archive/refs/tags/${PKG_ARGS_TAG}.tar.gz)
endif()
elseif(DEFINED PKG_ARGS_SHA)
elseif (DEFINED PKG_ARGS_SHA)
set(pkg_url "${pkg_git_url}/archive/${PKG_ARGS_SHA}.tar.gz")
else()
if(DEFINED PKG_ARGS_BRANCH)
if (DEFINED PKG_ARGS_BRANCH)
set(PKG_BRANCH ${PKG_ARGS_BRANCH})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"REPO defined but no TAG, SHA, BRANCH, or URL specified, defaulting to master")
"REPO defined but no TAG, SHA, BRANCH, or URL specified, defaulting to master")
set(PKG_BRANCH master)
endif()
@@ -345,72 +343,72 @@ function(AddPackage)
cpm_utils_message(STATUS ${PKG_ARGS_NAME} "Download URL is ${pkg_url}")
if(NOT DEFINED PKG_ARGS_KEY)
if(DEFINED PKG_ARGS_SHA)
if (NOT DEFINED PKG_ARGS_KEY)
if (DEFINED PKG_ARGS_SHA)
string(SUBSTRING ${PKG_ARGS_SHA} 0 4 pkg_key)
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key} from sha")
"No custom key defined, using ${pkg_key} from sha")
elseif(DEFINED PKG_ARGS_GIT_VERSION)
set(pkg_key ${PKG_ARGS_GIT_VERSION})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
elseif(DEFINED PKG_ARGS_TAG)
"No custom key defined, using ${pkg_key}")
elseif (DEFINED PKG_ARGS_TAG)
set(pkg_key ${PKG_ARGS_TAG})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
elseif(DEFINED PKG_ARGS_VERSION)
"No custom key defined, using ${pkg_key}")
elseif (DEFINED PKG_ARGS_VERSION)
set(pkg_key ${PKG_ARGS_VERSION})
cpm_utils_message(DEBUG ${PKG_ARGS_NAME}
"No custom key defined, using ${pkg_key}")
"No custom key defined, using ${pkg_key}")
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"Could not determine cache key, using CPM defaults")
"Could not determine cache key, using CPM defaults")
endif()
else()
set(pkg_key ${PKG_ARGS_KEY})
endif()
if(DEFINED PKG_ARGS_HASH_ALGO)
if (DEFINED PKG_ARGS_HASH_ALGO)
set(hash_algo ${PKG_ARGS_HASH_ALGO})
else()
set(hash_algo SHA512)
endif()
if(DEFINED PKG_ARGS_HASH)
if (DEFINED PKG_ARGS_HASH)
set(pkg_hash "${hash_algo}=${PKG_ARGS_HASH}")
elseif(DEFINED PKG_ARGS_HASH_SUFFIX)
elseif (DEFINED PKG_ARGS_HASH_SUFFIX)
# funny sanity check
string(TOLOWER ${hash_algo} hash_algo_lower)
string(TOLOWER ${PKG_ARGS_HASH_SUFFIX} suffix_lower)
if(NOT ${suffix_lower} MATCHES ${hash_algo_lower})
if (NOT ${suffix_lower} MATCHES ${hash_algo_lower})
cpm_utils_message(WARNING
"Hash algorithm and hash suffix do not match, errors may occur")
"Hash algorithm and hash suffix do not match, errors may occur")
endif()
set(hash_url ${pkg_url}.${PKG_ARGS_HASH_SUFFIX})
elseif(DEFINED PKG_ARGS_HASH_URL)
elseif (DEFINED PKG_ARGS_HASH_URL)
set(hash_url ${PKG_ARGS_HASH_URL})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"No hash or hash URL found")
"No hash or hash URL found")
endif()
if(DEFINED hash_url)
if (DEFINED hash_url)
set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${PKG_ARGS_NAME}.hash)
# TODO(crueter): This is kind of a bad solution
# because "technically" the hash is invalidated each week
# but it works for now kjsdnfkjdnfjksdn
string(TOLOWER ${PKG_ARGS_NAME} lowername)
if(NOT EXISTS ${outfile} AND NOT EXISTS ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
if (NOT EXISTS ${outfile} AND NOT EXISTS ${CPM_SOURCE_CACHE}/${lowername}/${pkg_key})
file(DOWNLOAD ${hash_url} ${outfile})
endif()
if(EXISTS ${outfile})
if (EXISTS ${outfile})
file(READ ${outfile} pkg_hash_tmp)
endif()
if(DEFINED ${pkg_hash_tmp})
if (DEFINED ${pkg_hash_tmp})
set(pkg_hash "${hash_algo}=${pkg_hash_tmp}")
endif()
endif()
@@ -428,19 +426,19 @@ function(AddPackage)
- CPMUTIL_FORCE_BUNDLED
- BUNDLED_PACKAGE
- default to allow local
]] #
if(PKG_ARGS_FORCE_BUNDLED_PACKAGE)
]]#
if (PKG_ARGS_FORCE_BUNDLED_PACKAGE)
set_precedence(OFF OFF)
elseif(${PKG_ARGS_NAME}_FORCE_SYSTEM)
elseif (${PKG_ARGS_NAME}_FORCE_SYSTEM)
set_precedence(ON ON)
elseif(${PKG_ARGS_NAME}_FORCE_BUNDLED)
elseif (${PKG_ARGS_NAME}_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif(CPMUTIL_FORCE_SYSTEM)
elseif (CPMUTIL_FORCE_SYSTEM)
set_precedence(ON ON)
elseif(CPMUTIL_FORCE_BUNDLED)
set_precedence(OFF OFF)
elseif(DEFINED PKG_ARGS_BUNDLED_PACKAGE AND NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if(PKG_ARGS_BUNDLED_PACKAGE)
elseif (DEFINED PKG_ARGS_BUNDLED_PACKAGE AND NOT PKG_ARGS_BUNDLED_PACKAGE STREQUAL "unset")
if (PKG_ARGS_BUNDLED_PACKAGE)
set(local OFF)
else()
set(local ON)
@@ -451,7 +449,7 @@ function(AddPackage)
set_precedence(ON OFF)
endif()
if(DEFINED PKG_ARGS_VERSION)
if (DEFINED PKG_ARGS_VERSION)
list(APPEND EXTRA_ARGS
VERSION ${PKG_ARGS_VERSION}
)
@@ -477,32 +475,32 @@ function(AddPackage)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_NAMES ${PKG_ARGS_NAME})
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS ${pkg_git_url})
if(${PKG_ARGS_NAME}_ADDED)
if(DEFINED PKG_ARGS_SHA)
if (${PKG_ARGS_NAME}_ADDED)
if (DEFINED PKG_ARGS_SHA)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_SHA})
elseif(DEFINED PKG_ARGS_GIT_VERSION)
${PKG_ARGS_SHA})
elseif (DEFINED PKG_ARGS_GIT_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_GIT_VERSION})
elseif (DEFINED PKG_ARGS_TAG)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_GIT_VERSION})
elseif(DEFINED PKG_ARGS_TAG)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_TAG})
elseif(DEFINED PKG_ARGS_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_VERSION})
${PKG_ARGS_TAG})
elseif(DEFINED PKG_ARGS_VERSION)
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
${PKG_ARGS_VERSION})
else()
cpm_utils_message(WARNING ${PKG_ARGS_NAME}
"Package has no specified sha, tag, or version")
"Package has no specified sha, tag, or version")
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS "unknown")
endif()
else()
if(DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION AND NOT
if (DEFINED CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION AND NOT
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION}" STREQUAL "")
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)")
"${CPM_PACKAGE_${PKG_ARGS_NAME}_VERSION} (system)")
else()
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_SHAS
"unknown (system)")
"unknown (system)")
endif()
endif()
@@ -563,7 +561,7 @@ function(AddCIPackage)
message(FATAL_ERROR "[CPMUtil] PACKAGE is required")
endif()
if(NOT DEFINED PKG_ARGS_CMAKE_FILENAME)
if (NOT DEFINED PKG_ARGS_CMAKE_FILENAME)
set(ARTIFACT_CMAKE ${PKG_ARGS_NAME})
else()
set(ARTIFACT_CMAKE ${PKG_ARGS_CMAKE_FILENAME})
@@ -575,11 +573,11 @@ function(AddCIPackage)
set(ARTIFACT_EXT ${PKG_ARGS_EXTENSION})
endif()
if(DEFINED PKG_ARGS_MIN_VERSION)
if (DEFINED PKG_ARGS_MIN_VERSION)
set(ARTIFACT_MIN_VERSION ${PKG_ARGS_MIN_VERSION})
endif()
if(DEFINED PKG_ARGS_DISABLED_PLATFORMS)
if (DEFINED PKG_ARGS_DISABLED_PLATFORMS)
set(DISABLED_PLATFORMS ${PKG_ARGS_DISABLED_PLATFORMS})
endif()
@@ -589,19 +587,19 @@ function(AddCIPackage)
set(ARTIFACT_REPO ${PKG_ARGS_REPO})
set(ARTIFACT_PACKAGE ${PKG_ARGS_PACKAGE})
if((MSVC AND ARCHITECTURE_x86_64) AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS)
if ((MSVC AND ARCHITECTURE_x86_64) AND NOT "windows-amd64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(windows-amd64)
endif()
if((MSVC AND ARCHITECTURE_arm64) AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
if ((MSVC AND ARCHITECTURE_arm64) AND NOT "windows-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(windows-arm64)
endif()
if((MINGW AND ARCHITECTURE_x86_64) AND NOT "mingw-amd64" IN_LIST DISABLED_PLATFORMS)
if ((MINGW AND ARCHITECTURE_x86_64) AND NOT "mingw-amd64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-amd64)
endif()
if((MINGW AND ARCHITECTURE_arm64) AND NOT "mingw-arm64" IN_LIST DISABLED_PLATFORMS)
if ((MINGW AND ARCHITECTURE_arm64) AND NOT "mingw-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-arm64)
endif()
@@ -630,11 +628,11 @@ function(AddCIPackage)
endif()
# TODO(crueter): macOS amd64/aarch64 split mayhaps
if(APPLE AND NOT "macos-universal" IN_LIST DISABLED_PLATFORMS)
if (APPLE AND NOT "macos-universal" IN_LIST DISABLED_PLATFORMS)
add_ci_package(macos-universal)
endif()
if(DEFINED ARTIFACT_DIR)
if (DEFINED ARTIFACT_DIR)
set(${ARTIFACT_PACKAGE}_ADDED TRUE PARENT_SCOPE)
set(${ARTIFACT_PACKAGE}_SOURCE_DIR "${ARTIFACT_DIR}" PARENT_SCOPE)
else()
+2 -2
View File
@@ -15,8 +15,8 @@
<br>
</h1>
<h4 align="center"><b>Eden</b> is a free and opensource (FOSS) Switch 1 emulator, derived from Yuzu and Sudachi - started by developer Camille LaVey.
It's written in C++ with portability in mind, with builds for Windows, Linux, macOS, Android, FreeBSD and more.
<h4 align="center"><b>Eden</b> is an open-source Nintendo Switch emulator, forked from the Yuzu emulator — started by former Citron developer Camille LaVey and the Eden team.
It is written in C++ with portability in mind, and we actively maintain builds for Windows, Linux and Android.
</h4>
<p align="center">
+1 -1
View File
@@ -9,7 +9,7 @@ Version=1.0
Type=Application
Name=Eden
GenericName=Switch Emulator
Comment=Multiplatform FOSS Switch 1 emulator written in C++, derived from Yuzu and Sudachi
Comment=Nintendo Switch video game console emulator
Icon=dev.eden_emu.eden
TryExec=eden
Exec=eden %f
+24 -19
View File
@@ -1,22 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2025 Eden Emulator Project
SPDX-License-Identifier: GPL-3.0-or-later
-->
<!--
SPDX-FileCopyrightText: 2021 yuzu Emulator Project
SPDX-License-Identifier: CC0-1.0
-->
<component type="desktop-application">
<id>org.eden_emu.eden</id>
<id>org.yuzu_emu.yuzu</id>
<metadata_license>CC0-1.0</metadata_license>
<name>eden</name>
<name>yuzu</name>
<summary>Nintendo Switch emulator</summary>
<description>
<p>Multiplatform FOSS Switch 1 emulator written in C++, derived from Yuzu and Sudachi</p>
<p>yuzu is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of Citra.</p>
<p>The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.</p>
<p>For a full list of games yuzu support, please visit our Compatibility page.</p>
<p>Check out our website for the latest news on exciting features, monthly progress reports, and more!</p>
</description>
<categories>
<category>Game</category>
@@ -26,16 +24,16 @@ SPDX-License-Identifier: CC0-1.0
<keyword>switch</keyword>
<keyword>emulator</keyword>
</keywords>
<url type="homepage">https://eden-emu.dev/</url>
<url type="bugtracker">https://git.eden-emu.dev/eden-emu/eden/issues</url>
<url type="faq">https://eden-emu.dev/docs</url>
<url type="help">https://eden-emu.dev/docs</url>
<url type="donation">https://eden-emu.dev/donations</url>
<url type="translate">https://explore.transifex.com/edenemu/eden-emulator</url>
<url type="contact">https://discord.gg/edenemu</url>
<url type="vcs-browser">https://git.eden-emu.dev</url>
<url type="contribute">https://git.eden-emu.dev/eden-emu/eden</url>
<launchable type="desktop-id">org.eden_emu.eden.desktop</launchable>
<url type="homepage">https://yuzu-emu.org/</url>
<url type="bugtracker">https://github.com/yuzu-emu/yuzu/issues</url>
<url type="faq">https://yuzu-emu.org/wiki/faq/</url>
<url type="help">https://yuzu-emu.org/wiki/home/</url>
<url type="donation">https://yuzu-emu.org/donate/</url>
<url type="translate">https://www.transifex.com/projects/p/yuzu</url>
<url type="contact">https://community.citra-emu.org/</url>
<url type="vcs-browser">https://github.com/yuzu-emu/yuzu</url>
<url type="contribute">https://yuzu-emu.org/wiki/contributing/</url>
<launchable type="desktop-id">org.yuzu_emu.yuzu.desktop</launchable>
<provides>
<binary>yuzu</binary>
<binary>yuzu-cmd</binary>
@@ -52,6 +50,13 @@ SPDX-License-Identifier: CC0-1.0
<memory>16384</memory>
</recommends>
<project_license>GPL-3.0-or-later</project_license>
<developer_name>Eden Emulator Team</developer_name>
<developer_name>yuzu Emulator Team</developer_name>
<content_rating type="oars-1.0"/>
<screenshots>
<screenshot type="default"><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/001-Super%20Mario%20Odyssey%20.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/004-The%20Legend%20of%20Zelda%20Skyward%20Sword%20HD.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/007-Pokemon%20Sword.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/010-Hyrule%20Warriors%20Age%20of%20Calamity.png</image></screenshot>
<screenshot><image>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/039-Pok%C3%A9mon%20Mystery%20Dungeon%20Rescue%20Team%20DX.png.png.png</image></screenshot>
</screenshots>
</component>
+1 -672
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

After

Width:  |  Height:  |  Size: 346 KiB

+420 -523
View File
File diff suppressed because it is too large Load Diff
+407 -518
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+407 -518
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+407 -518
View File
File diff suppressed because it is too large Load Diff
+401 -508
View File
File diff suppressed because it is too large Load Diff
+416 -527
View File
File diff suppressed because it is too large Load Diff
+401 -508
View File
File diff suppressed because it is too large Load Diff
+409 -519
View File
File diff suppressed because it is too large Load Diff
+410 -520
View File
File diff suppressed because it is too large Load Diff
+405 -510
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+410 -521
View File
File diff suppressed because it is too large Load Diff
+410 -513
View File
File diff suppressed because it is too large Load Diff
+412 -522
View File
File diff suppressed because it is too large Load Diff
+409 -519
View File
File diff suppressed because it is too large Load Diff
+407 -512
View File
File diff suppressed because it is too large Load Diff
+413 -518
View File
File diff suppressed because it is too large Load Diff
+413 -524
View File
File diff suppressed because it is too large Load Diff
+411 -517
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+408 -519
View File
File diff suppressed because it is too large Load Diff
+410 -513
View File
File diff suppressed because it is too large Load Diff
+413 -523
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

BIN
View File
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
# AddPackage
- `VERSION` (required): The version to get (the tag will be `v${VERSION}`)
- `NAME` (required): Name used within the artifacts
- `REPO` (required): CI repository, e.g. `crueter-ci/OpenSSL`
- `PACKAGE` (required): `find_package` package name
- `EXTENSION`: Artifact extension (default `tar.zst`)
- `MIN_VERSION`: Minimum version for `find_package`. Only used if platform does not support this package as a bundled artifact
- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. Options:
* `windows-amd64`
* `windows-arm64`
* `android`
* `solaris-amd64`
* `freebsd-amd64`
* `linux-amd64`
* `linux-aarch64`
* `macos-universal`
+3 -16
View File
@@ -10,13 +10,12 @@ Global Options:
You are highly encouraged to read AddPackage first, even if you plan to only interact with CPMUtil via `AddJsonPackage`.
<!-- TOC -->
- [AddPackage](#addpackage)
- [AddCIPackage](#addcipackage)
- [AddJsonPackage](#addjsonpackage)
- [Lists](#lists)
- [For Packagers](#for-packagers)
- [Network Sandbox](#network-sandbox)
- [Unsandboxed](#unsandboxed)
<!-- /TOC -->
## AddPackage
@@ -44,16 +43,4 @@ For an example of how this might be implemented in an application, see Eden's im
- [`dep_hashes.h.in`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/dep_hashes.h.in)
- [`GenerateDepHashes.cmake`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CMakeModules/GenerateDepHashes.cmake)
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
## For Packagers
If you are packaging a project that uses CPMUtil, read this!
### Network Sandbox
For sandboxed environments (e.g. Gentoo, nixOS) you must install all dependencies to the system beforehand and set `-DCPMUTIL_FORCE_SYSTEM=ON`. If a dependency is missing, get creating!
### Unsandboxed
For others (AUR, MPR, etc). CPMUtil will handle everything for you, including if some of the project's dependencies are missing from your distribution's repositories. That is pretty much half the reason I created this behemoth, after all.
- [`deps_dialog.cpp`](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/src/yuzu/deps_dialog.cpp)
+25 -45
View File
@@ -1,20 +1,17 @@
# Caveats
<!-- TOC -->
- [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux)
- [macOS](#macos)
- [Solaris](#solaris)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
- [MSYS2](#msys2)
- [RedoxOS](#redoxos)
- [Windows](#windows)
- [Windows 7, Windows 8 and Windows 8.1](#windows-7-windows-8-and-windows-81)
- [Windows Vista and below](#windows-vista-and-below)
- [Windows on ARM](#windows-on-arm)
- [Caveats](#caveats)
- [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux)
- [macOS](#macos)
- [Solaris](#solaris)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
- [MSYS2](#msys2)
- [Windows 8.1 and below](#windows-81-and-below)
<!-- /TOC -->
## Arch Linux
@@ -52,7 +49,6 @@ export PATH="$PATH:$PWD"
```
Default MESA is a bit outdated, the following environment variables should be set for a smoother experience:
```sh
export MESA_GL_VERSION_OVERRIDE=4.6
export MESA_GLSL_VERSION_OVERRIDE=460
@@ -97,16 +93,9 @@ Eden is not currently available as a port on FreeBSD, though it is in the works.
The available OpenSSL port (3.0.17) is out-of-date, and using a bundled static library instead is recommended; to do so, add `-DYUZU_USE_BUNDLED_OPENSSL=ON` to your CMake configure command.
Gamepad/controllers may not work on 15.0, this is due to an outdated SDL not responding well to the new `usbhid(2)` driver. To workaround this simply disable `usbhid(2)` (add the following to `/boot/loader.conf`):
```sh
hw.usb.usbhid.enable="0"
```
## NetBSD
Install `pkgin` if not already `pkg_add pkgin`, see also the general [pkgsrc guide](https://www.netbsd.org/docs/pkgsrc/using.html). For NetBSD 10.1 provide `echo 'PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/"' >/etc/pkg_install.conf`. If `pkgin` is taking too much time consider adding the following to `/etc/rc.conf`:
Install `pkgin` if not already `pkg_add pkgin`, see also the general [pkgsrc guide](https://www.netbsd.org/docs/pkgsrc/using.html). For NetBSD 10.1 provide `echo 'PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/x86_64/10.0_2025Q3/All/"' >/etc/pkg_install.conf`. If `pkgin` is taking too much time consider adding the following to `/etc/rc.conf`:
```sh
ip6addrctl=YES
ip6addrctl_policy=ipv4_prefer
@@ -120,10 +109,9 @@ System provides a default `g++-10` which doesn't support the current C++ codebas
Make may error out when generating C++ headers of SPIRV shaders, hence it's recommended to use `gmake` over the default system one.
[parallel/spirv-tools](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/parallel/spirv-tools/index.html) isn't available in binary form and must be built from source.
glslang is not available on NetBSD, to circumvent this simply build glslang by yourself:
[parallel/spirv-tools](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/parallel/spirv-tools/index.html) isn't available in binary form and must be build from source.
Such that glslang is not available on NetBSD, to circumvent this simply build glslang by yourself:
```sh
pkgin python313
git clone --depth=1 https://github.com/KhronosGroup/glslang.git
@@ -136,15 +124,13 @@ cmake --install build
However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version.
## DragonFlyBSD
# DragonFlyBSD
If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt:
```sh
rm /usr/local/lib/gcc11/libstdc++.so.6
ln -s /usr/local/lib/gcc14/libstdc++.so /usr/local/lib/gcc11/libstdc++.so.6
```
This may have unforeseen consequences of which we don't need to worry about for now.
Default `g++` (and the libstdc++) is too outdated - so install `gcc14` and redirect CMake to the new compiler toolchain `-DCMAKE_CXX_COMPILER=gcc14 -DCMAKE_C_COMPILER=g++14`.
@@ -155,9 +141,13 @@ If build hangs, use `hammer2 bulkfree`.
## MSYS2
Only the `MINGW64` environment is tested (or `CLANGARM64` on ARM); however, all of the others should work (in theory) sans `MINGW32`.
`qt6-static` isn't supported yet.
When packaging an MSYS2 build that is NOT fully static, you will need to copy all dependent DLLs recursively alongside the `windeployqt6`; for example:
Only the `MINGW64` environment is tested; however, all of the others should work (in theory) sans `MINGW32`.
Currently, only FFmpeg can be used as a system dependency; the others will result in linker errors.
When packaging an MSYS2 build, you will need to copy all dependent DLLs recursively alongside the `windeployqt6`; for example:
```sh
# MSYS_TOOLCHAIN is typically just mingw64
@@ -206,24 +196,14 @@ windeployqt6 --no-compiler-runtime --no-opengl-sw --no-system-dxc-compiler \
find ./*/ -name "*.dll" | while read -r dll; do deps "$dll"; done
```
## RedoxOS
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
## Windows
### Windows 7, Windows 8 and Windows 8.1
## Windows 8.1 and below
DirectX 12 is not available - simply copy and paste a random DLL and name it `d3d12.dll`.
Install [Qt6 compatibility libraries](github.com/ANightly/qt6windows7) specifically Qt 6.9.5.
### Windows Vista and below
## RedoxOS
No support for Windows Vista (or below) is present at the moment. Check back later.
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
### Windows on ARM
If you're using Snapdragon X or 8CX, use the [the Vulkan translation layer](https://apps.microsoft.com/detail/9nqpsl29bfff?hl=en-us&gl=USE) only if the stock drivers do not work. And of course always keep your system up-to-date.
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
+7 -51
View File
@@ -10,13 +10,7 @@ Simply put, types/classes are named as `PascalCase`, same for methods and functi
Except for Qt MOC where `functionName` is preferred.
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well. Do not use names like `SS` as systems like solaris define it for registers, in general do not use any of the following for short names:
- `SS`, `DS`, `GS`, `FS`: Segment registers, defined by Solaris `<ucontext.h>`
- `EAX`, `EBX`, `ECX`, `EDX`, `ESI`, `EDI`, `ESP`, `EBP`, `EIP`: Registers, defined by Solaris.
- `X`: Defined by some utility headers, avoid.
- `_`: Defined by gettext, avoid.
- `N`, `M`, `S`: Preferably don't use this for types, use it for numeric constants.
- `TR`: Used by some weird `<ucontext.h>` whom define the Task Register as a logical register to provide to the user... (Need to remember which OS in specific).
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well.
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
@@ -24,45 +18,25 @@ Try not using hungarian notation, if you're able.
## Formatting
Formatting is extremelly lax, the general rule of thumb is: Don't add new lines just to increase line count. The less lines we have to look at, the better. This means also packing densely your code while not making it a clusterfuck. Strike a balance of "this is a short and comprehensible piece of code" and "my eyes are actually happy to see this!". Don't just drop the entire thing in a single line and call it "dense code", that's just spaghetti posing as code. In general, be mindful of what other devs need to look at.
Do not put if/while/etc braces after lines:
```c++
// no dont do this
// this is more lines of code for no good reason (why braces need their separate lines?)
// and those take space in someone's screen, cumulatively
if (thing)
{ //<--
{
some(); // ...
} //<-- 2 lines of code for basically "opening" and "closing" an statment
}
// do this
if (thing) { //<-- [...] and with your brain you can deduce it's this piece of code
// that's being closed
if (thing) {
some(); // ...
} //<-- only one line, and it's clearer since you know its closing something [...]
}
// or this, albeit the extra line isn't needed (at your discretion of course)
// or this
if (thing)
some(); // ...
// this is also ok, keeps things in one line and makes it extremely clear
// this is also ok
if (thing) some();
// NOT ok, don't be "clever" and use the comma operator to stash a bunch of statments
// in a single line, doing this will definitely ruin someone's day - just do the thing below
// vvv
if (thing) some(), thing(), a2(a1(), y1(), j1()), do_complex_shit(wa(), wo(), ploo());
// ... and in general don't use the comma operator for "multiple statments", EXCEPT if you think
// that it makes the code more readable (the situation may be rare however)
// Wow so much clearer! Now I can actually see what each statment is meant to do!
if (thing) {
some();
thing();
a2(a1(), y1(), j1());
do_complex_shit(wa(), wo(), ploo());
}
```
Brace rules are lax, if you can get the point across, do it:
@@ -103,21 +77,3 @@ if (device_name.empty()) {
SDL_AudioSpec obtained;
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
```
A note about operators: Use them sparingly, yes, the language is lax on them, but some usages can be... tripping to say the least.
```c++
a, b, c; //<-- NOT OK multiple statments with comma operator is definitely a recipe for disaster
return c ? a : b; //<-- OK ternaries at end of return statments are clear and fine
return a, b; //<-- NOT OK return will take value of `b` but also evaluate `a`, just use a separate statment
void f(int a[]) //<-- OK? if you intend to use the pointer as an array, otherwise just mark it as *
```
And about templates, use them sparingly, don't just do meta-templating for the sake of it, do it when you actually need it. This isn't a competition to see who can make the most complicated and robust meta-templating system. Just use what works, and preferably stick to the standard libary instead of reinventing the wheel. Additionally:
```c++
// NOT OK This will create (T * N * C * P) versions of the same function. DO. NOT. DO. THIS.
template<typename T, size_t N, size_t C, size_t P> inline void what() const noexcept;
// OK use parameters like a normal person, don't be afraid to use them :)
template<typename T> inline void what(size_t n, size_t c, size_t p) const noexcept;
```
+2 -4
View File
@@ -1,8 +1,6 @@
# Cross compiling
# Cross Compile
General guide for cross compiling.
## Debian ARM64
## ARM64
A painless guide for cross compilation (or to test NCE) from a x86_64 system without polluting your main.
+37 -52
View File
@@ -1,44 +1,42 @@
# Dependencies
To build Eden, you MUST have a C++ compiler.
* On Linux, this is usually [GCC](https://gcc.gnu.org/) 11+ or [Clang](https://clang.llvm.org/) v14+
* GCC 12 also requires Clang 14+
- GCC 12 also requires Clang 14+
* On Windows, we support:
* **[MSVC](https://visualstudio.microsoft.com/downloads/)** (default)
* It's STRONGLY RECOMMENDED to use the **Community** option and **Visual Studio 2022**
* You need to install: **[Desktop development with C++](https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170)**
* **[clang-cl](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-180)**
* You need to install: **C++ Clang tools for Windows**
* **[MSYS2](https://www.msys2.org)**
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (default)
- It's STRONGLY RECOMMENDED to use the **Community** option and **Visual Studio 2022**
- You need to install: **[Desktop development with C++](https://learn.microsoft.com/en-us/cpp/build/vscpp-step-0-installation?view=msvc-170)**
- **[clang-cl](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-180)**
- You need to install: **C++ Clang tools for Windows**
- **[MSYS2](https://www.msys2.org)** (experimental)
* On macOS, this is Apple Clang
* This can be installed with `xcode-select --install`
- This can be installed with `xcode-select --install`
The following additional tools are also required:
* **[CMake](https://www.cmake.org/)** 3.22+ - already included with the Android SDK
* **[Git](https://git-scm.com/)** for version control
* **[Windows installer](https://gitforwindows.org)**
- **[Windows installer](https://gitforwindows.org)**
* **[Python3](https://www.python.org/downloads/)** 3.10+ - necessary to download external repositories
* On Windows, you must install the **[Vulkan SDK](https://vulkan.lunarg.com/sdk/home#windows)** as well
* *A convenience script to install the latest SDK is provided in:*
* `tools/windows/install-vulkan-sdk.ps1` (for PowerShell 5+)
* `tools/windows/install-vulkan-sdk.sh` (for Git Bash, etc)
- *A convenience script to install the latest SDK is provided in:*
- `tools/windows/install-vulkan-sdk.ps1` (for PowerShell 5+)
- `tools/windows/install-vulkan-sdk.sh` (for MSYS2, Git Bash, etc)
If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6, and optionally Qt Creator (the **RECOMMENDED** IDE for building)
* On Linux, *BSD and macOS, this can be done by the package manager
* If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below.
- If you wish to use Qt Creator, append `qtcreator` or `qt-creator` to the commands seen below.
* MSVC/clang-cl users on Windows must install through the official [Qt](https://www.qt.io/download-qt-installer-oss) installer
* Linux and macOS users may choose to use the installer as well.
* MSYS2 can also install Qt 6 via the package manager
* For help setting up Qt Creator, run `./install.sh -h qtcreator`
If you are on Windows, a convenience script to install MSVC, MSYS2, Qt, all necessary packages for MSYS2, and set up a zsh environment with useful keybinds and aliases can be found [here](https://git.crueter.xyz/scripts/windev).
- For help setting up Qt Creator, run `./install.sh -h qtcreator`
If you are on **Windows** and building with **MSVC** or **clang-cl**, you may go [back home](Build.md) and continue.
## Externals
The following are handled by Eden's externals:
* [FFmpeg](https://ffmpeg.org/) (should use `-DYUZU_USE_EXTERNAL_FFMPEG=ON`)
@@ -59,7 +57,6 @@ All other dependencies will be downloaded and built by [CPM](https://github.com/
* [MbedTLS](https://github.com/Mbed-TLS/mbedtls) 3+
Vulkan 1.3.274+ is also needed:
* [VulkanUtilityLibraries](https://github.com/KhronosGroup/Vulkan-Utility-Libraries)
* [VulkanHeaders](https://github.com/KhronosGroup/Vulkan-Headers)
* [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools)
@@ -74,21 +71,18 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
* [VulkanMemoryAllocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
* [sirit](https://github.com/eden-emulator/sirit)
* [httplib](https://github.com/yhirose/cpp-httplib) - if `ENABLE_UPDATE_CHECKER` or `ENABLE_WEB_SERVICE` are on
* This package is known to be broken on the AUR.
- This package is known to be broken on the AUR.
* [cpp-jwt](https://github.com/arun11299/cpp-jwt) 1.4+ - if `ENABLE_WEB_SERVICE` is on
* [unordered-dense](https://github.com/martinus/unordered_dense)
* [mcl](https://github.com/azahar-emu/mcl) - subject to removal
On amd64:
* [xbyak](https://github.com/herumi/xbyak) - 7.22 or earlier is recommended
On aarch64 OR if `DYNARMIC_TESTS` is on:
* [oaknut](https://github.com/merryhime/oaknut) 2.0.1+
On riscv64:
* [biscuit](https://github.com/lioncash/biscuit) 0.9.1+
## Commands
@@ -104,7 +98,7 @@ Click on the arrows to expand.
GURU must be enabled:
```sh
```
sudo emerge -a app-eselect/eselect-repository
sudo eselect repository enable guru
sudo emaint sync -r guru
@@ -130,18 +124,17 @@ sudo emerge -a \
virtual/pkgconfig
```
* On `amd64`, also add `dev-libs/xbyak`
* On `riscv64`, also add `dev-libs/biscuit` (currently unavailable)
* On `aarch64`, also add `dev-libs/oaknut`
* If tests are enabled, also add `dev-libs/oaknut` and `dev-cpp/catch`
- On `amd64`, also add `dev-libs/xbyak`
- On `riscv64`, also add `dev-libs/biscuit` (currently unavailable)
- On `aarch64`, also add `dev-libs/oaknut`
- If tests are enabled, also add `dev-libs/oaknut` and `dev-cpp/catch`
Required USE flags:
* `dev-qt/qtbase network concurrent dbus gui widgets`
* `dev-libs/quazip qt6`
* `net-libs/mbedtls cmac`
* `media-libs/libsdl2 haptic joystick sound video`
* `dev-cpp/cpp-httplib ssl`
- `dev-qt/qtbase network concurrent dbus gui widgets`
- `dev-libs/quazip qt6`
- `net-libs/mbedtls cmac`
- `media-libs/libsdl2 haptic joystick sound video`
- `dev-cpp/cpp-httplib ssl`
[Caveats](./Caveats.md#gentoo-linux)
@@ -157,7 +150,6 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
* Building with QT Web Engine requires `qt6-webengine` as well.
* Proper Wayland support requires `qt6-wayland`
* GCC 11 or later is required.
</details>
<details>
@@ -169,20 +161,17 @@ sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev l
* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
* To enable QT Web Engine, add `-DYUZU_USE_QT_WEB_ENGINE=ON` when running CMake.
</details>
<details>
<summary>AlmaLinux, Fedora, Red Hat Linux</summary>
Fedora:
```sh
sudo dnf install autoconf cmake fmt-devel gcc{,-c++} glslang hidapi-devel json-devel libtool libusb1-devel libzstd-devel lz4-devel nasm ninja-build openssl-devel pulseaudio-libs-devel qt6-linguist qt6-qtbase{-private,}-devel qt6-qtwebengine-devel qt6-qtmultimedia-devel speexdsp-devel wayland-devel zlib-devel ffmpeg-devel libXext-devel boost jq
```
AlmaLinux (use `YUZU_USE_CPM=ON`):
```sh
# vvv - Only if RPMfusion is not installed or EPEL isn't either
sudo dnf install epel-release dnf-utils
@@ -199,14 +188,12 @@ For systems like OpenEuler or derivates, don't forget to also install: `SDL2-dev
* [RPM Fusion](https://rpmfusion.org/Configuration) is required for `ffmpeg-devel`
* Fedora 32 or later is required.
* Fedora 36+ users with GCC 12 need Clang and should configure CMake with: `cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -B build`
</details>
<details>
<summary>Alpine Linux</summary>
First, enable the community repository; [see here](https://wiki.alpinelinux.org/wiki/Repositories#Enabling_the_community_repository).
```sh
# Enable the community repository
setup-apkrepos -c
@@ -246,9 +233,8 @@ brew install autoconf automake boost ffmpeg fmt glslang hidapi libtool libusb lz
If you are compiling on Intel Mac, or are using a Rosetta Homebrew installation, you must replace all references of `/opt/homebrew` with `/usr/local`.
To run with MoltenVK, install additional dependencies:
```sh
brew install molten-vk
brew install molten-vk vulkan-loader
```
[Caveats](./Caveats.md#macos).
@@ -267,7 +253,7 @@ If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
<details>
<summary>NetBSD</summary>
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers spirv-tools ffmpeg7 libva nlohmann-json jq libopus qt6 mbedtls3 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`.
For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv-headers ffmpeg7 libva nlohmann-json jq libopus qt6 mbedtls3 cpp-httplib lz4 vulkan-headers nasm autoconf enet pkg-config libusb1 libcxx`.
[Caveats](./Caveats.md#netbsd).
@@ -308,24 +294,23 @@ sudo pkg install qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json ope
* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`)
* Download and install all dependencies:
```sh
```
BASE="git make autoconf libtool automake-wrapper jq patch"
MINGW="qt6-base qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus mbedtls libusb unordered_dense openssl SDL2"
# Either x86_64 or clang-aarch64 (Windows on ARM)
MINGW="qt6-base qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus mbedtls libusb unordered_dense"
packages="$BASE"
for pkg in $MINGW; do
packages="$packages mingw-w64-x86_64-$pkg"
#packages="$packages mingw-w64-clang-aarch64-$pkg"
done
pacman -Syuu --needed --noconfirm $packages
```
* Notes:
* Using `qt6-static` is possible as well, provided you build with `-DYUZU_STATIC_BUILD=ON`.
* Other environments are entirely untested, but should theoretically work provided you install all the necessary packages.
* GCC is proven to work better with the MinGW environment. On ARM, only Clang is available through the CLANGARM64 environment, so use that until a GNU ARM environment is available.
* Add `qt-creator` to the `MINGW` variable to install Qt Creator. You can then create a Start Menu shortcut to the MinGW Qt Creator by running `powershell "\$s=(New-Object -COM WScript.Shell).CreateShortcut('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Qt Creator.lnk');\$s.TargetPath='C:\\msys64\\mingw64\\bin\\qtcreator.exe';\$s.Save()"` in Git Bash or MSYS2.
- Using `qt6-static` is possible but currently untested.
- Other environments are entirely untested, but should theoretically work provided you install all the necessary packages.
- GCC is proven to work better with the MinGW environment. If you choose to use Clang, you *may* be better off using the clang64 environment.
- Add `qt-creator` to the `MINGW` variable to install Qt Creator. You can then create a Start Menu shortcut to the MinGW Qt Creator by running `powershell "\$s=(New-Object -COM WScript.Shell).CreateShortcut('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Qt Creator.lnk');\$s.TargetPath='C:\\msys64\\mingw64\\bin\\qtcreator.exe';\$s.Save()"` in Git Bash or MSYS2.
* Add MinGW binaries to the PATH if they aren't already:
* `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
* or `echo 'PATH=/mingw64/bin:$PATH' >> ~/.zshrc`
-2
View File
@@ -13,5 +13,3 @@ This contains documentation created by developers. This contains build instructi
- **[The NVIDIA SM86 (Maxwell) GPU](./NvidiaGpu.md)**
- **[User Handbook](./user)**
- **[Release Policy](./ReleasePolicy.md)**
- **[Dynarmic](./dynarmic)**
- **[Cross compilation](./CrossCompile.md)**
+4 -4
View File
@@ -76,12 +76,12 @@ The APK and AAB will be output into "artifacts".
Examples:
* Build legacy release with update checker:
* `.ci/android/build.sh -r -t legacy`
* Build legacy release with update checker for ChromeOS:
* `.ci/android/build.sh -c -r -t legacy`
* Build standard release with debug info without update checker for phones:
* `.ci/android/build.sh -b RelWithDebInfo`
* Build optimized release with update checker for ChromeOS:
* `.ci/android/build.sh -c -r -t optimized`
* Build optimized release with update checker:
* `.ci/android/build.sh -r -t optimized`
### Additional Resources
+5 -5
View File
@@ -71,9 +71,9 @@
"hash": "9697e80a7d5d9bcb3ce51051a9a24962fb90ca79d215f1f03ae6b58da8ba13a63b5dda1b4dde3d26ac6445029696b8ef2883f4e5a777b342bba01283ed293856"
},
"libadrenotools": {
"repo": "eden-emulator/libadrenotools",
"sha": "8ba23b42d7",
"hash": "f6526620cb752876edc5ed4c0925d57b873a8218ee09ad10859ee476e9333259784f61c1dcc55a2bcba597352d18aff22cd2e4c1925ec2ae94074e09d7da2265",
"repo": "bylaws/libadrenotools",
"sha": "8fae8ce254",
"hash": "db4a74ce15559c75e01d1868a90701519b655d77f2a343bbee283a42f8332dc9046960fb022dc969f205e457348a3f99cb8be6e1cd91264d2ae1235294b9f9b2",
"patches": [
"0001-linkerns-cpm.patch"
]
@@ -180,8 +180,8 @@
"discord-rpc": {
"package": "DiscordRPC",
"repo": "eden-emulator/discord-rpc",
"sha": "0d8b2d6a37",
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
"sha": "1cf7772bb6",
"hash": "9a6c35887dcacceb4ba1bf3141edb73b05b2abc719a8d81dad9cb9dd5b039ce203946787335d9d738af669c10cf2534638b645635a22096fc28dcae2475e0cbe",
"find_args": "MODULE"
},
"simpleini": {
+1 -1
View File
@@ -1,5 +1,5 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-25.11";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShellNoCC {
+2 -1
View File
@@ -7,7 +7,8 @@
include_directories(.)
# Dynarmic
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64))
set(DYNARMIC_IGNORE_ASSERTS ON)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()
@@ -29,9 +29,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<application
android:name="org.yuzu.yuzu_emu.YuzuApplication"
@@ -61,11 +61,6 @@ class YuzuApplication : Application() {
application = this
documentsTree = DocumentsTree()
DirectoryInitialization.start()
// Initialize Freedreno config BEFORE loading native library
// This ensures GPU driver environment variables are set before adrenotools initializes
GpuDriverHelper.initializeFreedrenoConfigEarly()
NativeLibrary.playTimeManagerInit()
GpuDriverHelper.initializeDriverParameters()
NativeInput.reloadInputDevices()
@@ -639,7 +639,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
companion object {
const val EXTRA_SELECTED_GAME = "SelectedGame"
const val EXTRA_OVERLAY_GAMELESS_EDIT_MODE = "overlayGamelessEditMode"
fun stopForegroundService(activity: Activity) {
val startIntent = Intent(activity, ForegroundService::class.java)
@@ -653,12 +652,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener, InputManager
activity.startActivity(launcher)
}
fun launchForOverlayEdit(context: Context): Intent {
return Intent(context, EmulationActivity::class.java).apply {
putExtra(EXTRA_OVERLAY_GAMELESS_EDIT_MODE, true)
}
}
private fun areCoordinatesOutside(view: View?, x: Float, y: Float): Boolean {
if (view == null) {
return true
@@ -1,58 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.utils.FreedrenoPreset
import org.yuzu.yuzu_emu.databinding.ListItemFreedrenoPresetBinding
/**
* Adapter for displaying Freedreno preset configurations in a horizontal list.
*/
class FreedrenoPresetAdapter(
private val onPresetClicked: (FreedrenoPreset) -> Unit
) : ListAdapter<FreedrenoPreset, FreedrenoPresetAdapter.PresetViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PresetViewHolder {
val binding = ListItemFreedrenoPresetBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return PresetViewHolder(binding)
}
override fun onBindViewHolder(holder: PresetViewHolder, position: Int) {
holder.bind(getItem(position))
}
inner class PresetViewHolder(private val binding: ListItemFreedrenoPresetBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(preset: FreedrenoPreset) {
binding.presetButton.apply {
text = preset.name
setOnClickListener {
onPresetClicked(preset)
}
contentDescription = "${preset.name}: ${preset.description}"
}
}
}
companion object {
private val DiffCallback = object : DiffUtil.ItemCallback<FreedrenoPreset>() {
override fun areItemsTheSame(oldItem: FreedrenoPreset, newItem: FreedrenoPreset): Boolean =
oldItem.name == newItem.name
override fun areContentsTheSame(oldItem: FreedrenoPreset, newItem: FreedrenoPreset): Boolean =
oldItem == newItem
}
}
}
@@ -1,61 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.databinding.ListItemFreedrenoVariableBinding
import org.yuzu.yuzu_emu.fragments.FreedrenoVariable
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
/**
* Adapter for displaying currently set Freedreno environment variables in a list.
*/
class FreedrenoVariableAdapter(
private val context: Context,
private val onItemClicked: (FreedrenoVariable, () -> Unit) -> Unit
) : ListAdapter<FreedrenoVariable, FreedrenoVariableAdapter.VariableViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VariableViewHolder {
val binding = ListItemFreedrenoVariableBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return VariableViewHolder(binding)
}
override fun onBindViewHolder(holder: VariableViewHolder, position: Int) {
holder.bind(getItem(position))
}
inner class VariableViewHolder(private val binding: ListItemFreedrenoVariableBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(variable: FreedrenoVariable) {
binding.variableName.text = variable.name
binding.variableValue.text = variable.value
binding.buttonDelete.setOnClickListener {
onItemClicked(variable) {
NativeFreedrenoConfig.clearFreedrenoEnv(variable.name)
}
}
}
}
companion object {
private val DiffCallback = object : DiffUtil.ItemCallback<FreedrenoVariable>() {
override fun areItemsTheSame(oldItem: FreedrenoVariable, newItem: FreedrenoVariable): Boolean =
oldItem.name == newItem.name
override fun areContentsTheSame(oldItem: FreedrenoVariable, newItem: FreedrenoVariable): Boolean =
oldItem == newItem
}
}
}
@@ -40,7 +40,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
DPAD_SLIDE("dpad_slide"),
HAPTIC_FEEDBACK("haptic_feedback"),
SHOW_INPUT_OVERLAY("show_input_overlay"),
OVERLAY_SNAP_TO_GRID("overlay_snap_to_grid"),
TOUCHSCREEN("touchscreen"),
AIRPLANE_MODE("airplane_mode"),
@@ -64,7 +64,6 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"),
MY_PAGE_APPLET("my_page_applet_mode"),
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
OVERLAY_GRID_SIZE("overlay_grid_size"),
DEBUG_KNOBS("debug_knobs")
;
@@ -25,9 +25,7 @@ object Settings {
SECTION_INPUT_PLAYER_SEVEN,
SECTION_INPUT_PLAYER_EIGHT,
SECTION_APP_SETTINGS(R.string.app_settings),
SECTION_CUSTOM_PATHS(R.string.preferences_custom_paths),
SECTION_DEBUG(R.string.preferences_debug),
SECTION_FREEDRENO(R.string.gpu_driver_settings),
SECTION_APPLETS(R.string.applets_menu);
}
@@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import android.content.Intent
import androidx.annotation.StringRes
/**
* A settings item that launches an intent when clicked.
*/
class LaunchableSetting(
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = "",
val launchIntent: (android.content.Context) -> Intent
) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
override val type = SettingsItem.TYPE_LAUNCHABLE
}
@@ -1,40 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
class PathSetting(
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = "",
@DrawableRes val iconId: Int = 0,
val pathType: PathType,
val defaultPathGetter: () -> String,
val currentPathGetter: () -> String,
val pathSetter: (String) -> Unit
) : SettingsItem(emptySetting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_PATH
enum class PathType {
SAVE_DATA,
NAND,
SDMC
}
fun getCurrentPath(): String = currentPathGetter()
fun getDefaultPath(): String = defaultPathGetter()
fun setPath(path: String) = pathSetter(path)
fun isUsingDefaultPath(): Boolean = getCurrentPath() == getDefaultPath()
companion object {
const val TYPE_PATH = 14
}
}
@@ -97,8 +97,6 @@ abstract class SettingsItem(
const val TYPE_INPUT_PROFILE = 10
const val TYPE_STRING_INPUT = 11
const val TYPE_SPINBOX = 12
const val TYPE_LAUNCHABLE = 13
const val TYPE_PATH = 14
const val FASTMEM_COMBINED = "fastmem_combined"
@@ -306,7 +304,6 @@ abstract class SettingsItem(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
titleId = R.string.renderer_accuracy,
descriptionId = R.string.renderer_accuracy_description,
choicesId = R.array.rendererAccuracyNames,
valuesId = R.array.rendererAccuracyValues
)
@@ -367,30 +364,6 @@ abstract class SettingsItem(
warningMessage = R.string.warning_resolution
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_INPUT_OVERLAY,
titleId = R.string.show_input_overlay,
descriptionId = R.string.show_input_overlay_description
)
)
put(
SwitchSetting(
BooleanSetting.OVERLAY_SNAP_TO_GRID,
titleId = R.string.overlay_snap_to_grid,
descriptionId = R.string.overlay_snap_to_grid_description
)
)
put(
SliderSetting(
IntSetting.OVERLAY_GRID_SIZE,
titleId = R.string.overlay_grid_size,
descriptionId = R.string.overlay_grid_size_description,
min = 16,
max = 128,
units = "px"
)
)
put(
SwitchSetting(
BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE,
@@ -450,7 +423,7 @@ abstract class SettingsItem(
SwitchSetting(
BooleanSetting.SHOW_FRAMETIME,
R.string.show_frametime,
descriptionId = 0
descriptionId = R.string.show_frametime_description
)
)
put(
@@ -471,7 +444,7 @@ abstract class SettingsItem(
SwitchSetting(
BooleanSetting.SHOW_BAT_TEMPERATURE,
R.string.show_bat_temperature,
descriptionId = 0
descriptionId = R.string.show_bat_temperature_description
)
)
put(
@@ -501,21 +474,21 @@ abstract class SettingsItem(
SwitchSetting(
BooleanSetting.SHOW_SOC_OVERLAY,
R.string.enable_soc_overlay,
descriptionId = 0
descriptionId = R.string.soc_overlay_options_description
)
)
put(
SwitchSetting(
BooleanSetting.SOC_OVERLAY_BACKGROUND,
R.string.perf_overlay_background,
descriptionId = 0
descriptionId = R.string.perf_overlay_background_description
)
)
put(
SingleChoiceSetting(
IntSetting.SOC_OVERLAY_POSITION,
titleId = R.string.overlay_position,
descriptionId = 0,
descriptionId = R.string.overlay_position_description,
choicesId = R.array.statsPosition,
valuesId = R.array.staticThemeValues
)
@@ -525,28 +498,28 @@ abstract class SettingsItem(
SwitchSetting(
BooleanSetting.SHOW_DEVICE_MODEL,
titleId = R.string.show_device_model,
descriptionId = 0
descriptionId = R.string.show_device_model_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_GPU_MODEL,
titleId = R.string.show_gpu_model,
descriptionId = 0
descriptionId = R.string.show_gpu_model_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_SOC_MODEL,
titleId = R.string.show_soc_model,
descriptionId = 0
descriptionId = R.string.show_soc_model_description
)
)
put(
SwitchSetting(
BooleanSetting.SHOW_FW_VERSION,
titleId = R.string.show_fw_version,
descriptionId = 0
descriptionId = R.string.show_fw_version_description
)
)
@@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.features.settings.ui.viewholder.*
import org.yuzu.yuzu_emu.utils.ParamPackage
@@ -94,13 +93,6 @@ class SettingsAdapter(
StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_LAUNCHABLE -> {
LaunchableViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_PATH -> {
PathViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@@ -213,20 +205,8 @@ class SettingsAdapter(
}
fun onSubmenuClick(item: SubmenuSetting) {
// Check if this is the Freedreno Settings submenu
if (item.menuKey == Settings.MenuTag.SECTION_FREEDRENO) {
fragment.view?.findNavController()?.navigate(
R.id.action_settingsFragment_to_freedrenoSettingsFragment
)
} else {
val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null)
fragment.view?.findNavController()?.navigate(action)
}
}
fun onLaunchableClick(item: LaunchableSetting) {
val intent = item.launchIntent(context)
fragment.requireActivity().startActivity(intent)
val action = SettingsNavigationDirections.actionGlobalSettingsFragment(item.menuKey, null)
fragment.view?.findNavController()?.navigate(action)
}
fun onInputProfileClick(item: InputProfileSetting, position: Int) {
@@ -462,18 +442,6 @@ class SettingsAdapter(
settingsViewModel.setShouldReloadSettingsList(true)
}
fun onPathClick(item: PathSetting, position: Int) {
settingsViewModel.clickedItem = item
settingsViewModel.setPathSettingPosition(position)
settingsViewModel.setShouldShowPathPicker(true)
}
fun onPathReset(item: PathSetting, position: Int) {
settingsViewModel.clickedItem = item
settingsViewModel.setPathSettingPosition(position)
settingsViewModel.setShouldShowPathResetDialog(true)
}
private class DiffCallback : DiffUtil.ItemCallback<SettingsItem>() {
override fun areItemsTheSame(oldItem: SettingsItem, newItem: SettingsItem): Boolean {
return oldItem.setting.key == newItem.setting.key
@@ -7,16 +7,10 @@
package org.yuzu.yuzu_emu.features.settings.ui
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings as AndroidSettings
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -25,19 +19,14 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.PathUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.*
import java.io.File
import androidx.core.net.toUri
class SettingsFragment : Fragment() {
private lateinit var presenter: SettingsFragmentPresenter
@@ -50,20 +39,6 @@ class SettingsFragment : Fragment() {
private val settingsViewModel: SettingsViewModel by activityViewModels()
private val requestAllFilesPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (hasAllFilesPermission()) {
showPathPickerDialog()
} else {
Toast.makeText(
requireContext(),
R.string.all_files_permission_required,
Toast.LENGTH_LONG
).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
@@ -159,24 +134,6 @@ class SettingsFragment : Fragment() {
}
}
settingsViewModel.shouldShowPathPicker.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setShouldShowPathPicker(false) }
) {
if (it) {
handlePathPickerRequest()
}
}
settingsViewModel.shouldShowPathResetDialog.collect(
viewLifecycleOwner,
resetState = { settingsViewModel.setShouldShowPathResetDialog(false) }
) {
if (it) {
showPathResetDialog()
}
}
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
binding.toolbarSettings.inflateMenu(R.menu.menu_settings)
binding.toolbarSettings.setOnMenuItemClickListener {
@@ -227,199 +184,4 @@ class SettingsFragment : Fragment() {
windowInsets
}
}
private fun hasAllFilesPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
true
}
}
private fun requestAllFilesPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val intent = Intent(AndroidSettings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.data = "package:${requireContext().packageName}".toUri()
requestAllFilesPermissionLauncher.launch(intent)
}
}
private fun handlePathPickerRequest() {
if (!hasAllFilesPermission()) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.all_files_permission_required)
.setMessage(R.string.all_files_permission_required)
.setPositiveButton(R.string.grant_permission) { _, _ ->
requestAllFilesPermission()
}
.setNegativeButton(R.string.cancel, null)
.show()
return
}
showPathPickerDialog()
}
private fun showPathPickerDialog() {
directoryPickerLauncher.launch(null)
}
private val directoryPickerLauncher = registerForActivityResult(
ActivityResultContracts.OpenDocumentTree()
) { uri ->
if (uri != null) {
val pathSetting = settingsViewModel.clickedItem as? PathSetting ?: return@registerForActivityResult
val rawPath = PathUtil.getPathFromUri(uri)
if (rawPath != null) {
handleSelectedPath(pathSetting, rawPath)
} else {
Toast.makeText(
requireContext(),
R.string.invalid_directory,
Toast.LENGTH_SHORT
).show()
}
}
}
private fun handleSelectedPath(pathSetting: PathSetting, path: String) {
if (!PathUtil.validateDirectory(path)) {
Toast.makeText(
requireContext(),
R.string.invalid_directory,
Toast.LENGTH_SHORT
).show()
return
}
if (pathSetting.pathType == PathSetting.PathType.SAVE_DATA) {
val oldPath = pathSetting.getCurrentPath()
if (oldPath != path) {
promptSaveMigration(pathSetting, oldPath, path)
}
} else {
setPathAndNotify(pathSetting, path)
}
}
private fun promptSaveMigration(pathSetting: PathSetting, fromPath: String, toPath: String) {
val sourceSavePath = "$fromPath/user/save"
val destSavePath = "$toPath/user/save"
val sourceSaveDir = File(sourceSavePath)
val destSaveDir = File(destSavePath)
val sourceHasSaves = PathUtil.hasContent(sourceSavePath)
val destHasSaves = PathUtil.hasContent(destSavePath)
if (!sourceHasSaves) {
setPathAndNotify(pathSetting, toPath)
return
}
if (destHasSaves) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.migrate_save_data)
.setMessage(R.string.destination_has_saves)
.setPositiveButton(R.string.confirm) { _, _ ->
migrateSaveData(pathSetting, sourceSaveDir, destSaveDir, toPath)
}
.setNegativeButton(R.string.skip_migration) { _, _ ->
setPathAndNotify(pathSetting, toPath)
}
.setNeutralButton(R.string.cancel, null)
.show()
} else {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.migrate_save_data)
.setMessage(R.string.migrate_save_data_question)
.setPositiveButton(R.string.confirm) { _, _ ->
migrateSaveData(pathSetting, sourceSaveDir, destSaveDir, toPath)
}
.setNegativeButton(R.string.skip_migration) { _, _ ->
setPathAndNotify(pathSetting, toPath)
}
.setNeutralButton(R.string.cancel, null)
.show()
}
}
private fun migrateSaveData(
pathSetting: PathSetting,
sourceDir: File,
destDir: File,
newPath: String
) {
Thread {
val success = PathUtil.copyDirectory(sourceDir, destDir, overwrite = true)
requireActivity().runOnUiThread {
if (success) {
setPathAndNotify(pathSetting, newPath)
Toast.makeText(
requireContext(),
R.string.save_migration_complete,
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
requireContext(),
R.string.save_migration_failed,
Toast.LENGTH_SHORT
).show()
}
}
}.start()
}
private fun setPathAndNotify(pathSetting: PathSetting, path: String) {
pathSetting.setPath(path)
NativeConfig.saveGlobalConfig()
NativeConfig.reloadGlobalConfig()
val messageResId = if (pathSetting.pathType == PathSetting.PathType.SAVE_DATA) {
R.string.save_directory_set
} else {
R.string.path_set
}
Toast.makeText(
requireContext(),
messageResId,
Toast.LENGTH_SHORT
).show()
val position = settingsViewModel.pathSettingPosition.value
if (position >= 0) {
settingsAdapter?.notifyItemChanged(position)
}
}
private fun showPathResetDialog() {
val pathSetting = settingsViewModel.clickedItem as? PathSetting ?: return
if (pathSetting.isUsingDefaultPath()) {
return
}
val currentPath = pathSetting.getCurrentPath()
val defaultPath = pathSetting.getDefaultPath()
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.reset_to_nand)
.setMessage(R.string.migrate_save_data_question)
.setPositiveButton(R.string.confirm) { _, _ ->
val sourceSaveDir = File(currentPath, "user/save")
val destSaveDir = File(defaultPath, "user/save")
if (sourceSaveDir.exists() && sourceSaveDir.listFiles()?.isNotEmpty() == true) {
migrateSaveData(pathSetting, sourceSaveDir, destSaveDir, defaultPath)
} else {
setPathAndNotify(pathSetting, defaultPath)
}
}
.setNegativeButton(R.string.cancel) { _, _ ->
// just dismiss
}
.show()
}
}
@@ -10,7 +10,6 @@ import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
@@ -27,10 +26,8 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import androidx.core.content.edit
import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -110,9 +107,7 @@ class SettingsFragmentPresenter(
MenuTag.SECTION_INPUT_PLAYER_EIGHT -> addInputPlayer(sl, 7)
MenuTag.SECTION_APP_SETTINGS -> addThemeSettings(sl)
MenuTag.SECTION_DEBUG -> addDebugSettings(sl)
MenuTag.SECTION_FREEDRENO -> addFreedrenoSettings(sl)
MenuTag.SECTION_APPLETS -> addAppletSettings(sl)
MenuTag.SECTION_CUSTOM_PATHS -> addCustomPathsSettings(sl)
}
settingsList = sl
adapter.submitList(settingsList) {
@@ -183,16 +178,6 @@ class SettingsFragmentPresenter(
menuKey = MenuTag.SECTION_DEBUG
)
)
if (GpuDriverHelper.isAdrenoGpu() && !NativeConfig.isPerGameConfigLoaded()) {
add(
SubmenuSetting(
titleId = R.string.gpu_driver_settings,
descriptionId = R.string.freedreno_settings_title,
iconId = R.drawable.ic_graphics,
menuKey = MenuTag.SECTION_FREEDRENO
)
)
}
add(
SubmenuSetting(
titleId = R.string.applets_menu,
@@ -201,16 +186,6 @@ class SettingsFragmentPresenter(
menuKey = MenuTag.SECTION_APPLETS
)
)
if (!NativeConfig.isPerGameConfigLoaded()) {
add(
SubmenuSetting(
titleId = R.string.preferences_custom_paths,
descriptionId = R.string.preferences_custom_paths_description,
iconId = R.drawable.ic_folder_open,
menuKey = MenuTag.SECTION_CUSTOM_PATHS
)
)
}
add(
RunnableSetting(
titleId = R.string.reset_to_default,
@@ -319,19 +294,6 @@ class SettingsFragmentPresenter(
private fun addInputOverlaySettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(BooleanSetting.SHOW_INPUT_OVERLAY.key)
add(BooleanSetting.OVERLAY_SNAP_TO_GRID.key)
add(IntSetting.OVERLAY_GRID_SIZE.key)
add(
LaunchableSetting(
titleId = R.string.edit_overlay_layout,
descriptionId = R.string.edit_overlay_layout_description,
launchIntent = { context ->
EmulationActivity.launchForOverlayEdit(context)
}
)
)
add(HeaderSetting(R.string.input_overlay_behavior))
add(BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.key)
add(IntSetting.INPUT_OVERLAY_AUTO_HIDE.key)
add(BooleanSetting.HIDE_OVERLAY_ON_CONTROLLER_INPUT.key)
@@ -510,11 +472,6 @@ class SettingsFragmentPresenter(
}
}
private fun addFreedrenoSettings(sl: ArrayList<SettingsItem>) {
// No additional settings needed here - the SubmenuSetting handles navigation
// This method is kept for consistency with other menu sections
}
private fun addAppletSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(IntSetting.SWKBD_APPLET.key)
@@ -1142,16 +1099,14 @@ class SettingsFragmentPresenter(
)
)
if (IntSetting.THEME.getInt() != 1) {
add(
SingleChoiceSetting(
staticThemeColor,
titleId = R.string.static_theme_color,
choicesId = R.array.staticThemeNames,
valuesId = R.array.staticThemeValues
)
add(
SingleChoiceSetting(
staticThemeColor,
titleId = R.string.static_theme_color,
choicesId = R.array.staticThemeNames,
valuesId = R.array.staticThemeValues
)
}
)
val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
@@ -1213,42 +1168,4 @@ class SettingsFragmentPresenter(
add(IntSetting.DEBUG_KNOBS.key)
}
}
private fun addCustomPathsSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(
PathSetting(
titleId = R.string.custom_save_directory,
descriptionId = R.string.custom_save_directory_description,
iconId = R.drawable.ic_save,
pathType = PathSetting.PathType.SAVE_DATA,
defaultPathGetter = { NativeConfig.getDefaultSaveDir() },
currentPathGetter = { NativeConfig.getSaveDir() },
pathSetter = { path -> NativeConfig.setSaveDir(path) }
)
)
add(
PathSetting(
titleId = R.string.custom_nand_directory,
descriptionId = R.string.custom_nand_directory_description,
iconId = R.drawable.ic_folder_open,
pathType = PathSetting.PathType.NAND,
defaultPathGetter = { DirectoryInitialization.userDirectory + "/nand" },
currentPathGetter = { NativeConfig.getNandDir() },
pathSetter = { path -> NativeConfig.setNandDir(path) }
)
)
add(
PathSetting(
titleId = R.string.custom_sdmc_directory,
descriptionId = R.string.custom_sdmc_directory_description,
iconId = R.drawable.ic_folder_open,
pathType = PathSetting.PathType.SDMC,
defaultPathGetter = { DirectoryInitialization.userDirectory + "/sdmc" },
currentPathGetter = { NativeConfig.getSdmcDir() },
pathSetter = { path -> NativeConfig.setSdmcDir(path) }
)
)
}
}
}
@@ -59,16 +59,6 @@ class SettingsViewModel : ViewModel() {
private val _shouldRecreateForLanguageChange = MutableStateFlow(false)
val shouldRecreateForLanguageChange = _shouldRecreateForLanguageChange.asStateFlow()
private val _shouldShowPathPicker = MutableStateFlow(false)
val shouldShowPathPicker = _shouldShowPathPicker.asStateFlow()
private val _shouldShowPathResetDialog = MutableStateFlow(false)
val shouldShowPathResetDialog = _shouldShowPathResetDialog.asStateFlow()
private val _pathSettingPosition = MutableStateFlow(-1)
val pathSettingPosition = _pathSettingPosition.asStateFlow()
fun setShouldRecreate(value: Boolean) {
_shouldRecreate.value = value
}
@@ -122,18 +112,6 @@ class SettingsViewModel : ViewModel() {
_shouldRecreateForLanguageChange.value = value
}
fun setShouldShowPathPicker(value: Boolean) {
_shouldShowPathPicker.value = value
}
fun setShouldShowPathResetDialog(value: Boolean) {
_shouldShowPathResetDialog.value = value
}
fun setPathSettingPosition(value: Int) {
_pathSettingPosition.value = value
}
fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
try {
InputHandler.registeredControllers[currentDevice]
@@ -1,42 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 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.LaunchableSetting
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 LaunchableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: LaunchableSetting
override fun bind(item: SettingsItem) {
setting = item as LaunchableSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = ""
binding.textSettingValue.setCompoundDrawablesRelativeWithIntrinsicBounds(
0, 0, R.drawable.ic_arrow_forward, 0
)
binding.buttonClear.setVisible(false)
}
override fun onClick(clicked: View) {
adapter.onLaunchableClick(setting)
}
override fun onLongClick(clicked: View): Boolean {
// no-op
return true
}
}
@@ -1,64 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 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 androidx.core.content.res.ResourcesCompat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting
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.PathUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class PathViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: PathSetting
override fun bind(item: SettingsItem) {
setting = item as PathSetting
binding.icon.setVisible(setting.iconId != 0)
if (setting.iconId != 0) {
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.resources,
setting.iconId,
binding.icon.context.theme
)
)
}
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
val currentPath = setting.getCurrentPath()
val displayPath = PathUtil.truncatePathForDisplay(currentPath)
binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = if (setting.isUsingDefaultPath()) {
binding.root.context.getString(R.string.default_string)
} else {
displayPath
}
binding.buttonClear.setVisible(!setting.isUsingDefaultPath())
binding.buttonClear.text = binding.root.context.getString(R.string.reset_to_default)
binding.buttonClear.setOnClickListener {
adapter.onPathReset(setting, bindingAdapterPosition)
}
setStyle(true, binding)
}
override fun onClick(clicked: View) {
adapter.onPathClick(setting, bindingAdapterPosition)
}
override fun onLongClick(clicked: View): Boolean {
return false
}
}
@@ -45,7 +45,7 @@ class DriverFetcherFragment : Fragment() {
private val client = OkHttpClient()
private val gpuModel: String?
get() = GpuDriverHelper.hookLibPath?.let { GpuDriverHelper.getGpuModel(hookLibPath = it) }
get() = GpuDriverHelper.getGpuModel()
private val adrenoModel: Int
get() = parseAdrenoModel()
@@ -87,7 +87,6 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
@@ -202,10 +201,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onCreate(savedInstanceState)
updateOrientation()
if (args.overlayGamelessEditMode) {
return
}
val intent = requireActivity().intent
val intentUri: Uri? = intent.data
intentGame = null
@@ -304,18 +299,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
throw fallbackException
}
}
try {
if (GpuDriverHelper.isAdrenoGpu()) {
val programIdHex = game!!.programIdHex
if (NativeFreedrenoConfig.loadPerGameConfigWithGlobalFallback(programIdHex)) {
Log.info("[EmulationFragment] Loaded per-game Freedreno config for $programIdHex")
} else {
Log.info("[EmulationFragment] Using global Freedreno config for $programIdHex")
}
}
} catch (e: Exception) {
Log.warning("[EmulationFragment] Failed to load Freedreno config: ${e.message}")
}
emulationState = EmulationState(game!!.path) {
return@EmulationState driverViewModel.isInteractionAllowed.value
@@ -575,11 +558,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return
}
if (args.overlayGamelessEditMode) {
setupOverlayGamelessEditMode()
return
}
if (game == null) {
Log.warning(
"[EmulationFragment] Game not yet initialized in onViewCreated - will be set up by async intent handler"
@@ -590,46 +568,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
completeViewSetup()
}
private fun setupOverlayGamelessEditMode() {
binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.refreshControls(gameless = true)
}
binding.doneControlConfig.setOnClickListener {
finishOverlayGamelessEditMode()
}
binding.doneControlConfig.visibility = View.VISIBLE
binding.surfaceInputOverlay.setIsInEditMode(true)
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.surfaceInputOverlay.visibility = View.VISIBLE
binding.loadingIndicator.visibility = View.GONE
// in gameless edit mode, back = done
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
finishOverlayGamelessEditMode()
}
}
)
}
private fun finishOverlayGamelessEditMode() {
binding.surfaceInputOverlay.setIsInEditMode(false)
NativeConfig.saveGlobalConfig()
requireActivity().finish()
}
private fun completeViewSetup() {
if (_binding == null || game == null) {
return
}
Log.info("[EmulationFragment] Starting view setup for game: ${game?.title}")
gpuModel = GpuDriverHelper.hookLibPath?.let { GpuDriverHelper.getGpuModel(hookLibPath = it).toString() } ?: "Unknown"
gpuModel = GpuDriverHelper.getGpuModel().toString()
fwVersion = NativeLibrary.firmwareVersion()
updateQuickOverlayMenuEntry(BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean())
@@ -955,12 +900,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
b.surfaceInputOverlay.setVisible(visible = false, gone = false)
}
} else {
val shouldShowOverlay = if (args.overlayGamelessEditMode) {
true
} else {
b.surfaceInputOverlay.setVisible(
showInputOverlay && emulationViewModel.emulationStarted.value
}
b.surfaceInputOverlay.setVisible(shouldShowOverlay)
)
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
b.surfaceInputOverlay.layout = OverlayLayout.Portrait
@@ -1589,8 +1531,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
findItem(R.id.menu_show_overlay).isChecked =
BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
findItem(R.id.menu_snap_to_grid).isChecked =
BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()
findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
}
@@ -1619,13 +1559,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
R.id.menu_snap_to_grid -> {
it.isChecked = !it.isChecked
BooleanSetting.OVERLAY_SNAP_TO_GRID.setBoolean(it.isChecked)
binding.surfaceInputOverlay.invalidate()
true
}
R.id.menu_adjust_overlay -> {
adjustOverlay()
true
@@ -2002,17 +1935,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
handler.removeCallbacksAndMessages(null)
handler.postDelayed({
if (isOverlayVisible && isAdded && _binding != null) {
if (isOverlayVisible) {
hideOverlay()
}
}, seconds * 1000L)
}
fun handleScreenTap(isLongTap: Boolean) {
if (binding.surfaceInputOverlay.isGamelessMode()) {
return
}
val autoHideSeconds = IntSetting.INPUT_OVERLAY_AUTO_HIDE.getInt()
val shouldProceed = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean() && BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.getBoolean()
@@ -2034,10 +1963,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun initializeOverlayAutoHide() {
if (binding.surfaceInputOverlay.isGamelessMode()) {
return
}
val autoHideSeconds = IntSetting.INPUT_OVERLAY_AUTO_HIDE.getInt()
val autoHideEnabled = BooleanSetting.ENABLE_INPUT_OVERLAY_AUTO_HIDE.getBoolean()
val showOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
@@ -1,204 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 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.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.FreedrenoPresetAdapter
import org.yuzu.yuzu_emu.adapters.FreedrenoVariableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFreedrenoSettingsBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.FreedrenoPresets
class FreedrenoSettingsFragment : Fragment() {
private var _binding: FragmentFreedrenoSettingsBinding? = null
private val binding get() = _binding!!
private val args by navArgs<FreedrenoSettingsFragmentArgs>()
private val game: Game? get() = args.game
private val isPerGameConfig: Boolean get() = game != null
private lateinit var presetAdapter: FreedrenoPresetAdapter
private lateinit var settingsAdapter: FreedrenoVariableAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentFreedrenoSettingsBinding.inflate(layoutInflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
NativeFreedrenoConfig.setFreedrenoBasePath(requireContext().cacheDir.absolutePath)
NativeFreedrenoConfig.initializeFreedrenoConfig()
if (isPerGameConfig) {
NativeFreedrenoConfig.loadPerGameConfig(game!!.programIdHex)
} else {
NativeFreedrenoConfig.reloadFreedrenoConfig()
}
setupToolbar()
setupAdapters()
loadCurrentSettings()
setupButtonListeners()
setupWindowInsets()
}
private fun setupToolbar() {
binding.toolbarFreedreno.setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
if (isPerGameConfig) {
binding.toolbarFreedreno.title = getString(R.string.freedreno_per_game_title)
binding.toolbarFreedreno.subtitle = game!!.title
}
}
private fun setupAdapters() {
// Setup presets adapter (horizontal list)
presetAdapter = FreedrenoPresetAdapter { preset ->
applyPreset(preset)
}
binding.listFreedrenoPresets.apply {
adapter = presetAdapter
layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
}
presetAdapter.submitList(FreedrenoPresets.ALL_PRESETS)
// Setup current settings adapter (vertical list)
settingsAdapter = FreedrenoVariableAdapter(requireContext()) { variable, onDelete ->
onDelete()
loadCurrentSettings() // Refresh list after deletion
}
binding.listFreedrenoSettings.apply {
adapter = settingsAdapter
layoutManager = LinearLayoutManager(requireContext())
}
}
private fun loadCurrentSettings() {
// Load all currently set environment variables
val variables = mutableListOf<FreedrenoVariable>()
// Common variables to check
val commonVars = listOf(
"TU_DEBUG", "FD_MESA_DEBUG", "IR3_SHADER_DEBUG",
"FD_RD_DUMP", "FD_RD_DUMP_FRAMES", "FD_RD_DUMP_TESTNAME",
"TU_BREADCRUMBS"
)
for (varName in commonVars) {
if (NativeFreedrenoConfig.isFreedrenoEnvSet(varName)) {
val value = NativeFreedrenoConfig.getFreedrenoEnv(varName)
variables.add(FreedrenoVariable(varName, value))
}
}
settingsAdapter.submitList(variables)
}
private fun setupButtonListeners() {
binding.buttonAddVariable.setOnClickListener {
val varName = binding.variableNameInput.text.toString().trim()
val varValue = binding.variableValueInput.text.toString().trim()
if (varName.isEmpty()) {
showSnackbar(getString(R.string.freedreno_error_empty_name))
return@setOnClickListener
}
if (NativeFreedrenoConfig.setFreedrenoEnv(varName, varValue)) {
showSnackbar(getString(R.string.freedreno_variable_added, varName))
binding.variableNameInput.text?.clear()
binding.variableValueInput.text?.clear()
loadCurrentSettings()
} else {
showSnackbar(getString(R.string.freedreno_error_setting_variable))
}
}
binding.buttonClearAll.setOnClickListener {
NativeFreedrenoConfig.clearAllFreedrenoEnv()
showSnackbar(getString(R.string.freedreno_cleared_all))
loadCurrentSettings()
}
binding.buttonSave.setOnClickListener {
if (isPerGameConfig) {
NativeFreedrenoConfig.savePerGameConfig(game!!.programIdHex)
showSnackbar(getString(R.string.freedreno_per_game_saved))
} else {
NativeFreedrenoConfig.saveFreedrenoConfig()
showSnackbar(getString(R.string.freedreno_saved))
}
}
}
private fun applyPreset(preset: org.yuzu.yuzu_emu.utils.FreedrenoPreset) {
// Clear all first for consistency
NativeFreedrenoConfig.clearAllFreedrenoEnv()
// Apply all variables in the preset
for ((varName, varValue) in preset.variables) {
NativeFreedrenoConfig.setFreedrenoEnv(varName, varValue)
}
showSnackbar(getString(R.string.freedreno_preset_applied, preset.name))
loadCurrentSettings()
}
private fun setupWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val systemInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
binding.root.updatePadding(
left = systemInsets.left,
right = systemInsets.right,
bottom = systemInsets.bottom
)
insets
}
}
private fun showSnackbar(message: String) {
Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
/**
* Data class representing a Freedreno environment variable.
*/
data class FreedrenoVariable(
val name: String,
val value: String
)
@@ -325,20 +325,6 @@ class GamePropertiesFragment : Fragment() {
)
)
}
if (GpuDriverHelper.isAdrenoGpu()) {
add(
SubmenuProperty(
R.string.freedreno_per_game_title,
R.string.freedreno_per_game_description,
R.drawable.ic_graphics,
action = {
val action = GamePropertiesFragmentDirections
.actionPerGamePropertiesFragmentToFreedrenoSettingsFragment(args.game)
binding.root.findNavController().navigate(action)
}
)
)
}
if (!args.game.isHomebrew) {
add(
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -31,7 +31,6 @@ import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
@@ -100,11 +99,11 @@ class InstallableFragment : Fragment() {
},
export = {
val oldSaveDataFolder = File(
NativeConfig.getSaveDir() +
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
val futureSaveDataFolder = File(
NativeConfig.getSaveDir() +
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (!oldSaveDataFolder.exists() && !futureSaveDataFolder.exists()) {
@@ -214,7 +213,7 @@ class InstallableFragment : Fragment() {
}
val internalSaveFolder = File(
"${NativeConfig.getSaveDir()}$baseSaveDir"
"${DirectoryInitialization.userDirectory}/nand$baseSaveDir"
)
internalSaveFolder.deleteRecursively()
internalSaveFolder.mkdir()
@@ -291,7 +290,7 @@ class InstallableFragment : Fragment() {
cacheSaveDir.mkdir()
val oldSaveDataFolder = File(
NativeConfig.getSaveDir() +
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(false)
)
if (oldSaveDataFolder.exists()) {
@@ -299,7 +298,7 @@ class InstallableFragment : Fragment() {
}
val futureSaveDataFolder = File(
NativeConfig.getSaveDir() +
"${DirectoryInitialization.userDirectory}/nand" +
NativeLibrary.getDefaultProfileSaveDataRoot(true)
)
if (futureSaveDataFolder.exists()) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,7 +15,6 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.NativeConfig
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -61,7 +57,8 @@ class Game(
}.zip"
val saveDir: String
get() = NativeConfig.getSaveDir() + NativeLibrary.getSavePath(programId)
get() = DirectoryInitialization.userDirectory + "/nand" +
NativeLibrary.getSavePath(programId)
val addonDir: String
get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/"
@@ -8,8 +8,6 @@ import android.content.Context
import android.content.SharedPreferences
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.graphics.Rect
import android.graphics.drawable.Drawable
@@ -52,7 +50,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet()
private var inEditMode = false
private var gamelessMode = false
private var buttonBeingConfigured: InputOverlayDrawableButton? = null
private var dpadBeingConfigured: InputOverlayDrawableDpad? = null
private var joystickBeingConfigured: InputOverlayDrawableJoystick? = null
@@ -63,12 +60,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private var hasMoved = false
private val moveThreshold = 20f
private val gridPaint = Paint().apply {
color = Color.argb(60, 255, 255, 255)
strokeWidth = 1f
style = Paint.Style.STROKE
}
private lateinit var windowInsets: WindowInsets
var layout = OverlayLayout.Landscape
@@ -100,12 +91,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
override fun draw(canvas: Canvas) {
super.draw(canvas)
// Draw grid when in edit mode and snap-to-grid is enabled
if (inEditMode && BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
drawGrid(canvas)
}
for (button in overlayButtons) {
button.draw(canvas)
}
@@ -117,26 +102,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
}
private fun drawGrid(canvas: Canvas) {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
val width = canvas.width
val height = canvas.height
// Draw vertical lines
var x = 0
while (x <= width) {
canvas.drawLine(x.toFloat(), 0f, x.toFloat(), height.toFloat(), gridPaint)
x += gridSize
}
// Draw horizontal lines
var y = 0
while (y <= height) {
canvas.drawLine(0f, y.toFloat(), width.toFloat(), y.toFloat(), gridPaint)
y += gridSize
}
}
override fun onTouch(v: View, event: MotionEvent): Boolean {
if (inEditMode) {
return onTouchWhileEditing(event)
@@ -703,19 +668,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
}
fun refreshControls(gameless: Boolean = false) {
// Store gameless mode if set to true
if (gameless) {
gamelessMode = true
}
fun refreshControls() {
// Remove all the overlay buttons from the HashSet.
overlayButtons.clear()
overlayDpads.clear()
overlayJoysticks.clear()
// Add all the enabled overlay items back to the HashSet.
if (gamelessMode || BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
addOverlayControls(layout)
}
invalidate()
@@ -752,14 +712,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
if (!editMode) {
scaleDialog?.dismiss()
scaleDialog = null
gamelessMode = false
}
invalidate()
}
fun isGamelessMode(): Boolean = gamelessMode
private fun showScaleDialog(
button: InputOverlayDrawableButton?,
dpad: InputOverlayDrawableDpad?,
@@ -912,7 +867,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
}
companion object {
// Increase this number every time there is a breaking change to every overlay layout
const val OVERLAY_VERSION = 1
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,8 +11,6 @@ import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
/**
@@ -126,23 +121,11 @@ class InputOverlayDrawableButton(
MotionEvent.ACTION_MOVE -> {
controlPositionX += fingerPositionX - previousTouchX
controlPositionY += fingerPositionY - previousTouchY
val finalX = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionX)
} else {
controlPositionX
}
val finalY = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionY)
} else {
controlPositionY
}
setBounds(
finalX,
finalY,
width + finalX,
height + finalY
controlPositionX,
controlPositionY,
width + controlPositionX,
height + controlPositionY
)
previousTouchX = fingerPositionX
previousTouchY = fingerPositionY
@@ -151,11 +134,6 @@ class InputOverlayDrawableButton(
return true
}
private fun snapToGrid(value: Int): Int {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
return ((value + gridSize / 2) / gridSize) * gridSize
}
fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
defaultStateBitmap.setBounds(left, top, right, bottom)
pressedStateBitmap.setBounds(left, top, right, bottom)
@@ -11,8 +11,6 @@ import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
/**
* Custom [BitmapDrawable] that is capable
@@ -231,23 +229,11 @@ class InputOverlayDrawableDpad(
MotionEvent.ACTION_MOVE -> {
controlPositionX += fingerPositionX - previousTouchX
controlPositionY += fingerPositionY - previousTouchY
val finalX = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionX)
} else {
controlPositionX
}
val finalY = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionY)
} else {
controlPositionY
}
setBounds(
finalX,
finalY,
width + finalX,
height + finalY
controlPositionX,
controlPositionY,
width + controlPositionX,
height + controlPositionY
)
previousTouchX = fingerPositionX
previousTouchY = fingerPositionY
@@ -256,11 +242,6 @@ class InputOverlayDrawableDpad(
return true
}
private fun snapToGrid(value: Int): Int {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
return ((value + gridSize / 2) / gridSize) * gridSize
}
fun setPosition(x: Int, y: Int) {
controlPositionX = x
controlPositionY = y
@@ -17,7 +17,6 @@ import org.yuzu.yuzu_emu.features.input.NativeInput.ButtonState
import org.yuzu.yuzu_emu.features.input.model.NativeAnalog
import org.yuzu.yuzu_emu.features.input.model.NativeButton
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
/**
* Custom [BitmapDrawable] that is capable
@@ -214,37 +213,25 @@ class InputOverlayDrawableJoystick(
MotionEvent.ACTION_MOVE -> {
controlPositionX += fingerPositionX - previousTouchX
controlPositionY += fingerPositionY - previousTouchY
val finalX = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionX)
} else {
controlPositionX
}
val finalY = if (BooleanSetting.OVERLAY_SNAP_TO_GRID.getBoolean()) {
snapToGrid(controlPositionY)
} else {
controlPositionY
}
bounds = Rect(
finalX,
finalY,
outerBitmap.intrinsicWidth + finalX,
outerBitmap.intrinsicHeight + finalY
controlPositionX,
controlPositionY,
outerBitmap.intrinsicWidth + controlPositionX,
outerBitmap.intrinsicHeight + controlPositionY
)
virtBounds = Rect(
finalX,
finalY,
outerBitmap.intrinsicWidth + finalX,
outerBitmap.intrinsicHeight + finalY
controlPositionX,
controlPositionY,
outerBitmap.intrinsicWidth + controlPositionX,
outerBitmap.intrinsicHeight + controlPositionY
)
setInnerBounds()
bounds = Rect(
Rect(
finalX,
finalY,
outerBitmap.intrinsicWidth + finalX,
outerBitmap.intrinsicHeight + finalY
controlPositionX,
controlPositionY,
outerBitmap.intrinsicWidth + controlPositionX,
outerBitmap.intrinsicHeight + controlPositionY
)
)
previousTouchX = fingerPositionX
@@ -255,11 +242,6 @@ class InputOverlayDrawableJoystick(
return true
}
private fun snapToGrid(value: Int): Int {
val gridSize = IntSetting.OVERLAY_GRID_SIZE.getInt()
return ((value + gridSize / 2) / gridSize) * gridSize
}
private fun setInnerBounds() {
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
@@ -456,7 +456,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
val firmwarePath =
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
ProgressDialogFragment.newInstance(
@@ -499,7 +499,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
fun uninstallFirmware() {
val firmwarePath =
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
ProgressDialogFragment.newInstance(
this,
R.string.firmware_uninstalling
@@ -23,16 +23,10 @@ object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
private var fileRedirectionPath: String? = null
var driverInstallationPath: String? = null
internal var hookLibPath: String? = null
private var hookLibPath: String? = null
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
fun initializeFreedrenoConfigEarly() {
NativeFreedrenoConfig.setFreedrenoBasePath(YuzuApplication.appContext.cacheDir.absolutePath)
NativeFreedrenoConfig.initializeFreedrenoConfig()
NativeFreedrenoConfig.reloadFreedrenoConfig()
}
fun initializeDriverParameters() {
try {
// Initialize the file redirection directory.
@@ -46,9 +40,11 @@ object GpuDriverHelper {
throw RuntimeException(e)
}
// Initialize directories.
initializeDirectories()
// Initialize hook libraries directory.
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
NativeFreedrenoConfig.reloadFreedrenoConfig()
// Initialize GPU driver.
NativeLibrary.initializeGpuDriver(
@@ -215,17 +211,9 @@ object GpuDriverHelper {
external fun getGpuModel(
surface: Surface = Surface(SurfaceTexture(true)),
hookLibPath: String
hookLibPath: String = GpuDriverHelper.hookLibPath!!
): String?
fun isAdrenoGpu(): Boolean {
return try {
supportsCustomDriverLoading()
} catch (e: Exception) {
false
}
}
// Parse the custom driver metadata to retrieve the name.
val installedCustomDriverData: GpuDriverMetadata
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -186,22 +183,4 @@ object NativeConfig {
*/
@Synchronized
external fun saveControlPlayerValues()
/**
* Directory paths getters and setters
*/
@Synchronized
external fun getSaveDir(): String
@Synchronized
external fun getDefaultSaveDir(): String
@Synchronized
external fun setSaveDir(path: String)
@Synchronized
external fun getNandDir(): String
@Synchronized
external fun setNandDir(path: String)
@Synchronized
external fun getSdmcDir(): String
@Synchronized
external fun setSdmcDir(path: String)
}
@@ -1,164 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
/**
* Provides access to Freedreno/Turnip driver configuration through JNI bindings.
*
* This class allows Java/Kotlin code to configure Freedreno environment variables
* for the GPU driver (Turnip/Freedreno) that runs in the emulator on Android.
*
* Variables must be set BEFORE starting emulation for them to take effect.
*
* See https://docs.mesa3d.org/drivers/freedreno.html for documentation.
*/
object NativeFreedrenoConfig {
@Synchronized
external fun setFreedrenoBasePath(basePath: String)
@Synchronized
external fun initializeFreedrenoConfig()
@Synchronized
external fun saveFreedrenoConfig()
@Synchronized
external fun reloadFreedrenoConfig()
@Synchronized
external fun setFreedrenoEnv(varName: String, value: String): Boolean
@Synchronized
external fun getFreedrenoEnv(varName: String): String
@Synchronized
external fun isFreedrenoEnvSet(varName: String): Boolean
@Synchronized
external fun clearFreedrenoEnv(varName: String): Boolean
@Synchronized
external fun clearAllFreedrenoEnv()
@Synchronized
external fun getFreedrenoEnvSummary(): String
@Synchronized
external fun setCurrentProgramId(programId: String)
@Synchronized
external fun loadPerGameConfig(programId: String): Boolean
@Synchronized
external fun loadPerGameConfigWithGlobalFallback(programId: String): Boolean
@Synchronized
external fun savePerGameConfig(programId: String): Boolean
@Synchronized
external fun hasPerGameConfig(programId: String): Boolean
@Synchronized
external fun deletePerGameConfig(programId: String): Boolean
}
/**
* Data class representing a Freedreno preset configuration.
* Presets are commonly used debugging/profiling configurations.
*/
data class FreedrenoPreset(
val name: String, // Display name (e.g., "Debug - CPU Memory")
val description: String, // Description of what this preset does
val icon: String, // Icon identifier
val variables: Map<String, String> // Map of env vars to set
)
/**
* Predefined Freedreno presets for quick configuration.
*/
object FreedrenoPresets {
val DEBUG_CPU_MEMORY = FreedrenoPreset(
name = "Debug - CPU Memory",
description = "Use CPU memory (slower but more stable)",
icon = "ic_debug_cpu",
variables = mapOf(
"TU_DEBUG" to "sysmem"
)
)
val DEBUG_UBWC_DISABLED = FreedrenoPreset(
name = "Debug - No UBWC",
description = "Disable UBWC compression for debugging",
icon = "ic_debug_ubwc",
variables = mapOf(
"TU_DEBUG" to "noubwc"
)
)
val DEBUG_NO_BINNING = FreedrenoPreset(
name = "Debug - No Binning",
description = "Disable binning optimization",
icon = "ic_debug_bin",
variables = mapOf(
"TU_DEBUG" to "nobin"
)
)
val CAPTURE_RENDERPASS = FreedrenoPreset(
name = "Capture - Renderpass",
description = "Capture command stream data for debugging",
icon = "ic_capture",
variables = mapOf(
"FD_RD_DUMP" to "enable"
)
)
val CAPTURE_FRAMES = FreedrenoPreset(
name = "Capture - First 100 Frames",
description = "Capture command stream for first 100 frames only",
icon = "ic_capture",
variables = mapOf(
"FD_RD_DUMP" to "enable",
"FD_RD_DUMP_FRAMES" to "0-100"
)
)
val SHADER_DEBUG = FreedrenoPreset(
name = "Shader Debug",
description = "Enable IR3 shader compiler debugging",
icon = "ic_shader",
variables = mapOf(
"IR3_SHADER_DEBUG" to "nouboopt,spillall"
)
)
val GPU_HANG_TRACE = FreedrenoPreset(
name = "GPU Hang Trace",
description = "Trace GPU progress for debugging hangs",
icon = "ic_hang_trace",
variables = mapOf(
"TU_BREADCRUMBS" to "1"
)
)
val PERFORMANCE_DEFAULT = FreedrenoPreset(
name = "Performance - Default",
description = "Clear all debug options for performance",
icon = "ic_performance",
variables = emptyMap() // Clears all when applied
)
val ALL_PRESETS = listOf(
DEBUG_CPU_MEMORY,
DEBUG_UBWC_DISABLED,
DEBUG_NO_BINNING,
CAPTURE_RENDERPASS,
CAPTURE_FRAMES,
SHADER_DEBUG,
GPU_HANG_TRACE,
PERFORMANCE_DEFAULT
)
}
@@ -1,97 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.net.Uri
import android.provider.DocumentsContract
import java.io.File
object PathUtil {
/**
* Converts a content:// URI from the Storage Access Framework to a real filesystem path.
*/
fun getPathFromUri(uri: Uri): String? {
val docId = try {
DocumentsContract.getTreeDocumentId(uri)
} catch (_: Exception) {
return null
}
if (docId.startsWith("primary:")) {
val relativePath = docId.substringAfter(":")
val primaryStoragePath = android.os.Environment.getExternalStorageDirectory().absolutePath
return "$primaryStoragePath/$relativePath"
}
// external SD cards and other volumes)
val storageIdString = docId.substringBefore(":")
val removablePath = getRemovableStoragePath(storageIdString)
if (removablePath != null) {
return "$removablePath/${docId.substringAfter(":")}"
}
return null
}
/**
* Validates that a path is a valid, writable directory.
* Creates the directory if it doesn't exist.
*/
fun validateDirectory(path: String): Boolean {
val dir = File(path)
if (!dir.exists()) {
if (!dir.mkdirs()) {
return false
}
}
return dir.isDirectory && dir.canWrite()
}
/**
* Copies a directory recursively from source to destination.
*/
fun copyDirectory(source: File, destination: File, overwrite: Boolean = true): Boolean {
return try {
source.copyRecursively(destination, overwrite)
true
} catch (_: Exception) {
false
}
}
/**
* Checks if a directory has any content.
*/
fun hasContent(path: String): Boolean {
val dir = File(path)
return dir.exists() && dir.listFiles()?.isNotEmpty() == true
}
fun truncatePathForDisplay(path: String, maxLength: Int = 40): String {
return if (path.length > maxLength) {
"...${path.takeLast(maxLength - 3)}"
} else {
path
}
}
// This really shouldn't be necessary, but the Android API seemingly
// doesn't have a way of doing this?
// Apparently, on certain devices the mount location can vary, so add
// extra cases here if we discover any new ones.
fun getRemovableStoragePath(idString: String): String? {
var pathFile: File
pathFile = File("/mnt/media_rw/$idString");
if (pathFile.exists()) {
return pathFile.absolutePath
}
return null
}
}
@@ -35,14 +35,6 @@ class GradientBorderCardView @JvmOverloads constructor(
updateThemeState()
}
private fun updateBorderState() {
val shouldShow = isPressed || isFocused || isSelected
if (showGradientBorder != shouldShow) {
showGradientBorder = shouldShow
invalidate()
}
}
private fun updateThemeState() {
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val themeIndex = try {
@@ -51,7 +43,6 @@ class GradientBorderCardView @JvmOverloads constructor(
0 // Default to Eden theme if error
}
isEdenTheme = themeIndex == 0
invalidate()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
@@ -102,22 +93,27 @@ class GradientBorderCardView @JvmOverloads constructor(
override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
updateBorderState()
showGradientBorder = gainFocus
invalidate()
}
override fun setSelected(selected: Boolean) {
super.setSelected(selected)
updateBorderState()
showGradientBorder = selected
invalidate()
}
override fun setPressed(pressed: Boolean) {
super.setPressed(pressed)
updateBorderState()
if (pressed) {
showGradientBorder = true
invalidate()
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (showGradientBorder) {
if (showGradientBorder && !isPressed) {
canvas.drawPath(borderPath, borderPaint)
}
}
@@ -125,5 +121,6 @@ class GradientBorderCardView @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
updateThemeState()
requestLayout()
}
}
@@ -10,7 +10,6 @@ add_library(yuzu-android SHARED
native.cpp
native.h
native_config.cpp
native_freedreno.cpp
android_settings.cpp
game_metadata.cpp
native_log.cpp
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <common/fs/path_util.h>
#include <common/logging/log.h>
#include <input_common/main.h>
#include "android_config.h"
@@ -69,24 +68,6 @@ void AndroidConfig::ReadPathValues() {
}
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);
}
const auto sdmc_dir_setting = ReadStringSetting(std::string("sdmc_directory"));
if (!sdmc_dir_setting.empty()) {
Common::FS::SetEdenPath(Common::FS::EdenPath::SDMCDir, sdmc_dir_setting);
}
const auto save_dir_setting = ReadStringSetting(std::string("save_directory"));
if (save_dir_setting.empty()) {
Common::FS::SetEdenPath(Common::FS::EdenPath::SaveDir,
Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir));
} else {
Common::FS::SetEdenPath(Common::FS::EdenPath::SaveDir, save_dir_setting);
}
EndGroup();
}
@@ -241,26 +222,6 @@ void AndroidConfig::SavePathValues() {
}
EndArray();
// Save custom NAND directory
const auto nand_path = Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir);
WriteStringSetting(std::string("nand_directory"), nand_path,
std::make_optional(std::string("")));
// Save custom SDMC directory
const auto sdmc_path = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
WriteStringSetting(std::string("sdmc_directory"), sdmc_path,
std::make_optional(std::string("")));
// Save custom save directory
const auto save_path = Common::FS::GetEdenPathString(Common::FS::EdenPath::SaveDir);
if (save_path == nand_path) {
WriteStringSetting(std::string("save_directory"), std::string(""),
std::make_optional(std::string("")));
} else {
WriteStringSetting(std::string("save_directory"), save_path,
std::make_optional(std::string("")));
}
EndGroup();
}
@@ -146,10 +146,6 @@ namespace AndroidSettings {
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> overlay_snap_to_grid{linkage, false, "overlay_snap_to_grid",
Settings::Category::Overlay};
Settings::Setting<s32> overlay_grid_size{linkage, 32, "overlay_grid_size",
Settings::Category::Overlay};
Settings::Setting<bool> touchscreen{linkage, true, "touchscreen",
Settings::Category::Overlay};
Settings::Setting<s32> lock_drawer{linkage, false, "lock_drawer",
+17 -34
View File
@@ -730,28 +730,11 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
return false;
}
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env,
[[maybe_unused]] jclass clazz,
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
jstring hook_lib_dir,
jstring custom_driver_dir,
jstring custom_driver_name,
jstring file_redirect_dir) {
// Log active Freedreno environment variables
const char* tu_debug = getenv("TU_DEBUG");
const char* fd_debug = getenv("FD_MESA_DEBUG");
const char* ir3_debug = getenv("IR3_SHADER_DEBUG");
const char* fd_rd_dump = getenv("FD_RD_DUMP");
const char* tu_breadcrumbs = getenv("TU_BREADCRUMBS");
if (tu_debug || fd_debug || ir3_debug || fd_rd_dump || tu_breadcrumbs) {
LOG_INFO(Frontend, "[Freedreno] Initializing GPU driver with configuration:");
if (tu_debug) LOG_INFO(Frontend, "[Freedreno] TU_DEBUG={}", tu_debug);
if (fd_debug) LOG_INFO(Frontend, "[Freedreno] FD_MESA_DEBUG={}", fd_debug);
if (ir3_debug) LOG_INFO(Frontend, "[Freedreno] IR3_SHADER_DEBUG={}", ir3_debug);
if (fd_rd_dump) LOG_INFO(Frontend, "[Freedreno] FD_RD_DUMP={}", fd_rd_dump);
if (tu_breadcrumbs) LOG_INFO(Frontend, "[Freedreno] TU_BREADCRUMBS={}", tu_breadcrumbs);
}
EmulationSession::GetInstance().InitializeGpuDriver(
Common::Android::GetJString(env, hook_lib_dir),
Common::Android::GetJString(env, custom_driver_dir),
@@ -1183,8 +1166,11 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
void Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerInit(JNIEnv* env, jobject obj) {
// for some reason the full user directory isnt initialized in Android, so we need to create it
const auto play_time_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::PlayTimeDir);
if (!Common::FS::IsDir(play_time_dir) && !Common::FS::CreateDir(play_time_dir))
LOG_WARNING(Frontend, "Failed to create play time directory");
if (!Common::FS::IsDir(play_time_dir)) {
if (!Common::FS::CreateDir(play_time_dir)) {
LOG_WARNING(Frontend, "Failed to create play time directory");
}
}
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
}
@@ -1197,16 +1183,13 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerStart(JNIEnv* env, job
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerStop(JNIEnv* env, jobject obj) {
if (play_time_manager)
play_time_manager->Stop();
play_time_manager->Stop();
}
jlong Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerGetPlayTime(JNIEnv* env, jobject obj, jstring jprogramId) {
if (play_time_manager) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
return play_time_manager->GetPlayTime(program_id);
}
return 0UL;
jlong Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerGetPlayTime(JNIEnv* env, jobject obj,
jstring jprogramId) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
return play_time_manager->GetPlayTime(program_id);
}
jlong Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerGetCurrentTitleId(JNIEnv* env,
@@ -1216,17 +1199,17 @@ jlong Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerGetCurrentTitleId(JNI
void Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerResetProgramPlayTime(JNIEnv* env, jobject obj,
jstring jprogramId) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
if (play_time_manager) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
play_time_manager->ResetProgramPlayTime(program_id);
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_playTimeManagerSetPlayTime(JNIEnv* env, jobject obj,
jstring jprogramId, jlong playTimeSeconds) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
if (play_time_manager) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
play_time_manager->SetPlayTime(program_id, u64(playTimeSeconds));
play_time_manager->SetPlayTime(program_id, static_cast<u64>(playTimeSeconds));
}
}
@@ -1406,12 +1389,12 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
ASSERT(user_id);
const auto saveDir = Common::FS::GetEdenPath(Common::FS::EdenPath::SaveDir);
auto vfsSaveDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(saveDir),
const auto nandDir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir);
auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
FileSys::OpenMode::Read);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
{}, vfsSaveDir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, program_id,
{}, vfsNandDir, FileSys::SaveDataSpaceId::User, FileSys::SaveDataType::Account, program_id,
user_id->AsU128(), 0);
return Common::Android::ToJString(env, user_save_data_path);
}
@@ -4,7 +4,6 @@
#include <string>
#include <jni.h>
#include <common/fs/path_util.h>
#include "android_config.h"
#include "android_settings.h"
@@ -546,39 +545,4 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveControlPlayerValues(JNIEnv*
}
}
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getSaveDir(JNIEnv* env, jobject obj) {
return Common::Android::ToJString(env,
Common::FS::GetEdenPathString(Common::FS::EdenPath::SaveDir));
}
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultSaveDir(JNIEnv* env, jobject obj) {
return Common::Android::ToJString(env,
Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir));
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setSaveDir(JNIEnv* env, jobject obj, jstring jpath) {
auto path = Common::Android::GetJString(env, jpath);
Common::FS::SetEdenPath(Common::FS::EdenPath::SaveDir, path);
}
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getNandDir(JNIEnv* env, jobject obj) {
return Common::Android::ToJString(env,
Common::FS::GetEdenPathString(Common::FS::EdenPath::NANDDir));
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setNandDir(JNIEnv* env, jobject obj, jstring jpath) {
auto path = Common::Android::GetJString(env, jpath);
Common::FS::SetEdenPath(Common::FS::EdenPath::NANDDir, path);
}
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getSdmcDir(JNIEnv* env, jobject obj) {
return Common::Android::ToJString(env,
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir));
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setSdmcDir(JNIEnv* env, jobject obj, jstring jpath) {
auto path = Common::Android::GetJString(env, jpath);
Common::FS::SetEdenPath(Common::FS::EdenPath::SDMCDir, path);
}
} // extern "C"
@@ -1,477 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/**
* @file native_freedreno.cpp
* @brief JNI bindings for Freedreno/Turnip GPU driver configuration.
*
* Provides runtime configuration of Mesa Freedreno environment variables
* for the Turnip Vulkan driver on Adreno GPUs.
*
* @see https://docs.mesa3d.org/drivers/freedreno.html
*/
#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <map>
#include <memory>
#include <string>
#include <sys/stat.h>
#include <jni.h>
#include "common/android/android_common.h"
#include "common/logging/log.h"
#include "native.h"
namespace {
struct FreedrenoConfig {
std::map<std::string, std::string> env_vars;
std::string config_file_path;
};
std::unique_ptr<FreedrenoConfig> g_config;
std::string g_base_path;
std::string g_current_program_id;
constexpr const char* kConfigFileName = ".freedreno.conf";
constexpr const char* kPerGameConfigDir = "freedreno_games";
void LogActiveVariables() {
if (!g_config || g_config->env_vars.empty()) {
return;
}
for (const auto& [key, value] : g_config->env_vars) {
LOG_INFO(Frontend, "[Freedreno] {}={}", key, value);
}
}
bool ApplyEnvironmentVariable(const std::string& key, const std::string& value) {
if (setenv(key.c_str(), value.c_str(), 1) != 0) {
LOG_ERROR(Frontend, "[Freedreno] Failed to set {}={} (errno: {})", key, value, errno);
return false;
}
return true;
}
void ClearAllEnvironmentVariables() {
if (!g_config) return;
for (const auto& [key, value] : g_config->env_vars) {
unsetenv(key.c_str());
}
g_config->env_vars.clear();
}
std::string GetConfigPath() {
return g_base_path + "/" + kConfigFileName;
}
std::string GetPerGameConfigPath(const std::string& program_id) {
return g_base_path + "/" + kPerGameConfigDir + "/" + program_id + ".conf";
}
void EnsurePerGameConfigDir() {
std::string dir_path = g_base_path + "/" + kPerGameConfigDir;
mkdir(dir_path.c_str(), 0755);
}
bool LoadConfigFromFile(const std::string& config_path) {
if (!g_config) return false;
FILE* file = fopen(config_path.c_str(), "r");
if (!file) {
return false;
}
char line[512];
int count = 0;
while (fgets(line, sizeof(line), file)) {
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
len--;
}
if (len == 0 || line[0] == '#') {
continue;
}
const char* eq = strchr(line, '=');
if (!eq) {
continue;
}
std::string key(line, eq - line);
std::string value(eq + 1);
g_config->env_vars[key] = value;
ApplyEnvironmentVariable(key, value);
count++;
}
fclose(file);
return count > 0;
}
bool SaveConfigToFile(const std::string& config_path) {
if (!g_config) return false;
FILE* file = fopen(config_path.c_str(), "w");
if (!file) {
LOG_ERROR(Frontend, "[Freedreno] Failed to open {} for writing", config_path);
return false;
}
fprintf(file, "# Freedreno/Turnip Configuration\n");
fprintf(file, "# Auto-generated by Eden Emulator\n\n");
for (const auto& [key, value] : g_config->env_vars) {
fprintf(file, "%s=%s\n", key.c_str(), value.c_str());
}
fclose(file);
return true;
}
} // anonymous namespace
extern "C" {
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoBasePath(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jbasePath) {
g_base_path = Common::Android::GetJString(env, jbasePath);
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_initializeFreedrenoConfig(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
if (!g_config) {
g_config = std::make_unique<FreedrenoConfig>();
LOG_INFO(Frontend, "[Freedreno] Configuration system initialized");
}
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_saveFreedrenoConfig(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
if (!g_config) {
LOG_WARNING(Frontend, "[Freedreno] Cannot save: not initialized");
return;
}
const std::string config_path = GetConfigPath();
FILE* file = fopen(config_path.c_str(), "w");
if (!file) {
LOG_ERROR(Frontend, "[Freedreno] Failed to open {} for writing", config_path);
return;
}
fprintf(file, "# Freedreno/Turnip Configuration\n");
fprintf(file, "# Auto-generated by Eden Emulator\n\n");
for (const auto& [key, value] : g_config->env_vars) {
fprintf(file, "%s=%s\n", key.c_str(), value.c_str());
}
fclose(file);
g_config->config_file_path = config_path;
LOG_INFO(Frontend, "[Freedreno] Saved {} variables to {}",
g_config->env_vars.size(), config_path);
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_reloadFreedrenoConfig(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
if (!g_config) {
LOG_WARNING(Frontend, "[Freedreno] Cannot reload: not initialized");
return;
}
const std::string config_path = GetConfigPath();
g_config->env_vars.clear();
FILE* file = fopen(config_path.c_str(), "r");
if (!file) {
LOG_DEBUG(Frontend, "[Freedreno] No config file found at {}", config_path);
return;
}
char line[512];
while (fgets(line, sizeof(line), file)) {
// Remove trailing newline
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
len--;
}
// Skip empty lines and comments
if (len == 0 || line[0] == '#') {
continue;
}
// Parse key=value
const char* eq = strchr(line, '=');
if (!eq) {
continue;
}
std::string key(line, eq - line);
std::string value(eq + 1);
g_config->env_vars[key] = value;
ApplyEnvironmentVariable(key, value);
}
fclose(file);
g_config->config_file_path = config_path;
if (!g_config->env_vars.empty()) {
LOG_INFO(Frontend, "[Freedreno] Loaded {} variables:", g_config->env_vars.size());
LogActiveVariables();
}
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setFreedrenoEnv(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jvarName, jstring jvalue) {
if (!g_config) {
return JNI_FALSE;
}
auto var_name = Common::Android::GetJString(env, jvarName);
auto value = Common::Android::GetJString(env, jvalue);
if (var_name.empty()) {
return JNI_FALSE;
}
g_config->env_vars[var_name] = value;
if (!ApplyEnvironmentVariable(var_name, value)) {
return JNI_FALSE;
}
LOG_INFO(Frontend, "[Freedreno] Set {}={}", var_name, value);
return JNI_TRUE;
}
JNIEXPORT jstring JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_getFreedrenoEnv(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jvarName) {
if (!g_config) {
return env->NewStringUTF("");
}
auto var_name = Common::Android::GetJString(env, jvarName);
auto it = g_config->env_vars.find(var_name);
if (it != g_config->env_vars.end()) {
return env->NewStringUTF(it->second.c_str());
}
return env->NewStringUTF("");
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_isFreedrenoEnvSet(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jvarName) {
if (!g_config) {
return JNI_FALSE;
}
auto var_name = Common::Android::GetJString(env, jvarName);
auto it = g_config->env_vars.find(var_name);
return (it != g_config->env_vars.end() && !it->second.empty()) ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_clearFreedrenoEnv(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jvarName) {
if (!g_config) {
return JNI_FALSE;
}
auto var_name = Common::Android::GetJString(env, jvarName);
auto it = g_config->env_vars.find(var_name);
if (it != g_config->env_vars.end()) {
g_config->env_vars.erase(it);
unsetenv(var_name.c_str());
LOG_INFO(Frontend, "[Freedreno] Cleared {}", var_name);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_clearAllFreedrenoEnv(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject obj) {
if (!g_config) {
return;
}
for (const auto& [key, value] : g_config->env_vars) {
unsetenv(key.c_str());
}
size_t count = g_config->env_vars.size();
g_config->env_vars.clear();
if (count > 0) {
LOG_INFO(Frontend, "[Freedreno] Cleared all {} variables", count);
}
}
JNIEXPORT jstring JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_getFreedrenoEnvSummary(
JNIEnv* env, [[maybe_unused]] jobject obj) {
if (!g_config || g_config->env_vars.empty()) {
return env->NewStringUTF("");
}
std::string summary;
for (const auto& [key, value] : g_config->env_vars) {
if (!summary.empty()) {
summary += ",";
}
summary += key + "=" + value;
}
return env->NewStringUTF(summary.c_str());
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_setCurrentProgramId(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
g_current_program_id = Common::Android::GetJString(env, jprogramId);
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_loadPerGameConfig(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
if (!g_config) {
return JNI_FALSE;
}
auto program_id = Common::Android::GetJString(env, jprogramId);
if (program_id.empty()) {
return JNI_FALSE;
}
// Clear current environment variables first
ClearAllEnvironmentVariables();
g_current_program_id = program_id;
// Try to load per-game config - do NOT fall back to global
// Per-game config should start empty if no config exists yet
std::string per_game_path = GetPerGameConfigPath(program_id);
if (LoadConfigFromFile(per_game_path)) {
LOG_INFO(Frontend, "[Freedreno] Loaded per-game config for {}", program_id);
LogActiveVariables();
return JNI_TRUE;
}
// No per-game config exists - start with empty config
LOG_INFO(Frontend, "[Freedreno] No per-game config for {}, starting empty", program_id);
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_loadPerGameConfigWithGlobalFallback(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
if (!g_config) {
return JNI_FALSE;
}
auto program_id = Common::Android::GetJString(env, jprogramId);
if (program_id.empty()) {
return JNI_FALSE;
}
// Clear current environment variables first
ClearAllEnvironmentVariables();
g_current_program_id = program_id;
// Try to load per-game config first
std::string per_game_path = GetPerGameConfigPath(program_id);
if (LoadConfigFromFile(per_game_path)) {
LOG_INFO(Frontend, "[Freedreno] Loaded per-game config for {}", program_id);
LogActiveVariables();
return JNI_TRUE;
}
// Fall back to global config for emulation
std::string global_path = GetConfigPath();
if (LoadConfigFromFile(global_path)) {
LOG_INFO(Frontend, "[Freedreno] No per-game config for {}, using global for emulation", program_id);
LogActiveVariables();
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_savePerGameConfig(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
if (!g_config) {
return JNI_FALSE;
}
auto program_id = Common::Android::GetJString(env, jprogramId);
if (program_id.empty()) {
return JNI_FALSE;
}
EnsurePerGameConfigDir();
std::string config_path = GetPerGameConfigPath(program_id);
if (SaveConfigToFile(config_path)) {
LOG_INFO(Frontend, "[Freedreno] Saved per-game config for {}", program_id);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_hasPerGameConfig(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
auto program_id = Common::Android::GetJString(env, jprogramId);
if (program_id.empty()) {
return JNI_FALSE;
}
std::string config_path = GetPerGameConfigPath(program_id);
FILE* file = fopen(config_path.c_str(), "r");
if (file) {
fclose(file);
return JNI_TRUE;
}
return JNI_FALSE;
}
JNIEXPORT jboolean JNICALL
Java_org_yuzu_yuzu_1emu_utils_NativeFreedrenoConfig_deletePerGameConfig(
JNIEnv* env, [[maybe_unused]] jobject obj, jstring jprogramId) {
auto program_id = Common::Android::GetJString(env, jprogramId);
if (program_id.empty()) {
return JNI_FALSE;
}
std::string config_path = GetPerGameConfigPath(program_id);
if (remove(config_path.c_str()) == 0) {
LOG_INFO(Frontend, "[Freedreno] Deleted per-game config for {}", program_id);
return JNI_TRUE;
}
return JNI_FALSE;
}
} // extern "C"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

After

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M317.52,255.37C318.06,255.37 318.6,255.38 319.65,255.41C322.37,255.43 324.58,255.42 326.79,255.41C349.49,255.43 372.2,255.43 394.91,255.46C400.46,255.47 406.02,255.65 411.58,255.6C413.68,255.59 414.71,256.12 414.54,258.43C414.35,261.26 414.33,264.1 414.37,266.94C414.4,269.64 413.52,270.71 410.51,270.7C377.29,270.59 344.07,270.65 310.85,270.66C295.82,270.66 280.79,270.66 265.75,270.66C264.61,270.66 263.47,270.66 262.06,270.66C262.06,272.3 262.06,273.7 262.06,275.5C312.31,275.5 362.6,275.5 413.71,275.5C412.65,281.11 411.74,286.21 410.66,291.28C410.55,291.83 409.53,292.38 408.84,292.56C407.95,292.78 406.96,292.62 406.01,292.62C359.34,292.62 312.66,292.62 265.99,292.62C264.75,292.62 263.51,292.62 262.03,292.62C262.03,294.2 262.03,295.49 262.03,297.18C310.9,297.18 359.72,297.18 409.36,297.18C408.27,300.87 407.4,304.03 406.39,307.15C404.52,312.95 404.48,312.94 398.53,312.94C354.38,312.94 310.22,312.95 266.07,312.92C263.76,312.91 261.86,313.09 262.84,316.08C263.07,316.81 264.11,317.46 264.92,317.77C265.65,318.06 266.58,317.84 267.42,317.84C310.84,317.84 354.26,317.84 397.67,317.84C399.01,317.84 400.34,317.84 402.55,317.84C400.14,322.79 398.2,327.17 395.86,331.32C395.42,332.11 393.44,332.31 392.17,332.31C373.14,332.37 354.11,332.35 335.08,332.35C313.54,332.35 291.98,332.36 270.43,332.35C268.66,332.35 266.89,332.26 264.63,332.33C262.52,332.45 260.88,332.45 259.24,332.45C258.67,327.67 257.69,322.9 257.58,318.11C257.21,300.69 257.06,283.26 257.01,265.84C257,262.52 257.85,259.2 258.72,255.69C260.68,255.49 262.21,255.48 264.04,255.51C279.33,255.53 294.33,255.5 309.62,255.47C312.44,255.44 314.98,255.4 317.52,255.37Z"
android:fillColor="#BF42F6"/>
<path
android:pathData="M196.08,256.6C196.08,254.63 196.08,252.67 196.08,250.27C194.45,250.27 192.91,250.27 191.37,250.27C161.1,250.29 130.82,250.28 100.54,250.37C97.92,250.38 96.9,249.67 97.32,246.97C97.78,243.97 98.17,240.93 98.27,237.9C98.34,235.6 99.36,235.17 101.4,235.17C132.61,235.1 163.82,234.94 195.04,234.86C197.66,234.86 199.69,234.63 199.14,230.9C165.95,230.9 132.78,230.9 99.13,230.9C100.08,226.41 100.73,222.27 101.94,218.31C102.23,217.39 104.41,216.53 105.72,216.53C137.99,216.47 170.27,216.52 202.54,216.6C204.76,216.61 206.52,216.34 206.71,213.17C172.35,213.17 138.07,213.17 102.94,213.17C105.27,206.84 107.26,201.13 109.56,195.55C109.89,194.76 111.78,194.23 112.96,194.22C129.67,194.14 146.39,194.08 163.1,194.23C166.69,194.27 168.59,192.48 170.67,189.3C151.01,189.3 131.87,189.3 111.88,189.3C114.84,183.98 117.32,179.27 120.09,174.74C120.55,174 122.27,173.74 123.4,173.74C144.22,173.68 165.04,173.54 185.85,173.82C190.74,173.88 194.5,172.04 198.31,169.21C173.73,169.21 149.15,169.21 123.5,169.21C125.26,166.62 126.46,164.76 127.76,162.97C133.8,154.66 133.81,154.67 144.13,154.67C171.78,154.66 199.43,154.67 227.08,154.67C228.19,154.67 229.29,154.67 231.08,154.67C228.38,151.86 226.32,149.93 222.7,149.94C196.21,150.06 169.72,150.01 143.22,150.01C142.01,150.01 140.79,150.01 139.58,150.01C139.41,149.71 139.24,149.41 139.08,149.12C141.68,146.49 144.22,143.8 146.91,141.27C154.7,133.92 154.72,133.79 165.43,133.98C181.21,134.26 196.1,138.61 210.47,144.78C214.32,146.44 217.65,147.31 221.76,145.43C224.98,143.95 228.71,143.61 232.67,143.03C229.67,148.57 233.27,151.22 236.42,154.19C240.32,157.87 244.05,161.72 247.81,165.54C248.6,166.35 249.19,167.35 250.32,168.84C248.78,168.93 247.82,169.03 246.87,169.03C240.24,169.04 233.61,169.23 227,168.97C207.93,168.24 191.92,175.26 178.31,188.15C173.45,192.76 169.11,197.93 164.57,202.87C164.1,203.38 163.83,204.06 163.06,205.34C186.14,197.23 207.86,186.73 232.14,185.87C232.34,186.18 232.53,186.5 232.73,186.82C229.85,190.18 227.16,193.72 224.07,196.87C211.77,209.42 204.42,224.53 201.03,241.6C200.07,246.39 200.14,251.4 199.32,256.52C197.97,256.68 197.03,256.64 196.08,256.6ZM209.68,194.22C212.31,194.23 214.96,194.01 217.56,194.29C221.34,194.69 223.62,192.8 226.08,189.17C219.87,190.63 214.42,191.92 208.98,193.24C208.98,193.24 209.12,193.87 209.68,194.22Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M195.81,256.61C197.03,256.64 197.97,256.68 199.2,256.74C199.58,258.84 199.68,260.93 199.79,263.02C200.15,263.15 200.52,263.28 200.89,263.41C202.45,261.11 204.02,258.8 205.97,256.3C207.64,256.04 208.92,255.97 210.44,255.9C211.39,255.81 212.11,255.66 212.84,255.65C221.05,255.54 229.27,255.45 237.79,255.4C240.14,255.48 242.19,255.51 244.24,255.54C243.48,260.08 242.67,264.61 241.96,269.16C238.76,289.76 238.29,310.44 239.8,331.49C238.12,331.99 236.72,332.2 235.33,332.4C197.17,332.39 159.01,332.31 120.85,332.45C117.22,332.46 115.21,331.39 113.93,328.08C112.65,324.8 111,321.67 109.23,317.84C113.61,317.84 117.25,317.84 120.89,317.84C157.58,317.84 194.27,317.85 230.96,317.81C232.33,317.8 234.26,318.64 234.58,316.04C234.9,313.41 233.71,312.89 231.33,312.9C204.94,312.97 178.55,312.94 152.17,312.95C138.29,312.95 124.42,312.9 110.54,313.03C108.13,313.05 106.9,312.32 106.24,310.03C105.12,306.12 103.81,302.26 102.24,297.34C146.69,297.16 190.42,297.32 234.45,297.24C234.54,295.65 234.62,294.35 234.7,292.8C233.41,292.73 232.39,292.63 231.38,292.63C191.74,292.61 152.11,292.59 112.48,292.59C109.43,292.59 106.38,292.78 103.34,292.7C102.57,292.68 101.25,292.09 101.13,291.56C99.99,286.28 99.05,280.95 97.99,275.27C103.72,275.27 108.74,275.24 113.75,275.28C115.11,275.29 116.47,275.55 117.83,275.55C155.88,275.57 193.94,275.57 231.99,275.56C236.12,275.56 236.28,275.39 236.25,270.67C190.27,270.67 144.28,270.67 97.69,270.67C97.5,266.15 97.2,262.01 97.29,257.87C97.3,257.33 99.29,256.41 100.37,256.4C110.67,256.26 120.97,256.28 131.28,256.27C143.26,256.26 155.24,256.22 167.23,256.28C176.66,256.33 186.1,256.51 195.81,256.61Z"
android:fillColor="#BF43F5"/>
<path
android:pathData="M235.35,332.69C236.72,332.2 238.12,331.99 239.8,331.78C241.62,343.91 242.64,356.16 244.79,368.19C247.54,383.59 251.45,398.75 257.46,413.28C257.66,413.75 257.8,414.25 257.96,414.73C240.7,421.84 173.22,399.36 158.05,381.11C172.31,381.11 186.11,381.11 199.9,381.11C213.75,381.11 227.59,381.11 241.47,381.11C241.67,377.83 240.89,376.4 237.49,376.42C210.37,376.55 183.25,376.46 156.13,376.54C153.11,376.55 150.66,375.97 148.51,373.71C143.83,368.81 138.99,364.07 134.22,359.27C134.38,358.97 134.53,358.67 134.69,358.38C168.96,358.38 203.24,358.38 237.91,358.38C237.59,356.49 237.35,355.09 237.06,353.42C235.66,353.42 234.41,353.42 233.17,353.42C204.05,353.42 174.93,353.43 145.82,353.41C141.61,353.41 137.4,353.12 133.21,353.29C130.69,353.4 129.04,352.57 127.7,350.52C124.9,346.24 122.01,342.03 118.68,337.09C158.03,337.09 196.51,337.09 235.37,337.09C235.37,335.43 235.37,334.21 235.35,332.69Z"
android:fillColor="#985DED"/>
<path
android:pathData="M259.22,332.74C260.88,332.45 262.52,332.45 264.42,332.44C264.74,333.79 264.81,335.15 264.9,336.86C307.33,336.86 349.65,336.86 392.94,336.86C390.82,340.23 389.17,342.95 387.42,345.59C382.22,353.42 382.21,353.42 372.98,353.42C339.55,353.42 306.12,353.42 272.68,353.42C271.45,353.42 270.21,353.42 269.01,353.42C268.6,357.97 268.9,358.32 272.97,358.32C306.4,358.33 339.83,358.33 373.27,358.33C374.49,358.33 375.71,358.33 377.95,358.33C375.2,361.41 372.88,363.64 370.99,366.18C364.84,374.47 357,377.15 346.54,376.75C324.07,375.9 301.55,376.49 279.05,376.49C277.81,376.49 276.57,376.49 275.34,376.49C275.16,380.42 275.74,381.1 279.15,381.1C302.59,381.11 326.04,381.1 349.48,381.11C350.69,381.11 351.89,381.11 353.09,381.11C353.19,381.39 353.29,381.68 353.39,381.97C351.01,383.75 348.69,385.61 346.25,387.3C330.27,398.36 312.8,406.17 293.96,410.9C291.74,411.45 289.36,411.83 287.12,411.64C285.84,411.52 284.3,410.33 283.54,409.19C277.07,399.49 273.01,388.7 269.22,377.75C264.17,363.21 261.34,348.2 259.22,332.74Z"
android:fillColor="#985DED"/>
<path
android:pathData="M326.78,255.15C324.58,255.42 322.37,255.43 319.85,255.39C321.63,245.65 321.41,236 319.06,226.36C315.84,213.14 308.31,203.06 296.96,195.73C296.52,195.45 296.09,195.16 295.7,194.28C307.61,194.28 319.52,194.28 331.43,194.28C331.48,193.93 331.53,193.59 331.57,193.25C326.8,191.44 322.07,189.34 316.8,189.29C306.29,189.2 295.77,189.33 285.27,189.21C283.16,189.19 281.07,188.5 279.01,187.57C291.5,182.55 304.29,183.1 317.13,185.13C330.14,187.18 341.45,193.94 354.27,199.15C353.23,197.19 352.63,196.05 351.78,194.42C353.03,194.32 353.99,194.18 354.94,194.18C369.55,194.16 384.16,194.21 398.77,194.12C400.99,194.11 402.21,194.7 402.93,196.91C404.51,201.76 406.34,206.52 408.3,211.96C378.96,211.96 350.45,211.96 321.79,211.96C321.89,215.04 323.22,215.9 325.8,215.89C345.56,215.82 365.32,215.82 385.08,215.87C392.43,215.89 399.77,216.08 407.11,216.34C408.02,216.37 409.48,217.14 409.67,217.85C410.73,221.77 411.47,225.78 412.41,230.2C383.7,230.2 355.5,230.2 327.33,230.2C326.95,234.48 327.15,234.7 330.87,234.7C357.35,234.74 383.84,234.81 410.32,234.8C412.27,234.8 413.19,235.26 413.31,237.37C413.52,240.82 413.96,244.26 414.41,247.69C414.69,249.87 413.98,250.71 411.68,250.63C407.17,250.48 402.65,250.56 398.13,250.56C375.85,250.56 353.57,250.56 331.29,250.56C327.03,250.56 327.03,250.57 326.78,255.15Z"
android:fillColor="#FF43C4"/>
<path
android:pathData="M227.85,100.36C256.3,95.58 282.98,98.91 297.89,104.06C295.79,108.97 293.7,113.87 291.61,118.76C290.5,121.37 289.2,123.91 288.3,126.59C287.56,128.8 286.27,129.35 284.07,129.34C269.05,129.26 254.03,129.29 238.21,129.29C240.88,132.05 242.88,134.26 246.57,134.22C257.92,134.12 269.27,134.18 280.62,134.18C281.77,134.18 282.91,134.18 285.21,134.18C282.72,138.33 281.09,142.24 278.43,145.21C274.25,149.87 269.29,153.82 264.85,158.26C263.14,159.97 262.34,159.9 260.7,158.1C253.98,150.75 247.23,143.42 240.16,136.4C232.38,128.67 222.49,125.46 211.78,124.23C202.25,123.13 193.21,124.88 184.27,127.97C182.14,128.71 179.81,129.15 177.55,129.24C172.62,129.42 167.68,129.29 162.59,128.89C181.97,114.22 203.7,104.88 227.85,100.36ZM264.4,152.93C265.08,152.12 265.76,151.3 266.85,150.01C264.04,150.01 261.99,150.01 259.33,150.01C260.37,151.48 260.92,152.63 261.81,153.37C262.25,153.73 263.32,153.32 264.4,152.93Z"
android:fillColor="#FF42C3"/>
<path
android:pathData="M338.22,180.32C330,173.61 320.35,171.22 310.4,169.77C298.87,168.1 287.56,168.74 276.35,173.62C276.99,172.43 277.52,171.16 278.31,170.08C288.95,155.53 302.06,143.72 317.99,135.27C320.4,133.99 323.68,134.06 326.56,134.01C335.71,133.86 344.86,133.9 354,134.01C355.37,134.02 357.07,134.45 358.04,135.32C362.81,139.6 367.39,144.09 372.01,148.53C372.28,148.78 372.38,149.19 372.81,150.01C371.31,150.01 370.2,150.01 369.09,150.01C348.07,150.01 327.04,150.08 306.02,149.93C302.48,149.9 300.52,151.7 297.87,154.67C299.78,154.67 300.97,154.67 302.15,154.67C326.02,154.67 349.88,154.71 373.74,154.6C376.46,154.59 378.32,155.41 379.86,157.57C382.39,161.15 385.05,164.63 388.18,168.84C367.16,168.84 347,168.84 326.84,168.84C326.8,169.1 326.77,169.35 326.73,169.61C331.21,171.27 335.15,173.92 340.45,173.81C356.21,173.5 371.98,173.75 387.75,173.65C390.3,173.63 391.87,174.45 393.01,176.69C395.03,180.7 397.23,184.61 399.61,189.04C398.04,189.14 397.06,189.26 396.07,189.26C380.83,189.27 365.59,189.23 350.34,189.31C348.17,189.32 346.58,188.76 345.1,187.13C343,184.79 340.64,182.68 338.22,180.32Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M263.75,255.47C262.21,255.48 260.68,255.49 258.86,255.49C260.26,237.4 261.8,219.3 267.17,201.8C267.96,199.21 268.84,198.07 271.41,199.91C273.97,201.74 276.92,203.14 279.13,205.31C294.14,220.1 308.29,235.58 317.44,255.16C314.98,255.4 312.44,255.44 309.6,255.22C308.77,252.11 307.31,250.9 304.14,250.95C292.11,251.14 280.07,251.03 268.03,251.04C266.7,251.04 265.38,251.04 263.75,251.04C263.75,252.81 263.75,254.14 263.75,255.47ZM265.23,234.42C275.39,234.42 285.55,234.42 295.71,234.42C295.83,234.04 295.95,233.66 296.07,233.28C294.82,232.66 293.59,231.54 292.34,231.52C283.86,231.36 275.38,231.45 266.9,231.42C264.98,231.41 264.11,232.09 265.23,234.42ZM272.28,213.13C270.05,212.98 268.06,213.13 268.44,216.24C272.26,216.24 276,216.24 279.86,216.24C278.35,212.79 275.57,213.04 272.28,213.13Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M244.37,255.35C242.19,255.51 240.14,255.48 237.87,255.14C237.72,253.74 237.8,252.65 237.92,251.1C230.12,251.1 222.74,251.09 215.35,251.1C211.33,251.1 210.79,251.62 210.2,255.9C208.92,255.97 207.64,256.04 206.08,256.11C209.18,249.49 212.07,242.57 216.01,236.32C223.02,225.18 230.96,214.72 241.59,206.63C246.5,202.9 251.86,200.11 257.81,198.49C258.58,198.28 259.39,198.25 261.09,198.02C254,217.03 248.24,235.81 244.37,255.35ZM226.88,230.34C223.86,230.04 223.17,232.3 221.91,234.84C228.63,234.84 234.89,234.75 241.15,234.88C244,234.94 244.78,233.68 244.4,230.4C238.69,230.4 233.05,230.4 226.88,230.34ZM249.9,214.76C249.15,214.31 248.42,213.55 247.63,213.47C245.66,213.28 243.65,213.49 241.66,213.37C239.1,213.21 237.69,214.56 236.27,217.12C240.49,217.12 244.21,217.2 247.93,217.03C248.58,217 249.17,215.9 249.9,214.76Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M315.52,109.68C327.71,114.67 338.96,120.8 349.32,129.08C328.46,127.7 308.6,131.4 289.08,139.83C289.67,136.65 290.71,134.68 293.44,133.84C297.43,132.61 301.38,131.27 305.32,129.43C301.9,129.43 298.47,129.43 294.12,129.43C298.11,121.6 301.71,114.44 305.48,107.37C305.75,106.86 307.39,106.71 308.22,106.96C310.62,107.69 312.94,108.7 315.52,109.68Z"
android:fillColor="#FF44C4"/>
</vector>
@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M346.71,255.31C347.5,255.31 348.29,255.32 349.83,255.37C353.84,255.39 357.09,255.38 360.33,255.37C393.73,255.39 427.12,255.4 460.51,255.45C468.68,255.46 476.86,255.73 485.03,255.65C488.12,255.63 489.63,256.4 489.39,259.81C489.1,263.97 489.08,268.15 489.13,272.32C489.18,276.29 487.88,277.86 483.45,277.85C434.6,277.69 385.75,277.78 336.9,277.79C314.79,277.8 292.68,277.79 270.58,277.79C268.9,277.79 267.22,277.79 265.15,277.79C265.15,280.2 265.15,282.26 265.15,284.91C339.05,284.91 412.99,284.91 488.16,284.91C486.61,293.16 485.26,300.67 483.68,308.12C483.51,308.93 482.02,309.74 481,309.99C479.69,310.32 478.23,310.09 476.84,310.09C408.2,310.09 339.56,310.09 270.92,310.09C269.1,310.09 267.27,310.09 265.1,310.09C265.1,312.41 265.1,314.32 265.1,316.79C336.97,316.79 408.77,316.79 481.77,316.79C480.16,322.22 478.88,326.87 477.4,331.45C474.64,339.99 474.59,339.98 465.83,339.98C400.9,339.98 335.98,339.99 271.05,339.94C267.64,339.93 264.86,340.19 266.29,344.59C266.64,345.66 268.16,346.61 269.36,347.08C270.43,347.5 271.8,347.17 273.04,347.17C336.88,347.17 400.73,347.17 464.58,347.17C466.54,347.17 468.5,347.17 471.75,347.17C468.21,354.46 465.35,360.89 461.91,367C461.26,368.16 458.35,368.45 456.48,368.46C428.5,368.54 400.52,368.52 372.54,368.52C340.85,368.52 309.15,368.52 277.46,368.51C274.85,368.51 272.24,368.39 268.93,368.49C265.82,368.66 263.41,368.67 261.01,368.67C260.16,361.64 258.72,354.62 258.57,347.57C258.02,321.95 257.79,296.33 257.72,270.71C257.71,265.83 258.95,260.94 260.24,255.78C263.12,255.48 265.37,255.47 268.05,255.52C290.54,255.54 312.61,255.5 335.09,255.46C339.23,255.41 342.97,255.36 346.71,255.31Z"
android:fillColor="#BF42F6"/>
<path
android:pathData="M168.12,257.11C168.12,254.22 168.12,251.34 168.12,247.81C165.72,247.81 163.45,247.81 161.19,247.81C116.67,247.84 72.15,247.82 27.63,247.96C23.77,247.97 22.27,246.93 22.88,242.96C23.56,238.54 24.13,234.07 24.27,229.61C24.38,226.23 25.88,225.61 28.88,225.6C74.78,225.49 120.68,225.26 166.58,225.15C170.45,225.14 173.43,224.81 172.62,219.32C123.81,219.32 75.02,219.32 25.55,219.32C26.94,212.72 27.9,206.64 29.68,200.81C30.1,199.46 33.31,198.19 35.24,198.19C82.7,198.1 130.16,198.18 177.61,198.3C180.89,198.3 183.47,197.92 183.75,193.25C133.22,193.25 82.8,193.25 31.15,193.25C34.58,183.94 37.49,175.54 40.89,167.34C41.37,166.17 44.15,165.4 45.87,165.39C70.46,165.26 95.04,165.18 119.62,165.4C124.9,165.45 127.69,162.82 130.75,158.15C101.83,158.15 73.69,158.15 44.3,158.15C48.65,150.32 52.3,143.4 56.37,136.74C57.04,135.65 59.57,135.27 61.24,135.26C91.85,135.18 122.47,134.97 153.07,135.38C160.26,135.47 165.79,132.76 171.4,128.6C135.25,128.6 99.1,128.6 61.39,128.6C63.98,124.8 65.74,122.06 67.65,119.43C76.53,107.21 76.55,107.22 91.72,107.22C132.38,107.21 173.04,107.21 213.7,107.21C215.33,107.21 216.96,107.21 219.59,107.21C215.62,103.09 212.58,100.25 207.27,100.27C168.31,100.44 129.35,100.36 90.39,100.36C88.6,100.36 86.82,100.36 85.03,100.36C84.78,99.92 84.53,99.49 84.29,99.05C88.12,95.2 91.85,91.24 95.8,87.51C107.26,76.71 107.3,76.52 123.05,76.8C146.26,77.21 168.15,83.6 189.28,92.68C194.94,95.11 199.84,96.4 205.89,93.63C210.61,91.46 216.1,90.96 221.92,90.11C217.51,98.24 222.81,102.15 227.44,106.52C233.17,111.93 238.66,117.59 244.19,123.21C245.36,124.39 246.23,125.87 247.88,128.06C245.62,128.19 244.21,128.34 242.8,128.34C233.06,128.35 223.31,128.63 213.59,128.25C185.54,127.18 161.99,137.5 141.99,156.46C134.83,163.24 128.46,170.84 121.77,178.11C121.09,178.85 120.69,179.85 119.56,181.74C153.5,169.81 185.44,154.36 221.15,153.1C221.44,153.57 221.73,154.03 222.01,154.5C217.78,159.44 213.82,164.64 209.28,169.28C191.19,187.74 180.38,209.95 175.39,235.05C173.99,242.11 174.08,249.46 172.89,256.99C170.89,257.24 169.51,257.18 168.12,257.11ZM188.12,165.39C191.98,165.39 195.88,165.07 199.7,165.48C205.27,166.07 208.62,163.3 212.23,157.96C203.1,160.11 195.09,162 187.09,163.95C187.08,163.95 187.3,164.87 188.12,165.39Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M167.72,257.14C169.51,257.18 170.89,257.24 172.7,257.32C173.27,260.41 173.42,263.49 173.57,266.56C174.11,266.75 174.65,266.95 175.19,267.14C177.49,263.75 179.79,260.35 182.66,256.68C185.11,256.3 187,256.2 189.23,256.08C190.64,255.95 191.7,255.74 192.76,255.73C204.84,255.56 216.92,255.43 229.45,255.36C232.92,255.47 235.93,255.51 238.94,255.56C237.82,262.23 236.63,268.9 235.59,275.59C230.88,305.88 230.19,336.29 232.4,367.24C229.94,367.99 227.89,368.29 225.84,368.59C169.72,368.57 113.6,368.46 57.49,368.65C52.15,368.67 49.19,367.1 47.3,362.24C45.43,357.42 43.01,352.8 40.4,347.17C46.84,347.17 52.19,347.17 57.54,347.17C111.5,347.17 165.45,347.18 219.4,347.13C221.43,347.13 224.27,348.35 224.73,344.53C225.2,340.67 223.46,339.9 219.95,339.91C181.15,340.02 142.34,339.97 103.54,339.98C83.14,339.98 62.73,339.92 42.33,340.11C38.78,340.14 36.96,339.06 36,335.69C34.36,329.94 32.42,324.27 30.12,317.03C95.49,316.77 159.8,317 224.55,316.88C224.68,314.54 224.79,312.64 224.92,310.35C223.02,310.25 221.52,310.11 220.02,310.11C161.74,310.07 103.46,310.05 45.17,310.05C40.69,310.05 36.21,310.32 31.74,310.21C30.6,310.18 28.66,309.31 28.49,308.53C26.81,300.76 25.43,292.93 23.86,284.57C32.3,284.57 39.67,284.54 47.05,284.59C49.04,284.6 51.04,284.99 53.04,284.99C109,285.01 164.97,285.01 220.93,285C227.01,285 227.24,284.75 227.2,277.81C159.57,277.81 91.94,277.81 23.43,277.81C23.15,271.16 22.71,265.07 22.84,258.98C22.86,258.2 25.78,256.84 27.37,256.82C42.52,256.62 57.67,256.64 72.82,256.63C90.44,256.61 108.07,256.55 125.69,256.64C139.57,256.71 153.44,256.98 167.72,257.14Z"
android:fillColor="#BF43F5"/>
<path
android:pathData="M225.87,369.02C227.89,368.29 229.94,367.99 232.42,367.67C235.09,385.52 236.59,403.52 239.75,421.23C243.8,443.87 249.54,466.17 258.38,487.52C258.67,488.22 258.88,488.96 259.13,489.67C233.74,500.12 134.5,467.06 112.2,440.21C133.17,440.21 153.45,440.21 173.74,440.21C194.1,440.21 214.46,440.21 234.87,440.21C235.17,435.4 234.01,433.3 229.01,433.32C189.13,433.52 149.25,433.38 109.36,433.5C104.92,433.52 101.33,432.66 98.16,429.33C91.29,422.13 84.17,415.17 77.15,408.11C77.38,407.67 77.61,407.23 77.84,406.79C128.24,406.79 178.65,406.79 229.63,406.79C229.16,404.01 228.81,401.96 228.39,399.5C226.32,399.5 224.49,399.5 222.66,399.5C179.84,399.5 137.02,399.51 94.2,399.48C88.02,399.48 81.83,399.06 75.66,399.31C71.96,399.47 69.52,398.25 67.56,395.24C63.45,388.95 59.19,382.75 54.3,375.48C112.17,375.48 168.75,375.48 225.9,375.48C225.9,373.05 225.9,371.25 225.87,369.02Z"
android:fillColor="#985DED"/>
<path
android:pathData="M260.97,369.08C263.41,368.67 265.82,368.66 268.61,368.65C269.1,370.64 269.19,372.63 269.32,375.15C331.73,375.15 393.96,375.15 457.62,375.15C454.49,380.1 452.08,384.1 449.5,387.99C441.86,399.51 441.83,399.49 428.27,399.49C379.1,399.5 329.94,399.5 280.77,399.5C278.95,399.5 277.13,399.5 275.36,399.5C274.76,406.19 275.21,406.71 281.19,406.71C330.36,406.72 379.52,406.71 428.69,406.72C430.48,406.72 432.28,406.72 435.58,406.72C431.53,411.25 428.13,414.53 425.35,418.27C416.3,430.46 404.77,434.4 389.38,433.81C356.34,432.56 323.22,433.43 290.13,433.43C288.31,433.43 286.48,433.43 284.68,433.43C284.41,439.2 285.27,440.2 290.28,440.21C324.76,440.22 359.23,440.21 393.71,440.21C395.48,440.21 397.25,440.21 399.01,440.21C399.16,440.64 399.31,441.06 399.46,441.49C395.96,444.11 392.55,446.85 388.96,449.33C365.45,465.59 339.77,477.07 312.06,484.02C308.8,484.84 305.3,485.4 301.99,485.11C300.12,484.95 297.85,483.2 296.73,481.51C287.23,467.26 281.26,451.38 275.67,435.29C268.26,413.89 264.08,391.82 260.97,369.08Z"
android:fillColor="#985DED"/>
<path
android:pathData="M360.32,254.99C357.09,255.38 353.84,255.39 350.13,255.34C352.74,241.02 352.42,226.82 348.97,212.64C344.23,193.2 333.16,178.39 316.47,167.6C315.83,167.19 315.2,166.76 314.61,165.46C332.13,165.46 349.64,165.46 367.16,165.46C367.23,164.96 367.3,164.46 367.37,163.96C360.35,161.29 353.4,158.21 345.64,158.14C330.19,158 314.73,158.19 299.27,158.02C296.18,157.99 293.1,156.98 290.07,155.61C308.44,148.22 327.24,149.03 346.13,152.01C365.26,155.03 381.9,164.97 400.74,172.63C399.23,169.75 398.34,168.07 397.08,165.68C398.93,165.53 400.33,165.32 401.74,165.32C423.23,165.3 444.71,165.37 466.2,165.24C469.46,165.22 471.26,166.09 472.32,169.34C474.64,176.47 477.33,183.47 480.21,191.47C437.06,191.47 395.13,191.47 352.99,191.47C353.13,196.01 355.09,197.26 358.88,197.25C387.94,197.15 417,197.14 446.06,197.22C456.86,197.26 467.67,197.53 478.46,197.91C479.79,197.96 481.95,199.1 482.22,200.13C483.77,205.9 484.86,211.8 486.24,218.3C444.03,218.3 402.55,218.3 361.13,218.3C360.58,224.59 360.87,224.91 366.33,224.91C405.28,224.98 444.23,225.07 483.18,225.06C486.04,225.06 487.39,225.73 487.58,228.83C487.88,233.91 488.53,238.97 489.19,244.02C489.6,247.21 488.55,248.46 485.18,248.34C478.55,248.12 471.9,248.24 465.26,248.24C432.49,248.23 399.72,248.24 366.95,248.24C360.69,248.24 360.69,248.25 360.32,254.99Z"
android:fillColor="#FF43C4"/>
<path
android:pathData="M214.84,27.35C256.67,20.33 295.91,25.23 317.84,32.8C314.75,40.02 311.68,47.22 308.61,54.41C306.97,58.24 305.06,61.99 303.74,65.93C302.65,69.18 300.74,69.98 297.52,69.97C275.43,69.85 253.34,69.9 230.07,69.9C234.01,73.95 236.94,77.2 242.37,77.15C259.06,76.99 275.76,77.09 292.45,77.1C294.13,77.1 295.81,77.1 299.19,77.1C295.53,83.19 293.14,88.94 289.22,93.31C283.08,100.17 275.78,105.97 269.24,112.5C266.73,115.01 265.56,114.91 263.14,112.27C253.27,101.45 243.33,90.67 232.95,80.36C221.49,68.98 206.96,64.27 191.21,62.45C177.19,60.83 163.89,63.42 150.75,67.96C147.61,69.04 144.19,69.69 140.87,69.82C133.62,70.09 126.36,69.9 118.87,69.31C147.37,47.73 179.32,34 214.84,27.35ZM268.58,104.66C269.59,103.46 270.59,102.27 272.19,100.38C268.06,100.38 265.05,100.38 261.13,100.38C262.66,102.53 263.47,104.22 264.79,105.31C265.43,105.84 267,105.24 268.58,104.66Z"
android:fillColor="#FF42C3"/>
<path
android:pathData="M377.15,144.94C365.06,135.07 350.86,131.56 336.23,129.43C319.28,126.96 302.65,127.92 286.16,135.08C287.11,133.34 287.88,131.47 289.04,129.88C304.69,108.48 323.97,91.12 347.4,78.68C350.95,76.8 355.77,76.91 360.01,76.84C373.45,76.62 386.9,76.67 400.35,76.83C402.37,76.86 404.87,77.49 406.3,78.77C413.31,85.06 420.04,91.67 426.84,98.19C427.23,98.56 427.39,99.17 428.02,100.36C425.81,100.36 424.18,100.36 422.55,100.36C391.63,100.36 360.71,100.48 329.8,100.25C324.58,100.21 321.7,102.85 317.82,107.22C320.62,107.22 322.36,107.22 324.11,107.22C359.2,107.21 394.29,107.28 429.39,107.13C433.39,107.11 436.12,108.31 438.38,111.49C442.11,116.74 446.02,121.86 450.61,128.06C419.7,128.06 390.05,128.06 360.41,128.06C360.36,128.43 360.3,128.81 360.25,129.19C366.83,131.63 372.64,135.53 380.43,135.37C403.61,134.91 426.8,135.29 449.99,135.13C453.74,135.1 456.04,136.3 457.71,139.61C460.69,145.49 463.92,151.25 467.42,157.76C465.12,157.91 463.67,158.09 462.22,158.09C439.81,158.1 417.39,158.05 394.98,158.16C391.79,158.18 389.44,157.36 387.27,154.95C384.18,151.51 380.71,148.41 377.15,144.94Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M267.63,255.46C265.37,255.47 263.12,255.48 260.45,255.48C262.5,228.88 264.77,202.27 272.66,176.53C273.83,172.72 275.12,171.05 278.9,173.76C282.66,176.45 287.01,178.5 290.25,181.69C312.32,203.43 333.13,226.21 346.6,255C342.97,255.36 339.23,255.41 335.05,255.09C333.83,250.51 331.69,248.73 327.03,248.81C309.33,249.09 291.63,248.93 273.93,248.93C271.98,248.93 270.02,248.93 267.63,248.93C267.63,251.54 267.63,253.5 267.63,255.46ZM269.8,224.5C284.75,224.5 299.69,224.5 314.64,224.5C314.81,223.95 314.98,223.38 315.15,222.83C313.33,221.92 311.52,220.26 309.68,220.23C297.21,219.99 284.74,220.13 272.27,220.09C269.44,220.08 268.16,221.08 269.8,224.5ZM280.18,193.19C276.89,192.97 273.97,193.18 274.53,197.77C280.15,197.77 285.65,197.77 291.32,197.77C289.11,192.69 285.02,193.05 280.18,193.19Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M239.14,255.27C235.93,255.51 232.92,255.47 229.57,254.97C229.36,252.92 229.48,251.32 229.65,249.02C218.18,249.02 207.32,249.02 196.46,249.03C190.55,249.03 189.75,249.79 188.89,256.1C187,256.2 185.11,256.3 182.83,256.4C187.38,246.66 191.64,236.49 197.43,227.29C207.73,210.92 219.41,195.53 235.05,183.63C242.26,178.14 250.14,174.04 258.89,171.65C260.02,171.35 261.23,171.31 263.73,170.96C253.3,198.93 244.82,226.55 239.14,255.27ZM213.42,218.5C208.98,218.06 207.96,221.38 206.1,225.12C215.98,225.12 225.19,224.99 234.4,225.18C238.59,225.27 239.74,223.41 239.18,218.58C230.78,218.58 222.49,218.58 213.42,218.5ZM247.27,195.58C246.16,194.92 245.09,193.8 243.93,193.69C241.03,193.41 238.07,193.72 235.15,193.54C231.39,193.32 229.31,195.29 227.22,199.05C233.42,199.05 238.9,199.18 244.37,198.93C245.32,198.89 246.2,197.27 247.27,195.58Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M343.76,41.06C361.68,48.39 378.24,57.41 393.47,69.59C362.8,67.56 333.58,72.99 304.89,85.39C305.74,80.72 307.29,77.83 311.3,76.58C317.16,74.77 322.97,72.82 328.77,70.1C323.73,70.1 318.69,70.1 312.29,70.1C318.16,58.59 323.45,48.06 329,37.66C329.4,36.92 331.81,36.69 333.03,37.06C336.57,38.13 339.97,39.62 343.76,41.06Z"
android:fillColor="#FF44C4"/>
</vector>
@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M304.46,255.4C304.89,255.4 305.31,255.4 306.14,255.43C308.29,255.44 310.04,255.44 311.78,255.43C329.71,255.44 347.63,255.44 365.56,255.47C369.95,255.48 374.33,255.62 378.72,255.58C380.38,255.57 381.19,255.99 381.06,257.81C380.9,260.05 380.89,262.29 380.92,264.53C380.95,266.66 380.25,267.51 377.87,267.5C351.65,267.41 325.42,267.46 299.2,267.47C287.33,267.47 275.46,267.47 263.59,267.47C262.69,267.47 261.79,267.47 260.68,267.47C260.68,268.76 260.68,269.87 260.68,271.29C300.35,271.29 340.05,271.29 380.4,271.29C379.57,275.72 378.85,279.75 378,283.75C377.91,284.18 377.1,284.62 376.56,284.76C375.85,284.93 375.07,284.81 374.33,284.81C337.48,284.81 300.63,284.81 263.78,284.81C262.8,284.81 261.82,284.81 260.65,284.81C260.65,286.05 260.65,287.08 260.65,288.4C299.24,288.4 337.78,288.4 376.97,288.4C376.11,291.32 375.42,293.81 374.62,296.27C373.14,300.86 373.12,300.85 368.42,300.85C333.56,300.85 298.7,300.86 263.85,300.83C262.02,300.83 260.52,300.96 261.29,303.33C261.48,303.9 262.3,304.41 262.94,304.66C263.52,304.89 264.25,304.71 264.91,304.71C299.19,304.71 333.46,304.71 367.74,304.71C368.79,304.71 369.85,304.71 371.59,304.71C369.69,308.63 368.15,312.08 366.31,315.36C365.96,315.98 364.4,316.14 363.39,316.14C348.37,316.19 333.35,316.17 318.33,316.17C301.32,316.17 284.3,316.17 267.29,316.17C265.89,316.17 264.49,316.1 262.71,316.16C261.04,316.25 259.75,316.25 258.46,316.25C258,312.48 257.23,308.71 257.15,304.93C256.85,291.18 256.73,277.42 256.69,263.66C256.69,261.04 257.35,258.42 258.05,255.65C259.59,255.49 260.8,255.48 262.24,255.51C274.31,255.52 286.16,255.5 298.23,255.48C300.45,255.45 302.46,255.42 304.46,255.4Z"
android:fillColor="#BF42F6"/>
<path
android:pathData="M208.59,256.37C208.59,254.82 208.59,253.26 208.59,251.37C207.3,251.37 206.09,251.37 204.87,251.37C180.97,251.39 157.07,251.38 133.17,251.45C131.1,251.46 130.29,250.9 130.62,248.77C130.99,246.4 131.29,244 131.37,241.6C131.43,239.79 132.23,239.45 133.84,239.45C158.48,239.39 183.12,239.26 207.77,239.21C209.84,239.2 211.44,239.02 211,236.08C184.8,236.08 158.61,236.08 132.05,236.08C132.8,232.53 133.32,229.27 134.27,226.14C134.49,225.41 136.22,224.73 137.26,224.73C162.73,224.69 188.21,224.73 213.69,224.79C215.45,224.79 216.83,224.59 216.98,222.08C189.85,222.08 162.79,222.08 135.06,222.08C136.9,217.08 138.46,212.57 140.29,208.17C140.55,207.54 142.04,207.13 142.96,207.12C156.16,207.06 169.36,207.01 182.55,207.13C185.39,207.16 186.89,205.74 188.53,203.24C173.01,203.24 157.9,203.24 142.12,203.24C144.45,199.04 146.41,195.32 148.6,191.75C148.96,191.16 150.32,190.96 151.21,190.95C167.65,190.91 184.08,190.8 200.51,191.01C204.37,191.06 207.34,189.61 210.35,187.38C190.94,187.38 171.54,187.38 151.29,187.38C152.68,185.33 153.63,183.86 154.66,182.45C159.42,175.89 159.43,175.9 167.58,175.9C189.4,175.89 211.23,175.89 233.06,175.89C233.94,175.89 234.81,175.89 236.22,175.89C234.09,173.68 232.46,172.15 229.61,172.17C208.69,172.26 187.78,172.21 166.86,172.21C165.9,172.21 164.94,172.21 163.98,172.21C163.85,171.98 163.72,171.75 163.59,171.51C165.64,169.44 167.65,167.32 169.77,165.32C175.92,159.52 175.94,159.41 184.39,159.57C196.85,159.79 208.61,163.22 219.95,168.09C222.99,169.4 225.62,170.09 228.87,168.6C231.4,167.44 234.35,167.17 237.47,166.71C235.1,171.08 237.95,173.17 240.44,175.52C243.51,178.42 246.46,181.46 249.43,184.48C250.05,185.12 250.52,185.91 251.41,187.09C250.2,187.15 249.44,187.23 248.68,187.24C243.45,187.24 238.22,187.39 233,187.19C217.94,186.61 205.3,192.15 194.56,202.33C190.72,205.97 187.3,210.05 183.71,213.95C183.34,214.35 183.13,214.89 182.52,215.9C200.74,209.5 217.89,201.2 237.06,200.53C237.21,200.78 237.37,201.03 237.52,201.28C235.25,203.93 233.13,206.73 230.69,209.21C220.98,219.12 215.17,231.05 212.49,244.52C211.74,248.31 211.79,252.26 211.15,256.3C210.08,256.43 209.34,256.4 208.59,256.37ZM219.33,207.12C221.4,207.13 223.49,206.96 225.55,207.17C228.53,207.49 230.34,206 232.27,203.13C227.37,204.29 223.07,205.3 218.77,206.35C218.77,206.35 218.89,206.85 219.33,207.12Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M208.38,256.38C209.34,256.4 210.08,256.43 211.05,256.48C211.35,258.14 211.43,259.79 211.51,261.44C211.81,261.54 212.1,261.64 212.39,261.75C213.62,259.93 214.86,258.11 216.4,256.13C217.71,255.93 218.73,255.87 219.92,255.81C220.68,255.74 221.25,255.63 221.82,255.62C228.3,255.53 234.79,255.46 241.52,255.42C243.38,255.48 244.99,255.51 246.61,255.53C246.01,259.12 245.37,262.7 244.81,266.29C242.28,282.55 241.91,298.87 243.1,315.49C241.78,315.89 240.68,316.05 239.58,316.21C209.45,316.2 179.32,316.14 149.2,316.25C146.34,316.26 144.74,315.41 143.73,312.8C142.73,310.21 141.43,307.74 140.02,304.71C143.48,304.71 146.35,304.71 149.23,304.71C178.19,304.71 207.16,304.72 236.12,304.69C237.21,304.69 238.73,305.34 238.98,303.29C239.23,301.22 238.3,300.81 236.42,300.82C215.59,300.87 194.75,300.85 173.92,300.85C162.97,300.85 152.01,300.82 141.06,300.92C139.16,300.94 138.18,300.36 137.66,298.55C136.78,295.46 135.74,292.42 134.51,288.53C169.6,288.39 204.12,288.52 238.88,288.45C238.95,287.2 239.01,286.17 239.08,284.95C238.06,284.89 237.26,284.82 236.45,284.81C205.16,284.8 173.88,284.78 142.59,284.78C140.18,284.78 137.78,284.93 135.37,284.87C134.76,284.85 133.72,284.39 133.63,283.97C132.73,279.8 131.99,275.6 131.15,271.1C135.68,271.1 139.63,271.09 143.59,271.11C144.67,271.12 145.74,271.33 146.81,271.33C176.85,271.34 206.9,271.34 236.94,271.34C240.2,271.34 240.33,271.2 240.3,267.48C204,267.48 167.69,267.48 130.92,267.48C130.76,263.91 130.53,260.64 130.6,257.37C130.61,256.95 132.18,256.22 133.03,256.21C141.16,256.1 149.3,256.11 157.43,256.11C166.89,256.1 176.35,256.07 185.81,256.11C193.26,256.15 200.71,256.3 208.38,256.38Z"
android:fillColor="#BF43F5"/>
<path
android:pathData="M239.59,316.44C240.68,316.05 241.78,315.89 243.11,315.72C244.54,325.3 245.35,334.96 247.05,344.47C249.22,356.62 252.3,368.59 257.05,380.06C257.2,380.44 257.32,380.83 257.45,381.21C243.82,386.82 190.54,369.07 178.57,354.66C189.83,354.66 200.72,354.66 211.61,354.66C222.54,354.66 233.47,354.66 244.42,354.66C244.59,352.08 243.97,350.95 241.28,350.96C219.87,351.07 198.46,350.99 177.05,351.06C174.66,351.07 172.73,350.61 171.03,348.82C167.34,344.95 163.52,341.22 159.75,337.42C159.88,337.19 160,336.95 160.12,336.72C187.18,336.72 214.24,336.72 241.61,336.72C241.36,335.23 241.17,334.13 240.95,332.8C239.83,332.8 238.85,332.8 237.87,332.8C214.88,332.8 191.9,332.81 168.91,332.8C165.59,332.8 162.26,332.57 158.96,332.7C156.97,332.79 155.66,332.13 154.6,330.52C152.4,327.14 150.11,323.81 147.49,319.91C178.55,319.91 208.93,319.91 239.61,319.91C239.61,318.61 239.61,317.64 239.59,316.44Z"
android:fillColor="#985DED"/>
<path
android:pathData="M258.44,316.48C259.75,316.25 261.04,316.25 262.54,316.24C262.8,317.31 262.85,318.38 262.92,319.73C296.42,319.73 329.83,319.73 364.01,319.73C362.33,322.39 361.03,324.54 359.64,326.62C355.54,332.81 355.53,332.8 348.25,332.8C321.86,332.8 295.46,332.8 269.07,332.8C268.09,332.8 267.11,332.8 266.16,332.8C265.84,336.39 266.08,336.68 269.29,336.68C295.68,336.68 322.08,336.68 348.47,336.68C349.44,336.68 350.4,336.68 352.17,336.68C350,339.11 348.17,340.87 346.68,342.88C341.82,349.43 335.64,351.54 327.37,351.23C309.64,350.55 291.86,351.02 274.09,351.02C273.11,351.02 272.13,351.02 271.17,351.02C271.02,354.12 271.48,354.66 274.17,354.66C292.68,354.66 311.19,354.66 329.7,354.66C330.65,354.66 331.6,354.66 332.54,354.66C332.62,354.89 332.7,355.12 332.78,355.35C330.9,356.75 329.07,358.22 327.14,359.56C314.53,368.29 300.74,374.45 285.87,378.18C284.11,378.62 282.23,378.92 280.46,378.76C279.45,378.68 278.23,377.74 277.63,376.83C272.53,369.18 269.33,360.66 266.33,352.02C262.35,340.53 260.11,328.68 258.44,316.48Z"
android:fillColor="#985DED"/>
<path
android:pathData="M311.77,255.22C310.04,255.44 308.29,255.44 306.3,255.42C307.7,247.72 307.53,240.1 305.68,232.49C303.13,222.06 297.19,214.1 288.23,208.31C287.89,208.09 287.55,207.86 287.23,207.16C296.64,207.16 306.04,207.16 315.44,207.16C315.48,206.9 315.52,206.63 315.56,206.36C311.79,204.93 308.05,203.27 303.89,203.23C295.6,203.16 287.3,203.26 279,203.17C277.34,203.15 275.68,202.61 274.06,201.88C283.92,197.91 294.01,198.34 304.15,199.94C314.42,201.56 323.36,206.9 333.47,211.01C332.66,209.46 332.18,208.57 331.51,207.28C332.5,207.2 333.25,207.09 334.01,207.09C345.54,207.08 357.08,207.11 368.61,207.04C370.36,207.03 371.33,207.5 371.9,209.25C373.14,213.07 374.59,216.83 376.13,221.13C352.97,221.13 330.46,221.13 307.83,221.13C307.91,223.56 308.96,224.24 311,224.23C326.6,224.18 342.2,224.17 357.8,224.21C363.6,224.23 369.4,224.38 375.2,224.59C375.91,224.61 377.07,225.22 377.21,225.77C378.05,228.87 378.63,232.04 379.37,235.53C356.71,235.53 334.45,235.53 312.2,235.53C311.91,238.9 312.07,239.08 315,239.08C335.91,239.11 356.82,239.16 377.73,239.16C379.27,239.16 379.99,239.52 380.09,241.18C380.25,243.91 380.6,246.63 380.95,249.34C381.17,251.05 380.61,251.72 378.8,251.66C375.24,251.54 371.67,251.6 368.11,251.6C350.51,251.6 332.92,251.6 315.33,251.6C311.97,251.6 311.97,251.61 311.77,255.22Z"
android:fillColor="#FF43C4"/>
<path
android:pathData="M233.67,133.02C256.13,129.25 277.19,131.88 288.96,135.95C287.31,139.82 285.66,143.69 284.01,147.55C283.13,149.6 282.1,151.61 281.39,153.73C280.81,155.48 279.79,155.91 278.06,155.9C266.2,155.83 254.34,155.87 241.85,155.87C243.96,158.04 245.54,159.78 248.45,159.75C257.41,159.67 266.37,159.72 275.33,159.73C276.24,159.73 277.14,159.73 278.95,159.73C276.99,163 275.7,166.08 273.6,168.43C270.3,172.11 266.39,175.23 262.88,178.73C261.53,180.08 260.9,180.03 259.6,178.61C254.3,172.8 248.97,167.01 243.39,161.48C237.24,155.37 229.44,152.84 220.98,151.86C213.46,150.99 206.32,152.38 199.27,154.82C197.58,155.4 195.74,155.75 193.96,155.82C190.07,155.97 186.17,155.86 182.15,155.55C197.45,143.96 214.61,136.59 233.67,133.02ZM262.52,174.52C263.06,173.88 263.6,173.24 264.46,172.22C262.24,172.22 260.62,172.22 258.52,172.22C259.34,173.38 259.78,174.29 260.48,174.87C260.83,175.15 261.67,174.84 262.52,174.52Z"
android:fillColor="#FF42C3"/>
<path
android:pathData="M320.81,196.15C314.32,190.85 306.7,188.96 298.84,187.82C289.74,186.5 280.81,187.01 271.96,190.85C272.47,189.92 272.88,188.91 273.51,188.06C281.91,176.57 292.26,167.25 304.84,160.58C306.74,159.57 309.33,159.62 311.6,159.59C318.82,159.47 326.04,159.5 333.26,159.59C334.35,159.6 335.69,159.93 336.45,160.63C340.22,164 343.83,167.55 347.48,171.05C347.69,171.25 347.78,171.57 348.11,172.21C346.93,172.21 346.05,172.21 345.18,172.21C328.58,172.21 311.98,172.28 295.39,172.15C292.59,172.13 291.04,173.55 288.95,175.9C290.46,175.9 291.4,175.9 292.33,175.9C311.17,175.9 330.01,175.93 348.85,175.85C351,175.84 352.47,176.48 353.68,178.19C355.68,181.01 357.78,183.76 360.24,187.08C343.65,187.08 327.73,187.08 311.82,187.08C311.79,187.29 311.76,187.49 311.74,187.69C315.27,189 318.39,191.09 322.57,191.01C335.01,190.76 347.46,190.96 359.91,190.88C361.92,190.87 363.16,191.51 364.06,193.28C365.66,196.45 367.39,199.54 369.27,203.03C368.03,203.11 367.26,203.21 366.48,203.21C354.44,203.21 342.41,203.18 330.38,203.24C328.66,203.25 327.4,202.81 326.24,201.52C324.58,199.67 322.72,198.01 320.81,196.15Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M262.01,255.48C260.8,255.48 259.59,255.49 258.15,255.49C259.26,241.21 260.47,226.92 264.71,213.11C265.34,211.06 266.03,210.16 268.06,211.62C270.08,213.06 272.41,214.16 274.15,215.88C286.01,227.55 297.17,239.78 304.4,255.23C302.46,255.42 300.45,255.45 298.21,255.28C297.55,252.82 296.4,251.87 293.9,251.91C284.4,252.06 274.9,251.97 265.39,251.98C264.34,251.98 263.3,251.98 262.01,251.98C262.01,253.37 262.01,254.43 262.01,255.48ZM263.18,238.86C271.2,238.86 279.22,238.86 287.25,238.86C287.34,238.56 287.43,238.26 287.52,237.96C286.55,237.47 285.57,236.58 284.58,236.57C277.89,236.44 271.2,236.51 264.5,236.49C262.98,236.48 262.3,237.02 263.18,238.86ZM268.75,222.05C266.99,221.93 265.42,222.05 265.71,224.51C268.73,224.51 271.68,224.51 274.73,224.51C273.54,221.78 271.35,221.98 268.75,222.05Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M246.72,255.38C244.99,255.51 243.38,255.48 241.58,255.22C241.46,254.11 241.53,253.25 241.62,252.02C235.47,252.02 229.64,252.02 223.81,252.02C220.63,252.03 220.2,252.43 219.74,255.82C218.73,255.87 217.71,255.93 216.49,255.98C218.93,250.76 221.21,245.3 224.33,240.35C229.86,231.57 236.13,223.3 244.52,216.92C248.39,213.97 252.62,211.77 257.32,210.49C257.93,210.32 258.57,210.3 259.92,210.12C254.32,225.13 249.77,239.96 246.72,255.38ZM232.91,235.63C230.52,235.4 229.98,237.18 228.98,239.19C234.28,239.19 239.23,239.12 244.17,239.22C246.42,239.27 247.04,238.27 246.74,235.68C242.23,235.68 237.78,235.68 232.91,235.63ZM251.08,223.33C250.49,222.98 249.91,222.38 249.29,222.32C247.73,222.17 246.14,222.34 244.57,222.24C242.56,222.12 241.44,223.18 240.32,225.2C243.65,225.2 246.59,225.26 249.52,225.13C250.03,225.11 250.51,224.24 251.08,223.33Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M302.88,140.38C312.51,144.32 321.39,149.16 329.57,155.7C313.1,154.61 297.42,157.52 282.02,164.18C282.47,161.67 283.3,160.12 285.45,159.45C288.6,158.48 291.72,157.43 294.83,155.97C292.13,155.97 289.42,155.97 285.99,155.97C289.14,149.79 291.98,144.13 294.96,138.55C295.17,138.15 296.47,138.03 297.12,138.23C299.02,138.8 300.85,139.61 302.88,140.38Z"
android:fillColor="#FF44C4"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

@@ -0,0 +1,39 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="512dp"
android:height="512dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M317.52,168.37C318.06,168.37 318.6,168.38 319.65,168.41C322.37,168.43 324.58,168.42 326.79,168.41C349.49,168.43 372.2,168.43 394.91,168.46C400.46,168.47 406.02,168.65 411.58,168.6C413.68,168.59 414.71,169.12 414.54,171.43C414.35,174.26 414.33,177.1 414.37,179.94C414.4,182.64 413.52,183.71 410.51,183.7C377.29,183.59 344.07,183.65 310.85,183.66C295.82,183.66 280.79,183.66 265.75,183.66C264.61,183.66 263.47,183.66 262.06,183.66C262.06,185.29 262.06,186.7 262.06,188.5C312.31,188.5 362.6,188.5 413.71,188.5C412.65,194.11 411.74,199.21 410.66,204.28C410.55,204.83 409.53,205.38 408.84,205.56C407.95,205.78 406.96,205.62 406.01,205.62C359.34,205.62 312.66,205.62 265.99,205.62C264.75,205.62 263.51,205.62 262.03,205.62C262.03,207.2 262.03,208.49 262.03,210.18C310.9,210.18 359.72,210.18 409.36,210.18C408.27,213.87 407.4,217.03 406.39,220.15C404.52,225.95 404.48,225.94 398.53,225.94C354.38,225.94 310.22,225.95 266.07,225.92C263.76,225.91 261.86,226.09 262.84,229.08C263.07,229.81 264.11,230.46 264.92,230.77C265.65,231.06 266.58,230.84 267.42,230.84C310.84,230.84 354.26,230.84 397.67,230.84C399.01,230.84 400.34,230.84 402.55,230.84C400.14,235.79 398.2,240.17 395.86,244.32C395.42,245.11 393.44,245.31 392.17,245.31C373.14,245.37 354.11,245.35 335.08,245.35C313.54,245.35 291.98,245.35 270.43,245.35C268.66,245.35 266.89,245.26 264.63,245.33C262.52,245.45 260.88,245.45 259.24,245.46C258.67,240.67 257.69,235.9 257.58,231.11C257.21,213.69 257.06,196.26 257.01,178.84C257,175.52 257.85,172.2 258.72,168.69C260.68,168.49 262.21,168.48 264.04,168.51C279.33,168.53 294.33,168.5 309.62,168.47C312.44,168.44 314.98,168.4 317.52,168.37Z"
android:fillColor="#BF42F6"/>
<path
android:pathData="M196.08,169.6C196.08,167.63 196.08,165.67 196.08,163.27C194.45,163.27 192.91,163.27 191.37,163.27C161.1,163.29 130.82,163.28 100.54,163.37C97.92,163.38 96.9,162.67 97.32,159.97C97.78,156.97 98.17,153.93 98.27,150.9C98.34,148.6 99.36,148.17 101.4,148.17C132.61,148.1 163.82,147.94 195.04,147.86C197.66,147.86 199.69,147.63 199.14,143.9C165.95,143.9 132.78,143.9 99.13,143.9C100.08,139.41 100.73,135.27 101.94,131.31C102.23,130.39 104.41,129.53 105.72,129.53C137.99,129.47 170.27,129.52 202.54,129.6C204.76,129.61 206.52,129.34 206.71,126.17C172.35,126.17 138.07,126.17 102.94,126.17C105.27,119.84 107.26,114.13 109.56,108.55C109.89,107.76 111.78,107.23 112.96,107.22C129.67,107.14 146.39,107.08 163.1,107.23C166.69,107.27 168.59,105.48 170.67,102.31C151.01,102.31 131.87,102.31 111.88,102.31C114.84,96.98 117.32,92.27 120.09,87.75C120.55,87 122.27,86.74 123.4,86.74C144.22,86.68 165.04,86.54 185.85,86.82C190.74,86.88 194.5,85.04 198.31,82.21C173.73,82.21 149.15,82.21 123.5,82.21C125.26,79.62 126.46,77.76 127.76,75.97C133.8,67.66 133.81,67.67 144.13,67.67C171.78,67.67 199.43,67.67 227.08,67.67C228.19,67.67 229.29,67.67 231.08,67.67C228.38,64.86 226.32,62.93 222.7,62.94C196.21,63.06 169.72,63.01 143.22,63.01C142.01,63.01 140.79,63.01 139.58,63.01C139.41,62.71 139.24,62.41 139.08,62.12C141.68,59.5 144.22,56.8 146.91,54.27C154.7,46.92 154.72,46.79 165.43,46.98C181.21,47.26 196.1,51.61 210.47,57.78C214.32,59.44 217.65,60.31 221.76,58.43C224.98,56.95 228.71,56.61 232.67,56.03C229.67,61.57 233.27,64.22 236.42,67.19C240.32,70.87 244.05,74.72 247.81,78.54C248.6,79.35 249.19,80.35 250.32,81.84C248.78,81.93 247.82,82.03 246.87,82.03C240.24,82.04 233.61,82.23 227,81.97C207.93,81.24 191.92,88.26 178.31,101.15C173.45,105.76 169.11,110.93 164.57,115.87C164.1,116.38 163.83,117.06 163.06,118.34C186.14,110.23 207.86,99.73 232.14,98.87C232.34,99.18 232.53,99.5 232.73,99.82C229.85,103.18 227.16,106.72 224.07,109.87C211.77,122.42 204.42,137.53 201.03,154.6C200.07,159.39 200.14,164.4 199.32,169.51C197.97,169.68 197.03,169.64 196.08,169.6ZM209.68,107.22C212.31,107.23 214.96,107.01 217.56,107.29C221.34,107.69 223.62,105.8 226.08,102.17C219.87,103.64 214.42,104.92 208.98,106.24C208.98,106.24 209.12,106.87 209.68,107.22Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M195.81,169.61C197.03,169.64 197.97,169.68 199.2,169.74C199.58,171.84 199.68,173.93 199.79,176.02C200.15,176.15 200.52,176.28 200.89,176.41C202.45,174.11 204.02,171.8 205.97,169.3C207.64,169.04 208.92,168.97 210.44,168.9C211.39,168.81 212.11,168.66 212.84,168.65C221.05,168.54 229.27,168.45 237.79,168.4C240.14,168.48 242.19,168.51 244.24,168.54C243.48,173.08 242.67,177.61 241.96,182.16C238.76,202.76 238.29,223.44 239.8,244.49C238.12,244.99 236.72,245.2 235.33,245.4C197.17,245.39 159.01,245.31 120.85,245.45C117.22,245.46 115.21,244.38 113.93,241.08C112.65,237.8 111,234.67 109.23,230.84C113.61,230.84 117.25,230.84 120.89,230.84C157.58,230.84 194.27,230.85 230.96,230.81C232.33,230.8 234.26,231.63 234.58,229.04C234.9,226.41 233.71,225.89 231.33,225.9C204.94,225.97 178.55,225.94 152.17,225.95C138.29,225.95 124.42,225.9 110.54,226.03C108.13,226.05 106.9,225.32 106.24,223.03C105.12,219.12 103.81,215.26 102.24,210.34C146.69,210.16 190.42,210.32 234.45,210.24C234.54,208.65 234.62,207.35 234.7,205.8C233.41,205.73 232.39,205.63 231.38,205.63C191.74,205.61 152.11,205.59 112.48,205.59C109.43,205.59 106.38,205.78 103.34,205.7C102.57,205.68 101.25,205.09 101.13,204.56C99.99,199.28 99.05,193.95 97.99,188.26C103.72,188.26 108.74,188.24 113.75,188.28C115.11,188.29 116.47,188.55 117.83,188.55C155.88,188.57 193.94,188.57 231.99,188.56C236.12,188.56 236.28,188.39 236.25,183.67C190.27,183.67 144.28,183.67 97.69,183.67C97.5,179.15 97.2,175.01 97.29,170.87C97.3,170.33 99.29,169.41 100.37,169.4C110.67,169.26 120.97,169.28 131.28,169.27C143.26,169.26 155.24,169.22 167.23,169.28C176.66,169.33 186.1,169.51 195.81,169.61Z"
android:fillColor="#BF43F5"/>
<path
android:pathData="M235.35,245.69C236.72,245.2 238.12,244.99 239.8,244.78C241.62,256.91 242.64,269.16 244.79,281.19C247.54,296.59 251.45,311.75 257.46,326.28C257.66,326.75 257.8,327.25 257.96,327.73C240.7,334.84 173.22,312.36 158.05,294.11C172.31,294.11 186.11,294.11 199.9,294.11C213.75,294.11 227.59,294.11 241.47,294.11C241.67,290.83 240.89,289.4 237.49,289.42C210.37,289.55 183.25,289.46 156.13,289.54C153.11,289.55 150.66,288.97 148.51,286.71C143.83,281.81 138.99,277.07 134.22,272.27C134.38,271.97 134.53,271.67 134.69,271.38C168.96,271.38 203.24,271.38 237.91,271.38C237.59,269.49 237.35,268.09 237.06,266.42C235.66,266.42 234.41,266.42 233.17,266.42C204.05,266.42 174.93,266.43 145.82,266.41C141.61,266.41 137.4,266.12 133.21,266.29C130.69,266.4 129.04,265.57 127.7,263.52C124.9,259.24 122.01,255.03 118.68,250.09C158.03,250.09 196.51,250.09 235.37,250.09C235.37,248.43 235.37,247.21 235.35,245.69Z"
android:fillColor="#985DED"/>
<path
android:pathData="M259.22,245.74C260.88,245.45 262.52,245.45 264.42,245.44C264.74,246.79 264.81,248.15 264.9,249.86C307.33,249.86 349.65,249.86 392.94,249.86C390.82,253.23 389.17,255.95 387.42,258.59C382.22,266.42 382.21,266.42 372.98,266.42C339.55,266.42 306.12,266.42 272.68,266.42C271.45,266.42 270.21,266.42 269.01,266.42C268.6,270.97 268.9,271.32 272.97,271.32C306.4,271.33 339.83,271.33 373.27,271.33C374.49,271.33 375.71,271.33 377.95,271.33C375.2,274.41 372.88,276.64 370.99,279.18C364.84,287.47 357,290.15 346.54,289.75C324.07,288.9 301.55,289.49 279.05,289.49C277.81,289.49 276.57,289.49 275.34,289.49C275.16,293.42 275.74,294.1 279.15,294.1C302.59,294.11 326.04,294.1 349.48,294.11C350.69,294.11 351.89,294.11 353.09,294.11C353.19,294.39 353.29,294.68 353.39,294.97C351.01,296.75 348.69,298.61 346.25,300.3C330.27,311.36 312.8,319.17 293.96,323.9C291.74,324.45 289.36,324.83 287.12,324.64C285.84,324.52 284.3,323.33 283.54,322.19C277.07,312.49 273.01,301.7 269.22,290.75C264.17,276.21 261.34,261.2 259.22,245.74Z"
android:fillColor="#985DED"/>
<path
android:pathData="M326.78,168.15C324.58,168.42 322.37,168.43 319.85,168.39C321.63,158.65 321.41,149 319.06,139.36C315.84,126.14 308.31,116.06 296.96,108.73C296.52,108.45 296.09,108.16 295.7,107.28C307.61,107.28 319.52,107.28 331.43,107.28C331.48,106.93 331.53,106.59 331.57,106.25C326.8,104.44 322.07,102.34 316.8,102.29C306.29,102.2 295.77,102.33 285.27,102.21C283.16,102.19 281.07,101.5 279.01,100.57C291.5,95.55 304.29,96.1 317.13,98.13C330.14,100.18 341.45,106.94 354.27,112.15C353.23,110.19 352.63,109.05 351.78,107.42C353.03,107.32 353.99,107.18 354.94,107.18C369.55,107.16 384.16,107.21 398.77,107.12C400.99,107.11 402.21,107.7 402.93,109.91C404.51,114.76 406.34,119.52 408.3,124.96C378.96,124.96 350.45,124.96 321.79,124.96C321.89,128.04 323.22,128.9 325.8,128.89C345.56,128.82 365.32,128.82 385.08,128.87C392.43,128.89 399.77,129.08 407.11,129.34C408.02,129.37 409.48,130.14 409.67,130.85C410.73,134.77 411.47,138.78 412.41,143.2C383.7,143.2 355.5,143.2 327.33,143.2C326.95,147.48 327.15,147.7 330.87,147.7C357.35,147.74 383.84,147.81 410.32,147.8C412.27,147.8 413.19,148.26 413.31,150.37C413.52,153.82 413.96,157.26 414.41,160.69C414.69,162.87 413.98,163.71 411.68,163.63C407.17,163.48 402.65,163.56 398.13,163.56C375.85,163.56 353.57,163.56 331.29,163.56C327.03,163.56 327.03,163.57 326.78,168.15Z"
android:fillColor="#FF43C4"/>
<path
android:pathData="M227.85,13.36C256.3,8.58 282.98,11.91 297.89,17.06C295.79,21.98 293.7,26.87 291.61,31.76C290.5,34.37 289.2,36.91 288.3,39.59C287.56,41.8 286.27,42.35 284.07,42.34C269.05,42.26 254.03,42.3 238.21,42.3C240.88,45.05 242.88,47.26 246.57,47.22C257.92,47.11 269.27,47.18 280.62,47.19C281.77,47.19 282.91,47.19 285.21,47.19C282.72,51.33 281.09,55.24 278.43,58.21C274.25,62.87 269.29,66.82 264.85,71.26C263.14,72.97 262.34,72.9 260.7,71.1C253.98,63.75 247.23,56.42 240.16,49.4C232.38,41.67 222.49,38.46 211.78,37.23C202.25,36.13 193.21,37.88 184.27,40.97C182.14,41.71 179.81,42.15 177.55,42.24C172.62,42.42 167.68,42.29 162.59,41.89C181.97,27.22 203.7,17.88 227.85,13.36ZM264.4,65.93C265.08,65.12 265.76,64.31 266.85,63.01C264.04,63.01 261.99,63.01 259.33,63.01C260.37,64.48 260.92,65.63 261.81,66.37C262.25,66.73 263.32,66.32 264.4,65.93Z"
android:fillColor="#FF42C3"/>
<path
android:pathData="M338.22,93.32C330,86.61 320.35,84.22 310.4,82.77C298.87,81.1 287.56,81.75 276.35,86.62C276.99,85.43 277.52,84.16 278.31,83.08C288.95,68.53 302.06,56.72 317.99,48.27C320.4,46.99 323.68,47.06 326.56,47.01C335.71,46.86 344.86,46.9 354,47.01C355.37,47.02 357.07,47.45 358.04,48.33C362.81,52.6 367.39,57.09 372.01,61.53C372.28,61.78 372.38,62.19 372.81,63.01C371.31,63.01 370.2,63.01 369.09,63.01C348.07,63.01 327.04,63.08 306.02,62.93C302.48,62.9 300.52,64.7 297.87,67.67C299.78,67.67 300.97,67.67 302.15,67.67C326.02,67.67 349.88,67.71 373.74,67.6C376.46,67.59 378.32,68.41 379.86,70.57C382.39,74.14 385.05,77.63 388.18,81.84C367.16,81.84 347,81.84 326.84,81.84C326.8,82.1 326.77,82.35 326.73,82.61C331.21,84.27 335.15,86.92 340.45,86.81C356.21,86.5 371.98,86.75 387.75,86.65C390.3,86.63 391.87,87.45 393.01,89.69C395.03,93.7 397.23,97.61 399.61,102.04C398.04,102.14 397.06,102.26 396.07,102.26C380.83,102.27 365.59,102.23 350.34,102.31C348.17,102.32 346.58,101.76 345.1,100.13C343,97.79 340.64,95.68 338.22,93.32Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M263.75,168.47C262.21,168.48 260.68,168.49 258.86,168.49C260.26,150.4 261.8,132.3 267.17,114.8C267.96,112.21 268.84,111.07 271.41,112.92C273.97,114.75 276.92,116.14 279.13,118.31C294.14,133.1 308.29,148.58 317.44,168.16C314.98,168.4 312.44,168.44 309.6,168.22C308.77,165.11 307.31,163.9 304.14,163.95C292.11,164.14 280.07,164.03 268.03,164.04C266.7,164.04 265.38,164.04 263.75,164.04C263.75,165.81 263.75,167.14 263.75,168.47ZM265.23,147.42C275.39,147.42 285.55,147.42 295.71,147.42C295.83,147.04 295.95,146.66 296.07,146.28C294.82,145.66 293.59,144.54 292.34,144.52C283.86,144.36 275.38,144.45 266.9,144.42C264.98,144.41 264.11,145.09 265.23,147.42ZM272.28,126.13C270.05,125.98 268.06,126.13 268.44,129.24C272.26,129.24 276,129.24 279.86,129.24C278.35,125.79 275.57,126.04 272.28,126.13Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M244.37,168.35C242.19,168.51 240.14,168.48 237.87,168.14C237.72,166.74 237.8,165.65 237.92,164.1C230.12,164.1 222.74,164.09 215.35,164.1C211.33,164.1 210.79,164.62 210.2,168.9C208.92,168.97 207.64,169.04 206.08,169.11C209.18,162.49 212.07,155.57 216.01,149.32C223.02,138.18 230.96,127.72 241.59,119.63C246.5,115.89 251.86,113.11 257.81,111.49C258.58,111.28 259.39,111.25 261.09,111.02C254,130.03 248.24,148.81 244.37,168.35ZM226.88,143.34C223.86,143.04 223.17,145.3 221.91,147.84C228.63,147.84 234.89,147.75 241.15,147.88C244,147.94 244.78,146.68 244.4,143.4C238.69,143.4 233.05,143.4 226.88,143.34ZM249.9,127.76C249.15,127.31 248.42,126.55 247.63,126.47C245.66,126.28 243.65,126.49 241.66,126.37C239.1,126.21 237.69,127.56 236.27,130.12C240.49,130.12 244.21,130.2 247.93,130.03C248.58,130 249.17,128.9 249.9,127.76Z"
android:fillColor="#FF44C4"/>
<path
android:pathData="M315.52,22.68C327.71,27.67 338.96,33.8 349.32,42.08C328.46,40.7 308.6,44.4 289.08,52.83C289.67,49.65 290.71,47.68 293.44,46.84C297.43,45.61 301.38,44.27 305.32,42.43C301.9,42.43 298.47,42.43 294.12,42.43C298.11,34.6 301.71,27.44 305.48,20.37C305.75,19.86 307.39,19.71 308.22,19.96C310.62,20.69 312.94,21.7 315.52,22.68Z"
android:fillColor="#FF44C4"/>
</vector>
@@ -48,7 +48,7 @@
android:layout_height="200dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_yuzu_named" />
android:src="@drawable/ic_yuzu_title" />
<LinearLayout
android:layout_width="0dp"
@@ -44,11 +44,12 @@
<ImageView
android:id="@+id/image_logo"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginVertical="24dp"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="28dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu_named" />
android:src="@drawable/ic_yuzu_title" />
<com.google.android.material.card.MaterialCardView
style="@style/EdenCard"

Some files were not shown because too many files have changed in this diff Show More