Compare commits

..

33 Commits

Author SHA1 Message Date
crueter f0a4ac7359 [frontend] only set https on Android (#3733)
Otherwise it breaks the shit out of every other platform.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3733
2026-03-15 02:57:32 +01:00
Eden CI d604a9da7b [dist, android] Update translations from Transifex for Mar 14 (#3732)
Automatic translation update for Mar 14

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3732
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: Eden CI <ci@eden-emu.dev>
Co-committed-by: Eden CI <ci@eden-emu.dev>
2026-03-15 02:52:34 +01:00
lizzie 0ec60f21ff Revert "[updater] Explicit set https for api url (#3720)" (#3726)
#3720 was merged without proper revision.
AAAA
It's fine, if there are issues just merge the revert of.

This reverts commit 8de1dd151f.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3726
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-15 02:06:29 +01:00
crueter 9cf1e19d98 [externals] Update SDL2 (#3731)
https://github.com/libsdl-org/SDL/pull/15207

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3731
2026-03-15 01:45:11 +01:00
John 98a93561de Revert #3690 to fix regression. (#3728)
- Fixes Ender Magnolia and Demon Slayer freezing on Android.

May be related to this PR or other fixes on the master but:
- Using Cure in Ender Magnolia no longer crashes the game.
- Fixes Demon Slayer vertex explosion on Android.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3728
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: John <john@eden-emu.dev>
Co-committed-by: John <john@eden-emu.dev>
2026-03-14 19:37:24 +01:00
xbzk 4a60085a76 [android,settings] fix for *_combined (fastmem, gpu unswizzle) kotlin side only settings being unnecesarily requested to native (#3724)
Fixes jni reporting error when these settings change.
Combined settings are the grouping reference for settings like gpu unswizzle.
As far as i recall, fastmen only uses it coz it has a sub setting in the past.

[ 445.450666] Frontend <Error> main/jni/native_config.cpp:33:getSetting: [Android Native] Could not find setting - gpu_unswizzle_combined
[ 450.054470] Frontend <Error> main/jni/native_config.cpp:33:getSetting: [Android Native] Could not find setting - fastmem_combined

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3724
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-14 03:30:37 +01:00
PavelBARABANOV 47ed86d3e2 [vk] Partial return of the old buffer update logic (#3690)
Fixes shadows in Metroid Prime Remastered

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3690
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
Co-committed-by: PavelBARABANOV <pavelbarabanov94@gmail.com>
2026-03-13 19:59:11 +01:00
xbzk 2aea7f9584 [android,settings] addons: fix crash when launching Install addons from per game addons menu (#3727)
to fix navigation issues (PR3699), i've bounded all orphaned fragments to SettingsSubscreenActivity.
(IIRC that were tested, whatever) Somehow the launching from per-game settings (where addons fragment is bounded to MainActivity got broken).
This PR just made ContentTypeSelectionDialogFragment self-sufficient so it lives under both MainActivity and SettingsSubscreenActivity.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3727
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-13 19:46:35 +01:00
lizzie 59b0e66722 Revert "[core/memory] remove indirection handling for unaligned access (#3584)" (#3725)
This reverts commit 2d27359074.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3725
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-13 19:09:53 +01:00
DraVee 8de1dd151f [updater] Explicit set https for api url (#3720)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3720
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: DraVee <chimera@dravee.dev>
Co-committed-by: DraVee <chimera@dravee.dev>
2026-03-13 17:55:38 +01:00
DraVee 98604d369a [vulkan] Revert "[vulkan]fix vuid 02751 (#3573)" (#3721)
Attached backtrace on PR comments

This reverts commit cdf9b556b2.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3721
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: DraVee <chimera@dravee.dev>
Co-committed-by: DraVee <chimera@dravee.dev>
2026-03-13 02:47:47 +01:00
lizzie 4337135910 [common/logging] fix android stupid ctor() init order not initializing logging first (#3719)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3719
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-12 23:16:56 +01:00
lizzie 395613b01f [common/logging] Simplify logging logic and fix issues when logging before system is created (#3688)
- our logging code was bigger than spdlog itself, why???? just keep it simple
- fix issues when logging before logging system is even started
- removes the "initialized logging twice" issue
- removes uneeded indirection in file logging
- uses direct formatting instead of jumping hoopla-around the fmt::format() ressult
- code duplication and dead code removal as usual

I did explore dup2() but I think it's not worth the hassle
I did try `fwopen()` but it's better if things are just kept as-is.

there is a lot of noise because I removed a bunch of redundant files on logging and just put everything in one file

now normally this wouldn't be a good idea, however consider: the complexity of logging; it's less than 500 lines... does it really need a whole subsystem?!?!?! ITS JUST LOGGING

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3688
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-12 18:29:15 +01:00
xbzk 2896fa3835 [android,ui] chore: settings subscreens transition and other minor conformances (#3699)
- Fix black screen in transition animations

- Adjustments to about fragment
made about text more label and less button like, header transparency, spacing adjustments, word Contributors replaced by People in Contributors field for de-duplication.

- installable actions code de-duplication
Extracted install/update/import firmware/user data flows into InstallableActions.kt and reused it from MainActivity and InstallableFragment, reducing duplicated logic, ensuring single source of truth.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3699
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-11 22:47:16 +01:00
lizzie 0dad29698e [frontend] allow to specify input profile name for first player on command line (#3684)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3684
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-11 16:50:00 +01:00
lizzie 5a0780b826 [video_core] Properly disable/avoid building OpenGL when it's disabled (#3692)
- OpenGL symbols would still be included in builds without OpenGL, this pr fixes that
- Same goes for Vulkan, but now with `ENABLE_VULKAN`
- Add support to have OpenGL-only builds (why would you do this?)
- Add support for headless runs (yes you could just select NULL backend, but why not compile it headless? :)

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3692
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-11 16:49:29 +01:00
lizzie d35fc7b7ee [docs] testing guidelines, unify controller guide, gamemode (#3709)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3709
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-11 16:49:03 +01:00
crueter 8678cb06eb [meta] clang-format literally all of the Qt code (#3706)
I'm tired of dealing with this tbh

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3706
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-03-10 06:51:08 +01:00
crueter 769edbfea3 [video_core] Revert "Simplify TextureCache GC and remove redundant code" (#3652) (#3704)
regr. Steam Deck

Please, for the love of God, stop saying "YOLO good to merge" after
testers report performance regressions (and promptly get brushed to the
side). Seriously, what the hell?

This reverts commit f8ea09fa0f.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3704
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: DraVee <chimera@dravee.dev>
2026-03-10 05:44:51 +01:00
crueter 0ff1d215c8 [desktop] Port some QtCommon changes from QML branch (#3703)
- Linker now resolves implementation differences
- Remove unneeded ifdefs
- Better abstractions overall

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3703
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-03-10 05:37:45 +01:00
crueter 07e3a2aa46 [settings] Disable fastmem on Linux systems with non-4kb page sizes (#3669)
Asahi, etc

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3669
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-03-10 05:36:12 +01:00
xbzk a1b50e9339 [android] patches bin button + version bug fixes (#3691)
This fixed the delete button enabled for external content (which is auto handled and the proper way to get rid of them is either by removing its folder from ext content list, or removing the file itself) by streaming patch source thru jni.

Along the way stumbled upon another bug: If you have an external content update installed (say latest version for example) and you NAND install a previous update (like in silksong's hard mode update), the newest update version string would leak to the previous one.

Did videos for both. Fixed both. Seems good to go.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3691
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: DraVee <chimera@dravee.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2026-03-09 00:30:10 +01:00
crueter f5e2b1fb13 [dynarmic] Remove incorrect LICENSE (#3698)
Our dynarmic is GPLv3, not BSD.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3698
2026-03-08 23:22:51 +01:00
lizzie 6693b99ae4 [core] coalesce tracking entries for GPU (#3677)
I think I may have attempted this before, but I doubt it.

Anyways this should reduce virtual buffers from 3 to just 1, also improved access times :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3677
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-08 22:45:38 +01:00
MaranBr f8ea09fa0f [video_core] Simplify TextureCache GC and remove redundant code (#3652)
This enhances the garbage collection in TextureCache to make it more responsive and reliable during long gameplay sessions.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3652
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2026-03-08 22:45:35 +01:00
smiRaphi 361e6209b2 [settings] Add back & properly implement use_dev_keys (#3631)
Was removed recently but also wasn't really working before, this adds it to the debug UI (under the kiosk option) and also makes it properly reload the keys on launch & setting change.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3631
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: smiRaphi <neogt404@gmail.com>
Co-committed-by: smiRaphi <neogt404@gmail.com>
2026-03-08 22:37:20 +01:00
crueter 3d1a67af18 [externals] Update dependencies (#3664)
* zlib: 1.3.1.2 -> 1.3.2
* vulkan-validation-layers: 1.4.335.0 -> 1.4.341.0
* sirit: 1.0.3 -> 1.0.4
* httplib: 0.35.0 -> 0.37.0
* xbyak: 7.33.3 -> 7.35.2
* catch2: 3.12.0 -> 3.13.0
* vulkan-headers: 1.4.342 -> 1.4.345
* vulkan-utility-libraries: 1.4.342 -> 1.4.345

Also fixed a build error with newer xbyak.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3664
2026-03-08 22:33:51 +01:00
crueter c7b23f4a1a [time] fix: guard timezone out buffer logging (#3668)
Original text, per the emailed patch:

---------------

Hello,

I am submitting a small fix to prevent an unintended abort when _GLIBCXX_ASSERTIONS is enabled, caused by out-of-bounds access in debug logging.
Background / Issue

In the server-side implementations of ITimeZoneService::ToPosixTime and ToPosixTimeWithMyRule, the SCOPE_EXIT debug logging previously accessed out_times[0] and out_times[1] unconditionally.

However, out_times is an IPC-provided output buffer (OutArray, which inherits from std::span). Its length depends on the caller-provided buffer capacity. During debugging, I encountered a case where out_times.size() == 1.

Under _GLIBCXX_ASSERTIONS, accessing out_times[1] triggers a std::span::operator[] assertion failure (std::__glibcxx_assert_fail) and aborts the process, causing the service thread to crash. This results in an unintended crash caused solely by debug logging.
Change Description

In the SCOPE_EXIT logging blocks of both ToPosixTime and ToPosixTimeWithMyRule, I added bounds checks before accessing out_times[0] and out_times[1]:

    Access out_times[0] only if out_times.size() > 0

    Access out_times[1] only if out_times.size() > 1

    Print 0 when the corresponding element is unavailable

This change only affects debug log output. It does not modify IPC semantics or the time conversion logic itself.
Reproduction Context (for reference)

I encountered this issue while running 13 Sentinels: Aegis Rim (title ID: 01008D7016438000). During the “Load Game” flow, ToPosixTimeWithMyRule is invoked with an out_times buffer of length 1, which previously led to the out-of-bounds access in the logging code.

Thank you for your time and review.

Best regards,
darkpaper

Environment: Arch Linux / KDE / X11

This email and the accompanying patch were prepared with assistance from
an LLM.

Authored-by: darkpaper <lirunzhou2021@gamil.com>
Signed-off-by: crueter <crueter@eden-emu.dev>

Co-authored-by: darkpaper <lirunzhou2021@gamil.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3668
2026-03-08 20:53:37 +01:00
lizzie 38aa2bc5e1 [hle/services] use ankerl:: for Service's function handlers map, use const char* instead of std::string{} (#3671)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3671
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-08 20:50:29 +01:00
lizzie 1864160326 [qt] remove 'None' interface since net code already uses first avail interface anyways, disable changing ifaces at runtime (#3683)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3683
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-08 20:49:17 +01:00
lizzie 0a169dec4d [qt] fix crash when having an invalid graphics backend (#3693)
closely related to #3692, however the crash may also occur when sharing configs between macOS and a system that had OpenGL

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3693
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-08 20:48:47 +01:00
wildcard 80bafc8fe8 [vulkan] Fix incorrect offset application for Array2D textures (#3696)
Earlier we were taking the coords and adding them to coords instead of actually taking offsets and adding it to coord

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3696
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: wildcard <wildcard@eden-emu.dev>
Co-committed-by: wildcard <wildcard@eden-emu.dev>
2026-03-08 20:48:19 +01:00
lizzie f2c46eadc1 [ports] build fixes for *BSD make (#3496)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3496
Reviewed-by: DraVee <chimera@dravee.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2026-03-08 19:32:24 +01:00
526 changed files with 53870 additions and 39732 deletions
+2 -2
View File
@@ -115,7 +115,7 @@ for file in $FILES; do
*.cmake|*.sh|*CMakeLists.txt)
begin="#"
;;
*.kt*|*.cpp|*.h)
*.kt*|*.cpp|*.h|*.qml)
begin="//"
;;
*)
@@ -193,7 +193,7 @@ if [ "$UPDATE" = "true" ]; then
begin="#"
shell=true
;;
*.kt*|*.cpp|*.h)
*)
begin="//"
shell="false"
;;
+89
View File
@@ -0,0 +1,89 @@
From 509be32bbfa6eb95014860f7c9ea6d45c8ddaa56 Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Sun, 8 Mar 2026 15:11:12 -0400
Subject: [PATCH] [cmake] Simplify zstd find logic, and support pre-existing
zstd target
Some deduplication work on the zstd required/if-available logic. Also
adds support for pre-existing `zstd::libzstd` which is useful for
projects that bundle their own zstd in a way that doesn't get caught by
`CONFIG`
Signed-off-by: crueter <crueter@eden-emu.dev>
---
CMakeLists.txt | 46 ++++++++++++++++++++++++++--------------------
1 file changed, 26 insertions(+), 20 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1874e36be0..8d31198006 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -241,28 +241,34 @@ endif()
# NOTE:
# zstd < 1.5.6 does not provide the CMake imported target `zstd::libzstd`.
# Older versions must be consumed via their pkg-config file.
-if(HTTPLIB_REQUIRE_ZSTD)
- find_package(zstd 1.5.6 CONFIG)
- if(NOT zstd_FOUND)
- find_package(PkgConfig REQUIRED)
- pkg_check_modules(zstd REQUIRED IMPORTED_TARGET libzstd)
- add_library(zstd::libzstd ALIAS PkgConfig::zstd)
- endif()
- set(HTTPLIB_IS_USING_ZSTD TRUE)
-elseif(HTTPLIB_USE_ZSTD_IF_AVAILABLE)
- find_package(zstd 1.5.6 CONFIG QUIET)
- if(NOT zstd_FOUND)
- find_package(PkgConfig QUIET)
- if(PKG_CONFIG_FOUND)
- pkg_check_modules(zstd QUIET IMPORTED_TARGET libzstd)
-
- if(TARGET PkgConfig::zstd)
+if (HTTPLIB_REQUIRE_ZSTD)
+ set(HTTPLIB_ZSTD_REQUESTED ON)
+ set(HTTPLIB_ZSTD_REQUIRED REQUIRED)
+elseif (HTTPLIB_USE_ZSTD_IF_AVAILABLE)
+ set(HTTPLIB_ZSTD_REQUESTED ON)
+ set(HTTPLIB_ZSTD_REQUIRED QUIET)
+endif()
+
+if (HTTPLIB_ZSTD_REQUESTED)
+ if (TARGET zstd::libzstd)
+ set(HTTPLIB_IS_USING_ZSTD TRUE)
+ else()
+ find_package(zstd 1.5.6 CONFIG QUIET)
+
+ if (NOT zstd_FOUND)
+ find_package(PkgConfig ${HTTPLIB_ZSTD_REQUIRED})
+ pkg_check_modules(zstd ${HTTPLIB_ZSTD_REQUIRED} IMPORTED_TARGET libzstd)
+
+ if (TARGET PkgConfig::zstd)
add_library(zstd::libzstd ALIAS PkgConfig::zstd)
endif()
endif()
+
+ # This will always be true if zstd is required.
+ # If zstd *isn't* found when zstd is set to required,
+ # CMake will error out earlier in this block.
+ set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND})
endif()
- # Both find_package and PkgConf set a XXX_FOUND var
- set(HTTPLIB_IS_USING_ZSTD ${zstd_FOUND})
endif()
# Used for default, common dirs that the end-user can change (if needed)
@@ -317,13 +323,13 @@ if(HTTPLIB_COMPILE)
$<BUILD_INTERFACE:${_httplib_build_includedir}/httplib.h>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/httplib.h>
)
-
+
# Add C++20 module support if requested
# Include from separate file to prevent parse errors on older CMake versions
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.28")
include(cmake/modules.cmake)
endif()
-
+
set_target_properties(${PROJECT_NAME}
PROPERTIES
VERSION ${${PROJECT_NAME}_VERSION}
+1 -1
View File
@@ -196,7 +196,7 @@ option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR PLATFORM_FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT WIN32 OR NOT ARCHITECTURE_arm64" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
+4 -4
View File
@@ -46,9 +46,9 @@
"package": "ZLIB",
"repo": "madler/zlib",
"tag": "v%VERSION%",
"hash": "06eaa3a1eaaeb31f461a2283b03a91ed8eb2406e62cd97ea1c69836324909edeecd93edd03ff0bf593d9dde223e3376149134c5b1fe2e8688c258cadf8cd60ff",
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
"version": "1.2",
"git_version": "1.3.1.2",
"git_version": "1.3.2",
"options": [
"ZLIB_BUILD_SHARED OFF",
"ZLIB_INSTALL OFF"
@@ -98,9 +98,9 @@
"package": "VVL",
"repo": "KhronosGroup/Vulkan-ValidationLayers",
"tag": "vulkan-sdk-%VERSION%",
"git_version": "1.4.335.0",
"git_version": "1.4.341.0",
"artifact": "android-binaries-%VERSION%.zip",
"hash": "48167c4a17736301bd08f9290f41830443e1f18cce8ad867fc6f289b49e18b40e93c9850b377951af82f51b5b6d7313aa6a884fc5df79f5ce3df82696c1c1244"
"hash": "8812ae84cbe49e6a3418ade9c458d3be6d74a3dffd319d4502007b564d580998056e8190414368ec11b27bc83993c7a0dad713c31bcc3d9553b51243efee3753"
},
"quazip": {
"package": "QuaZip-Qt6",
+1742 -1242
View File
File diff suppressed because it is too large Load Diff
+1843 -1350
View File
File diff suppressed because it is too large Load Diff
+1693 -1202
View File
File diff suppressed because it is too large Load Diff
+1692 -1201
View File
File diff suppressed because it is too large Load Diff
+1827 -1328
View File
File diff suppressed because it is too large Load Diff
+1692 -1201
View File
File diff suppressed because it is too large Load Diff
+2299 -1768
View File
File diff suppressed because it is too large Load Diff
+1692 -1201
View File
File diff suppressed because it is too large Load Diff
+1698 -1208
View File
File diff suppressed because it is too large Load Diff
+1671 -1180
View File
File diff suppressed because it is too large Load Diff
+1692 -1201
View File
File diff suppressed because it is too large Load Diff
+1750 -1247
View File
File diff suppressed because it is too large Load Diff
+1734 -1240
View File
File diff suppressed because it is too large Load Diff
+1666 -1175
View File
File diff suppressed because it is too large Load Diff
+2230 -1738
View File
File diff suppressed because it is too large Load Diff
+1667 -1177
View File
File diff suppressed because it is too large Load Diff
+1675 -1185
View File
File diff suppressed because it is too large Load Diff
+1827 -1311
View File
File diff suppressed because it is too large Load Diff
+1671 -1180
View File
File diff suppressed because it is too large Load Diff
+2265 -1720
View File
File diff suppressed because it is too large Load Diff
+1685 -1184
View File
File diff suppressed because it is too large Load Diff
+1669 -1179
View File
File diff suppressed because it is too large Load Diff
+1724 -1223
View File
File diff suppressed because it is too large Load Diff
+1666 -1175
View File
File diff suppressed because it is too large Load Diff
+1666 -1175
View File
File diff suppressed because it is too large Load Diff
+1710 -1211
View File
File diff suppressed because it is too large Load Diff
+1672 -1182
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -91,7 +91,7 @@ After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up.
If clang has errors, try using `g++-11`.
If clang has errors, try using `g++11`.
## FreeBSD
@@ -107,6 +107,8 @@ hw.usb.usbhid.enable="0"
## NetBSD
2026-02-07: `vulkan-headers` must not be installed, since the version found in `pkgsrc` is older than required. Either wait for binary packages to update or build newer versions from source.
Install `pkgin` if not already `pkg_add pkgin`, see also the general [pkgsrc guide](https://www.netbsd.org/docs/pkgsrc/using.html). For NetBSD 10.1 provide `echo 'PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/"' >/etc/pkg_install.conf`. If `pkgin` is taking too much time consider adding the following to `/etc/rc.conf`:
```sh
@@ -116,7 +118,7 @@ ip6addrctl_policy=ipv4_prefer
System provides a default `g++-10` which doesn't support the current C++ codebase; install `clang-19` with `pkgin install clang-19`. Or install `gcc14` (or `gcc15` with current pkgsrc). Provided that, the following CMake commands may work:
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -Bbuild` (Recommended)
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc14/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc14/bin/g++ -Bbuild`
- `cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/pkg/gcc15/bin/gcc -DCMAKE_CXX_COMPILER=/usr/pkg/gcc15/bin/g++ -Bbuild`
@@ -138,8 +140,12 @@ cmake --install build
However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version.
`QtCore` on NetBSD is included, but due to misconfigurations(!) we MUST include one of the standard headers that include `bits/c++config.h`, since source_location (required by `QtCore`) isn't properly configured to intake `bits/c++config.h` (none of the experimental library is). This is a bug with NetBSD packaging and not our fault, but alas.
## DragonFlyBSD
2026-02-07: `vulkan-headers` and `vulkan-utility-libraries` must NOT be uninstalled, since they're too old: `1.3.289`. Either wait for binary packages to update or build newer versions from source.
If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt:
```sh
+2 -2
View File
@@ -70,8 +70,8 @@ These options control executables and build flavors.
The following options are desktop only.
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input frontend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics frontend
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend
- Unavailable on Windows/ARM64
- You probably shouldn't turn this off.
+3 -3
View File
@@ -1,6 +1,6 @@
# Setting a Custom Date/Time in Eden
Use this guide whenever you want to modify the Date or Time that Eden reports to games. This can be useful for modifying RNG elements, skipping wait times in games, etc.
Use this guide whenever you want to modify the Date or Time that Eden reports to games. This can be useful for modifying RNG elements, skipping wait times in games, etc.
**Click [Here](https://evilperson1337.notion.site/Setting-a-Custom-Date-Time-in-Eden-2b357c2edaf680acb8d4e63ccc126564) for a version of this guide with images & visual elements.**
@@ -16,5 +16,5 @@ Use this guide whenever you want to modify the Date or Time that Eden reports to
1. Navigate to *Emulation → Configure*.
2. Click on the **System** item on the left-hand side navigation, then check the *Custom RTC Date* box.
3. The Date/Time option now becomes editable. Set it to the value you want and hit **OK**.
4. GREAT SCOTT! We have time traveled! You can of course go forward or backward in time (as long as it is not before the year 1970) and your game should update accordingly (e.g. certain *Super Mario Odyssey* moons that take time for flowers to grow will now be fully grown.).
3. The Date/Time option now becomes editable. Set it to the value you want and hit **OK**.
4. GREAT SCOTT! We have time traveled! You can of course go forward or backward in time (as long as it is not before the year 1970) and your game should update accordingly (e.g. certain *Super Mario Odyssey* moons that take time for flowers to grow will now be fully grown.).
+9
View File
@@ -16,6 +16,15 @@ The CPU must support FMA for an optimal gameplay experience. The GPU needs to su
If your GPU doesn't support or is just behind by a minor version, see Mesa environment variables below (*nix only).
## Releases and versions
- Stable releases/Versioned releases: Has a version number and it's the versions we expect 3rd party repositories to host (package managers and such), these are, well, stable, have low amount of regressions (wrt. to master and nightlies) and generally focus on "keeping things without regressions", recommended for the average user.
- RC releases: Release candidate, generally "less stable but still stable" versions.
- Full release: "The stablest possible you could get".
- Nightly: Builds done around 2PM UTC (if there are any changes), generally stable, but not recommended for the average user. These contain daily updates and may contain critical fixes for some games.
- Master: Unstable builds, can lead from a game working exceptionally fine to absolute crashing in some systems because someone forgot to check if NixOS or Solaris worked. These contain straight from the oven fixes, please don't use them unless you plan to contribute something! They're very experimental! Still 95% of the time it will work just fine.
- PR builds: Highly experimental builds, testers may grab from these. The average user should treat them the same as master builds, except sometimes they straight up don't build/work.
## User configuration
### Configuration directories
+2
View File
@@ -7,6 +7,7 @@ There are two main applications, an SDL2 based app (`eden-cli`) and a Qt based a
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `-f`: Use fullscreen.
- `-u <number>`: Select the index of the user to load as.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `-qlaunch`: Launch QLaunch.
- `-setup`: Launch setup applet.
@@ -20,3 +21,4 @@ There are two main applications, an SDL2 based app (`eden-cli`) and a Qt based a
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
-49
View File
@@ -1,49 +0,0 @@
# Configuring Controller Profiles
Use this guide for when you want to configure specific controller settings to be reused.
**Click [Here](https://evilperson1337.notion.site/Configuring-Controller-Profiles-2be57c2edaf680eabc3ac8c333ec75c4) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden Set Up and Configured
---
### Steps
1. Launch Eden and wait for it to load.
2. Navigate to *Emulation > Configure…*
3. Select **Controls** from the left-hand menu and configure your controller for the way you want it to be in game.
4. Select **New** and enter a name for the profile in the box that appears. Press **OK** to save the profile settings.
5. Select **OK** to close the settings menu.
## Setting Controller Profiles By Game
Use this guide when you want to set up specific controller profiles for specific games. This can be useful for certain games like *Captain Toad Treasure Tracker* where a blue dot appears in the middle of the screen when you have docked mode enabled, but not handheld mode.
**Click [Here](https://evilperson1337.notion.site/Setting-Controller-Profiles-By-Game-2b057c2edaf681658a57f0c199cb6083) for a version of this guide with images & visual elements.**
---
### Pre-Requisites
- Eden Emulator set up and fully configured
- Controller Profile Created
- See [*Configuring Controller Profiles*](./ControllerProfiles.md) for instructions on how to do this if needed.
---
### Steps
1. *Right-Click* the game you want to apply the profile to in the main window and select **Properties.**
2. Navigate to the **Input Profiles** tab in the window that appears. Drop down on *Player 1 profile* (or whatever player profile you want to apply it to) and select the profile you want.
<aside>
***NOTE***: You may have to resize the window to see all tabs, or press the arrows by the tabs to see **Input Profiles**.
</aside>
1. Click **OK** to apply the profile mapping.
2. Launch the game and confirm that the profile is applied, regardless of what the global configuration is.
+65
View File
@@ -0,0 +1,65 @@
# User Handbook - Controllers
Most of the controls should work out of the box. If not, please use a joystick calibrator to ensure it's not an issue with your own controller, for example:
- https://github.com/dkosmari/calibrate-joystick
## Using external controllers on the Steamdeck
In desktop mode ignore your pro controller/xbox contoller external controller and use **Steam Virtual Gamepad 0 as Player 1**. If you have multiple external controllers set **Player 2 to Steam Virtual Gamepad 1**. Steam app must not be closed on desktop mode.
Here's the annoying part of it. When waking up the steam deck from sleep try not to touch any button on the Steamdeck and turn on your external controller. Then open the Eden.AppImage. If you're lucky you can get your external controller to be position 0 and also Steam Virtual Gamepad 0 in desktop mode. If not that is ok too unless you need to configure player 1 to have gyro. You might need to repeat this to get your external controller as Steam Virtual Gamepad 0 so you can config Player 1 having gyro. You might be able to config player 1 to have gyro with the Steamdeck itself. Or you can also config player 1, 2, 3, etc, to have gyro somehow. Make sure they are all using Virtual Gamepads though.
Turn off controller then go to gaming mode. Try not to touch any buttons on the physical Steamdeck. When in gaming mode turn on the external controller. If lucky it will be assigned as Steam Virtual Gamepad 0. If not just use steam Gamemode feature to rearrange controller positions order.
Basically the Steamdeck or the external controller is fighting for position 0 and it depends on what is touched first after waking from sleep.
## Configuring Controller Profiles
Use this guide for when you want to configure specific controller settings to be reused.
**Click [Here](https://evilperson1337.notion.site/Configuring-Controller-Profiles-2be57c2edaf680eabc3ac8c333ec75c4) for a version of this guide with images & visual elements.**
---
#### Pre-Requisites
- Eden Set Up and Configured
---
#### Steps
1. Launch Eden and wait for it to load.
2. Navigate to *Emulation > Configure…*
3. Select **Controls** from the left-hand menu and configure your controller for the way you want it to be in game.
4. Select **New** and enter a name for the profile in the box that appears. Press **OK** to save the profile settings.
5. Select **OK** to close the settings menu.
### Setting Controller Profiles By Game
Use this guide when you want to set up specific controller profiles for specific games. This can be useful for certain games like *Captain Toad Treasure Tracker* where a blue dot appears in the middle of the screen when you have docked mode enabled, but not handheld mode.
**Click [Here](https://evilperson1337.notion.site/Setting-Controller-Profiles-By-Game-2b057c2edaf681658a57f0c199cb6083) for a version of this guide with images & visual elements.**
---
#### Pre-Requisites
- Eden Emulator set up and fully configured
- Controller Profile Created
- See [*Configuring Controller Profiles*](./ControllerProfiles.md) for instructions on how to do this if needed.
---
#### Steps
1. *Right-Click* the game you want to apply the profile to in the main window and select **Properties.**
2. Navigate to the **Input Profiles** tab in the window that appears. Drop down on *Player 1 profile* (or whatever player profile you want to apply it to) and select the profile you want.
<aside>
***NOTE***: You may have to resize the window to see all tabs, or press the arrows by the tabs to see **Input Profiles**.
</aside>
1. Click **OK** to apply the profile mapping.
2. Launch the game and confirm that the profile is applied, regardless of what the global configuration is.
+5 -3
View File
@@ -11,10 +11,12 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
- **[The Basics](Basics.md)**
- **[Quickstart](./QuickStart.md)**
- **[Settings](./Settings.md)**
- **[Installing Mods](./Mods.md)**
- **[Run On macOS](./RunOnMacOS.md)**
- **[Controllers](./Controllers.md)**
- **[Controller profiles](./Controllers.md#configuring-controller-profiles)**
- **[Audio](Audio.md)**
- **[Graphics](Graphics.md)**
- **[Installing Mods](./Mods.md)**
- **[Run On macOS](./RunOnMacOS.md)**
- **[Data, Savefiles and Storage](Storage.md)**
- **[Orphaned Profiles](Orphaned.md)**
- **[Troubleshooting](./Troubleshoot.md)**
@@ -23,7 +25,6 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
- **[Importing Saves](./ImportingSaves.md)**
- **[Installing Atmosphere Mods](./InstallingAtmosphereMods.md)**
- **[Installing Updates & DLCs](./InstallingUpdatesDLC.md)**
- **[Controller Profiles](./ControllerProfiles.md)**
- **[Alter Date & Time](./AlterDateTime.md)**
## 3rd-party Integration
@@ -35,6 +36,7 @@ A copy of this handbook is [available online](https://git.eden-emu.dev/eden-emu/
- **[Obtainium](./ThirdParty.md#configuring-obtainium)**
- **[ES-DE](./ThirdParty.md#configuring-es-de)**
- **[Mirrors](./ThirdParty.md#mirrors)**
- **[GameMode](./ThirdParty.md#configuring-gamemode)**
## Advanced
+12 -4
View File
@@ -1,4 +1,12 @@
# Allowing Eden to Run on MacOS
# User Handbook - Run on macOS
Current macOS support is still experimental and very reliant on MoltenVK developments, plans have shifted to properly provide support for KosmicKrisp and similar new GPU endeavours, but macOS users still are bound to MoltenVK itself.
Users of macOS may wish to use [Asahi Linux](https://wiki.gentoo.org/wiki/Project:Asahi/Guide) for the rising KosmicKrisp support.
As of writing, neither macOS nor Asahi has support for NCE; additionally Asahi has extraneous paging bugs with fastmem.
## Allowing Eden to Run on MacOS
Use this guide when you need to allow Eden to run on a Mac system, but are being blocked by Apple Security policy.
@@ -6,19 +14,19 @@ Use this guide when you need to allow Eden to run on a Mac system, but are being
---
### Pre-Requisites
#### Pre-Requisites
- Permissions to modify settings in MacOS
---
## Why am I Seeing This?
### Why am I Seeing This?
Recent versions of MacOS (Catalina & newer) introduced the **Gatekeeper** security functionality, requiring software to be signed by Apple or a trusted (aka - paying) developer. If the signature isnt on the list of trusted ones, it will stop the program from executing and display the message above.
---
## Steps
### Steps
1. Open the *System Settings* panel.
2. Navigate to *Privacy & Security*.
+1 -2
View File
@@ -50,5 +50,4 @@ See also [an extended breakdown of some options](./Graphics.md).
## Controls
Most of the controls should work out of the box. If not, please use a joystick calibrator to ensure it's not an issue with your own controller, for example:
- https://github.com/dkosmari/calibrate-joystick
See [controllers](./Controllers.md).
+80 -20
View File
@@ -1,39 +1,99 @@
# User Handbook - Testing
While this is mainly aimed for testers - normal users can benefit from these guidelines to make their life easier when trying to outline and/or report an issue.
## Getting logs
In order to get more information, you can find logs in the following location:
## How to Test a PR Against the Based Master When Issues Arise
# Testing
When you're testing a pull request (PR) and encounter unexpected behavior, it's important to determine whether the issue was introduced by the PR or if it already exists in the base code. To do this, compare the behavior against the based master branch.
Even before an issue occurs, it is best practice to keep the same settings and delete the shader cache. Using an already made shader cache can make the PR look like it is having a regression in some rare cases.
### What to Do When Something Seems Off
Try not to test PRs which are for documentation or extremely trivial changes (like a PR that changes the app icon), unless you really want to; generally avoid any PRs marked `[docs]`.
If a PR specifies it is for a given platform (i.e `linux`) then just test on Linux. If it says `NCE` then test on Android and Linux ARM64 (Raspberry Pi and such). macOS fixes may also affect Asahi, test that if you can too.
You may also build artifacts yourself, be aware that the resulting builds are NOT the same as those from CI, because of package versioning and build environment differences. One famous example is FFmpeg randomly breaking on many Arch distros due to packaging differences.
## Quickstart
Think of the source code as a "tree", with the "trunk" of that tree being our `master` branch, any other branches are PRs or separate development branches, only our stable releases pull from `master` - all other branches are considered unstable and aren't recommended to pull from unless you're testing multiple branches at once.
Here's some terminology you may want to familiarize yourself with:
- PR: Pull request, a change in the codebase; from which the author of said change (the programmer) requests a pull of that branch into master (make it so the new code makes it into a release basically).
- Bisect: Bilinear method of searching regressions, some regressions may be sporadic and can't be bisected, but the overwhelming majority are.
- WIP: Work-in-progress.
- Regression: A new bug/glitch caused by new code, i.e "Zelda broke in android after commit xyz".
- Master: The "root" branch, this is where all merged code goes to, traditionally called `main`, `trunk` or just `master`, it contains all the code that eventually make it to stable releases.
- `HEAD`: Latest commit in a given branch, `HEAD` of `master` is the latest commit on branch `master`.
- `origin`: The default "remote", basically the URL from where git is located at, for most of the time that location is https://git.eden-emu.dev/eden-emu/eden.
## Testing checklist
For regressions/bugs from PRs or commits:
- [ ] Occurs in master?
- If it occurs on master:
- [ ] Occurs on previous stable release? (before this particular PR).
- If it occurs on previous stable release:
- [ ] Occurs on previous-previous stable release?
- And so on and so forth... some bugs come from way before Eden was even conceived.
- Otherwise, try bisecting between the previous stable release AND the latest `HEAD` of master
- [ ] Occurs in given commit?
- [ ] Occurs in PR?
- If it occurs on PR:
- [ ] Bisected PR? (if it has commits)
- [ ] Found bisected commit?
If an issue sporadically appears, try to do multiple runs, try if possible, to count the number of times it has failed and the number of times it has "worked just fine"; say it worked 3 times but failed 1. then there is a 1/4th chance every run that the issue is replicated - so every bisect step would require 4 runs to ensure there is atleast a chance of triggering the bug.
## What to do when something seems off
If you notice something odd during testing:
- Reproduce the issue using the based master branch.
- Observe whether the same behavior occurs.
### Two Possible Outcomes
From there onwards there can be two possible outcomes:
- If the issue exists in the based master: This means the problem was already present before the PR. The PR most likely did not introduce the regression.
- If the issue does not exist in the based master: This suggests the PR most likely introduced the regression and needs further investigation.
### Report your findings
## Reporting Your Findings
When you report your results:
- Clearly state whether the behavior was observed in the based master.
- Indicate whether the result is good (expected behavior) or bad (unexpected or broken behavior). Without mentioning if your post/report/log is good or bad it may confuse the Developer of the PR.
- Example:
```
1. "Tested on based master — issue not present. Bad result for PR, likely regression introduced."
2. "Tested on based master issue already present. Good result for PR, not a regression."
```
- Indicate whether the result is good (expected behavior) or bad (unexpected or broken behavior). Without mentioning if your post/report/log is good or bad it may confuse the developer of the PR.
For example:
1. "Bad result for PR: Tested on based master - issue not present. Likely regression introduced."
2. "Good result for PR: Tested on based master - issue already present. Not a regression."
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently.
If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
If a master build for the PR' based master does not exist. It will be helpful to just test past and future builds nearby. That would help with gathering more information about the problem.
**Always include [debugging info](../Debug.md) as needed**.
**Always include [debugging info](../Debug.md) as needed**.
## Bisecting
One happy reminder, when testing, *know how to bisect!*
Say you're trying to find an issue between 1st of Jan and 8th of Jan, you can search by dividing "in half" the time between each commit:
- Check for 4th of Jan
- If 4th of Jan is "working" then the issue must be in the future
- So then check 6th of Jan
- If 6th of Jan isn't working then the issue must be in the past
- So then check 5th of Jan
- If 5th of Jan worked, then the issue starts at 6th of Jan
The faulty commit then, is 6th of Jan. This is called bisection https://git-scm.com/docs/git-bisect
## Notes
- PR's marked with **WIP** do NOT need to be tested unless explicitly asked (check the git in case)
- Sometimes license checks may fail, hover over the build icon to see if builds did succeed, as the CI will push builds even if license checks fail.
- All open PRs can be viewed [here](https://git.eden-emu.dev/eden-emu/eden/pulls/).
- If site is down use one of the [mirrors](./user/ThirdParty.md#mirrors).
+6 -1
View File
@@ -4,7 +4,6 @@ The Eden emulator by itself lacks some functionality - or otherwise requires ext
While most of the links mentioned in this guide are relatively "safe"; we urge users to use their due diligence and appropriatedly verify the integrity of all files downloaded and ensure they're not compromised.
- [Nightly Eden builds](https://github.com/pflyly/eden-nightly)
- [NixOS Eden Flake](https://github.com/Grantimatter/eden-flake)
- [ES-DE Frontend Support](https://github.com/GlazedBelmont/es-de-android-custom-systems)
@@ -66,3 +65,9 @@ Note: Even though the site isn't Codeberg, it uses the same Forgejo/Gitea backen
```xml
<command label="Eden (Standalone)">%EMULATOR_EDEN% %ACTION%=android.nfc.action.TECH_DISCOVERED %DATA%=%ROMPROVIDER%</command>
```
## Configuring GameMode
There is a checkbox to enable gamemode automatically. The `libgamemode.so` library must be findable on the standard `LD_LIBRARY_PATH` otherwise it will not properly be enabled. If for whatever reason it doesn't work, see [Arch wiki: GameMode](https://wiki.archlinux.org/title/GameMode) for more info.
You may launch the emulator directly via the wrapper `gamemode <program>`, and things should work out of the box.
+14 -13
View File
@@ -9,7 +9,7 @@
},
"sirit": {
"repo": "eden-emulator/sirit",
"git_version": "1.0.3",
"git_version": "1.0.4",
"tag": "v%VERSION%",
"artifact": "sirit-source-%VERSION%.tar.zst",
"hash_suffix": "sha512sum",
@@ -28,11 +28,12 @@
"httplib": {
"repo": "yhirose/cpp-httplib",
"tag": "v%VERSION%",
"hash": "a229e24cca4afe78e5c0aa2e482f15108ac34101fd8dbd927365f15e8c37dec4de38c5277d635017d692a5b320e1b929f8bfcc076f52b8e4dcdab8fe53bfdf2e",
"git_version": "0.30.1",
"hash": "5efa8140aadffe105dcf39935b732476e95755f6c7473ada3d0b64df2bc02c557633ae3948a25b45e1cf67e89a3ff6329fb30362e4ac033b9a1d1e453aa2eded",
"git_version": "0.37.0",
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch"
"0001-mingw.patch",
"0002-fix-zstd.patch"
],
"options": [
"HTTPLIB_REQUIRE_OPENSSL ON"
@@ -55,8 +56,8 @@
"package": "xbyak",
"repo": "herumi/xbyak",
"tag": "v%VERSION%",
"hash": "ac333d7bea1d61865bebebb116201a58db431946aa2f11aa042ef5795c390ff30af4d6c90ed3b3d24443a1d430703b08f14fc13b2fa405c155a241456ed78a47",
"git_version": "7.33.2"
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
"git_version": "7.35.2"
},
"oaknut": {
"repo": "eden-emulator/oaknut",
@@ -139,16 +140,16 @@
"package": "SDL2",
"name": "SDL2",
"repo": "crueter-ci/SDL2",
"version": "2.32.10-cf5dabd6ea",
"version": "2.32.10-3c28e8ecc0",
"min_version": "2.26.4"
},
"catch2": {
"package": "Catch2",
"repo": "catchorg/Catch2",
"tag": "v%VERSION%",
"hash": "acb3f463a7404d6a3bce52e474075cdadf9bb241d93feaf147c182d756e5a2f8bd412f4658ca186d15ab8fed36fc587d79ec311f55642d8e4ded16df9e213656",
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
"version": "3.0.1",
"git_version": "3.12.0",
"git_version": "3.13.0",
"patches": [
"0001-solaris-isnan-fix.patch"
]
@@ -256,15 +257,15 @@
"repo": "KhronosGroup/Vulkan-Headers",
"package": "VulkanHeaders",
"version": "1.4.317",
"hash": "26e0ad8fa34ab65a91ca62ddc54cc4410d209a94f64f2817dcdb8061dc621539a4262eab6387e9b9aa421db3dbf2cf8e2a4b041b696d0d03746bae1f25191272",
"git_version": "1.4.342",
"hash": "d2846ea228415772645eea4b52a9efd33e6a563043dd3de059e798be6391a8f0ca089f455ae420ff22574939ed0f48ed7c6ff3d5a9987d5231dbf3b3f89b484b",
"git_version": "1.4.345",
"tag": "v%VERSION%"
},
"vulkan-utility-libraries": {
"repo": "KhronosGroup/Vulkan-Utility-Libraries",
"package": "VulkanUtilityLibraries",
"hash": "8147370f964fd82c315d6bb89adeda30186098427bf3efaa641d36282d42a263f31e96e4586bfd7ae0410ff015379c19aa4512ba160630444d3d8553afd1ec14",
"git_version": "1.4.342",
"hash": "114f6b237a6dcba923ccc576befb5dea3f1c9b3a30de7dc741f234a831d1c2d52d8a224afb37dd57dffca67ac0df461eaaab6a5ab5e503b393f91c166680c3e1",
"git_version": "1.4.345",
"tag": "v%VERSION%"
},
"frozen": {
+1 -1
View File
@@ -1,5 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -241,7 +242,6 @@ if (YUZU_CMD)
endif()
if (ENABLE_QT)
add_definitions(-DYUZU_QT_WIDGETS)
add_subdirectory(qt_common)
add_subdirectory(yuzu)
endif()
@@ -73,6 +73,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:theme="@style/Theme.Yuzu.Main"
android:label="@string/preferences_settings"/>
<activity
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreenActivity"
android:theme="@style/Theme.Yuzu.Main"
android:label="@string/preferences_settings"/>
<activity
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
android:theme="@style/Theme.Yuzu.Main"
@@ -40,11 +40,21 @@ class AddonAdapter(val addonViewModel: AddonViewModel) :
}
}
val deleteAction = {
addonViewModel.setAddonToDelete(model)
val canDelete = model.isRemovable
binding.deleteCard.isEnabled = canDelete
binding.buttonDelete.isEnabled = canDelete
binding.deleteCard.alpha = if (canDelete) 1f else 0.38f
if (canDelete) {
val deleteAction = {
addonViewModel.setAddonToDelete(model)
}
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
} else {
binding.deleteCard.setOnClickListener(null)
binding.buttonDelete.setOnClickListener(null)
}
binding.deleteCard.setOnClickListener { deleteAction() }
binding.buttonDelete.setOnClickListener { deleteAction() }
}
}
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -6,10 +9,10 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.os.bundleOf
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
@@ -67,8 +70,13 @@ class AppletAdapter(val activity: FragmentActivity, applets: List<Applet>) :
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
binding.root.findNavController().navigate(action)
binding.root.findNavController().navigate(
R.id.action_global_emulationActivity,
bundleOf(
"game" to appletGame,
"custom" to false
)
)
}
}
}
@@ -31,7 +31,6 @@ import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding
import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import java.util.Locale
class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
@@ -48,7 +47,6 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
binding = DialogLobbyBrowserBinding.inflate(layoutInflater)
setContentView(binding.root)
BackgroundHelper.applyBackground(binding.backgroundLogo, context)
binding.emptyRefreshButton.setOnClickListener {
binding.progressBar.visibility = View.VISIBLE
@@ -105,14 +105,6 @@ object Settings {
const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
const val PREF_STATIC_THEME_COLOR = "StaticThemeColor"
// App background preference keys
const val PREF_HOME_BACKGROUND_STYLE = "HomeBackgroundStyle"
const val HOME_BACKGROUND_STYLE_NONE = 0
const val HOME_BACKGROUND_STYLE_EDEN = 1
const val HOME_BACKGROUND_STYLE_DEFAULT = HOME_BACKGROUND_STYLE_NONE
const val PREF_HOME_BACKGROUND_ALPHA = "HomeBackgroundAlpha"
const val HOME_BACKGROUND_ALPHA_DEFAULT = 40
enum class EmulationOrientation(val int: Int) {
Unspecified(0),
SensorLandscape(5),
@@ -27,6 +27,20 @@ class GpuUnswizzleSetting(
override val isSaveable = true
override val isRuntimeModifiable = true
override val isSwitchable = true
override val pairedSettingKey: String = ""
override var global: Boolean
get() {
return BooleanSetting.GPU_UNSWIZZLE_ENABLED.global &&
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global &&
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global &&
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global
}
set(value) {
BooleanSetting.GPU_UNSWIZZLE_ENABLED.global = value
IntSetting.GPU_UNSWIZZLE_TEXTURE_SIZE.global = value
IntSetting.GPU_UNSWIZZLE_STREAM_SIZE.global = value
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.global = value
}
override fun getValueAsString(needsGlobal: Boolean): String = "combined"
override fun reset() {
BooleanSetting.GPU_UNSWIZZLE_ENABLED.reset()
@@ -72,4 +86,4 @@ class GpuUnswizzleSetting(
IntSetting.GPU_UNSWIZZLE_CHUNK_SIZE.setInt(value)
fun reset() = setting.reset()
}
}
@@ -989,6 +989,7 @@ abstract class SettingsItem(
override val isRuntimeModifiable: Boolean = false
override val defaultValue: Boolean = true
override val isSwitchable: Boolean = true
override val pairedSettingKey: String = ""
override var global: Boolean
get() {
return BooleanSetting.FASTMEM.global &&
@@ -111,18 +111,10 @@ class SettingsActivity : AppCompatActivity() {
if (navHostFragment.childFragmentManager.backStackEntryCount > 0) {
navHostFragment.navController.popBackStack()
} else {
finishWithFragmentLikeAnimation()
finish()
}
}
private fun finishWithFragmentLikeAnimation() {
finish()
overridePendingTransition(
androidx.navigation.ui.R.anim.nav_default_pop_enter_anim,
androidx.navigation.ui.R.anim.nav_default_pop_exit_anim
)
}
override fun onStart() {
super.onStart()
if (!DirectoryInitialization.areDirectoriesReady) {
@@ -178,7 +170,7 @@ class SettingsActivity : AppCompatActivity() {
getString(R.string.settings_reset),
Toast.LENGTH_LONG
).show()
finishWithFragmentLikeAnimation()
finish()
}
private fun setInsets() {
@@ -33,7 +33,6 @@ import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.view.PathSetting
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.PathUtil
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.*
@@ -179,17 +178,9 @@ class SettingsFragment : Fragment() {
}
presenter.onViewCreated()
applyBackgroundPreference()
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun getPlayerIndex(): Int =
private fun getPlayerIndex(): Int =
when (args.menuTag) {
Settings.MenuTag.SECTION_INPUT_PLAYER_ONE -> 0
Settings.MenuTag.SECTION_INPUT_PLAYER_TWO -> 1
@@ -241,10 +232,6 @@ class SettingsFragment : Fragment() {
}
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun hasAllFilesPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
@@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import androidx.fragment.app.FragmentActivity
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -1072,26 +1071,6 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.theme_and_color))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntriesA12,
valuesId = R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntries,
valuesId = R.array.themeValues
)
)
}
val themeMode: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
override fun setInt(value: Int) {
@@ -1122,6 +1101,26 @@ class SettingsFragmentPresenter(
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntriesA12,
valuesId = R.array.themeValuesA12
)
)
} else {
add(
SingleChoiceSetting(
theme,
titleId = R.string.change_app_theme,
choicesId = R.array.themeEntries,
valuesId = R.array.themeValues
)
)
}
val staticThemeColor: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
IntSetting.STATIC_THEME_COLOR.getInt(needsGlobal)
@@ -1188,79 +1187,6 @@ class SettingsFragmentPresenter(
)
)
val backgroundStyleSetting: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
BackgroundHelper.getBackgroundStyle(context)
override fun setInt(value: Int) {
BackgroundHelper.setBackgroundStyle(context, value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_HOME_BACKGROUND_STYLE
override val isRuntimeModifiable: Boolean = true
override val pairedSettingKey: String = ""
override val isSwitchable: Boolean = false
override var global: Boolean = true
override val isSaveable: Boolean = true
override val defaultValue: Int = Settings.HOME_BACKGROUND_STYLE_DEFAULT
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() {
setInt(defaultValue)
}
}
add(
SingleChoiceSetting(
backgroundStyleSetting,
titleId = R.string.home_background,
descriptionId = R.string.home_background_description,
choicesId = R.array.homeBackgroundEntries,
valuesId = R.array.homeBackgroundValues
)
)
val backgroundAlphaSetting: AbstractIntSetting = object : AbstractIntSetting {
override fun getInt(needsGlobal: Boolean): Int =
(BackgroundHelper.getBackgroundAlpha(context) * 100).toInt()
override fun setInt(value: Int) {
BackgroundHelper.setBackgroundAlpha(context, value)
settingsViewModel.setShouldRecreate(true)
}
override val key: String = Settings.PREF_HOME_BACKGROUND_ALPHA
override val isRuntimeModifiable: Boolean = true
override val pairedSettingKey: String = ""
override val isSwitchable: Boolean = false
override var global: Boolean = true
override val isSaveable: Boolean = true
override val defaultValue: Int = Settings.HOME_BACKGROUND_ALPHA_DEFAULT
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() {
setInt(defaultValue)
}
}
if (BackgroundHelper.getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE) {
add(
SliderSetting(
backgroundAlphaSetting,
titleId = R.string.home_background_opacity,
descriptionId = R.string.home_background_opacity_description,
min = 0,
max = 100,
units = "%"
)
)
}
add(HeaderSetting(R.string.buttons))
add(BooleanSetting.ENABLE_FOLDER_BUTTON.key)
add(BooleanSetting.ENABLE_QLAUNCH_BUTTON.key)
@@ -0,0 +1,152 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.navArgs
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.InsetsHelper
import org.yuzu.yuzu_emu.utils.ThemeHelper
enum class SettingsSubscreen {
PROFILE_MANAGER,
DRIVER_MANAGER,
DRIVER_FETCHER,
FREEDRENO_SETTINGS,
APPLET_LAUNCHER,
INSTALLABLE,
GAME_FOLDERS,
ABOUT,
LICENSES,
GAME_INFO,
ADDONS,
}
class SettingsSubscreenActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsBinding
private val args by navArgs<SettingsSubscreenActivityArgs>()
override fun attachBaseContext(base: Context) {
super.attachBaseContext(YuzuApplication.applyLanguage(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
if (savedInstanceState == null) {
val navController = navHostFragment.navController
val navGraph = navController.navInflater.inflate(
R.navigation.settings_subscreen_navigation
)
navGraph.setStartDestination(resolveStartDestination())
navController.setGraph(navGraph, createStartDestinationArgs())
}
WindowCompat.setDecorFitsSystemWindows(window, false)
if (InsetsHelper.getSystemGestureType(applicationContext) !=
InsetsHelper.GESTURE_NAVIGATION
) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
binding.navigationBarShade,
com.google.android.material.R.attr.colorSurface
),
ThemeHelper.SYSTEM_BAR_ALPHA
)
)
}
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() = navigateBack()
}
)
setInsets()
}
override fun onStart() {
super.onStart()
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
}
fun navigateBack() {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
if (!navHostFragment.navController.popBackStack()) {
finish()
}
}
private fun resolveStartDestination(): Int =
when (args.destination) {
SettingsSubscreen.PROFILE_MANAGER -> R.id.profileManagerFragment
SettingsSubscreen.DRIVER_MANAGER -> R.id.driverManagerFragment
SettingsSubscreen.DRIVER_FETCHER -> R.id.driverFetcherFragment
SettingsSubscreen.FREEDRENO_SETTINGS -> R.id.freedrenoSettingsFragment
SettingsSubscreen.APPLET_LAUNCHER -> R.id.appletLauncherFragment
SettingsSubscreen.INSTALLABLE -> R.id.installableFragment
SettingsSubscreen.GAME_FOLDERS -> R.id.gameFoldersFragment
SettingsSubscreen.ABOUT -> R.id.aboutFragment
SettingsSubscreen.LICENSES -> R.id.licensesFragment
SettingsSubscreen.GAME_INFO -> R.id.gameInfoFragment
SettingsSubscreen.ADDONS -> R.id.addonsFragment
}
private fun createStartDestinationArgs(): Bundle =
when (args.destination) {
SettingsSubscreen.DRIVER_MANAGER,
SettingsSubscreen.FREEDRENO_SETTINGS -> bundleOf("game" to args.game)
SettingsSubscreen.GAME_INFO,
SettingsSubscreen.ADDONS -> bundleOf(
"game" to requireNotNull(args.game) {
"Game is required for ${args.destination}"
}
)
else -> Bundle()
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(
binding.navigationBarShade
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
mlpNavShade.height = barInsets.bottom
binding.navigationBarShade.layoutParams = mlpNavShade
windowInsets
}
}
}
@@ -21,11 +21,11 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.NativeLibrary
@@ -54,10 +54,8 @@ class AboutFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarAbout.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
binding.imageLogo.setOnLongClickListener {
@@ -75,8 +73,11 @@ class AboutFragment : Fragment() {
)
}
binding.buttonLicenses.setOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.LICENSES,
null
)
binding.root.findNavController().navigate(action)
}
val buildName = getString(R.string.app_name_suffixed)
@@ -108,11 +109,6 @@ class AboutFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent)
@@ -135,8 +131,4 @@ class AboutFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -15,7 +15,6 @@ import androidx.core.view.updatePadding
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
@@ -25,7 +24,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentAddonsBinding
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.AddonUtil
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
@@ -60,10 +58,9 @@ class AddonsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.toolbarAddons.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
binding.toolbarAddons.title = getString(R.string.addons_game, args.game.title)
@@ -124,7 +121,6 @@ class AddonsFragment : Fragment() {
override fun onResume() {
super.onResume()
addonViewModel.onAddonsViewStarted(args.game)
applyBackgroundPreference()
}
override fun onDestroy() {
@@ -205,8 +201,4 @@ class AddonsFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -12,7 +12,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
@@ -21,7 +20,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class AppletLauncherFragment : Fragment() {
@@ -49,10 +47,9 @@ class AppletLauncherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarApplets.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
val applets = listOf(
@@ -93,11 +90,6 @@ class AppletLauncherFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -115,8 +107,4 @@ class AppletLauncherFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -5,15 +8,18 @@ package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.DialogInterface
import android.net.Uri
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.InstallableActions
class ContentTypeSelectionDialogFragment : DialogFragment() {
private val addonViewModel: AddonViewModel by activityViewModels()
@@ -23,6 +29,52 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
private var selectedItem = 0
private val installGameUpdateLauncher =
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { documents ->
if (documents.isEmpty()) {
return@registerForActivityResult
}
val game = addonViewModel.game
if (game == null) {
installContent(documents)
return@registerForActivityResult
}
ProgressDialogFragment.newInstance(
requireActivity(),
R.string.verifying_content,
false
) { _, _ ->
var updatesMatchProgram = true
for (document in documents) {
val valid = NativeLibrary.doesUpdateMatchProgram(
game.programId,
document.toString()
)
if (!valid) {
updatesMatchProgram = false
break
}
}
requireActivity().runOnUiThread {
if (updatesMatchProgram) {
installContent(documents)
} else {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description,
positiveAction = { installContent(documents) },
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
return@newInstance Any()
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val launchOptions =
arrayOf(getString(R.string.updates_and_dlc), getString(R.string.mods_and_cheats))
@@ -31,12 +83,11 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
selectedItem = savedInstanceState.getInt(SELECTED_ITEM)
}
val mainActivity = requireActivity() as MainActivity
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.select_content_type)
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
when (selectedItem) {
0 -> mainActivity.installGameUpdate.launch(arrayOf("*/*"))
0 -> installGameUpdateLauncher.launch(arrayOf("*/*"))
else -> {
if (!preferences.getBoolean(MOD_NOTICE_SEEN, false)) {
preferences.edit().putBoolean(MOD_NOTICE_SEEN, true).apply()
@@ -47,7 +98,7 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
}
}
}
.setSingleChoiceItems(launchOptions, 0) { _: DialogInterface, i: Int ->
.setSingleChoiceItems(launchOptions, selectedItem) { _: DialogInterface, i: Int ->
selectedItem = i
}
.setNegativeButton(android.R.string.cancel, null)
@@ -65,4 +116,13 @@ class ContentTypeSelectionDialogFragment : DialogFragment() {
private const val SELECTED_ITEM = "SelectedItem"
private const val MOD_NOTICE_SEEN = "ModNoticeSeen"
}
private fun installContent(documents: List<Uri>) {
InstallableActions.installContent(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel,
documents = documents
)
}
}
@@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
@@ -30,7 +29,6 @@ import org.yuzu.yuzu_emu.databinding.FragmentDriverFetcherBinding
import org.yuzu.yuzu_emu.features.fetcher.DriverGroupAdapter
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import java.io.IOException
@@ -142,10 +140,8 @@ class DriverFetcherFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
binding.listDrivers.layoutManager = LinearLayoutManager(context)
@@ -157,11 +153,6 @@ class DriverFetcherFragment : Fragment() {
fetchDrivers()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun fetchDrivers() {
binding.loadingIndicator.isVisible = true
@@ -250,10 +241,6 @@ class DriverFetcherFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
data class Artifact(val url: URL, val name: String)
data class Release(
@@ -19,6 +19,7 @@ import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.HomeNavigationDirections
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -27,13 +28,13 @@ import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.File
@@ -67,7 +68,6 @@ class DriverManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
@@ -107,7 +107,7 @@ class DriverManagerFragment : Fragment() {
}
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
binding.buttonInstall.setOnClickListener {
@@ -115,9 +115,11 @@ class DriverManagerFragment : Fragment() {
}
binding.buttonFetch.setOnClickListener {
binding.root.findNavController().navigate(
R.id.action_driverManagerFragment_to_driverFetcherFragment
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.DRIVER_FETCHER,
null
)
binding.root.findNavController().navigate(action)
}
binding.listDrivers.apply {
@@ -140,11 +142,6 @@ class DriverManagerFragment : Fragment() {
driverViewModel.onCloseDriverManager(args.game)
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -263,8 +260,4 @@ class DriverManagerFragment : Fragment() {
.setCancelable(false)
.show()
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -20,7 +20,6 @@ import org.yuzu.yuzu_emu.adapters.FreedrenoPresetAdapter
import org.yuzu.yuzu_emu.adapters.FreedrenoVariableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFreedrenoSettingsBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.FreedrenoPresets
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -69,7 +68,6 @@ class FreedrenoSettingsFragment : Fragment() {
setupAdapters()
loadCurrentSettings()
setupButtonListeners()
applyBackgroundPreference()
setupWindowInsets()
}
@@ -195,17 +193,7 @@ class FreedrenoSettingsFragment : Fragment() {
insets
}
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun showSnackbar(message: String) {
private fun showSnackbar(message: String) {
Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
}
@@ -4,20 +4,21 @@
package org.yuzu.yuzu_emu.fragments
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.FolderAdapter
import org.yuzu.yuzu_emu.databinding.FragmentFoldersBinding
@@ -25,8 +26,6 @@ import org.yuzu.yuzu_emu.model.DirectoryType
import org.yuzu.yuzu_emu.model.GameDir
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
@@ -37,6 +36,20 @@ class GameFoldersFragment : Fragment() {
private val homeViewModel: HomeViewModel by activityViewModels()
private val gamesViewModel: GamesViewModel by activityViewModels()
private val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
processGamesDir(result)
}
}
private val getExternalContentDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result != null) {
processExternalContentDir(result)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
@@ -58,10 +71,9 @@ class GameFoldersFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarFolders.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
binding.listFolders.apply {
@@ -76,7 +88,6 @@ class GameFoldersFragment : Fragment() {
(binding.listFolders.adapter as FolderAdapter).submitList(it)
}
val mainActivity = requireActivity() as MainActivity
binding.buttonAdd.setOnClickListener {
// Show a model to choose between Game and External Content
val options = arrayOf(
@@ -89,10 +100,10 @@ class GameFoldersFragment : Fragment() {
.setItems(options) { _, which ->
when (which) {
0 -> { // Game Folder
mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
}
1 -> { // External Content Folder
mainActivity.getExternalContentDirectory.launch(null)
getExternalContentDirectory.launch(null)
}
}
}
@@ -102,16 +113,55 @@ class GameFoldersFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
override fun onStop() {
super.onStop()
gamesViewModel.onCloseGameFoldersFragment()
}
private fun processGamesDir(result: Uri) {
requireContext().contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val uriString = result.toString()
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
if (folder != null) {
Toast.makeText(
requireContext().applicationContext,
R.string.folder_already_added,
Toast.LENGTH_SHORT
).show()
return
}
AddGameFolderDialogFragment.newInstance(uriString, calledFromGameFragment = false)
.show(parentFragmentManager, AddGameFolderDialogFragment.TAG)
}
private fun processExternalContentDir(result: Uri) {
requireContext().contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val uriString = result.toString()
val folder = gamesViewModel.folders.value.firstOrNull {
it.uriString == uriString && it.type == DirectoryType.EXTERNAL_CONTENT
}
if (folder != null) {
Toast.makeText(
requireContext().applicationContext,
R.string.folder_already_added,
Toast.LENGTH_SHORT
).show()
return
}
val externalContentDir = GameDir(uriString, deepScan = false, DirectoryType.EXTERNAL_CONTENT)
gamesViewModel.addFolder(externalContentDir, savedFromGameFragment = false)
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -140,8 +190,4 @@ class GameFoldersFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -18,7 +18,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.NativeLibrary
@@ -26,7 +25,6 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentGameInfoBinding
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.GameMetadata
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
@@ -61,12 +59,11 @@ class GameInfoFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(false)
applyBackgroundPreference()
binding.apply {
toolbarInfo.title = args.game.title
toolbarInfo.setNavigationOnClickListener {
view.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
val pathString = Uri.parse(args.game.path).path ?: ""
@@ -145,11 +142,6 @@ class GameInfoFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun copyToClipboard(label: String, body: String) {
val clipBoard =
requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@@ -182,8 +174,4 @@ class GameInfoFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -35,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.GamePropertiesAdapter
import org.yuzu.yuzu_emu.databinding.FragmentGamePropertiesBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GameProperty
import org.yuzu.yuzu_emu.model.GamesViewModel
@@ -48,7 +49,6 @@ import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
@@ -84,7 +84,6 @@ class GamePropertiesFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true)
applyBackgroundPreference()
binding.buttonBack.setOnClickListener {
view.findNavController().popBackStack()
@@ -252,8 +251,10 @@ class GamePropertiesFragment : Fragment() {
R.string.info_description,
R.drawable.ic_info_outline,
action = {
val action = GamePropertiesFragmentDirections
.actionPerGamePropertiesFragmentToGameInfoFragment(args.game)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.GAME_INFO,
args.game
)
binding.root.findNavController().navigate(action)
}
)
@@ -319,8 +320,11 @@ class GamePropertiesFragment : Fragment() {
R.string.add_ons_description,
R.drawable.ic_edit,
action = {
val action = GamePropertiesFragmentDirections
.actionPerGamePropertiesFragmentToAddonsFragment(args.game)
val action =
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.ADDONS,
args.game
)
binding.root.findNavController().navigate(action)
}
)
@@ -335,8 +339,11 @@ class GamePropertiesFragment : Fragment() {
R.drawable.ic_build,
detailsFlow = driverViewModel.selectedDriverTitle,
action = {
val action = GamePropertiesFragmentDirections
.actionPerGamePropertiesFragmentToDriverManagerFragment(args.game)
val action =
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.DRIVER_MANAGER,
args.game
)
binding.root.findNavController().navigate(action)
}
)
@@ -349,8 +356,11 @@ class GamePropertiesFragment : Fragment() {
R.string.freedreno_per_game_description,
R.drawable.ic_graphics,
action = {
val action = GamePropertiesFragmentDirections
.actionPerGamePropertiesFragmentToFreedrenoSettingsFragment(args.game)
val action =
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.FREEDRENO_SETTINGS,
args.game
)
binding.root.findNavController().navigate(action)
}
)
@@ -489,7 +499,6 @@ class GamePropertiesFragment : Fragment() {
override fun onResume() {
super.onResume()
applyBackgroundPreference()
driverViewModel.updateDriverNameForGame(args.game)
getPlayTime()
reloadList()
@@ -714,8 +723,4 @@ class GamePropertiesFragment : Fragment() {
).show()
}
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -36,11 +36,11 @@ import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsSubscreen
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
@@ -77,7 +77,6 @@ class HomeSettingsFragment : Fragment() {
findNavController().popBackStack()
}
binding.toolbarHomeSettings.title = getString(R.string.preferences_settings)
applyBackgroundPreference()
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
add(
@@ -128,8 +127,11 @@ class HomeSettingsFragment : Fragment() {
R.string.profile_manager_description,
R.drawable.ic_account_circle,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_profileManagerFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.PROFILE_MANAGER,
null
)
binding.root.findNavController().navigate(action)
}
)
)
@@ -139,8 +141,10 @@ class HomeSettingsFragment : Fragment() {
R.string.install_gpu_driver_description,
R.drawable.ic_build,
{
val action = HomeSettingsFragmentDirections
.actionHomeSettingsFragmentToDriverManagerFragment(null)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.DRIVER_MANAGER,
null
)
binding.root.findNavController().navigate(action)
},
{ true },
@@ -156,7 +160,12 @@ class HomeSettingsFragment : Fragment() {
R.string.gpu_driver_settings,
R.drawable.ic_graphics,
{
binding.root.findNavController().navigate(R.id.freedrenoSettingsFragment)
val action =
HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.FREEDRENO_SETTINGS,
null
)
binding.root.findNavController().navigate(action)
}
)
)
@@ -177,8 +186,11 @@ class HomeSettingsFragment : Fragment() {
R.string.applets_description,
R.drawable.ic_applet,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.APPLET_LAUNCHER,
null
)
binding.root.findNavController().navigate(action)
},
{ NativeLibrary.isFirmwareAvailable() },
R.string.applets_error_firmware,
@@ -191,8 +203,11 @@ class HomeSettingsFragment : Fragment() {
R.string.manage_yuzu_data_description,
R.drawable.ic_install,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_installableFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.INSTALLABLE,
null
)
binding.root.findNavController().navigate(action)
}
)
)
@@ -202,8 +217,11 @@ class HomeSettingsFragment : Fragment() {
R.string.select_games_folder_description,
R.drawable.ic_add,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_gameFoldersFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.GAME_FOLDERS,
null
)
binding.root.findNavController().navigate(action)
}
)
)
@@ -286,9 +304,11 @@ class HomeSettingsFragment : Fragment() {
R.string.about_description,
R.drawable.ic_info_outline,
{
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
parentFragmentManager.primaryNavigationFragment?.findNavController()
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
val action = HomeNavigationDirections.actionGlobalSettingsSubscreenActivity(
SettingsSubscreen.ABOUT,
null
)
binding.root.findNavController().navigate(action)
}
)
)
@@ -317,7 +337,6 @@ class HomeSettingsFragment : Fragment() {
override fun onResume() {
super.onResume()
driverViewModel.updateDriverNameForGame(null)
applyBackgroundPreference()
}
override fun onDestroyView() {
@@ -503,8 +522,4 @@ class HomeSettingsFragment : Fragment() {
windowInsets
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.logoImage, requireContext())
}
}
@@ -14,25 +14,24 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.Installable
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.InstallableActions
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.BufferedOutputStream
@@ -46,6 +45,9 @@ class InstallableFragment : Fragment() {
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val gamesViewModel: GamesViewModel by activityViewModels()
private val addonViewModel: AddonViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -66,13 +68,10 @@ class InstallableFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mainActivity = requireActivity() as MainActivity
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarInstallables.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
@@ -86,8 +85,8 @@ class InstallableFragment : Fragment() {
Installable(
R.string.user_data,
R.string.user_data_description,
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
export = { mainActivity.exportUserData.launch("export.zip") }
install = { importUserDataLauncher.launch(arrayOf("application/zip")) },
export = { exportUserDataLauncher.launch("export.zip") }
),
Installable(
R.string.manage_save_data,
@@ -129,27 +128,33 @@ class InstallableFragment : Fragment() {
Installable(
R.string.install_game_content,
R.string.install_game_content_description,
install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
install = { installGameUpdateLauncher.launch(arrayOf("*/*")) }
),
Installable(
R.string.install_firmware,
R.string.install_firmware_description,
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
install = { getFirmwareLauncher.launch(arrayOf("application/zip")) }
),
Installable(
R.string.uninstall_firmware,
R.string.uninstall_firmware_description,
install = { mainActivity.uninstallFirmware() }
install = {
InstallableActions.uninstallFirmware(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
homeViewModel = homeViewModel
)
}
),
Installable(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
install = { mainActivity.getProdKey.launch(arrayOf("*/*")) }
install = { getProdKeyLauncher.launch(arrayOf("*/*")) }
),
Installable(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
install = { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
install = { getAmiiboKeyLauncher.launch(arrayOf("*/*")) }
)
)
@@ -164,11 +169,6 @@ class InstallableFragment : Fragment() {
setInsets()
}
override fun onResume() {
super.onResume()
applyBackgroundPreference()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -187,6 +187,132 @@ class InstallableFragment : Fragment() {
windowInsets
}
private val getProdKeyLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
InstallableActions.processKey(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
gamesViewModel = gamesViewModel,
result = result,
extension = "keys"
)
}
}
private val getAmiiboKeyLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
InstallableActions.processKey(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
gamesViewModel = gamesViewModel,
result = result,
extension = "bin"
)
}
}
private val getFirmwareLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
InstallableActions.processFirmware(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
homeViewModel = homeViewModel,
result = result
)
}
}
private val installGameUpdateLauncher =
registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { documents ->
if (documents.isEmpty()) {
return@registerForActivityResult
}
if (addonViewModel.game == null) {
InstallableActions.installContent(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel,
documents = documents
)
return@registerForActivityResult
}
ProgressDialogFragment.newInstance(
requireActivity(),
R.string.verifying_content,
false
) { _, _ ->
var updatesMatchProgram = true
for (document in documents) {
val valid = NativeLibrary.doesUpdateMatchProgram(
addonViewModel.game!!.programId,
document.toString()
)
if (!valid) {
updatesMatchProgram = false
break
}
}
if (updatesMatchProgram) {
requireActivity().runOnUiThread {
InstallableActions.installContent(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel,
documents = documents
)
}
} else {
requireActivity().runOnUiThread {
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description,
positiveAction = {
InstallableActions.installContent(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
addonViewModel = addonViewModel,
documents = documents
)
},
negativeAction = {}
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
}
return@newInstance Any()
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
}
private val importUserDataLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result != null) {
InstallableActions.importUserData(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
gamesViewModel = gamesViewModel,
driverViewModel = driverViewModel,
result = result
)
}
}
private val exportUserDataLauncher =
registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { result ->
if (result != null) {
InstallableActions.exportUserData(
activity = requireActivity(),
fragmentManager = parentFragmentManager,
result = result
)
}
}
private val importSaves =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
@@ -332,8 +458,4 @@ class InstallableFragment : Fragment() {
}
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.fragments
@@ -13,7 +13,6 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
@@ -48,7 +47,7 @@ class LicensesFragment : Fragment() {
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarLicenses.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
val licenses = listOf(
@@ -21,7 +21,6 @@ import org.yuzu.yuzu_emu.adapters.ProfileAdapter
import org.yuzu.yuzu_emu.databinding.FragmentProfileManagerBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.UserProfile
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
class ProfileManagerFragment : Fragment() {
@@ -50,10 +49,9 @@ class ProfileManagerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(visible = false)
applyBackgroundPreference()
binding.toolbarProfiles.setNavigationOnClickListener {
findNavController().popBackStack()
requireActivity().onBackPressedDispatcher.onBackPressed()
}
setupRecyclerView()
@@ -77,7 +75,6 @@ class ProfileManagerFragment : Fragment() {
override fun onResume() {
super.onResume()
loadProfiles()
applyBackgroundPreference()
}
private fun setupRecyclerView() {
@@ -190,8 +187,4 @@ class ProfileManagerFragment : Fragment() {
NativeConfig.saveGlobalConfig()
_binding = null
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
}
@@ -16,5 +16,17 @@ data class Patch(
val type: Int,
val programId: String,
val titleId: String,
val numericVersion: Long = 0
)
val numericVersion: Long = 0,
val source: Int = 0
) {
companion object {
const val SOURCE_UNKNOWN = 0
const val SOURCE_NAND = 1
const val SOURCE_SDMC = 2
const val SOURCE_EXTERNAL = 3
const val SOURCE_PACKED = 4
}
val isRemovable: Boolean
get() = source != SOURCE_EXTERNAL && source != SOURCE_PACKED
}
@@ -40,7 +40,6 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.BackgroundHelper
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
import info.debatty.java.stringsimilarity.Jaccard
@@ -107,7 +106,6 @@ class GamesFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setStatusBarShadeVisibility(true)
mainActivity = requireActivity() as MainActivity
applyBackgroundPreference()
if (savedInstanceState != null) {
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
@@ -254,7 +252,6 @@ class GamesFragment : Fragment() {
override fun onResume() {
super.onResume()
applyBackgroundPreference()
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
(binding.gridGames as? CarouselRecyclerView)?.setupCarousel(true)
(binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
@@ -500,10 +497,6 @@ class GamesFragment : Fragment() {
binding.addDirectory.visibility = if (showFolder) View.VISIBLE else View.GONE
}
private fun applyBackgroundPreference() {
BackgroundHelper.applyBackground(binding.backgroundLogo, requireContext())
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
@@ -26,7 +26,6 @@ import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.File
import java.io.FilenameFilter
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
@@ -39,16 +38,10 @@ import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.InstallResult
import android.os.Build
import org.yuzu.yuzu_emu.model.TaskState
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import androidx.core.content.edit
import org.yuzu.yuzu_emu.activities.EmulationActivity
import kotlin.text.compareTo
@@ -453,35 +446,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
fun processKey(result: Uri, extension: String = "keys") {
contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
InstallableActions.processKey(
activity = this,
fragmentManager = supportFragmentManager,
gamesViewModel = gamesViewModel,
result = result,
extension = extension
)
val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension)
if (resultCode == 0) {
// TODO(crueter): It may be worth it to switch some of these Toasts to snackbars,
// since most of it is foreground-only anyways.
Toast.makeText(
applicationContext,
R.string.keys_install_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
return
}
val resultString: String =
resources.getStringArray(R.array.installKeysResults)[resultCode]
MessageDialogFragment.newInstance(
titleId = R.string.keys_failed,
descriptionString = resultString,
helpLinkId = R.string.keys_missing_help
).show(supportFragmentManager, MessageDialogFragment.TAG)
}
val getFirmware = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
@@ -491,75 +462,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
fun processFirmware(result: Uri, onComplete: (() -> Unit)? = null) {
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
val firmwarePath =
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
ProgressDialogFragment.newInstance(
this,
R.string.firmware_installing
) { progressCallback, _ ->
var messageToShow: Any
try {
FileUtil.unzipToInternalStorage(
result.toString(),
cacheFirmwareDir,
progressCallback
)
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
MessageDialogFragment.newInstance(
this,
titleId = R.string.firmware_installed_failure,
descriptionId = R.string.firmware_installed_failure_description
)
} else {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
Log.error("[MainActivity] Firmware install failed - ${e.message}")
messageToShow = getString(R.string.fatal_error)
} finally {
cacheFirmwareDir.deleteRecursively()
}
messageToShow
}.apply {
onDialogComplete = onComplete
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
InstallableActions.processFirmware(
activity = this,
fragmentManager = supportFragmentManager,
homeViewModel = homeViewModel,
result = result,
onComplete = onComplete
)
}
fun uninstallFirmware() {
val firmwarePath =
File(NativeConfig.getNandDir() + "/system/Contents/registered/")
ProgressDialogFragment.newInstance(
this,
R.string.firmware_uninstalling
) { progressCallback, _ ->
var messageToShow: Any
try {
// Ensure the firmware directory exists before attempting to delete
if (firmwarePath.exists()) {
firmwarePath.deleteRecursively()
// Optionally reinitialize the system or perform other necessary steps
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
messageToShow = getString(R.string.firmware_uninstalled_success)
} else {
messageToShow = getString(R.string.firmware_uninstalled_failure)
}
} catch (e: Exception) {
Log.error("[MainActivity] Firmware uninstall failed - ${e.message}")
messageToShow = getString(R.string.fatal_error)
}
messageToShow
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
InstallableActions.uninstallFirmware(
activity = this,
fragmentManager = supportFragmentManager,
homeViewModel = homeViewModel
)
}
val installGameUpdate = registerForActivityResult(
@@ -606,101 +523,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
private fun installContent(documents: List<Uri>) {
ProgressDialogFragment.newInstance(
this@MainActivity,
R.string.installing_game_content
) { progressCallback, messageCallback ->
var installSuccess = 0
var installOverwrite = 0
var errorBaseGame = 0
var error = 0
documents.forEach {
messageCallback.invoke(FileUtil.getFilename(it))
when (
InstallResult.from(
NativeLibrary.installFileToNand(
it.toString(),
progressCallback
)
)
) {
InstallResult.Success -> {
installSuccess += 1
}
InstallResult.Overwrite -> {
installOverwrite += 1
}
InstallResult.BaseInstallAttempted -> {
errorBaseGame += 1
}
InstallResult.Failure -> {
error += 1
}
}
}
addonViewModel.refreshAddons(force = true)
val separator = System.lineSeparator() ?: "\n"
val installResult = StringBuilder()
if (installSuccess > 0) {
installResult.append(
getString(
R.string.install_game_content_success_install,
installSuccess
)
)
installResult.append(separator)
}
if (installOverwrite > 0) {
installResult.append(
getString(
R.string.install_game_content_success_overwrite,
installOverwrite
)
)
installResult.append(separator)
}
val errorTotal: Int = errorBaseGame + error
if (errorTotal > 0) {
installResult.append(separator)
installResult.append(
getString(
R.string.install_game_content_failed_count,
errorTotal
)
)
installResult.append(separator)
if (errorBaseGame > 0) {
installResult.append(separator)
installResult.append(
getString(R.string.install_game_content_failure_base)
)
installResult.append(separator)
}
if (error > 0) {
installResult.append(
getString(R.string.install_game_content_failure_description)
)
installResult.append(separator)
}
return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.install_game_content_failure,
descriptionString = installResult.toString().trim(),
helpLinkId = R.string.install_game_content_help_link
)
} else {
return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.install_game_content_success,
descriptionString = installResult.toString().trim()
)
}
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
InstallableActions.installContent(
activity = this,
fragmentManager = supportFragmentManager,
addonViewModel = addonViewModel,
documents = documents
)
}
val exportUserData = registerForActivityResult(
@@ -709,25 +537,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (result == null) {
return@registerForActivityResult
}
ProgressDialogFragment.newInstance(
this,
R.string.exporting_user_data,
true
) { progressCallback, _ ->
val zipResult = FileUtil.zipFromInternalStorage(
File(DirectoryInitialization.userDirectory!!),
DirectoryInitialization.userDirectory!!,
BufferedOutputStream(contentResolver.openOutputStream(result)),
progressCallback,
compression = false
)
return@newInstance when (zipResult) {
TaskState.Completed -> getString(R.string.user_data_export_success)
TaskState.Failed -> R.string.export_failed
TaskState.Cancelled -> R.string.user_data_export_cancelled
}
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
InstallableActions.exportUserData(
activity = this,
fragmentManager = supportFragmentManager,
result = result
)
}
val importUserData =
@@ -735,58 +549,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (result == null) {
return@registerForActivityResult
}
ProgressDialogFragment.newInstance(
this,
R.string.importing_user_data
) { progressCallback, _ ->
val checkStream =
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
var isYuzuBackup = false
checkStream.use { stream ->
var ze: ZipEntry? = null
while (stream.nextEntry?.also { ze = it } != null) {
val itemName = ze!!.name.trim()
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
isYuzuBackup = true
return@use
}
}
}
if (!isYuzuBackup) {
return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.invalid_yuzu_backup,
descriptionId = R.string.user_data_import_failed_description
)
}
// Clear existing user data
NativeConfig.unloadGlobalConfig()
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
// Copy archive to internal storage
try {
FileUtil.unzipToInternalStorage(
result.toString(),
File(DirectoryInitialization.userDirectory!!),
progressCallback
)
} catch (e: Exception) {
return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.import_failed,
descriptionId = R.string.user_data_import_failed_description
)
}
// Reinitialize relevant data
NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false)
driverViewModel.reloadDriverData()
return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
InstallableActions.importUserData(
activity = this,
fragmentManager = supportFragmentManager,
gamesViewModel = gamesViewModel,
driverViewModel = driverViewModel,
result = result
)
}
}
@@ -1,64 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.view.View
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.Settings
object BackgroundHelper {
fun getBackgroundStyle(context: Context): Int {
return PreferenceManager.getDefaultSharedPreferences(context).getInt(
Settings.PREF_HOME_BACKGROUND_STYLE,
Settings.HOME_BACKGROUND_STYLE_DEFAULT
)
}
fun setBackgroundStyle(context: Context, style: Int) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putInt(Settings.PREF_HOME_BACKGROUND_STYLE, style)
}
}
fun getBackgroundAlpha(context: Context): Float {
val alphaPercent = PreferenceManager.getDefaultSharedPreferences(context).getInt(
Settings.PREF_HOME_BACKGROUND_ALPHA,
Settings.HOME_BACKGROUND_ALPHA_DEFAULT
).coerceIn(0, 100)
return alphaPercent / 100f
}
fun setBackgroundAlpha(context: Context, alphaPercent: Int) {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putInt(Settings.PREF_HOME_BACKGROUND_ALPHA, alphaPercent.coerceIn(0, 100))
}
}
fun applyBackground(view: View, context: Context) {
val isEnabled = getBackgroundStyle(context) != Settings.HOME_BACKGROUND_STYLE_NONE
val visibilityStrength = getBackgroundAlpha(context)
val parent = view.parent as? View
val scrim = parent?.findViewById<View>(R.id.background_scrim)
view.visibility = if (isEnabled) View.VISIBLE else View.GONE
if (!isEnabled) {
scrim?.visibility = View.GONE
return
}
if (scrim != null) {
// Keep background image opaque; control perceived intensity through a cheap flat-color scrim.
view.alpha = 1f
scrim.visibility = View.VISIBLE
scrim.alpha = (1f - visibilityStrength).coerceIn(0f, 1f)
} else {
view.alpha = visibilityStrength
}
}
}
@@ -0,0 +1,327 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
import org.yuzu.yuzu_emu.model.AddonViewModel
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FilenameFilter
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
object InstallableActions {
fun processKey(
activity: FragmentActivity,
fragmentManager: FragmentManager,
gamesViewModel: GamesViewModel,
result: Uri,
extension: String = "keys"
) {
activity.contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val resultCode = NativeLibrary.installKeys(result.toString(), extension)
if (resultCode == 0) {
Toast.makeText(
activity.applicationContext,
R.string.keys_install_success,
Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
return
}
val resultString = activity.resources.getStringArray(R.array.installKeysResults)[resultCode]
MessageDialogFragment.newInstance(
titleId = R.string.keys_failed,
descriptionString = resultString,
helpLinkId = R.string.keys_missing_help
).show(fragmentManager, MessageDialogFragment.TAG)
}
fun processFirmware(
activity: FragmentActivity,
fragmentManager: FragmentManager,
homeViewModel: HomeViewModel,
result: Uri,
onComplete: (() -> Unit)? = null
) {
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
val firmwarePath = File(NativeConfig.getNandDir() + "/system/Contents/registered/")
val cacheFirmwareDir = File("${activity.cacheDir.path}/registered/")
ProgressDialogFragment.newInstance(
activity,
R.string.firmware_installing
) { progressCallback, _ ->
var messageToShow: Any
try {
FileUtil.unzipToInternalStorage(
result.toString(),
cacheFirmwareDir,
progressCallback
)
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
MessageDialogFragment.newInstance(
activity,
titleId = R.string.firmware_installed_failure,
descriptionId = R.string.firmware_installed_failure_description
)
} else {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, overwrite = true)
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
activity.getString(R.string.save_file_imported_success)
}
} catch (_: Exception) {
messageToShow = activity.getString(R.string.fatal_error)
} finally {
cacheFirmwareDir.deleteRecursively()
}
messageToShow
}.apply {
onDialogComplete = onComplete
}.show(fragmentManager, ProgressDialogFragment.TAG)
}
fun uninstallFirmware(
activity: FragmentActivity,
fragmentManager: FragmentManager,
homeViewModel: HomeViewModel
) {
val firmwarePath = File(NativeConfig.getNandDir() + "/system/Contents/registered/")
ProgressDialogFragment.newInstance(
activity,
R.string.firmware_uninstalling
) { _, _ ->
val messageToShow: Any = try {
if (firmwarePath.exists()) {
firmwarePath.deleteRecursively()
NativeLibrary.initializeSystem(true)
homeViewModel.setCheckKeys(true)
activity.getString(R.string.firmware_uninstalled_success)
} else {
activity.getString(R.string.firmware_uninstalled_failure)
}
} catch (_: Exception) {
activity.getString(R.string.fatal_error)
}
messageToShow
}.show(fragmentManager, ProgressDialogFragment.TAG)
}
fun installContent(
activity: FragmentActivity,
fragmentManager: FragmentManager,
addonViewModel: AddonViewModel,
documents: List<Uri>
) {
ProgressDialogFragment.newInstance(
activity,
R.string.installing_game_content
) { progressCallback, messageCallback ->
var installSuccess = 0
var installOverwrite = 0
var errorBaseGame = 0
var error = 0
documents.forEach {
messageCallback.invoke(FileUtil.getFilename(it))
when (
InstallResult.from(
NativeLibrary.installFileToNand(
it.toString(),
progressCallback
)
)
) {
InstallResult.Success -> installSuccess += 1
InstallResult.Overwrite -> installOverwrite += 1
InstallResult.BaseInstallAttempted -> errorBaseGame += 1
InstallResult.Failure -> error += 1
}
}
addonViewModel.refreshAddons(force = true)
val separator = System.lineSeparator() ?: "\n"
val installResult = StringBuilder()
if (installSuccess > 0) {
installResult.append(
activity.getString(
R.string.install_game_content_success_install,
installSuccess
)
)
installResult.append(separator)
}
if (installOverwrite > 0) {
installResult.append(
activity.getString(
R.string.install_game_content_success_overwrite,
installOverwrite
)
)
installResult.append(separator)
}
val errorTotal = errorBaseGame + error
if (errorTotal > 0) {
installResult.append(separator)
installResult.append(
activity.getString(
R.string.install_game_content_failed_count,
errorTotal
)
)
installResult.append(separator)
if (errorBaseGame > 0) {
installResult.append(separator)
installResult.append(activity.getString(R.string.install_game_content_failure_base))
installResult.append(separator)
}
if (error > 0) {
installResult.append(
activity.getString(R.string.install_game_content_failure_description)
)
installResult.append(separator)
}
return@newInstance MessageDialogFragment.newInstance(
activity,
titleId = R.string.install_game_content_failure,
descriptionString = installResult.toString().trim(),
helpLinkId = R.string.install_game_content_help_link
)
} else {
return@newInstance MessageDialogFragment.newInstance(
activity,
titleId = R.string.install_game_content_success,
descriptionString = installResult.toString().trim()
)
}
}.show(fragmentManager, ProgressDialogFragment.TAG)
}
fun exportUserData(
activity: FragmentActivity,
fragmentManager: FragmentManager,
result: Uri
) {
val userDirectory = DirectoryInitialization.userDirectory
if (userDirectory == null) {
Toast.makeText(
activity.applicationContext,
R.string.fatal_error,
Toast.LENGTH_SHORT
).show()
return
}
ProgressDialogFragment.newInstance(
activity,
R.string.exporting_user_data,
true
) { progressCallback, _ ->
val zipResult = FileUtil.zipFromInternalStorage(
File(userDirectory),
userDirectory,
BufferedOutputStream(activity.contentResolver.openOutputStream(result)),
progressCallback,
compression = false
)
return@newInstance when (zipResult) {
TaskState.Completed -> activity.getString(R.string.user_data_export_success)
TaskState.Failed -> R.string.export_failed
TaskState.Cancelled -> R.string.user_data_export_cancelled
}
}.show(fragmentManager, ProgressDialogFragment.TAG)
}
fun importUserData(
activity: FragmentActivity,
fragmentManager: FragmentManager,
gamesViewModel: GamesViewModel,
driverViewModel: DriverViewModel,
result: Uri
) {
val userDirectory = DirectoryInitialization.userDirectory
if (userDirectory == null) {
Toast.makeText(
activity.applicationContext,
R.string.fatal_error,
Toast.LENGTH_SHORT
).show()
return
}
ProgressDialogFragment.newInstance(
activity,
R.string.importing_user_data
) { progressCallback, _ ->
val checkStream = ZipInputStream(
BufferedInputStream(activity.contentResolver.openInputStream(result))
)
var isYuzuBackup = false
checkStream.use { stream ->
var ze: ZipEntry? = null
while (stream.nextEntry?.also { ze = it } != null) {
val itemName = ze!!.name.trim()
if (itemName == "/config/config.ini" || itemName == "config/config.ini") {
isYuzuBackup = true
return@use
}
}
}
if (!isYuzuBackup) {
return@newInstance MessageDialogFragment.newInstance(
activity,
titleId = R.string.invalid_yuzu_backup,
descriptionId = R.string.user_data_import_failed_description
)
}
NativeConfig.unloadGlobalConfig()
File(userDirectory).deleteRecursively()
try {
FileUtil.unzipToInternalStorage(
result.toString(),
File(userDirectory),
progressCallback
)
} catch (_: Exception) {
return@newInstance MessageDialogFragment.newInstance(
activity,
titleId = R.string.import_failed,
descriptionId = R.string.user_data_import_failed_description
)
}
NativeLibrary.initializeSystem(true)
NativeConfig.initializeGlobalConfig()
gamesViewModel.reloadGames(false)
driverViewModel.reloadDriverData()
return@newInstance activity.getString(R.string.user_data_import_success)
}.show(fragmentManager, ProgressDialogFragment.TAG)
}
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <common/fs/path_util.h>
#include <common/logging/log.h>
#include <common/logging.h>
#include <common/settings.h>
#include <input_common/main.h>
#include "android_config.h"
@@ -7,7 +7,7 @@
#include <android/native_window_jni.h>
#include "common/android/id_cache.h"
#include "common/logging/log.h"
#include "common/logging.h"
#include "input_common/drivers/android.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/drivers/virtual_amiibo.h"
+2 -3
View File
@@ -42,8 +42,7 @@
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
#include "common/fs/path_util.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#include "common/settings.h"
@@ -1407,7 +1406,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPatchesForFile(JNIEnv* env
Common::Android::ToJString(env, patch.version), static_cast<jint>(patch.type),
Common::Android::ToJString(env, std::to_string(patch.program_id)),
Common::Android::ToJString(env, std::to_string(patch.title_id)),
static_cast<jlong>(patch.numeric_version));
static_cast<jlong>(patch.numeric_version), static_cast<jint>(patch.source));
env->SetObjectArrayElement(jpatchArray, i, jpatch);
++i;
}
@@ -10,7 +10,7 @@
#include "common/android/android_common.h"
#include "common/android/id_cache.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/logging.h"
#include "common/settings.h"
#include "frontend_common/config.h"
#include "frontend_common/settings_generator.h"
@@ -23,7 +23,7 @@
#include <jni.h>
#include "common/android/android_common.h"
#include "common/logging/log.h"
#include "common/logging.h"
#include "native.h"
namespace {
+4 -1
View File
@@ -1,8 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <common/android/android_common.h>
#include <common/logging/log.h>
#include <common/logging.h>
#include <jni.h>
extern "C" {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

@@ -6,17 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -8,29 +8,6 @@
android:clipChildren="false"
>
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/background_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
@@ -282,4 +259,4 @@
app:rippleColor="#99FFFFFF"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -8,25 +8,13 @@
android:background="?attr/colorSurface"
>
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
android:background="@android:color/transparent"
app:elevation="0dp">
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
@@ -52,15 +40,41 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="24dp">
android:paddingBottom="24dp"
android:paddingStart="24dp"
android:paddingTop="0dp"
android:paddingEnd="24dp">
<ImageView
android:id="@+id/image_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_vertical"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:src="@drawable/ic_yuzu" />
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/image_logo"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_yuzu" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textAlignment="center" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="220dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/about_app_description"
android:textAlignment="center" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
@@ -68,39 +82,6 @@
android:layout_weight="1"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardBackgroundColor="?attr/colorSurface"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="24dp"
android:paddingVertical="20dp">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/about"
android:textAlignment="viewStart" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="@string/about_app_description"
android:textAlignment="viewStart" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/button_contributors"
android:layout_width="match_parent"
@@ -216,7 +197,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginTop="12dp"
android:gravity="start"
android:orientation="horizontal">
@@ -7,16 +7,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_info"
android:layout_width="match_parent"
@@ -7,20 +7,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.core.widget.NestedScrollView
android:id="@+id/list_all"
android:layout_width="0dp"
@@ -6,17 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -257,4 +246,4 @@
style="@style/Widget.Material3.Button.ElevatedButton" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -8,25 +8,13 @@
android:background="?attr/colorSurface"
>
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:touchscreenBlocksFocus="false"
android:background="@android:color/transparent"
app:elevation="0dp">
android:touchscreenBlocksFocus="false">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_about"
@@ -54,48 +42,35 @@
android:orientation="vertical"
android:paddingBottom="24dp">
<ImageView
android:id="@+id/image_logo"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginVertical="24dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_yuzu" />
<com.google.android.material.card.MaterialCardView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
app:cardBackgroundColor="@android:color/transparent"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp">
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<ImageView
android:id="@+id/image_logo"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginBottom="8dp"
android:src="@drawable/ic_yuzu" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="20dp"
android:paddingHorizontal="20dp"
android:orientation="vertical">
android:text="@string/app_name"
android:textAlignment="center" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:text="@string/about" />
<com.google.android.material.textview.MaterialTextView
style="@style/SynthwaveText.Body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAlignment="center"
android:text="@string/about_app_description" />
<com.google.android.material.textview.MaterialTextView
style="@style/SynthwaveText.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
android:text="@string/about_app_description" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<com.google.android.material.card.MaterialCardView
android:id="@+id/button_contributors"
@@ -217,7 +192,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_marginTop="24dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="16dp"
android:layout_marginHorizontal="40dp">
@@ -231,7 +206,9 @@
app:icon="@drawable/ic_discord"
app:iconSize="24dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp" />
<com.google.android.material.button.MaterialButton
style="@style/EdenButton.Secondary"
@@ -243,7 +220,9 @@
app:icon="@drawable/ic_stoat"
app:iconSize="24dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp" />
<com.google.android.material.button.MaterialButton
style="@style/EdenButton.Secondary"
@@ -255,7 +234,9 @@
app:icon="@drawable/ic_x"
app:iconSize="24dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp" />
<com.google.android.material.button.MaterialButton
style="@style/EdenButton.Secondary"
@@ -267,7 +248,9 @@
app:icon="@drawable/ic_website"
app:iconSize="24dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_github"
@@ -279,7 +262,9 @@
app:icon="@drawable/ic_github"
app:iconSize="24dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutline"
app:strokeWidth="1dp" />
</LinearLayout>
@@ -6,20 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_addons"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,17 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_applets"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,20 +7,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_drivers"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,20 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
@@ -6,20 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -6,16 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_freedreno"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -7,16 +7,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_info"
android:layout_width="match_parent"
@@ -6,20 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.core.widget.NestedScrollView
android:id="@+id/list_all"
android:layout_width="match_parent"
@@ -8,29 +8,6 @@
android:clipChildren="false"
>
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/background_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
@@ -289,4 +266,4 @@
app:rippleColor="#99FFFFFF"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -6,20 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/logo_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.12"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_home_settings"
style="@style/Widget.Eden.TransparentTopAppBarLayout"
@@ -6,17 +6,6 @@
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<ImageView
android:id="@+id/background_logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:alpha="0.1"
android:contentDescription="@null"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="@drawable/eden_background" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_installables"
style="@style/Widget.Eden.TransparentTopAppBarLayout"

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