Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 795a52c75a | |||
| e22756160c | |||
| a33956f738 | |||
| 4642e82ca7 | |||
| 97054357d2 | |||
| 941caf31ce | |||
| c72144abad | |||
| d656e347c8 | |||
| 8859809ebd | |||
| 318998cbb5 | |||
| f58097e814 | |||
| 1efef85352 | |||
| ea0e5d630c | |||
| 9e610ea098 | |||
| 41af6ea645 | |||
| 91b0432591 | |||
| 46239dafa1 | |||
| bf23921f07 | |||
| e63f71c787 | |||
| 027085e5ba | |||
| f40025fd9b | |||
| ed39ec4738 | |||
| fe13539d72 | |||
| be218cc020 | |||
| c3cbe2d4d0 | |||
| 79b162a37c | |||
| f3fbb3812f | |||
| d8caa74233 | |||
| 17fe74ef11 | |||
| 73713737c6 | |||
| 61f3ce643c | |||
| f7f6a4cde4 | |||
| 65fa1a37e2 | |||
| 41192e6e3d | |||
| d8f2e22bf8 | |||
| 51506ef836 | |||
| 0d3cef65ad | |||
| c2794985b2 | |||
| 2e1af92852 | |||
| b9655669b3 | |||
| 6b01977005 | |||
| 91d41d1c34 | |||
| 2e4ee8d9a4 | |||
| 92274ff280 | |||
| d1ac5b2e50 | |||
| c160d6b752 | |||
| eb2d9ea574 | |||
| 4f389338bd | |||
| 227a007413 | |||
| 74d6eaf721 | |||
| 9807fa02ed | |||
| 7d239df065 | |||
| b690813196 | |||
| b9f54f4979 | |||
| 7e730a121b | |||
| 680384f7b8 | |||
| a27914f0f4 | |||
| 3f226678dd | |||
| e9d84d098d | |||
| 32db6c1877 | |||
| 8eaa7c28ce | |||
| 3edfcabdea | |||
| 450c483de0 | |||
| 66db2613b5 | |||
| 9a046190c7 | |||
| cfbef5c487 | |||
| f51d61e4a4 | |||
| ba9e03a612 | |||
| 7832afc5dd | |||
| 028765867f | |||
| d89df63a28 | |||
| 87c4f658ce | |||
| b7584cb2c3 | |||
| f32f356c40 | |||
| 7ca657d22f | |||
| 7764cdd57e | |||
| 42863027e2 |
@@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# specify full path if dupes may exist
|
||||
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
|
||||
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh tools/windows/vcvarsall.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
|
||||
|
||||
# license header constants, please change when needed :))))
|
||||
YEAR=2025
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: yuzu Discord
|
||||
url: https://discord.com/invite/u77vRWY
|
||||
about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
|
||||
- name: Community forums
|
||||
url: https://community.citra-emu.org
|
||||
about: This is an alternative place for tech support, however helpers there are not as active.
|
||||
- name: Eden Discord
|
||||
url: https://discord.gg/HstXbPch7X
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
name: tx-src
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
sources:
|
||||
runs-on: source
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Push New Sources
|
||||
run: |
|
||||
export PATH=/usr/lib/qt6/bin:$PATH
|
||||
./tools/translations/qt-source.sh
|
||||
tx-cli push -s
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Check Strings
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
check-strings:
|
||||
runs-on: source
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find Unused Strings
|
||||
run: ./tools/find-unused-strings.sh
|
||||
|
||||
- name: Find Stale Translations
|
||||
run: ./tools/stale-translations.sh
|
||||
|
||||
- name: Diff
|
||||
run: git --no-pager diff --exit-code HEAD
|
||||
@@ -0,0 +1,62 @@
|
||||
name: tx-pull
|
||||
|
||||
on:
|
||||
# monday, wednesday, saturday at 2pm
|
||||
schedule:
|
||||
cron:
|
||||
- '0 14 * * 1,3,6'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tx-update:
|
||||
runs-on: source
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get New Translations
|
||||
run: tx-cli pull -t -f
|
||||
|
||||
- name: Push branch
|
||||
run: |
|
||||
git config --local user.name "Eden CI"
|
||||
git config --local user.email "ci@eden-emu.dev"
|
||||
git config --local user.signingkey "D57652791BB25D2A"
|
||||
git config --local push.autoSetupRemote true
|
||||
|
||||
git remote set-url origin ci:eden-emu/eden.git
|
||||
|
||||
TIMESTAMP=$(date +"%s")
|
||||
echo "TIMESTAMP=$TIMESTAMP" >> "$GITHUB_ENV"
|
||||
|
||||
git switch -c update-translations-$TIMESTAMP
|
||||
git add dist src/android/app/src/main/res
|
||||
|
||||
git commit -sS -m "[dist, android] Update translations from Transifex"
|
||||
git push
|
||||
|
||||
- name: Create PR
|
||||
run: |
|
||||
DATE=$(date +"%b %d")
|
||||
TITLE="[dist, android] Update translations from Transifex for $DATE"
|
||||
BODY="Automatic translation update for $DATE"
|
||||
BASE=master
|
||||
HEAD=update-translations-$TIMESTAMP
|
||||
|
||||
cat << EOF > data.json
|
||||
{
|
||||
"base": "$BASE",
|
||||
"body": "$BODY",
|
||||
"head": "$HEAD",
|
||||
"title": "$TITLE"
|
||||
}
|
||||
EOF
|
||||
|
||||
curl -X 'POST' \
|
||||
'https://git.eden-emu.dev/api/v1/repos/eden-emu/eden/pulls' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'Authorization: Bearer ${{ secrets.CI_FJ_TOKEN }}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "@data.json" --fail
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# Build directory
|
||||
/[Bb]uild*/
|
||||
doc-build/
|
||||
out/
|
||||
AppDir/
|
||||
uruntime
|
||||
|
||||
@@ -59,3 +60,4 @@ eden-windows-msvc
|
||||
artifacts
|
||||
*.AppImage*
|
||||
/install*
|
||||
vulkansdk*.exe
|
||||
|
||||
@@ -14,6 +14,7 @@ License: GPL-2.0-or-later
|
||||
|
||||
Files: dist/qt_themes/default/icons/256x256/eden.png
|
||||
dist/qt_themes/default/icons/256x256/eden_named.png
|
||||
dist/Assets.car
|
||||
dist/yuzu.bmp
|
||||
dist/eden.icns
|
||||
dist/eden.ico
|
||||
|
||||
@@ -21,6 +21,11 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(PLATFORM_LINUX ON)
|
||||
endif()
|
||||
|
||||
# dumb heuristic to detect msys2
|
||||
if (CMAKE_COMMAND MATCHES "msys64")
|
||||
set(PLATFORM_MSYS ON)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(CXX_CLANG ON)
|
||||
if (MSVC)
|
||||
@@ -85,6 +90,93 @@ if (PLATFORM_NETBSD)
|
||||
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
|
||||
endif()
|
||||
|
||||
# MSYS2 utilities
|
||||
if (PLATFORM_MSYS)
|
||||
include(FixMsysPaths)
|
||||
# really, really dumb heuristic to detect what environment we are in
|
||||
macro(system var)
|
||||
if (CMAKE_COMMAND MATCHES ${var})
|
||||
set(MSYSTEM ${var})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
system(mingw64)
|
||||
system(clang64)
|
||||
system(clangarm64)
|
||||
system(ucrt64)
|
||||
|
||||
if (NOT DEFINED MSYSTEM)
|
||||
set(MSYSTEM msys2)
|
||||
endif()
|
||||
|
||||
# we (generally) want to prioritize environment-specific binaries if possible
|
||||
# some, like autoconf, are not present on environments besides msys2 though
|
||||
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
|
||||
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
|
||||
endif()
|
||||
|
||||
# static stuff
|
||||
option(YUZU_STATIC_BUILD "Use static libraries and executables if available" OFF)
|
||||
|
||||
if (YUZU_STATIC_BUILD)
|
||||
include(StaticQtLibs)
|
||||
|
||||
# lol
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
## find .a libs first (static, usually)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
|
||||
|
||||
## some libraries use CMAKE_IMPORT_LIBRARY_SUFFIX e.g. Harfbuzz ##
|
||||
set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a")
|
||||
|
||||
if (MINGW)
|
||||
# simple hook to reject dynamic libs
|
||||
function(find_library var)
|
||||
# also skip previously-found libraries cuz... yaknow
|
||||
# UNLESS they are dll.a :{
|
||||
if (${var} AND NOT ${var} MATCHES "dll\\.a$")
|
||||
return()
|
||||
endif()
|
||||
|
||||
_find_library(${var} ${ARGN})
|
||||
if (${var})
|
||||
get_filename_component(lib_name "${${var}}" NAME)
|
||||
if (lib_name MATCHES "dll\\.a$")
|
||||
unset(${var} CACHE)
|
||||
set(${var} "${var}-NOTFOUND" CACHE INTERNAL "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# msys2 quazip does not build a static lib
|
||||
set(QuaZip-Qt6_FORCE_BUNDLED ON)
|
||||
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
set(YUZU_USE_BUNDLED_SDL2 ON)
|
||||
set(YUZU_USE_BUNDLED_OPENSSL ON)
|
||||
|
||||
set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF)
|
||||
|
||||
## some libraries define a Library::Name_static alternative ##
|
||||
set(MBEDTLS_LIB_SUFFIX _static)
|
||||
elseif(APPLE)
|
||||
set(YUZU_USE_CPM ON)
|
||||
|
||||
set(YUZU_USE_BUNDLED_FFMPEG ON)
|
||||
set(YUZU_USE_BUNDLED_SDL2 ON)
|
||||
set(YUZU_USE_BUNDLED_OPENSSL ON)
|
||||
|
||||
# these libs do not properly provide static libs/let you do it with cmake
|
||||
set(fmt_FORCE_BUNDLED ON)
|
||||
set(SPIRV-Tools_FORCE_BUNDLED ON)
|
||||
set(SPIRV-Headers_FORCE_BUNDLED ON)
|
||||
set(zstd_FORCE_BUNDLED ON)
|
||||
set(MbedTLS_FORCE_BUNDLED ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Detect current compilation architecture and create standard definitions
|
||||
# =======================================================================
|
||||
|
||||
@@ -219,11 +311,9 @@ endif()
|
||||
# On Linux system SDL2 is likely to be lacking HIDAPI support which have drawbacks but is needed for SDL motion
|
||||
cmake_dependent_option(ENABLE_SDL2 "Enable the SDL2 frontend" ON "NOT ANDROID" OFF)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Compile external SDL2" OFF "NOT MSVC" OFF)
|
||||
option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}")
|
||||
endif()
|
||||
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
|
||||
cmake_dependent_option(YUZU_USE_EXTERNAL_SDL2 "Build SDL2 from external source" OFF "ENABLE_SDL2;NOT MSVC" OFF)
|
||||
cmake_dependent_option(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 build" "${MSVC}" "ENABLE_SDL2" OFF)
|
||||
|
||||
# qt stuff
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
@@ -244,10 +334,14 @@ option(YUZU_USE_CPM "Use CPM to fetch system dependencies (fmt, boost, etc) if n
|
||||
|
||||
# ffmpeg
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
|
||||
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
|
||||
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
|
||||
|
||||
# sirit
|
||||
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${EXT_DEFAULT})
|
||||
set(BUNDLED_SIRIT_DEFAULT OFF)
|
||||
if ((MSVC AND NOT (CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo") OR ANDROID))
|
||||
set(BUNDLED_SIRIT_DEFAULT ON)
|
||||
endif()
|
||||
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
|
||||
|
||||
# Re-allow on FreeBSD once its on mainline ports
|
||||
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR APPLE" OFF)
|
||||
@@ -284,6 +378,17 @@ if(USE_CCACHE)
|
||||
else()
|
||||
message(FATAL_ERROR "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
|
||||
endif()
|
||||
# Follow SCCache recommendations:
|
||||
# <https://github.com/mozilla/sccache/blob/main/README.md?plain=1#L144>
|
||||
if(WIN32)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TODO(crueter): CI this?
|
||||
@@ -318,7 +423,7 @@ if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN)
|
||||
set(DEFAULT_ENABLE_OPENSSL OFF)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
if (ENABLE_WEB_SERVICE OR USE_DISCORD_PRESENCE)
|
||||
set(DEFAULT_ENABLE_OPENSSL ON)
|
||||
endif()
|
||||
|
||||
@@ -355,13 +460,6 @@ if (ANDROID)
|
||||
set(CMAKE_POLICY_VERSION_MINIMUM 3.5) # Workaround for Oboe
|
||||
endif()
|
||||
|
||||
# We need to downgrade debug info (/Zi -> /Z7) to use an older but more cacheable format
|
||||
# See https://github.com/nanoant/CMakePCHCompiler/issues/21
|
||||
if(WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
endif()
|
||||
|
||||
# Default to a Release build
|
||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||
@@ -582,6 +680,7 @@ else()
|
||||
find_package(stb MODULE)
|
||||
|
||||
find_package(Opus 1.3 MODULE REQUIRED)
|
||||
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED MODULE)
|
||||
|
||||
@@ -634,15 +733,15 @@ if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
# find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
# list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
elseif (WIN32)
|
||||
# Target Windows 10
|
||||
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
|
||||
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} winmm iphlpapi ws2_32 wlanapi)
|
||||
if (MINGW)
|
||||
# PSAPI is the Process Status API
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
|
||||
endif()
|
||||
elseif (PLATFORM_HAIKU)
|
||||
# Haiku is so special :)
|
||||
@@ -655,6 +754,8 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
endif()
|
||||
|
||||
message(STATUS "Platform Libraries: ${PLATFORM_LIBRARIES}")
|
||||
|
||||
add_subdirectory(externals)
|
||||
|
||||
# pass targets from externals
|
||||
|
||||
@@ -542,10 +542,11 @@ function(AddCIPackage)
|
||||
PACKAGE
|
||||
EXTENSION
|
||||
MIN_VERSION
|
||||
DISABLED_PLATFORMS
|
||||
)
|
||||
|
||||
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "" ${ARGN})
|
||||
set(multiValueArgs DISABLED_PLATFORMS)
|
||||
|
||||
cmake_parse_arguments(PKG_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT DEFINED PKG_ARGS_VERSION)
|
||||
message(FATAL_ERROR "[CPMUtil] VERSION is required")
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,10 @@ find_package_handle_standard_args(Opus
|
||||
VERSION_VAR OPUS_VERSION
|
||||
)
|
||||
|
||||
if (PLATFORM_MSYS)
|
||||
FixMsysPath(PkgConfig::OPUS)
|
||||
endif()
|
||||
|
||||
if (Opus_FOUND AND NOT TARGET Opus::opus)
|
||||
add_library(Opus::opus ALIAS PkgConfig::OPUS)
|
||||
endif()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,10 @@ find_package_handle_standard_args(SPIRV-Tools
|
||||
VERSION_VAR SPIRV-Tools_VERSION
|
||||
)
|
||||
|
||||
if (PLATFORM_MSYS)
|
||||
FixMsysPath(PkgConfig::SPIRV-Tools)
|
||||
endif()
|
||||
|
||||
if (SPIRV-Tools_FOUND AND NOT TARGET SPIRV-Tools::SPIRV-Tools)
|
||||
if (TARGET SPIRV-Tools)
|
||||
add_library(SPIRV-Tools::SPIRV-Tools ALIAS SPIRV-Tools)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@@ -11,6 +14,10 @@ find_package_handle_standard_args(enet
|
||||
VERSION_VAR ENET_VERSION
|
||||
)
|
||||
|
||||
if (PLATFORM_MSYS)
|
||||
FixMsysPath(PkgConfig::ENET)
|
||||
endif()
|
||||
|
||||
if (enet_FOUND AND NOT TARGET enet::enet)
|
||||
add_library(enet::enet ALIAS PkgConfig::ENET)
|
||||
endif()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
@@ -11,6 +14,10 @@ find_package_handle_standard_args(libusb
|
||||
VERSION_VAR LIBUSB_VERSION
|
||||
)
|
||||
|
||||
if (PLATFORM_MSYS)
|
||||
FixMsysPath(PkgConfig::LIBUSB)
|
||||
endif()
|
||||
|
||||
if (libusb_FOUND AND NOT TARGET libusb::usb)
|
||||
add_library(libusb::usb ALIAS PkgConfig::LIBUSB)
|
||||
endif()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -9,6 +12,11 @@ if (lz4_CONSIDERED_CONFIGS)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
|
||||
|
||||
if (PLATFORM_MSYS)
|
||||
FixMsysPath(PkgConfig::LZ4)
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(lz4
|
||||
REQUIRED_VARS LZ4_LINK_LIBRARIES
|
||||
VERSION_VAR LZ4_VERSION
|
||||
|
||||
@@ -6,22 +6,34 @@
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
|
||||
find_package_handle_standard_args(zstd
|
||||
REQUIRED_VARS ZSTD_LINK_LIBRARIES
|
||||
VERSION_VAR ZSTD_VERSION
|
||||
)
|
||||
find_package(zstd QUIET CONFIG)
|
||||
if (zstd_CONSIDERED_CONFIGS)
|
||||
find_package_handle_standard_args(zstd CONFIG_MODE)
|
||||
else()
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd)
|
||||
find_package_handle_standard_args(zstd
|
||||
REQUIRED_VARS ZSTD_LINK_LIBRARIES
|
||||
VERSION_VAR ZSTD_VERSION
|
||||
)
|
||||
endif()
|
||||
|
||||
if (zstd_FOUND AND NOT TARGET zstd::zstd)
|
||||
if (TARGET zstd::libzstd_shared)
|
||||
if (TARGET zstd::libzstd_shared AND NOT YUZU_STATIC_BUILD)
|
||||
add_library(zstd::zstd ALIAS zstd::libzstd_shared)
|
||||
add_library(zstd::libzstd ALIAS zstd::libzstd_shared)
|
||||
elseif (TARGET zstd::libzstd_static)
|
||||
add_library(zstd::zstd ALIAS zstd::libzstd_static)
|
||||
add_library(zstd::libzstd ALIAS zstd::libzstd_static)
|
||||
else()
|
||||
add_library(zstd::zstd ALIAS PkgConfig::ZSTD)
|
||||
add_library(zstd::libzstd ALIAS PkgConfig::ZSTD)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_target_property(ZSTD_TARGET zstd::zstd ALIASED_TARGET)
|
||||
|
||||
if (NOT TARGET zstd::libzstd)
|
||||
if (ZSTD_TARGET)
|
||||
add_library(zstd::libzstd ALIAS ${ZSTD_TARGET})
|
||||
else()
|
||||
add_library(zstd::libzstd ALIAS zstd::zstd)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
function(FixMsysPath target)
|
||||
get_target_property(include_dir ${target} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
||||
if (NOT (include_dir MATCHES "^/"))
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(root_default $ENV{MSYS2_LOCATION})
|
||||
if (root_default STREQUAL "")
|
||||
set(root_default "C:/msys64")
|
||||
endif()
|
||||
|
||||
set(MSYS_ROOT_PATH ${root_default} CACHE STRING "Location of the MSYS2 root")
|
||||
|
||||
set(include_dir "C:/msys64${include_dir}")
|
||||
set_target_properties(${target} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
|
||||
endfunction()
|
||||
@@ -1,58 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}clang)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
@@ -1,57 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2018 tech4me <guiwanglong@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
# Actually a hack, w/o this will cause some strange errors
|
||||
set(CMAKE_HOST_WIN32 TRUE)
|
||||
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
@@ -0,0 +1,31 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
## When linking to a static Qt build on MinGW, certain additional libraries
|
||||
## must be statically linked to as well.
|
||||
|
||||
function(static_qt_link target)
|
||||
macro(extra_libs)
|
||||
foreach(lib ${ARGN})
|
||||
find_library(${lib}_LIBRARY ${lib} REQUIRED)
|
||||
target_link_libraries(${target} PRIVATE ${${lib}_LIBRARY})
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# I am constantly impressed at how ridiculously stupid the linker is
|
||||
# NB: yes, we have to put them here twice. I have no idea why
|
||||
|
||||
# libtiff.a
|
||||
extra_libs(tiff jbig bz2 lzma deflate jpeg tiff)
|
||||
|
||||
# libfreetype.a
|
||||
extra_libs(freetype bz2 Lerc brotlidec brotlicommon freetype)
|
||||
|
||||
# libharfbuzz.a
|
||||
extra_libs(harfbuzz graphite2)
|
||||
|
||||
# sijfjkfnjkdfjsbjsbsdfhvbdf
|
||||
if (ENABLE_OPENSSL)
|
||||
target_link_libraries(${target} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -1,6 +1,5 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
# Contributing
|
||||
|
||||
**The Contributor's Guide has moved to [the yuzu wiki](https://github.com/yuzu-emu/yuzu/wiki/Contributing).**
|
||||
You want to contribute? Please consult [the development guide](./docs/Development.md).
|
||||
|
||||
Don't forget to [get a git account](./docs/SIGNUP.md) - not a requirement per se but it's highly recommended.
|
||||
|
||||
@@ -21,7 +21,7 @@ It is written in C++ with portability in mind, and we actively maintain builds f
|
||||
|
||||
<p align="center">
|
||||
</a>
|
||||
<a href="https://discord.gg/kXAmGCXBGD">
|
||||
<a href="https://discord.gg/HstXbPch7X">
|
||||
<img src="https://img.shields.io/discord/1367654015269339267?color=5865F2&label=Eden&logo=discord&logoColor=white"
|
||||
alt="Discord">
|
||||
</a>
|
||||
@@ -52,8 +52,8 @@ Check out our [website](https://eden-emu.dev) for the latest news on exciting fe
|
||||
|
||||
## Development
|
||||
|
||||
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/kXAmGCXBGD) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
||||
You can also follow us on [X (Twitter)](https://x.com/edenemuofficial) for updates and announcements.
|
||||
Most of the development happens on our Git server. It is also where [our central repository](https://git.eden-emu.dev/eden-emu/eden) is hosted. For development discussions, please join us on [Discord](https://discord.gg/HstXbPch7X) or [Revolt](https://rvlt.gg/qKgFEAbH).
|
||||
You can also follow us on [X (Twitter)](https://nitter.poast.org/edenemuofficial) for updates and announcements.
|
||||
|
||||
If you would like to contribute, we are open to new developers and pull requests. Please ensure that your work is of a high standard and properly documented. You can also contact any of the developers on Discord or Revolt to learn more about the current state of the emulator.
|
||||
|
||||
@@ -82,7 +82,7 @@ Any donations received will go towards things such as:
|
||||
* Additional hardware (e.g. GPUs as needed to improve rendering support, other peripherals to add support for, etc.)
|
||||
* CI Infrastructure
|
||||
|
||||
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/edenemu) and talk to Camille or any of our other developers.
|
||||
If you would prefer to support us in a different way, please join our [Discord](https://discord.gg/HstXbPch7X) and talk to Camille or any of our other developers.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -4,13 +4,8 @@
|
||||
"package": "OpenSSL",
|
||||
"name": "openssl",
|
||||
"repo": "crueter-ci/OpenSSL",
|
||||
"version": "3.6.0",
|
||||
"min_version": "1.1.1",
|
||||
"disabled_platforms": [
|
||||
"macos-universal",
|
||||
"mingw-amd64",
|
||||
"mingw-arm64"
|
||||
]
|
||||
"version": "3.6.0-e3608d80df",
|
||||
"min_version": "1.1.1"
|
||||
},
|
||||
"boost": {
|
||||
"package": "Boost",
|
||||
|
||||
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 516 KiB |
@@ -0,0 +1,9 @@
|
||||
# Icon variations
|
||||
|
||||
These icons are licensed under GPLv3. Please see the [script for generating icons](../../tools/README.md) and appropriatedly redirect for seasonal icons.
|
||||
|
||||
- `base_named.svg` - Named variant.
|
||||
- `base_small.svg`: Variant used for tiny icons (16x16, 64x64, etc).
|
||||
- `base.svg`: Variant without branding/naming.
|
||||
|
||||
Try to keep the icons simple. And preferably automatically be able to be generated (to reduce maintanaince burden). Additionally keep the files small (use `svgo` and `optipng`) to not clutter the git.
|
||||
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
fill="none"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="base_small.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs7">
|
||||
<linearGradient
|
||||
id="linearGradient10"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
style="stop-color:#f977d9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10" />
|
||||
<stop
|
||||
style="stop-color:#a655d5;stop-opacity:1;"
|
||||
offset="0.54051435"
|
||||
id="stop13" />
|
||||
<stop
|
||||
style="stop-color:#984fd5;stop-opacity:1;"
|
||||
offset="0.5714342"
|
||||
id="stop12" />
|
||||
<stop
|
||||
style="stop-color:#8c4ad4;stop-opacity:1;"
|
||||
offset="0.59666741"
|
||||
id="stop14" />
|
||||
<stop
|
||||
style="stop-color:#3928d2;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop11" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10"
|
||||
id="linearGradient11"
|
||||
x1="264.17508"
|
||||
y1="28.385544"
|
||||
x2="264.17508"
|
||||
y2="488.65109"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.97815818,0,0,0.97880258,4.570042,5.8799159)" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.88388348"
|
||||
inkscape:cx="153.30075"
|
||||
inkscape:cy="243.24473"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="849"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7"
|
||||
showguides="false" />
|
||||
<path
|
||||
id="path9"
|
||||
style="fill:url(#linearGradient11);fill-opacity:1;stroke:none;stroke-width:13.3314;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="M 262.97461 33.6875 A 218.44267 225.23091 0 0 0 138.83789 73.589844 L 141.13867 72.265625 L 161.2207 65.195312 L 181.01953 61.517578 L 204.35352 64.205078 L 228.96094 76.650391 L 265.80078 115 L 283 99.400391 L 304.59961 85.800781 L 331 76.400391 L 360.59961 69.599609 L 379.95508 69.208984 A 218.44267 225.23091 0 0 0 262.97461 33.6875 z M 380.07617 69.291016 L 350.19922 77.800781 L 329.19922 89.199219 L 307.40039 108 L 288.80078 129.80078 L 287.40039 135 L 302.40039 129.59961 L 319 127.80078 L 348.80078 131.80078 L 370.19922 141.40039 L 393 161.40039 L 399.59961 171.59961 L 374.80078 160.80078 L 338.40039 150.80078 L 309.19922 150 L 288.80078 154.40039 L 293.19922 155.19922 L 319.19922 168.80078 L 338 187.19922 L 350.80078 224 L 349.19922 260 L 326 222.80078 L 302.80078 194 L 277.59961 172.40039 L 269.19922 187.59961 L 256.80078 281.59961 L 258 364 L 278.40039 452.80078 L 297.19531 481.36914 A 218.44267 225.23091 0 0 0 481.41797 258.91797 A 218.44267 225.23091 0 0 0 380.07617 69.291016 z M 133.07422 77.839844 A 218.44267 225.23091 0 0 0 44.533203 258.91797 A 218.44267 225.23091 0 0 0 257.04102 484.06641 L 247.3457 458.62891 L 237.87109 418.18359 L 233.0625 380.42383 L 230.375 354.9668 L 229.95117 321.30859 L 232.35547 291.32812 L 237.44727 254.98242 L 254.55859 191.9082 L 261.62891 172.5332 L 255.54883 174.08984 L 240.98242 180.87695 L 229.59961 190.19922 L 210.59961 208.19922 L 197.40039 229.40039 L 186.40039 252.59961 L 173.40039 269 L 171.40039 253.19922 L 173.59961 229.40039 L 183 202.40039 L 199.40039 178 L 221.04102 153.44141 L 209.02148 155.70508 L 177.05859 162.77539 L 148.06836 171.40234 L 119.92578 183 L 120.63281 180.16992 L 129.82422 165.88672 L 151.17969 147.50195 L 172.95898 135.48242 L 190.07031 129.54102 L 209.02148 127.98633 L 227.6875 128.69336 L 247.76953 128.41016 L 246.49805 125.1582 L 226.69922 105.92383 L 219.62695 97.439453 L 221.4668 90.085938 L 206.75781 94.044922 L 195.86914 95.458984 L 179.46289 88.671875 L 156.12891 81.458984 L 133.07422 77.839844 z " />
|
||||
<path
|
||||
style="fill:#1b1b1b;fill-opacity:0.12492698;stroke:none;stroke-width:13.374;stroke-opacity:0.415999;paint-order:stroke fill markers"
|
||||
d="m 259.36665,490.16617 39.03323,-6.96642 -20,-30.4 -20.4,-88.8 -1.2,-82.4 12.4,-94 8.4,-15.2 25.2,21.6 23.2,28.8 23.2,37.2 1.6,-36 -12.8,-36.8 -18.8,-18.4 -26,-13.6 -4.4,-0.8 20.4,-4.4 29.2,0.8 36.4,10 24.8,10.8 -6.6,-10.2 -22.8,-20 -21.4,-9.6 -29.8,-4 -16.6,1.8 -15,5.4 1.4,-5.2 18.6,-21.8 21.8,-18.800003 21,-11.4 30.2,-8.6 -19.8,0.4 -29.6,6.8 -26.4,9.4 -21.6,13.6 -17.2,15.600003 -36.83882,-38.349628 -24.60732,-12.445079 -23.33452,-2.687006 -19.79899,3.676955 -20.08184,7.071068 -9.33381,5.374012 24.32448,3.818376 23.33452,7.212489 16.40488,6.788226 10.88944,-1.414214 14.70782,-3.959798 -1.83847,7.353911 7.07106,8.485288 19.79899,19.2333 1.2728,3.25269 -20.08184,0.28284 -18.66762,-0.7071 -18.95046,1.55563 -17.11198,5.9397 -21.77889,12.02081 -21.35462,18.38478 -9.19239,14.28356 -0.70711,2.82843 28.14285,-11.59656 28.99138,-8.6267 31.96122,-7.07107 12.02082,-2.26274 -21.64158,24.55783 -16.4,24.4 -9.4,27 -2.2,23.8 2,15.8 13,-16.4 11,-23.2 13.2,-21.2 19,-18 11.38199,-9.32209 14.5664,-6.78822 6.08112,-1.55564 -7.07107,19.37473 -17.11198,63.07393 -5.09117,36.34528 -2.40416,29.98133 0.42426,33.65828 2.68701,25.45585 4.80832,37.7595 9.47523,40.44651 z"
|
||||
id="path8-4" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 256 KiB |
@@ -26,7 +26,7 @@ Android has a completely different build process than other platforms. See its [
|
||||
|
||||
If the configure phase fails, see the `Troubleshooting` section below. Usually, as long as you followed the dependencies guide, the defaults *should* successfully configure and build.
|
||||
|
||||
### Qt Creator
|
||||
### Option A: Qt Creator
|
||||
|
||||
This is the recommended GUI method for Linux, macOS, and Windows.
|
||||
|
||||
@@ -46,39 +46,51 @@ Hit "Configure Project", then wait for CMake to finish configuring (may take a w
|
||||
|
||||
</details>
|
||||
|
||||
### Command Line
|
||||
|
||||
This is recommended for *BSD, Solaris, Linux, and MSYS2. MSVC is possible, but not recommended.
|
||||
### Option B: Command Line
|
||||
|
||||
<details>
|
||||
<summary>Click to Open</summary>
|
||||
|
||||
Note that CMake must be in your PATH, and you must be in the cloned Eden directory. On Windows, you must also set up a Visual C++ development environment. This can be done by running `C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat` in the same terminal.
|
||||
|
||||
Recommended generators:
|
||||
> [!WARNING]
|
||||
>For all systems:
|
||||
>- *CMake* **MUST** be in your PATH (and also *ninja*, if you are using it as `<GENERATOR>`)
|
||||
>- You *MUST* be in the cloned *Eden* directory
|
||||
>
|
||||
>On Windows:
|
||||
> - It's recommended to install **[Ninja](https://ninja-build.org/)**
|
||||
> - You must load **Visual C++ development environment**, this can be done by running our convenience script:
|
||||
> - `tools/windows/load-msvc-env.ps1` (for PowerShell 5+)
|
||||
> - `tools/windows/load-msvc-env.sh` (for MSYS2, Git Bash, etc)
|
||||
|
||||
Available `<GENERATOR>`:
|
||||
- MSYS2: `MSYS Makefiles`
|
||||
- MSVC: Install **[ninja](https://ninja-build.org/)** and use `Ninja`, OR use `Visual Studio 17 2022`
|
||||
- MSVC: `Ninja` (preferred) or `Visual Studio 17 2022`
|
||||
- macOS: `Ninja` (preferred) or `Xcode`
|
||||
- Others: `Ninja` (preferred) or `UNIX Makefiles`
|
||||
|
||||
BUILD_TYPE should usually be `Release` or `RelWithDebInfo` (debug symbols--compiled executable will be large). If you are using a debugger and annoyed with stuff getting optimized out, try `Debug`.
|
||||
Available `<BUILD_TYPE>`:
|
||||
- `Release` (default)
|
||||
- `RelWithDebInfo` (debug symbols--compiled executable will be large)
|
||||
- `Debug` (if you are using a debugger and annoyed with stuff getting optimized out)
|
||||
|
||||
Caveat for Debug Builds:
|
||||
- If you're building with CCache, you will need to add the environment variable `CL` with the `/FS` flag ([Reference](https://learn.microsoft.com/pt-br/cpp/build/reference/fs-force-synchronous-pdb-writes?view=msvc-170))
|
||||
|
||||
Also see the [Options](Options.md) page for additional CMake options.
|
||||
|
||||
```sh
|
||||
cmake -S . -B build -G "GENERATOR" -DCMAKE_BUILD_TYPE=<BUILD_TYPE> -DYUZU_TESTS=OFF
|
||||
cmake -S . -B build -G "<GENERATOR>" -DCMAKE_BUILD_TYPE=<BUILD_TYPE> -DYUZU_TESTS=OFF
|
||||
```
|
||||
|
||||
If you are on Windows and prefer to use Clang:
|
||||
|
||||
```sh
|
||||
cmake -S . -B build -G "GENERATOR" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
cmake -S . -B build -G "<GENERATOR>" -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### [CLion](https://www.jetbrains.com/clion/)
|
||||
### Option C: [CLion](https://www.jetbrains.com/clion/)
|
||||
|
||||
<details>
|
||||
<summary>Click to Open</summary>
|
||||
@@ -133,13 +145,13 @@ Many platforms have quirks, bugs, and other fun stuff that may cause issues when
|
||||
|
||||
## Building & Running
|
||||
|
||||
### Qt Creator
|
||||
### On Qt Creator
|
||||
|
||||
Simply hit Ctrl+B, or the "hammer" icon in the bottom left. To run, hit the "play" icon, or Ctrl+R.
|
||||
|
||||
### Command Line
|
||||
### On Command Line
|
||||
|
||||
If you are not on Windows and are using the `UNIX Makefiles` generator, you must also add `-j$(nproc)` to this command.
|
||||
If you are using the `UNIX Makefiles` or `Visual Studio 17 2022` as `<GENERATOR>`, you should also add `--parallel` for faster build times.
|
||||
|
||||
```
|
||||
cmake --build build
|
||||
@@ -157,4 +169,4 @@ Some platforms have convenience scripts provided for building.
|
||||
- **[Linux](scripts/Linux.md)**
|
||||
- **[Windows](scripts/Windows.md)**
|
||||
|
||||
macOS scripts will come soon.
|
||||
macOS scripts will come soon.
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
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+
|
||||
* On Windows, this is either:
|
||||
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option),
|
||||
- clang-cl - can be downloaded from the MSVC installer,
|
||||
- or **[MSYS2](https://www.msys2.org)**
|
||||
* 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)** (experimental)
|
||||
* On macOS, this is Apple Clang
|
||||
- This can be installed with `xcode-select --install`
|
||||
|
||||
@@ -15,20 +18,23 @@ 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)**
|
||||
* **[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 `.ci/windows/install-vulkan-sdk.ps1`*
|
||||
- *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)
|
||||
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.
|
||||
* MSVC/clang-cl users on Windows must install through the [official installer](https://www.qt.io/download-qt-installer-oss)
|
||||
* 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
|
||||
|
||||
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 NOT building with MSYS2, you may go [back home](Build.md) and continue.
|
||||
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:
|
||||
|
||||
@@ -43,6 +43,12 @@ Various graphical filters exist - each of them aimed at a specific target/image
|
||||
- **Pros**: Offers decent pixel-art upscaling.
|
||||
- **Cons**: Only works for pixel-art.
|
||||
|
||||
### Anisotropy values
|
||||
|
||||
The anisotropy value is (value game wants + the set value); **Default** will use the native anisotropy value as it would be on hardware. **Automatic** sets it according to screen resolution. Turning off anisotropy is not recommended as it can break a myriad of games, however it is provided in the name of flexibility.
|
||||
|
||||
Values from x2, x4, x8, x16, x32 up to x64 values are provided. This should be enough to not need to revise those values in my lifetime ever again.
|
||||
|
||||
### External
|
||||
|
||||
While stock shaders offer a basic subset of options for most users, programs such as [ReShade](https://github.com/crosire/reshade) offer a more flexible experience. In addition to that users can also seek out modifications (mods) for enhancing visual experience (60 FPS mods, HDR, etc).
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# User Handbook - Native Application Development
|
||||
|
||||
Debugging on physical hardware can get tedious and time consuming. Users are empowered with the debugging capabilities of the emulator to ensure their applications run as-is on the system. To the greatest extent possible atleast.
|
||||
|
||||
## Debugging
|
||||
|
||||
**Standard key prefix**: Allows to redirect the key manager to a file other than `prod.keys` (for example `other` would redirect to `other.keys`). This is useful for testing multiple keysets. Default is `prod`.
|
||||
|
||||
**Changing serial**: Very basic way to set debug values for the serial (and battery number). Developers do not need to write the full serial as it will be writen in-place (that is, it will be filled with the default serial and then overwrite the serial from the beginning).
|
||||
- Battery serial: `YUZU0EMULATOR14022024`
|
||||
- Board serial: `YUZ10000000001`
|
||||
If the user were to set their board serial as `ABC`, then it will be written in-place and the resulting serial would be `ABC10000000001`. There are no underlying checks to ensure correctness of serials other than a hard limit of 16-characters for both.
|
||||
@@ -11,3 +11,4 @@ This handbook is primarily aimed at the end-user - baking useful knowledge for e
|
||||
- **[Testing](Testing.md)**
|
||||
- **[Data, savefiles and storage](Storage.md)**
|
||||
- **[Orphaned Profiles](Orphaned.md)**
|
||||
- **[Native Application Development](Native.md)**
|
||||
|
||||
@@ -27,7 +27,7 @@ set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL ON)
|
||||
|
||||
# Xbyak (also used by Dynarmic, so needs to be added first)
|
||||
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
|
||||
if (PLATFORM_SUN OR PLATFORM_OPENBSD)
|
||||
if (PLATFORM_SUN OR PLATFORM_OPENBSD OR PLATFORM_NETBSD OR PLATFORM_DRAGONFLY)
|
||||
AddJsonPackage(xbyak_sun)
|
||||
else()
|
||||
AddJsonPackage(xbyak)
|
||||
@@ -160,13 +160,6 @@ if (YUZU_USE_BUNDLED_SIRIT)
|
||||
AddJsonPackage(sirit-ci)
|
||||
else()
|
||||
AddJsonPackage(sirit)
|
||||
# Change to old-but-more-cacheable debug info on Windows
|
||||
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
get_target_property(sirit_opts sirit COMPILE_OPTIONS)
|
||||
list(FILTER sirit_opts EXCLUDE REGEX "/Zi")
|
||||
list(APPEND sirit_opts "/Z7")
|
||||
set_target_properties(sirit PROPERTIES COMPILE_OPTIONS "${sirit_opts}")
|
||||
endif()
|
||||
if(MSVC AND CXX_CLANG)
|
||||
target_compile_options(siritobj PRIVATE -Wno-error=unused-command-line-argument)
|
||||
endif()
|
||||
|
||||
@@ -167,11 +167,8 @@
|
||||
"package": "SDL2",
|
||||
"name": "SDL2",
|
||||
"repo": "crueter-ci/SDL2",
|
||||
"version": "2.32.10",
|
||||
"min_version": "2.26.4",
|
||||
"disabled_platforms": [
|
||||
"macos-universal"
|
||||
]
|
||||
"version": "2.32.10-38e0094637",
|
||||
"min_version": "2.26.4"
|
||||
},
|
||||
"catch2": {
|
||||
"package": "Catch2",
|
||||
|
||||
@@ -25,18 +25,26 @@ if (UNIX AND NOT ANDROID)
|
||||
if (NOT APPLE)
|
||||
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
|
||||
if(PLATFORM_SUN)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
X11
|
||||
"/usr/lib/xorg/amd64/libdrm.so")
|
||||
find_library(LIBDRM_LIB libdrm PATHS /usr/lib/64 /usr/lib/amd64 /usr/lib)
|
||||
if(LIBDRM_LIB)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
X11
|
||||
"${LIBDRM_LIB}")
|
||||
message(STATUS "Found libdrm at: ${LIBDRM_LIB}")
|
||||
else()
|
||||
message(WARNING "libdrm not found, disabling libdrm support")
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--disable-libdrm)
|
||||
endif()
|
||||
else()
|
||||
pkg_check_modules(LIBDRM libdrm REQUIRED)
|
||||
list(APPEND FFmpeg_HWACCEL_LIBRARIES
|
||||
${LIBDRM_LIBRARIES})
|
||||
list(APPEND FFmpeg_HWACCEL_INCLUDE_DIRS
|
||||
${LIBDRM_INCLUDE_DIRS})
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-libdrm)
|
||||
endif()
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-libdrm)
|
||||
endif()
|
||||
|
||||
if(LIBVA_FOUND)
|
||||
@@ -89,7 +97,7 @@ if (UNIX AND NOT ANDROID)
|
||||
endif(CUDA_FOUND)
|
||||
endif()
|
||||
|
||||
if (VDPAU_FOUND)
|
||||
if (VDPAU_FOUND AND NOT APPLE)
|
||||
list(APPEND FFmpeg_HWACCEL_FLAGS
|
||||
--enable-vdpau
|
||||
--enable-hwaccel=h264_vdpau
|
||||
@@ -247,11 +255,19 @@ else()
|
||||
SYSTEM_THREADS)
|
||||
|
||||
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
|
||||
|
||||
# BSD make or Solaris make don't support ffmpeg make-j8
|
||||
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
|
||||
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
|
||||
else()
|
||||
set(FFmpeg_MAKE_ARGS "")
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${FFmpeg_BUILD_LIBRARIES}
|
||||
COMMAND
|
||||
make -j${SYSTEM_THREADS}
|
||||
make ${FFmpeg_MAKE_ARGS}
|
||||
WORKING_DIRECTORY
|
||||
${FFmpeg_BUILD_DIR}
|
||||
)
|
||||
|
||||
@@ -10,13 +10,7 @@
|
||||
"package": "FFmpeg",
|
||||
"name": "ffmpeg",
|
||||
"repo": "crueter-ci/FFmpeg",
|
||||
"version": "8.0",
|
||||
"min_version": "4.1",
|
||||
"disabled_platforms": [
|
||||
"freebsd-amd64",
|
||||
"solaris-amd64",
|
||||
"openbsd-amd64",
|
||||
"macos-universal"
|
||||
]
|
||||
"version": "8.0-be99d2c0b2",
|
||||
"min_version": "4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"git_host": "git.crueter.xyz",
|
||||
"artifact": "%VERSION%.tar.gz",
|
||||
"tag": "%VERSION%",
|
||||
"hash": "87abb2aeca716d5d77b05317086dbc2f8acfc2f3f76ce4778345ee3df19973e6cd8ecbf16cfab5ad94c9636a6c44fd3588f9aadd3cba89403cfd56c8bec645c5",
|
||||
"version": "091025"
|
||||
"hash": "dc37a189a44ce8b5c988ca550582431a6c7eadfd3c6e709bee6277116ee803e714333e85c9e6cbb5c69346a14d6f2cc7ed96e8aa09cc5fb8a89f945059651db6",
|
||||
"version": "121125"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ if (MSVC AND NOT CXX_CLANG)
|
||||
|
||||
# /W4 - Level 4 warnings
|
||||
# /MP - Multi-threaded compilation
|
||||
# /Zi - Output debugging information
|
||||
# /Zm - Specifies the precompiled header memory allocation limit
|
||||
# /Zo - Enhanced debug info for optimized builds
|
||||
# /permissive- - Enables stricter C++ standards conformance checks
|
||||
@@ -98,11 +97,6 @@ if (MSVC AND NOT CXX_CLANG)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options(/QIntel-jcc-erratum)
|
||||
endif()
|
||||
@@ -176,16 +170,28 @@ else()
|
||||
add_compile_definitions(_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
if (YUZU_STATIC_BUILD)
|
||||
add_compile_definitions(QT_STATICPLUGIN)
|
||||
|
||||
# macos doesn't even let you make static executables... wtf?
|
||||
if (NOT APPLE)
|
||||
add_compile_options(-static)
|
||||
if (YUZU_STATIC_BUILD)
|
||||
# yuzu-cmd requires us to explicitly link libpthread, libgcc, and libstdc++ as static
|
||||
# At a guess, it's probably because Qt handles the Qt executable for us, whereas this does not
|
||||
add_link_options(-static -lpthread)
|
||||
add_link_options(-static-libgcc -static-libstdc++)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MINGW)
|
||||
add_compile_definitions(MINGW_HAS_SECURE_API)
|
||||
|
||||
# Only windows has this requirement, thanks windows
|
||||
if (WIN32)
|
||||
if (WIN32 AND ARCHITECTURE_x86_64)
|
||||
add_compile_options("-msse4.1")
|
||||
endif()
|
||||
if (MINGW_STATIC_BUILD)
|
||||
add_compile_definitions(QT_STATICPLUGIN)
|
||||
add_compile_options("-static")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
|
||||
@@ -61,7 +61,6 @@ android {
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
versionName = getGitVersion()
|
||||
|
||||
versionCode = autoVersion
|
||||
|
||||
ndk {
|
||||
@@ -69,9 +68,6 @@ android {
|
||||
abiFilters += listOf("arm64-v8a")
|
||||
}
|
||||
|
||||
buildConfigField("String", "GIT_HASH", "\"${getGitHash()}\"")
|
||||
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
val extraCMakeArgs =
|
||||
@@ -92,14 +88,14 @@ android {
|
||||
"-DYUZU_TESTS=OFF",
|
||||
"-DDYNARMIC_TESTS=OFF",
|
||||
*extraCMakeArgs.toTypedArray()
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
abiFilters("arm64-v8a")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
||||
signingConfigs {
|
||||
if (keystoreFile != null) {
|
||||
@@ -162,7 +158,39 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// this is really annoying but idk any other ways to fix this behavior
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("mainline") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden")
|
||||
}
|
||||
|
||||
create("genshinSpoof") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden Optimized")
|
||||
applicationId = "com.miHoYo.Yuanshen"
|
||||
}
|
||||
|
||||
create("legacy") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden Legacy")
|
||||
applicationId = "dev.legacy.eden_emulator"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DYUZU_LEGACY=ON")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("legacy") {
|
||||
res.srcDirs("src/main/legacy")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is really annoying but idk any other ways to fix this behavior
|
||||
applicationVariants.all {
|
||||
val variant = this
|
||||
when {
|
||||
@@ -187,40 +215,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
flavorDimensions.add("version")
|
||||
productFlavors {
|
||||
create("mainline") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden")
|
||||
}
|
||||
|
||||
create("genshinSpoof") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden Optimized")
|
||||
applicationId = "com.miHoYo.Yuanshen"
|
||||
}
|
||||
|
||||
create("legacy") {
|
||||
dimension = "version"
|
||||
resValue("string", "app_name_suffixed", "Eden Legacy")
|
||||
applicationId = "dev.legacy.eden_emulator"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DYUZU_LEGACY=ON")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("legacy") {
|
||||
res.srcDirs("src/main/legacy")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version = "3.22.1"
|
||||
@@ -284,7 +278,6 @@ dependencies {
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
|
||||
implementation("androidx.window:window:1.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("org.commonmark:commonmark:0.22.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.8.9")
|
||||
@@ -302,7 +295,9 @@ fun runGitCommand(command: List<String>): String {
|
||||
.directory(project.rootDir)
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
.start().inputStream.bufferedReader().use { it.readText() }
|
||||
.start()
|
||||
.inputStream.bufferedReader()
|
||||
.use { it.readText() }
|
||||
.trim()
|
||||
} catch (e: Exception) {
|
||||
logger.error("Cannot find git")
|
||||
@@ -326,9 +321,3 @@ fun getGitVersion(): String {
|
||||
}
|
||||
return versionName.ifEmpty { "0.0" }
|
||||
}
|
||||
|
||||
fun getGitHash(): String =
|
||||
runGitCommand(listOf("git", "rev-parse", "--short", "HEAD")).ifEmpty { "dummy-hash" }
|
||||
|
||||
fun getBranch(): String =
|
||||
runGitCommand(listOf("git", "rev-parse", "--abbrev-ref", "HEAD")).ifEmpty { "dummy-hash" }
|
||||
|
||||
@@ -29,8 +29,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:name="org.yuzu.yuzu_emu.YuzuApplication"
|
||||
@@ -110,5 +109,15 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</intent-filter>
|
||||
</provider>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -200,6 +200,18 @@ object NativeLibrary {
|
||||
|
||||
external fun logSettings()
|
||||
|
||||
/**
|
||||
* Returns Vulkan driver version / API version / GPU model
|
||||
*/
|
||||
external fun getVulkanDriverVersion(): String
|
||||
external fun getVulkanApiVersion(): String
|
||||
external fun getGpuModel(): String
|
||||
|
||||
/**
|
||||
* Returns a summary of detailed information about the CPU.
|
||||
*/
|
||||
external fun getCpuSummary(): String
|
||||
|
||||
/**
|
||||
* Checks for available updates.
|
||||
*/
|
||||
@@ -210,11 +222,21 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun getUpdateUrl(version: String): String
|
||||
|
||||
/**
|
||||
* Return the URL to download the APK for the given version
|
||||
*/
|
||||
external fun getUpdateApkUrl(version: String, packageId: String): String
|
||||
|
||||
/**
|
||||
* Returns whether the update checker is enabled through CMAKE options.
|
||||
*/
|
||||
external fun isUpdateCheckerEnabled(): Boolean
|
||||
|
||||
/**
|
||||
* Returns the build version generated by CMake (BUILD_VERSION).
|
||||
*/
|
||||
external fun getBuildVersion(): String
|
||||
|
||||
enum class CoreError {
|
||||
ErrorSystemFiles,
|
||||
ErrorSavestate,
|
||||
|
||||
@@ -16,11 +16,15 @@ import java.io.FileOutputStream
|
||||
import java.security.KeyStore
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import android.content.res.Configuration
|
||||
import android.os.LocaleList
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.DocumentsTree
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.PowerStateUpdater
|
||||
import java.util.Locale
|
||||
|
||||
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||
|
||||
@@ -73,5 +77,38 @@ class YuzuApplication : Application() {
|
||||
|
||||
val appContext: Context
|
||||
get() = application.applicationContext
|
||||
|
||||
private val LANGUAGE_CODES = arrayOf(
|
||||
"system", "en", "es", "fr", "de", "it", "pt", "pt-BR", "ru", "ja", "ko",
|
||||
"zh-CN", "zh-TW", "pl", "cs", "nb", "hu", "uk", "vi", "id", "ar", "ckb", "fa", "he", "sr"
|
||||
)
|
||||
|
||||
fun applyLanguage(context: Context): Context {
|
||||
val languageIndex = IntSetting.APP_LANGUAGE.getInt()
|
||||
val langCode = if (languageIndex in LANGUAGE_CODES.indices) {
|
||||
LANGUAGE_CODES[languageIndex]
|
||||
} else {
|
||||
"system"
|
||||
}
|
||||
|
||||
if (langCode == "system") {
|
||||
return context
|
||||
}
|
||||
|
||||
val locale = when {
|
||||
langCode.contains("-") -> {
|
||||
val parts = langCode.split("-")
|
||||
Locale.Builder().setLanguage(parts[0]).setRegion(parts[1]).build()
|
||||
}
|
||||
else -> Locale.Builder().setLanguage(langCode).build()
|
||||
}
|
||||
|
||||
Locale.setDefault(locale)
|
||||
|
||||
val config = Configuration(context.resources.configuration)
|
||||
config.setLocales(LocaleList(locale))
|
||||
|
||||
return context.createConfigurationContext(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
|
||||
private var foregroundService: Intent? = null
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(YuzuApplication.applyLanguage(base))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.gameLaunched = true
|
||||
ThemeHelper.setTheme(this)
|
||||
@@ -231,10 +235,13 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
val isPhysicalKeyboard = event.source and InputDevice.SOURCE_KEYBOARD == InputDevice.SOURCE_KEYBOARD &&
|
||||
event.device?.isVirtual == false
|
||||
|
||||
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
|
||||
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD &&
|
||||
event.source and InputDevice.SOURCE_KEYBOARD != InputDevice.SOURCE_KEYBOARD &&
|
||||
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE
|
||||
event.source and InputDevice.SOURCE_MOUSE != InputDevice.SOURCE_MOUSE &&
|
||||
!isPhysicalKeyboard
|
||||
) {
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
|
||||
RENDERER_PROVOKING_VERTEX("provoking_vertex"),
|
||||
RENDERER_DESCRIPTOR_INDEXING("descriptor_indexing"),
|
||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||
@@ -69,7 +70,10 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
SHOW_SHADERS_BUILDING("show_shaders_building"),
|
||||
|
||||
DEBUG_FLUSH_BY_LINE("flush_line"),
|
||||
USE_LRU_CACHE("use_lru_cache");
|
||||
USE_LRU_CACHE("use_lru_cache"),
|
||||
|
||||
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
|
||||
ENABLE_OVERLAY("enable_overlay");
|
||||
|
||||
|
||||
// external fun isFrameSkippingEnabled(): Boolean
|
||||
|
||||
@@ -32,6 +32,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
MAX_ANISOTROPY("max_anisotropy"),
|
||||
THEME("theme"),
|
||||
THEME_MODE("theme_mode"),
|
||||
APP_LANGUAGE("app_language"),
|
||||
OVERLAY_SCALE("control_scale"),
|
||||
OVERLAY_OPACITY("control_opacity"),
|
||||
LOCK_DRAWER("lock_drawer"),
|
||||
@@ -60,7 +61,8 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
|
||||
LOGIN_SHARE_APPLET("login_share_applet_mode"),
|
||||
WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"),
|
||||
MY_PAGE_APPLET("my_page_applet_mode"),
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide")
|
||||
INPUT_OVERLAY_AUTO_HIDE("input_overlay_auto_hide"),
|
||||
DEBUG_KNOBS("debug_knobs")
|
||||
;
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
|
||||
|
||||
@@ -146,6 +146,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.provoking_vertex_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
|
||||
titleId = R.string.vertex_input_dynamic_state,
|
||||
descriptionId = R.string.vertex_input_dynamic_state_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_DESCRIPTOR_INDEXING,
|
||||
@@ -667,10 +674,11 @@ abstract class SettingsItem(
|
||||
)
|
||||
)
|
||||
put(
|
||||
SliderSetting(
|
||||
SpinBoxSetting(
|
||||
IntSetting.CPU_TICKS,
|
||||
titleId = R.string.cpu_ticks,
|
||||
descriptionId = 0,
|
||||
valueHint = R.string.cpu_ticks,
|
||||
min = 77,
|
||||
max = 65535
|
||||
)
|
||||
@@ -754,6 +762,16 @@ abstract class SettingsItem(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_UPDATE_CHECKS,
|
||||
titleId = R.string.enable_update_checks,
|
||||
descriptionId = R.string.enable_update_checks_description,
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.APP_LANGUAGE,
|
||||
titleId = R.string.app_language,
|
||||
descriptionId = R.string.app_language_description,
|
||||
choicesId = R.array.appLanguageNames,
|
||||
valuesId = R.array.appLanguageValues
|
||||
)
|
||||
)
|
||||
put(
|
||||
@@ -770,6 +788,16 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.use_auto_stub_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SpinBoxSetting(
|
||||
IntSetting.DEBUG_KNOBS,
|
||||
titleId = R.string.debug_knobs,
|
||||
descriptionId = R.string.debug_knobs_description,
|
||||
valueHint = R.string.debug_knobs_hint,
|
||||
min = 0,
|
||||
max = 65535
|
||||
)
|
||||
)
|
||||
|
||||
val fastmem = object : AbstractBooleanSetting {
|
||||
override fun getBoolean(needsGlobal: Boolean): Boolean =
|
||||
@@ -821,7 +849,14 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.airplane_mode_description
|
||||
)
|
||||
)
|
||||
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_OVERLAY,
|
||||
titleId = R.string.enable_overlay,
|
||||
descriptionId = R.string.enable_overlay_description
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
// 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
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
@@ -24,6 +29,7 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||
import org.yuzu.yuzu_emu.fragments.ResetSettingsDialogFragment
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
import org.yuzu.yuzu_emu.utils.collect
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
@@ -32,6 +38,10 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(YuzuApplication.applyLanguage(base))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
ThemeHelper.setTheme(this)
|
||||
|
||||
@@ -125,6 +135,16 @@ class SettingsActivity : AppCompatActivity() {
|
||||
NativeConfig.savePerGameConfig()
|
||||
NativeConfig.unloadPerGameConfig()
|
||||
}
|
||||
|
||||
if (settingsViewModel.shouldRecreateForLanguageChange.value) {
|
||||
settingsViewModel.setShouldRecreateForLanguageChange(false)
|
||||
val relaunchIntent = packageManager?.getLaunchIntentForPackage(packageName)
|
||||
if (relaunchIntent != null) {
|
||||
relaunchIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK or android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(relaunchIntent)
|
||||
android.os.Process.killProcess(android.os.Process.myPid())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -425,6 +425,14 @@ class SettingsAdapter(
|
||||
position
|
||||
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
|
||||
|
||||
// reset language if detected
|
||||
if (item.setting.key == "app_language") {
|
||||
// recreate page apply language change instantly
|
||||
fragment.requireActivity().recreate()
|
||||
|
||||
settingsViewModel.setShouldRecreateForLanguageChange(true)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.MotionEvent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
@@ -183,6 +186,12 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
updateButtonState(isValid)
|
||||
}
|
||||
|
||||
/*
|
||||
* xbzk: these two events, along with attachRepeat feature,
|
||||
* were causing spinbox buttons to respond twice per press
|
||||
* cutting these out to retain accelerated press functionality
|
||||
* TODO: clean this out later if no issues arise
|
||||
*
|
||||
spinboxBinding.buttonDecrement.setOnClickListener {
|
||||
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
|
||||
val newValue = current - 1
|
||||
@@ -196,6 +205,55 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
spinboxBinding.editValue.setText(newValue.toString())
|
||||
updateValidity(newValue)
|
||||
}
|
||||
*/
|
||||
|
||||
fun attachRepeat(button: View, delta: Int) {
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
var runnable: Runnable? = null
|
||||
val initialDelay = 400L
|
||||
val minDelay = 40L
|
||||
val accelerationFactor = 0.75f
|
||||
|
||||
button.setOnTouchListener { v, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
val current = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
|
||||
val newValue = (current + delta)
|
||||
spinboxBinding.editValue.setText(newValue.toString())
|
||||
updateValidity(newValue)
|
||||
|
||||
var delay = initialDelay
|
||||
runnable = object : Runnable {
|
||||
override fun run() {
|
||||
val curr = spinboxBinding.editValue.text.toString().toIntOrNull() ?: currentValue
|
||||
val next = curr + delta
|
||||
spinboxBinding.editValue.setText(next.toString())
|
||||
updateValidity(next)
|
||||
// accelerate
|
||||
delay = (delay * accelerationFactor).toLong().coerceAtLeast(minDelay)
|
||||
handler.postDelayed(this, delay)
|
||||
}
|
||||
}
|
||||
handler.postDelayed(runnable!!, initialDelay)
|
||||
true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (runnable != null) {
|
||||
handler.removeCallbacks(runnable!!)
|
||||
runnable = null
|
||||
}
|
||||
v.performClick()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attachRepeat(spinboxBinding.buttonDecrement, -1)
|
||||
attachRepeat(spinboxBinding.buttonIncrement, 1)
|
||||
|
||||
spinboxBinding.editValue.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
@@ -336,6 +394,12 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
||||
.show()
|
||||
}
|
||||
scSetting.setSelectedValue(value)
|
||||
|
||||
if (scSetting.setting.key == "app_language") {
|
||||
settingsViewModel.setShouldRecreateForLanguageChange(true)
|
||||
// recreate page apply language change instantly
|
||||
requireActivity().recreate()
|
||||
}
|
||||
}
|
||||
|
||||
is StringSingleChoiceSetting -> {
|
||||
|
||||
@@ -453,6 +453,7 @@ class SettingsFragmentPresenter(
|
||||
sl.apply {
|
||||
add(HeaderSetting(R.string.veil_extensions))
|
||||
add(ByteSetting.RENDERER_DYNA_STATE.key)
|
||||
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
|
||||
add(BooleanSetting.RENDERER_PROVOKING_VERTEX.key)
|
||||
add(BooleanSetting.RENDERER_DESCRIPTOR_INDEXING.key)
|
||||
add(BooleanSetting.RENDERER_SAMPLE_SHADING.key)
|
||||
@@ -490,6 +491,7 @@ class SettingsFragmentPresenter(
|
||||
sl.apply {
|
||||
add(IntSetting.SWKBD_APPLET.key)
|
||||
add(BooleanSetting.AIRPLANE_MODE.key)
|
||||
add(BooleanSetting.ENABLE_OVERLAY.key)
|
||||
}
|
||||
}
|
||||
private fun addInputPlayer(sl: ArrayList<SettingsItem>, playerIndex: Int) {
|
||||
@@ -1030,8 +1032,10 @@ class SettingsFragmentPresenter(
|
||||
override fun reset() = IntSetting.THEME.setInt(defaultValue)
|
||||
}
|
||||
|
||||
add(HeaderSetting(R.string.app_settings))
|
||||
add(IntSetting.APP_LANGUAGE.key)
|
||||
|
||||
if (NativeLibrary.isUpdateCheckerEnabled()) {
|
||||
add(HeaderSetting(R.string.app_settings))
|
||||
add(BooleanSetting.ENABLE_UPDATE_CHECKS.key)
|
||||
}
|
||||
|
||||
@@ -1164,9 +1168,10 @@ class SettingsFragmentPresenter(
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.USE_AUTO_STUB.key)
|
||||
add(SettingsItem.FASTMEM_COMBINED)
|
||||
|
||||
add(HeaderSetting(R.string.log))
|
||||
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
||||
add(HeaderSetting(R.string.general))
|
||||
add(IntSetting.DEBUG_KNOBS.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -54,6 +57,8 @@ class SettingsViewModel : ViewModel() {
|
||||
private val _shouldShowResetInputDialog = MutableStateFlow(false)
|
||||
val shouldShowResetInputDialog = _shouldShowResetInputDialog.asStateFlow()
|
||||
|
||||
private val _shouldRecreateForLanguageChange = MutableStateFlow(false)
|
||||
val shouldRecreateForLanguageChange = _shouldRecreateForLanguageChange.asStateFlow()
|
||||
fun setShouldRecreate(value: Boolean) {
|
||||
_shouldRecreate.value = value
|
||||
}
|
||||
@@ -103,6 +108,10 @@ class SettingsViewModel : ViewModel() {
|
||||
_shouldShowResetInputDialog.value = value
|
||||
}
|
||||
|
||||
fun setShouldRecreateForLanguageChange(value: Boolean) {
|
||||
_shouldRecreateForLanguageChange.value = value
|
||||
}
|
||||
|
||||
fun getCurrentDeviceParams(defaultParams: ParamPackage): ParamPackage =
|
||||
try {
|
||||
InputHandler.registeredControllers[currentDevice]
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
private var _binding: FragmentAboutBinding? = null
|
||||
@@ -78,11 +79,15 @@ class AboutFragment : Fragment() {
|
||||
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
|
||||
}
|
||||
|
||||
binding.textVersionName.text = BuildConfig.VERSION_NAME
|
||||
val buildName = getString(R.string.app_name_suffixed)
|
||||
val buildVersion = NativeLibrary.getBuildVersion()
|
||||
val fullVersionText = "$buildName ($buildVersion)"
|
||||
|
||||
binding.textVersionName.text = fullVersionText
|
||||
binding.buttonVersionName.setOnClickListener {
|
||||
val clipBoard =
|
||||
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.build), BuildConfig.GIT_HASH)
|
||||
val clip = ClipData.newPlainText(getString(R.string.build), fullVersionText)
|
||||
clipBoard.setPrimaryClip(clip)
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
@@ -91,6 +91,12 @@ class DriverManagerFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
driverViewModel.shouldShowDriverShaderDialog.collect(viewLifecycleOwner) { shouldShow ->
|
||||
if (shouldShow) {
|
||||
showDriverShaderWipeDialog()
|
||||
}
|
||||
}
|
||||
|
||||
if (!driverViewModel.isInteractionAllowed.value) {
|
||||
DriversLoadingDialogFragment().show(
|
||||
childFragmentManager,
|
||||
@@ -236,4 +242,18 @@ class DriverManagerFragment : Fragment() {
|
||||
).show(requireActivity().supportFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDriverShaderWipeDialog() {
|
||||
com.google.android.material.dialog.MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.driver_shader_wipe_dialog_title)
|
||||
.setMessage(R.string.driver_shader_wipe_dialog_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
driverViewModel.onDriverShaderDialogDismissed(dontShowAgain = false)
|
||||
}
|
||||
.setNegativeButton(R.string.dont_show_again) { _, _ ->
|
||||
driverViewModel.onDriverShaderDialogDismissed(dontShowAgain = true)
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,13 +145,12 @@ class GamePropertiesFragment : Fragment() {
|
||||
val seconds = playTimeSeconds % 60
|
||||
|
||||
val readablePlayTime = when {
|
||||
hours > 0 -> "${hours}h ${minutes}m ${seconds}s"
|
||||
minutes > 0 -> "${minutes}m ${seconds}s"
|
||||
else -> "${seconds}s"
|
||||
}
|
||||
hours > 0 -> "$hours${getString(R.string.hours_abbr)} $minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||
minutes > 0 -> "$minutes${getString(R.string.minutes_abbr)} $seconds${getString(R.string.seconds_abbr)}"
|
||||
else -> "$seconds${getString(R.string.seconds_abbr)}"
|
||||
}
|
||||
|
||||
append(getString(R.string.playtime))
|
||||
append(readablePlayTime)
|
||||
append(getString(R.string.playtime) + " " + readablePlayTime)
|
||||
}
|
||||
|
||||
binding.playtime.setOnClickListener {
|
||||
|
||||
@@ -230,6 +230,17 @@ class HomeSettingsFragment : Fragment() {
|
||||
{ openFileManager() }
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.system_information,
|
||||
R.string.system_information_description,
|
||||
R.drawable.ic_system,
|
||||
{
|
||||
SystemInfoDialogFragment.newInstance()
|
||||
.show(parentFragmentManager, SystemInfoDialogFragment.TAG)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.about,
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogSystemInfoBinding
|
||||
|
||||
class SystemInfoDialogFragment : DialogFragment() {
|
||||
private var _binding: DialogSystemInfoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
_binding = DialogSystemInfoBinding.inflate(layoutInflater)
|
||||
|
||||
populateSystemInfo()
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.system_information)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.create()
|
||||
|
||||
dialog.setView(binding.root)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun populateSystemInfo() {
|
||||
val systemInfo = buildString {
|
||||
// General Device Info
|
||||
appendLine("=== ${getString(R.string.general_information)} ===")
|
||||
appendLine("${getString(R.string.device_manufacturer)}: ${Build.MANUFACTURER}")
|
||||
appendLine("${getString(R.string.device_model)}: ${Build.MODEL}")
|
||||
appendLine("${getString(R.string.device_name)}: ${Build.DEVICE}")
|
||||
appendLine("${getString(R.string.product)}: ${Build.PRODUCT}")
|
||||
appendLine("${getString(R.string.hardware)}: ${Build.HARDWARE}")
|
||||
appendLine("${getString(R.string.supported_abis)}: ${Build.SUPPORTED_ABIS.joinToString(", ")}")
|
||||
appendLine("${getString(R.string.android_version)}: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})")
|
||||
appendLine("${getString(R.string.android_security_patch)}: ${Build.VERSION.SECURITY_PATCH}")
|
||||
appendLine("${getString(R.string.build_id)}: ${Build.ID}")
|
||||
|
||||
appendLine()
|
||||
appendLine("=== ${getString(R.string.cpu_info)} ===")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && Build.SOC_MODEL.isNotBlank()) {
|
||||
appendLine("${getString(R.string.soc)} ${Build.SOC_MODEL}")
|
||||
}
|
||||
|
||||
val cpuSummary = NativeLibrary.getCpuSummary()
|
||||
if (cpuSummary.isNotEmpty() && cpuSummary != "Unknown") {
|
||||
appendLine(cpuSummary)
|
||||
}
|
||||
|
||||
appendLine()
|
||||
|
||||
// GPU Info
|
||||
appendLine("=== ${getString(R.string.gpu_information)} ===")
|
||||
try {
|
||||
val gpuModel = NativeLibrary.getGpuModel()
|
||||
appendLine("${getString(R.string.gpu_model)}: $gpuModel")
|
||||
|
||||
val vulkanApi = NativeLibrary.getVulkanApiVersion()
|
||||
appendLine("Vulkan API: $vulkanApi")
|
||||
|
||||
val vulkanDriver = NativeLibrary.getVulkanDriverVersion()
|
||||
appendLine("${getString(R.string.vulkan_driver_version)}: $vulkanDriver")
|
||||
} catch (e: Exception) {
|
||||
appendLine("${getString(R.string.error_getting_emulator_info)}: ${e.message}")
|
||||
}
|
||||
appendLine()
|
||||
|
||||
// Memory Info
|
||||
appendLine("=== ${getString(R.string.memory_info)} ===")
|
||||
|
||||
val activityManager =
|
||||
requireContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val memInfo = ActivityManager.MemoryInfo()
|
||||
activityManager.getMemoryInfo(memInfo)
|
||||
val totalDeviceRam = memInfo.totalMem / (1024 * 1024)
|
||||
|
||||
appendLine("${getString(R.string.total_memory)}: $totalDeviceRam MB")
|
||||
}
|
||||
|
||||
binding.textSystemInfo.text = systemInfo
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "SystemInfoDialogFragment"
|
||||
|
||||
fun newInstance(): SystemInfoDialogFragment {
|
||||
return SystemInfoDialogFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -16,6 +19,7 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
|
||||
@@ -52,9 +56,15 @@ class DriverViewModel : ViewModel() {
|
||||
|
||||
private val driversToDelete = mutableListOf<String>()
|
||||
|
||||
private var previousDriverPath: String = ""
|
||||
|
||||
private val _shouldShowDriverShaderDialog = MutableStateFlow(false)
|
||||
val shouldShowDriverShaderDialog: StateFlow<Boolean> get() = _shouldShowDriverShaderDialog
|
||||
|
||||
init {
|
||||
updateDriverList()
|
||||
updateDriverNameForGame(null)
|
||||
previousDriverPath = StringSetting.DRIVER_PATH.getString()
|
||||
}
|
||||
|
||||
fun reloadDriverData() {
|
||||
@@ -79,6 +89,7 @@ class DriverViewModel : ViewModel() {
|
||||
newDriverList.add(it.second.toDriver(it.second == selectedDriver))
|
||||
}
|
||||
_driverList.value = newDriverList
|
||||
previousDriverPath = StringSetting.DRIVER_PATH.getString()
|
||||
}
|
||||
|
||||
fun onOpenDriverManager(game: Game?) {
|
||||
@@ -92,12 +103,49 @@ class DriverViewModel : ViewModel() {
|
||||
_showClearButton.value = value
|
||||
}
|
||||
|
||||
fun onDriverSelected(position: Int) {
|
||||
fun onDriverSelected(position: Int, skipShaderWipe: Boolean = false) {
|
||||
val newDriverPath = if (position == 0) {
|
||||
""
|
||||
} else {
|
||||
driverData[position - 1].first
|
||||
}
|
||||
|
||||
if (!skipShaderWipe && newDriverPath != previousDriverPath) {
|
||||
wipeAllShaders()
|
||||
|
||||
if (!BooleanSetting.DONT_SHOW_DRIVER_SHADER_WARNING.getBoolean(needsGlobal = true)) {
|
||||
_shouldShowDriverShaderDialog.value = true
|
||||
}
|
||||
}
|
||||
|
||||
if (position == 0) {
|
||||
StringSetting.DRIVER_PATH.setString("")
|
||||
} else {
|
||||
StringSetting.DRIVER_PATH.setString(driverData[position - 1].first)
|
||||
}
|
||||
previousDriverPath = newDriverPath
|
||||
}
|
||||
|
||||
fun onDriverShaderDialogDismissed(dontShowAgain: Boolean) {
|
||||
if (dontShowAgain) {
|
||||
BooleanSetting.DONT_SHOW_DRIVER_SHADER_WARNING.setBoolean(true)
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
_shouldShowDriverShaderDialog.value = false
|
||||
}
|
||||
|
||||
private fun wipeAllShaders() {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val shaderDir = File(
|
||||
YuzuApplication.appContext.getExternalFilesDir(null)?.canonicalPath +
|
||||
"/shader/"
|
||||
)
|
||||
if (shaderDir.exists()) {
|
||||
shaderDir.deleteRecursively()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onDriverRemoved(removedPosition: Int, selectedPosition: Int) {
|
||||
|
||||
@@ -53,6 +53,7 @@ class GamesFragment : Fragment() {
|
||||
private var originalHeaderLeftMargin: Int? = null
|
||||
|
||||
private var lastViewType: Int = GameAdapter.VIEW_TYPE_GRID
|
||||
private var fallbackBottomInset: Int = 0
|
||||
|
||||
companion object {
|
||||
private const val SEARCH_TEXT = "SearchText"
|
||||
@@ -208,12 +209,12 @@ class GamesFragment : Fragment() {
|
||||
else -> throw IllegalArgumentException("Invalid view type: $savedViewType")
|
||||
}
|
||||
if (savedViewType == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
doOnNextLayout {
|
||||
(this as? CarouselRecyclerView)?.setCarouselMode(true, gameAdapter)
|
||||
adapter = gameAdapter
|
||||
(binding.gridGames as? View)?.let { it -> ViewCompat.requestApplyInsets(it)}
|
||||
doOnNextLayout { //Carousel: important to avoid overlap issues
|
||||
(this as? CarouselRecyclerView)?.notifyLaidOut(fallbackBottomInset)
|
||||
}
|
||||
} else {
|
||||
(this as? CarouselRecyclerView)?.setCarouselMode(false)
|
||||
(this as? CarouselRecyclerView)?.setupCarousel(false)
|
||||
}
|
||||
adapter = gameAdapter
|
||||
lastViewType = savedViewType
|
||||
@@ -237,9 +238,8 @@ class GamesFragment : Fragment() {
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
|
||||
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(
|
||||
gamesViewModel.lastScrollPosition
|
||||
)
|
||||
(binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true)
|
||||
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,6 +494,11 @@ class GamesFragment : Fragment() {
|
||||
mlpFab.rightMargin = rightInset + fabPadding
|
||||
binding.addDirectory.layoutParams = mlpFab
|
||||
|
||||
val navInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
val gestureInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
|
||||
val bottomInset = maxOf(navInsets.bottom, gestureInsets.bottom, cutoutInsets.bottom)
|
||||
fallbackBottomInset = bottomInset
|
||||
(binding.gridGames as? CarouselRecyclerView)?.notifyInsetsReady(bottomInset)
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.ui.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
@@ -52,7 +53,16 @@ import androidx.core.content.edit
|
||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||
import kotlin.text.compareTo
|
||||
import androidx.core.net.toUri
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.updater.APKDownloader
|
||||
import org.yuzu.yuzu_emu.updater.APKInstaller
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
@@ -68,6 +78,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
private val CHECKED_DECRYPTION = "CheckedDecryption"
|
||||
private var checkedDecryption = false
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(YuzuApplication.applyLanguage(base))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
|
||||
@@ -180,9 +194,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
.setTitle(R.string.update_available)
|
||||
.setMessage(getString(R.string.update_available_description, version))
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val url = NativeLibrary.getUpdateUrl(version)
|
||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
startActivity(intent)
|
||||
downloadAndInstallUpdate(version)
|
||||
}
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
@@ -195,6 +207,87 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun downloadAndInstallUpdate(version: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val packageId = applicationContext.packageName
|
||||
val apkUrl = NativeLibrary.getUpdateApkUrl(version, packageId)
|
||||
val apkFile = File(cacheDir, "update-$version.apk")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
showDownloadProgressDialog()
|
||||
}
|
||||
|
||||
val downloader = APKDownloader(apkUrl, apkFile)
|
||||
downloader.download(
|
||||
onProgress = { progress ->
|
||||
runOnUiThread {
|
||||
updateDownloadProgress(progress)
|
||||
}
|
||||
},
|
||||
onComplete = { success ->
|
||||
runOnUiThread {
|
||||
dismissDownloadProgressDialog()
|
||||
if (success) {
|
||||
val installer = APKInstaller(this@MainActivity)
|
||||
installer.install(
|
||||
apkFile,
|
||||
onComplete = {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
R.string.update_installed_successfully,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
},
|
||||
onFailure = { exception ->
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.update_install_failed, exception.message),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.update_download_failed) + "\n\nURL: $apkUrl",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var progressDialog: androidx.appcompat.app.AlertDialog? = null
|
||||
private var progressBar: LinearProgressIndicator? = null
|
||||
private var progressMessage: MaterialTextView? = null
|
||||
|
||||
private fun showDownloadProgressDialog() {
|
||||
val progressView = layoutInflater.inflate(R.layout.dialog_download_progress, null)
|
||||
progressBar = progressView.findViewById(R.id.download_progress_bar)
|
||||
progressMessage = progressView.findViewById(R.id.download_progress_message)
|
||||
|
||||
progressDialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.downloading_update)
|
||||
.setView(progressView)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
progressDialog?.show()
|
||||
}
|
||||
|
||||
private fun updateDownloadProgress(progress: Int) {
|
||||
progressBar?.progress = progress
|
||||
progressMessage?.text = "$progress%"
|
||||
}
|
||||
|
||||
private fun dismissDownloadProgressDialog() {
|
||||
progressDialog?.dismiss()
|
||||
progressDialog = null
|
||||
progressBar = null
|
||||
progressMessage = null
|
||||
}
|
||||
|
||||
|
||||
fun displayMultiplayerDialog() {
|
||||
val dialog = NetPlayDialog(this)
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class APKDownloader(private val url: String, private val outputFile: File) {
|
||||
|
||||
fun download(onProgress: (Int) -> Unit, onComplete: (Boolean) -> Unit) {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder().url(url).build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
e.printStackTrace()
|
||||
onComplete(false)
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
if (response.isSuccessful) {
|
||||
response.body?.let { body ->
|
||||
val contentLength = body.contentLength()
|
||||
try {
|
||||
val inputStream = body.byteStream()
|
||||
val outputStream = FileOutputStream(outputFile)
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
var totalBytesRead: Long = 0
|
||||
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
totalBytesRead += bytesRead
|
||||
val progress = (totalBytesRead * 100 / contentLength).toInt()
|
||||
onProgress(progress)
|
||||
}
|
||||
outputStream.flush()
|
||||
outputStream.close()
|
||||
inputStream.close()
|
||||
onComplete(true)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
onComplete(false)
|
||||
}
|
||||
} ?: run {
|
||||
onComplete(false)
|
||||
}
|
||||
} else {
|
||||
onComplete(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import androidx.core.content.FileProvider
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
class APKInstaller(private val context: Context) {
|
||||
|
||||
fun install(apkFile: File, onComplete: () -> Unit, onFailure: (Exception) -> Unit) {
|
||||
try {
|
||||
val apkUri: Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
context.applicationContext.packageName + ".provider",
|
||||
apkFile
|
||||
)
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
|
||||
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
context.startActivity(intent)
|
||||
|
||||
GlobalScope.launch {
|
||||
val receiver = AppInstallReceiver(onComplete, onFailure)
|
||||
context.registerReceiver(receiver, IntentFilter().apply {
|
||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||
addAction(Intent.ACTION_PACKAGE_REPLACED)
|
||||
addDataScheme("package")
|
||||
})
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
onFailure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.updater
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
||||
class AppInstallReceiver(
|
||||
private val onComplete: () -> Unit,
|
||||
private val onFailure: (Exception) -> Unit
|
||||
) : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val packageName = intent.data?.schemeSpecificPart
|
||||
when (intent.action) {
|
||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REPLACED -> {
|
||||
Log.i("AppInstallReceiver", "Package installed or updated: $packageName")
|
||||
onComplete()
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
else -> {
|
||||
onFailure(Exception("Installation failed for package: $packageName"))
|
||||
context.unregisterReceiver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
// 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
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
|
||||
import android.os.Build
|
||||
|
||||
@@ -25,6 +29,15 @@ object Log {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
|
||||
info("SoC Manufacturer - ${Build.SOC_MANUFACTURER}")
|
||||
info("SoC Model - ${Build.SOC_MODEL}")
|
||||
NativeLibrary.getCpuSummary().split('\n').forEach {
|
||||
info("CPU Info - $it")
|
||||
}
|
||||
}
|
||||
NativeLibrary.getVulkanDriverVersion().split('\n').forEach {
|
||||
info("Vulkan Driver: - $it")
|
||||
}
|
||||
NativeLibrary.getVulkanApiVersion().split('\n').forEach {
|
||||
info("Vulkan API Version: - $it")
|
||||
}
|
||||
info("Total System Memory - ${MemoryUtil.getDeviceRAM()}")
|
||||
}
|
||||
|
||||
@@ -20,9 +20,8 @@ import androidx.core.view.doOnNextLayout
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
/**
|
||||
* CarouselRecyclerView encapsulates all carousel logic for the games UI.
|
||||
* CarouselRecyclerView encapsulates all carousel content for the games UI.
|
||||
* It manages overlapping cards, center snapping, custom drawing order,
|
||||
* joypad & fling navigation and mid-screen swipe-to-refresh.
|
||||
*/
|
||||
@@ -34,6 +33,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
|
||||
private var overlapFactor: Float = 0f
|
||||
private var overlapPx: Int = 0
|
||||
private var bottomInset: Int = -1
|
||||
private var overlapDecoration: OverlappingDecoration? = null
|
||||
private var pagerSnapHelper: PagerSnapHelper? = null
|
||||
private var scalingScrollListener: OnScrollListener? = null
|
||||
@@ -202,46 +202,61 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun setCarouselMode(enabled: Boolean, gameAdapter: GameAdapter? = null) {
|
||||
fun refreshView() {
|
||||
updateChildScalesAndAlpha()
|
||||
focusCenteredCard()
|
||||
}
|
||||
|
||||
fun notifyInsetsReady(newBottomInset: Int) {
|
||||
if (bottomInset != newBottomInset) {
|
||||
bottomInset = newBottomInset
|
||||
}
|
||||
setupCarousel(true)
|
||||
}
|
||||
|
||||
fun notifyLaidOut(fallBackBottomInset: Int) {
|
||||
if (bottomInset < 0) bottomInset = fallBackBottomInset
|
||||
var gameAdapter = adapter as? GameAdapter ?: return
|
||||
var newCardSize = cardSize(bottomInset)
|
||||
if (gameAdapter.cardSize != newCardSize) {
|
||||
gameAdapter.setCardSize(newCardSize)
|
||||
}
|
||||
setupCarousel(true)
|
||||
}
|
||||
|
||||
fun cardSize(bottomInset: Int): Int {
|
||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
return (userFactor * (height - bottomInset)).toInt()
|
||||
}
|
||||
|
||||
fun setupCarousel(enabled: Boolean) {
|
||||
if (enabled) {
|
||||
val gameAdapter = adapter as? GameAdapter ?: return
|
||||
if (gameAdapter.cardSize == 0) return
|
||||
if (bottomInset < 0) return
|
||||
|
||||
useCustomDrawingOrder = true
|
||||
val cardSize = gameAdapter.cardSize
|
||||
|
||||
val insets = rootWindowInsets?.let { WindowInsetsCompat.toWindowInsetsCompat(it, this) }
|
||||
val bottomInset = insets?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0
|
||||
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
|
||||
val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val cardSize = (userFactor * (height - bottomInset)).toInt()
|
||||
gameAdapter?.setCardSize(cardSize)
|
||||
|
||||
val internalOverlapFactor = resources.getFraction(
|
||||
R.fraction.carousel_overlap_factor,
|
||||
1,
|
||||
1
|
||||
)
|
||||
overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(
|
||||
0f,
|
||||
1f
|
||||
)
|
||||
val internalOverlapFactor = resources.getFraction(R.fraction.carousel_overlap_factor,1,1)
|
||||
overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(0f,1f)
|
||||
overlapPx = (cardSize * overlapFactor).toInt()
|
||||
|
||||
val internalFlingMultiplier = resources.getFraction(
|
||||
R.fraction.carousel_fling_multiplier,
|
||||
1,
|
||||
1
|
||||
)
|
||||
val internalFlingMultiplier = resources.getFraction(R.fraction.carousel_fling_multiplier,1,1)
|
||||
flingMultiplier = preferences.getFloat(
|
||||
CAROUSEL_FLING_MULTIPLIER,
|
||||
internalFlingMultiplier
|
||||
).coerceIn(1f, 5f)
|
||||
|
||||
gameAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
gameAdapter .registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
if (pendingScrollAfterReload) {
|
||||
post {
|
||||
jigglyScroll()
|
||||
doOnNextLayout {
|
||||
refreshView()
|
||||
pendingScrollAfterReload = false
|
||||
}
|
||||
}
|
||||
@@ -257,7 +272,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
addItemDecoration(overlapDecoration!!)
|
||||
}
|
||||
|
||||
// Gradual scalingAdd commentMore actions
|
||||
// Gradual scaling on scroll
|
||||
if (scalingScrollListener == null) {
|
||||
scalingScrollListener = object : OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
@@ -315,8 +330,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
super.scrollToPosition(position)
|
||||
(layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(position, overlapPx)
|
||||
doOnNextLayout {
|
||||
updateChildScalesAndAlpha()
|
||||
focusCenteredCard()
|
||||
refreshView()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,12 +396,6 @@ class CarouselRecyclerView @JvmOverloads constructor(
|
||||
return sorted[i].first
|
||||
}
|
||||
|
||||
fun jigglyScroll() {
|
||||
scrollBy(-1, 0)
|
||||
scrollBy(1, 0)
|
||||
focusCenteredCard()
|
||||
}
|
||||
|
||||
inner class OverlappingDecoration(private val overlap: Int) : ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace AndroidSettings {
|
||||
Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
|
||||
Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
|
||||
Settings::Category::Android};
|
||||
Settings::Setting<s32> app_language{linkage, 0, "app_language", Settings::Category::Android};
|
||||
Settings::Setting<bool> enable_update_checks{linkage, true, "enable_update_checks",
|
||||
Settings::Category::Android};
|
||||
|
||||
@@ -181,9 +182,9 @@ namespace AndroidSettings {
|
||||
Settings::Specialization::Default, true, true,
|
||||
&show_soc_overlay};
|
||||
|
||||
Settings::Setting<bool> dont_show_eden_veil_warning{linkage, false,
|
||||
"dont_show_eden_veil_warning",
|
||||
Settings::Category::Miscellaneous};
|
||||
Settings::Setting<bool> dont_show_driver_shader_warning{linkage, false,
|
||||
"dont_show_driver_shader_warning",
|
||||
Settings::Category::Android, Settings::Specialization::Default, true, true};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
||||
@@ -4,15 +4,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include "video_core/vulkan_common/vma.h"
|
||||
|
||||
#include <codecvt>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <dlfcn.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
#include <adrenotools/driver.h>
|
||||
#endif
|
||||
@@ -463,6 +473,205 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath,
|
||||
return Core::SystemResultStatus::Success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct CpuPartInfo {
|
||||
u32 vendor;
|
||||
u32 part;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
constexpr CpuPartInfo s_cpu_list[] = {
|
||||
// ARM - 0x41
|
||||
{0x41, 0xd01, "Cortex-A32"},
|
||||
{0x41, 0xd02, "Cortex-A34"},
|
||||
{0x41, 0xd04, "Cortex-A35"},
|
||||
{0x41, 0xd03, "Cortex-A53"},
|
||||
{0x41, 0xd05, "Cortex-A55"},
|
||||
{0x41, 0xd46, "Cortex-A510"},
|
||||
{0x41, 0xd80, "Cortex-A520"},
|
||||
{0x41, 0xd88, "Cortex-A520AE"},
|
||||
{0x41, 0xd07, "Cortex-A57"},
|
||||
{0x41, 0xd06, "Cortex-A65"},
|
||||
{0x41, 0xd43, "Cortex-A65AE"},
|
||||
{0x41, 0xd08, "Cortex-A72"},
|
||||
{0x41, 0xd09, "Cortex-A73"},
|
||||
{0x41, 0xd0a, "Cortex-A75"},
|
||||
{0x41, 0xd0b, "Cortex-A76"},
|
||||
{0x41, 0xd0e, "Cortex-A76AE"},
|
||||
{0x41, 0xd0d, "Cortex-A77"},
|
||||
{0x41, 0xd41, "Cortex-A78"},
|
||||
{0x41, 0xd42, "Cortex-A78AE"},
|
||||
{0x41, 0xd4b, "Cortex-A78C"},
|
||||
{0x41, 0xd47, "Cortex-A710"},
|
||||
{0x41, 0xd4d, "Cortex-A715"},
|
||||
{0x41, 0xd81, "Cortex-A720"},
|
||||
{0x41, 0xd89, "Cortex-A720AE"},
|
||||
{0x41, 0xd87, "Cortex-A725"},
|
||||
{0x41, 0xd44, "Cortex-X1"},
|
||||
{0x41, 0xd4c, "Cortex-X1C"},
|
||||
{0x41, 0xd48, "Cortex-X2"},
|
||||
{0x41, 0xd4e, "Cortex-X3"},
|
||||
{0x41, 0xd82, "Cortex-X4"},
|
||||
{0x41, 0xd85, "Cortex-X925"},
|
||||
{0x41, 0xd4a, "Neoverse E1"},
|
||||
{0x41, 0xd0c, "Neoverse N1"},
|
||||
{0x41, 0xd49, "Neoverse N2"},
|
||||
{0x41, 0xd8e, "Neoverse N3"},
|
||||
{0x41, 0xd40, "Neoverse V1"},
|
||||
{0x41, 0xd4f, "Neoverse V2"},
|
||||
{0x41, 0xd84, "Neoverse V3"},
|
||||
{0x41, 0xd83, "Neoverse V3AE"},
|
||||
// Qualcomm - 0x51
|
||||
{0x51, 0x201, "Kryo"},
|
||||
{0x51, 0x205, "Kryo"},
|
||||
{0x51, 0x211, "Kryo"},
|
||||
{0x51, 0x800, "Kryo 385 Gold"},
|
||||
{0x51, 0x801, "Kryo 385 Silver"},
|
||||
{0x51, 0x802, "Kryo 485 Gold"},
|
||||
{0x51, 0x803, "Kryo 485 Silver"},
|
||||
{0x51, 0x804, "Kryo 680 Prime"},
|
||||
{0x51, 0x805, "Kryo 680 Gold"},
|
||||
{0x51, 0x06f, "Krait"},
|
||||
{0x51, 0xc00, "Falkor"},
|
||||
{0x51, 0xc01, "Saphira"},
|
||||
{0x51, 0x001, "Oryon"},
|
||||
};
|
||||
|
||||
const char* find_cpu_name(u32 vendor, u32 part) {
|
||||
for (const auto& cpu : s_cpu_list) {
|
||||
if (cpu.vendor == vendor && cpu.part == part) {
|
||||
return cpu.name;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u64 read_midr_sysfs(u32 cpu_id) {
|
||||
char path[128];
|
||||
std::snprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%u/regs/identification/midr_el1", cpu_id);
|
||||
|
||||
FILE* f = std::fopen(path, "r");
|
||||
if (!f) return 0;
|
||||
|
||||
char value[32];
|
||||
if (!std::fgets(value, sizeof(value), f)) {
|
||||
std::fclose(f);
|
||||
return 0;
|
||||
}
|
||||
std::fclose(f);
|
||||
|
||||
return std::strtoull(value, nullptr, 16);
|
||||
}
|
||||
|
||||
std::pair<u32, std::string> get_pretty_cpus() {
|
||||
std::map<u64, int> core_layout;
|
||||
u32 valid_cpus = 0;
|
||||
for (u32 i = 0; i < std::thread::hardware_concurrency(); ++i) {
|
||||
const auto midr = read_midr_sysfs(i);
|
||||
if (midr == 0) break;
|
||||
|
||||
valid_cpus++;
|
||||
core_layout[midr]++;
|
||||
}
|
||||
|
||||
std::string cpus;
|
||||
|
||||
if (!core_layout.empty()) {
|
||||
const CpuPartInfo* lowest_part = nullptr;
|
||||
u32 lowest_part_id = 0xFFFFFFFF;
|
||||
|
||||
for (const auto& [midr, count] : core_layout) {
|
||||
const auto vendor = (midr >> 24) & 0xff;
|
||||
const auto part = (midr >> 4) & 0xfff;
|
||||
|
||||
if (!cpus.empty()) cpus += " + ";
|
||||
cpus += fmt::format("{}x {}", count, find_cpu_name(vendor, part));
|
||||
}
|
||||
}
|
||||
|
||||
return {valid_cpus, cpus};
|
||||
}
|
||||
|
||||
std::string get_arm_cpu_name() {
|
||||
std::map<u64, int> core_layout;
|
||||
for (u32 i = 0; i < std::thread::hardware_concurrency(); ++i) {
|
||||
const auto midr = read_midr_sysfs(i);
|
||||
if (midr == 0) break;
|
||||
|
||||
core_layout[midr]++;
|
||||
}
|
||||
|
||||
if (!core_layout.empty()) {
|
||||
const CpuPartInfo* lowest_part = nullptr;
|
||||
u32 lowest_part_id = 0xFFFFFFFF;
|
||||
|
||||
for (const auto& [midr, count] : core_layout) {
|
||||
const auto vendor = (midr >> 24) & 0xff;
|
||||
const auto part = (midr >> 4) & 0xfff;
|
||||
|
||||
for (const auto& cpu : s_cpu_list) {
|
||||
if (cpu.vendor == vendor && cpu.part == part) {
|
||||
if (cpu.part < lowest_part_id) {
|
||||
lowest_part_id = cpu.part;
|
||||
lowest_part = &cpu;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lowest_part) {
|
||||
return lowest_part->name;
|
||||
}
|
||||
}
|
||||
|
||||
FILE* f = std::fopen("/proc/cpuinfo", "r");
|
||||
if (!f) return "";
|
||||
|
||||
char buf[512];
|
||||
std::string result;
|
||||
|
||||
auto trim = [](std::string& s) {
|
||||
const auto start = s.find_first_not_of(" \t\r\n");
|
||||
const auto end = s.find_last_not_of(" \t\r\n");
|
||||
s = (start == std::string::npos) ? "" : s.substr(start, end - start + 1);
|
||||
};
|
||||
|
||||
while (std::fgets(buf, sizeof(buf), f)) {
|
||||
std::string line(buf);
|
||||
if (line.find("Hardware") == 0) {
|
||||
auto pos = line.find(':');
|
||||
if (pos != std::string::npos) {
|
||||
result = line.substr(pos + 1);
|
||||
trim(result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::fclose(f);
|
||||
|
||||
if (!result.empty()) {
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* fallback_cpu_detection() {
|
||||
static std::string s_result = []() -> std::string {
|
||||
std::string result = get_arm_cpu_name();
|
||||
if (result.empty()) {
|
||||
return "Cortex-A34";
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
|
||||
return s_result.c_str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
|
||||
@@ -555,6 +764,7 @@ jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDri
|
||||
|
||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
JNIEnv* env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
@@ -577,15 +787,19 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
||||
auto version_string =
|
||||
fmt::format("{}.{}.{}", VK_API_VERSION_MAJOR(driver_version),
|
||||
VK_API_VERSION_MINOR(driver_version), VK_API_VERSION_PATCH(driver_version));
|
||||
|
||||
jobjectArray j_driver_info = env->NewObjectArray(
|
||||
2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
|
||||
env->SetObjectArrayElement(j_driver_info, 1,
|
||||
Common::Android::ToJString(env, device.GetDriverName()));
|
||||
auto driver_name = device.GetDriverName();
|
||||
#else
|
||||
auto driver_version = "1.0.0";
|
||||
auto version_string = "1.1.0"; //Assume lowest Vulkan level
|
||||
auto driver_name = "generic";
|
||||
#endif
|
||||
jobjectArray j_driver_info = env->NewObjectArray(2, Common::Android::GetStringClass(), Common::Android::ToJString(env, version_string));
|
||||
env->SetObjectArrayElement(j_driver_info, 1, Common::Android::ToJString(env, driver_name));
|
||||
return j_driver_info;
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getGpuModel(JNIEnv *env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
@@ -609,6 +823,9 @@ jstring Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getGpuModel(JNIEnv *env, j
|
||||
window.release();
|
||||
|
||||
return Common::Android::ToJString(env, model_name);
|
||||
#else
|
||||
return Common::Android::ToJString(env, "no-info");
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
|
||||
@@ -684,6 +901,209 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject
|
||||
env, EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuSummary(JNIEnv* env, jobject /*jobj*/) {
|
||||
get_arm_cpu_name();
|
||||
constexpr const char* CPUINFO_PATH = "/proc/cpuinfo";
|
||||
|
||||
auto trim = [](std::string& s) {
|
||||
const auto start = s.find_first_not_of(" \t\r\n");
|
||||
const auto end = s.find_last_not_of(" \t\r\n");
|
||||
s = (start == std::string::npos) ? "" : s.substr(start, end - start + 1);
|
||||
};
|
||||
|
||||
auto to_lower = [](std::string s) {
|
||||
for (auto& c : s) c = std::tolower(c);
|
||||
return s;
|
||||
};
|
||||
|
||||
try {
|
||||
std::string result;
|
||||
std::pair<u32, std::string> pretty_cpus = get_pretty_cpus();
|
||||
u32 threads = pretty_cpus.first;
|
||||
std::string cpus = pretty_cpus.second;
|
||||
|
||||
fmt::format_to(std::back_inserter(result), "CPUs: {}\n{} Threads",
|
||||
cpus, threads);
|
||||
|
||||
FILE* f = std::fopen(CPUINFO_PATH, "r");
|
||||
if (!f) return Common::Android::ToJString(env, result);
|
||||
|
||||
char buf[512];
|
||||
|
||||
if (f) {
|
||||
std::set<std::string> feature_set;
|
||||
while (std::fgets(buf, sizeof(buf), f)) {
|
||||
std::string line(buf);
|
||||
if (line.find("Features") == 0 || line.find("features") == 0) {
|
||||
auto pos = line.find(':');
|
||||
if (pos != std::string::npos) {
|
||||
std::string feat_line = line.substr(pos + 1);
|
||||
trim(feat_line);
|
||||
|
||||
std::istringstream iss(feat_line);
|
||||
std::string feature;
|
||||
while (iss >> feature) {
|
||||
feature_set.insert(to_lower(feature));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::fclose(f);
|
||||
|
||||
bool has_neon = feature_set.count("neon") || feature_set.count("asimd");
|
||||
bool has_fp = feature_set.count("fp") || feature_set.count("vfp");
|
||||
bool has_sve = feature_set.count("sve");
|
||||
bool has_sve2 = feature_set.count("sve2");
|
||||
bool has_crypto = feature_set.count("aes") || feature_set.count("sha1") ||
|
||||
feature_set.count("sha2") || feature_set.count("pmull");
|
||||
bool has_dotprod = feature_set.count("asimddp") || feature_set.count("dotprod");
|
||||
bool has_i8mm = feature_set.count("i8mm");
|
||||
bool has_bf16 = feature_set.count("bf16");
|
||||
bool has_atomics = feature_set.count("atomics") || feature_set.count("lse");
|
||||
|
||||
std::string features;
|
||||
if (has_neon || has_fp) {
|
||||
features += "NEON";
|
||||
if (has_dotprod) features += "+DP";
|
||||
if (has_i8mm) features += "+I8MM";
|
||||
if (has_bf16) features += "+BF16";
|
||||
}
|
||||
|
||||
if (has_sve) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "SVE";
|
||||
if (has_sve2) features += "2";
|
||||
}
|
||||
|
||||
if (has_crypto) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "Crypto";
|
||||
}
|
||||
|
||||
if (has_atomics) {
|
||||
if (!features.empty()) features += " | ";
|
||||
features += "LSE";
|
||||
}
|
||||
|
||||
if (!features.empty()) {
|
||||
result += "\nFeatures: " + features;
|
||||
}
|
||||
}
|
||||
|
||||
fmt::format_to(std::back_inserter(result), "\nLLVM CPU: {}", fallback_cpu_detection());
|
||||
|
||||
return Common::Android::ToJString(env, result);
|
||||
} catch (...) {
|
||||
return Common::Android::ToJString(env, "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
constexpr u32 VENDOR_QUALCOMM = 0x5143;
|
||||
constexpr u32 VENDOR_ARM = 0x13B5;
|
||||
|
||||
VkPhysicalDeviceProperties GetVulkanDeviceProperties() {
|
||||
Common::DynamicLibrary library;
|
||||
if (!library.Open("libvulkan.so")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
// TODO: warn the user that Vulkan is unavailable rather than hard crash
|
||||
const auto instance = Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
|
||||
const auto physical_devices = instance.EnumeratePhysicalDevices();
|
||||
if (physical_devices.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const Vulkan::vk::PhysicalDevice physical_device(physical_devices[0], dld);
|
||||
return physical_device.GetProperties();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanDriverVersion(JNIEnv* env, jobject jobj) {
|
||||
try {
|
||||
const auto props = GetVulkanDeviceProperties();
|
||||
if (props.deviceID == 0) {
|
||||
return Common::Android::ToJString(env, "N/A");
|
||||
}
|
||||
|
||||
const u32 driver_version = props.driverVersion;
|
||||
const u32 vendor_id = props.vendorID;
|
||||
|
||||
if (driver_version == 0) {
|
||||
return Common::Android::ToJString(env, "N/A");
|
||||
}
|
||||
|
||||
std::string version_str;
|
||||
|
||||
if (vendor_id == VENDOR_QUALCOMM) {
|
||||
const u32 major = (driver_version >> 24) << 2;
|
||||
const u32 minor = (driver_version >> 12) & 0xFFF;
|
||||
const u32 patch = driver_version & 0xFFF;
|
||||
version_str = fmt::format("{}.{}.{}", major, minor, patch);
|
||||
}
|
||||
else if (vendor_id == VENDOR_ARM) {
|
||||
u32 major = VK_API_VERSION_MAJOR(driver_version);
|
||||
u32 minor = VK_API_VERSION_MINOR(driver_version);
|
||||
u32 patch = VK_API_VERSION_PATCH(driver_version);
|
||||
|
||||
// ARM custom encoding for newer drivers
|
||||
if (major > 10) {
|
||||
major = (driver_version >> 22) & 0x3FF;
|
||||
minor = (driver_version >> 12) & 0x3FF;
|
||||
patch = driver_version & 0xFFF;
|
||||
}
|
||||
version_str = fmt::format("{}.{}.{}", major, minor, patch);
|
||||
}
|
||||
// Standard Vulkan version encoding for other vendors
|
||||
else {
|
||||
const u32 major = VK_API_VERSION_MAJOR(driver_version);
|
||||
const u32 minor = VK_API_VERSION_MINOR(driver_version);
|
||||
const u32 patch = VK_API_VERSION_PATCH(driver_version);
|
||||
version_str = fmt::format("{}.{}.{}", major, minor, patch);
|
||||
}
|
||||
|
||||
return Common::Android::ToJString(env, version_str);
|
||||
} catch (...) {
|
||||
return Common::Android::ToJString(env, "N/A");
|
||||
}
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanApiVersion(JNIEnv* env, jobject jobj) {
|
||||
try {
|
||||
const auto props = GetVulkanDeviceProperties();
|
||||
if (props.deviceID == 0) {
|
||||
return Common::Android::ToJString(env, "N/A");
|
||||
}
|
||||
|
||||
const u32 api_version = props.apiVersion;
|
||||
const u32 major = VK_API_VERSION_MAJOR(api_version);
|
||||
const u32 minor = VK_API_VERSION_MINOR(api_version);
|
||||
const u32 patch = VK_API_VERSION_PATCH(api_version);
|
||||
const u32 variant = VK_API_VERSION_VARIANT(api_version);
|
||||
|
||||
// Include variant if non-zero (rare on Android)
|
||||
const std::string version_str = variant > 0
|
||||
? fmt::format("{}.{}.{}.{}", variant, major, minor, patch)
|
||||
: fmt::format("{}.{}.{}", major, minor, patch);
|
||||
|
||||
return Common::Android::ToJString(env, version_str);
|
||||
} catch (...) {
|
||||
return Common::Android::ToJString(env, "N/A");
|
||||
}
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuModel(JNIEnv* env, jobject jobj) {
|
||||
const auto props = GetVulkanDeviceProperties();
|
||||
if (props.deviceID == 0) {
|
||||
return Common::Android::ToJString(env, "Unknown");
|
||||
}
|
||||
|
||||
return Common::Android::ToJString(env, props.deviceName);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||
EmulationSession::GetInstance().System().ApplySettings();
|
||||
EmulationSession::GetInstance().System().HIDCore().ReloadInputDevices();
|
||||
@@ -1193,6 +1613,43 @@ JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
|
||||
env->ReleaseStringUTFChars(version, version_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||
JNIEnv* env,
|
||||
jobject obj,
|
||||
jstring version,
|
||||
jstring packageId) {
|
||||
const char* version_str = env->GetStringUTFChars(version, nullptr);
|
||||
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
|
||||
|
||||
std::string variant;
|
||||
std::string package_id(package_id_str);
|
||||
|
||||
if (package_id.find("dev.legacy.eden_emulator") != std::string::npos) {
|
||||
variant = "legacy";
|
||||
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
|
||||
variant = "optimized";
|
||||
} else {
|
||||
variant = "standard";
|
||||
}
|
||||
|
||||
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", version_str, variant);
|
||||
const std::string url = fmt::format("{}/{}/releases/download/{}/{}",
|
||||
std::string{Common::g_build_auto_update_website},
|
||||
std::string{Common::g_build_auto_update_repo},
|
||||
version_str,
|
||||
apk_filename);
|
||||
|
||||
env->ReleaseStringUTFChars(version, version_str);
|
||||
env->ReleaseStringUTFChars(packageId, package_id_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
|
||||
JNIEnv* env,
|
||||
[[maybe_unused]] jobject obj) {
|
||||
return env->NewStringUTF(Common::g_build_version);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 104 KiB |