Compare commits

..

169 commits

Author SHA1 Message Date
crueter
379649dbce
cmake: Fix MoltenVK fetch order/library conflicts (#2183)
* [cmake] Fix MoltenVK fetch order/library conflicts

Rather than dealing with `find_library` shenanigans, just set the
library path directly (when using bundled MoltenVK). System MoltenVK
solely uses `find_library`.

Avoids cache nonsense that can cause system/bundled versions to get
mixed up, and overall makes the system/bundled mvk handling a lot more
consistent

```
cmake -S . -B build -DUSE_SYSTEM_MOLTENVK=ON
-- Using MoltenVK at /opt/homebrew/lib/libMoltenVK.dylib.
cmake -S . -B build -DUSE_SYSTEM_MOLTENVK=OFF
-- Using MoltenVK at /Users/crueter/code/azahar/build/externals/MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib.
cmake -S . -B build -DUSE_SYSTEM_MOLTENVK=ON
-- Using MoltenVK at /opt/homebrew/lib/libMoltenVK.dylib.
```

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

* remove old comment

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

* Cleanup

---------

Signed-off-by: crueter <crueter@eden-emu.dev>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-06-05 16:36:48 +01:00
PabloMK7
c03248f158
externals: Switch to cryptopp-modern (#2139)
* externals: Switch to cryptopp-modern

* Revert cryptopp package name under USE_SYSTEM_CRYPTOPP condition

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-06-04 22:38:47 +02:00
RedBlackAka
4867bb2e2b
video_core: Change unimplemented gas stub behaviour for Vulkan (#2165) 2026-05-30 23:53:16 +02:00
Wunk
4e4c7e687b
renderer_gl: Fix disabled cubemap texture units (#2159)
Disabled texture units on OpenGL always use a null 2D texture, but there are
cases where the null texture should be a null cubemap rather than a 2D
texture to match the active texture binding state.

Fixes some errors that occur in `OpenGLState::Apply()` found
in Brain Age: Concentration Training.
2026-05-28 15:42:02 +02:00
OpenSauce04
56f738eb06 jni/config.cpp: Reworded default config assert message for clarity 2026-05-27 18:05:48 +01:00
RedBlackAka
b1e537a485
Qt: Do not show Microprofile option at all if disabled (#2156) 2026-05-26 22:44:51 +02:00
OpenSauce04
59da460177 Migrate translations to new language codes 2026-05-25 19:08:09 +01:00
OpenSauce04
8bdb60a6e1 Updated translations via Transifex 2026-05-25 19:08:09 +01:00
OpenSauce04
383a28795e ci: Build azahar-room Docker image for ARM64 2026-05-25 17:04:47 +01:00
OpenSauce04
725544f3b4 ci: Add --no-cache to Docker build command
This is mostly just for specificity. In practice this will never do anything in CI because there will never be any cache to use, but if there was, we wouldn't want to use it
2026-05-25 17:04:47 +01:00
OpenSauce04
135f10320a docker: Fix azahar-room Dockerfile failing to build with podman 2026-05-25 17:04:47 +01:00
OpenSauce04
0b7114cbf8 docker: Break up build commands into seperate RUN calls
I think this was from back when the Dockerfile was first being written, before the 'builder' and 'final' stages were introduced which make this former optimization attempt completely redundant
2026-05-25 17:04:47 +01:00
OpenSauce04
95d42cb40a docker: Remove now-redundant CMake options from azahar-room Dockerfile 2026-05-25 17:04:47 +01:00
OpenSauce04
f0bc64d967 IntListSetting.kt: Use generated layouts_to_cycle key 2026-05-24 12:55:09 +01:00
Masamune3210
ad8526c4cf
pica_core.cpp: Initialize IRQ_CMP (#2143)
* initialize IRQ_CMP

* add comment explaining requirement

* Fix formatting

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-05-24 11:07:57 +01:00
project516
ae7d7dca1f ci: Update github actions to NodeJS 24 (#2110)
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-05-23 21:24:54 +01:00
OpenSauce04
4a4b75b0de libretro.yml: Don't fail fast in macOS job matrix 2026-05-23 19:59:34 +01:00
PabloMK7
b186b04995
core: Refactor thread unschedule and add debug frontend unschedule (#2145) 2026-05-23 17:25:14 +02:00
Wunk
ab6896a2ca
core: Fix debug compile error (#2132)
Fixes a compilation error when building in Debug mode. `count` should be `async_data->count` in this log message.
2026-05-17 15:15:21 +02:00
OpenSauce04
e11f3da493 Updated translations via Transifex 2026-05-16 17:32:14 +01:00
OpenSauce04
8ffb94b06c Implement Z3DS compression CLI in new citra_cli static library 2026-05-16 17:13:31 +01:00
PabloMK7
267887d7a9
Add attestation support to increase release security (#2117)
* ci: Add sbom and attestation

* tools: Add verify-release.sh

* verify-release.sh: Set executable permission

* verify-release.sh: Put downloads into a gitignored directory

* tools: Make verify-release also download sbom

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-05-14 14:52:10 +02:00
OpenSauce04
778ca369cd ci: Strip libretro cores after building 2026-05-12 11:56:22 +01:00
OpenSauce04
dbe7fd979f cmake: Add EXCLUDE_FROM_ALL to targets where applicable 2026-05-09 14:01:56 +01:00
PabloMK7
1c7c7a5f1b
svc: Fix instruction cache invalidation only affecting current core (#2100) 2026-05-09 14:03:55 +02:00
OpenSauce04
bf59d26c48 externals: Update dllwalker to commit 2f8b349 2026-05-09 10:32:52 +01:00
OpenSauce04
652fc02175 ci: Implement MXE CI/CD build job 2026-05-09 10:32:52 +01:00
OpenSauce04
854e198196 cmake: Implemented bundle target for MXE builds
Just copies the content of bin/<type>/ to bundle/
2026-05-09 10:32:52 +01:00
OpenSauce04
5ecd402811 cmake: Explicitly use gcc-ar instead of ar for MXE builds 2026-05-09 10:32:52 +01:00
OpenSauce04
0ce2a30d20 Implement proper DLL resolution for MXE builds 2026-05-09 10:32:52 +01:00
OpenSauce04
644a181aff cmake: Explicitly disable BUILD_SHARED_LIBS 2026-05-09 10:32:52 +01:00
OpenSauce04
422c7865a3 For Linux --> Windows cross-compilation, copy all cross-compiled DLLs during build
As per the comment, this is just to get the build functioning pending a real solution
2026-05-09 10:32:52 +01:00
OpenSauce04
ca99574700 tests: Don't run catch_discover_tests when cross-compiling to a different OS 2026-05-09 10:32:52 +01:00
OpenSauce04
f902010f04 externals: Don't fall back to bundled OpenSSL if USE_SYSTEM_OPENSSL is enabled 2026-05-09 10:32:52 +01:00
PabloMK7
929a51afc6
audio: Add option to simulate headphones plugged in (#2099)
Some checks failed
citra-build / source (push) Failing after 6m47s
citra-build / linux-x86_64 (appimage-wayland) (push) Successful in 4m21s
citra-build / linux-x86_64 (gcc-nopch) (push) Failing after 4s
citra-build / android (googleplay) (push) Successful in 7s
citra-build / android (vanilla) (push) Failing after 3m24s
citra-build / docker (push) Failing after 20s
citra-format / clang-format (push) Failing after 1s
citra-libretro / android (push) Failing after 2m54s
citra-build / linux-x86_64 (appimage) (push) Failing after 14m13s
citra-libretro / linux (push) Failing after 3m16s
citra-libretro / windows (push) Failing after 3m22s
citra-transifex / transifex (push) Has been skipped
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
2026-05-08 15:19:53 +02:00
PabloMK7
260f08c497
core: Add async filesystem operations (#2098) 2026-05-08 11:35:47 +02:00
PabloMK7
921ea178b9
ui: Made rom loading errors more clear and user friendly (#2097)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-05-07 20:39:30 +02:00
PabloMK7
b540725090
Revamp GDB implemenation and add a some minor debug features (#2086)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-05-07 13:48:35 +02:00
PabloMK7
5ddbaeae23
gsp: Fix GPU interrupt queue and add GPU timing emulation (#2095)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-05-07 01:36:21 +02:00
OpenSauce04
b081f800a4 Revert "ci: Override Android SDK Ninja with newer version"
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
This reverts commit eee7f076ee.
2026-05-04 17:40:37 +01:00
OpenSauce04
76a71d76d4 externals: Revert to a patched version of OpenAL v1.24.1 2026-05-04 17:40:37 +01:00
Rodrigo Iglesias
83eef0012e macOS: normalize SDMC directory filenames (#2080)
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
* macOS: normalize SDMC directory filenames

* Guard macOS filename normalization behind __APPLE__

* Guard macOS filename normalization test

* Apply clang-format

* Update license headers
2026-05-03 00:21:53 +02:00
OpenSauce04
ec6a0dd1c8 ci: Migrate Transifex runner to latest tag
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
The `transifex` tag has now been removed due to a seperate image no longer being necessary
2026-04-26 15:29:29 +01:00
OpenSauce04
eb498e5ecd qt: Fixed outdated use of qt_add_lupdate 2026-04-26 15:25:38 +01:00
OpenSauce04
4fa793b945 android: Bump NDK and AGP versions
NDK: 27.1.x --> 27.3.x
AGP: 8.13.1 --> 8.13.2
2026-04-26 13:55:16 +01:00
OpenSauce04
5d84dfed91 Fix building w/ OpenAL on OpenBSD
- Explicitly disable Solaris backend on OpenBSD
- Update OpenAL to v1.25.1
2026-04-26 12:52:32 +01:00
OpenSauce04
eee7f076ee ci: Override Android SDK Ninja with newer version 2026-04-26 12:32:49 +01:00
Francesco Saltori
996abd1eaf Remove old CONTRIBUTING.md file
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-25 21:57:46 +01:00
Wunk
91128d6625
shader_jit: Emit LG2/EX2 subroutines on-demand (#2046)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
Rather than emitting these subroutine functions for _every_ shader, only emit
the subroutines when the `LG2` and `EX2` instructions are actually used.
This saves a good chunk of memory across all shaders.

Inspired by Tanuki3DS.
2026-04-24 20:34:46 +02:00
Eric Warmenhoven
37b6c91de6 libretro: update docker image for mxe github action
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-24 14:50:09 +01:00
OpenSauce
b6c54ac8c7
Update PR template 2026-04-24 14:04:16 +01:00
OpenSauce04
b3ee2d8ac5 tools/README.md: Re-updated release checklist
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
Knew I'd forgotten something (:
2026-04-21 14:55:40 +01:00
OpenSauce04
9701a3d874 tools/README.md: Updated release checklist
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
2026-04-21 10:47:52 +01:00
OpenSauce04
a276623dbb Updated translations via Transifex 2026-04-21 10:47:49 +01:00
PabloMK7
2fff086e81
qt: Temporarily fix fullscreen on msys2 builds (#2049)
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
* qt: Temporarily fix fullscreen on msys2 builds

* Removed excessive endif comments

We really only need these when nesting ifdefs

* blockRoundedCorners: Invert if condition for readability

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-04-19 13:47:49 +01:00
OpenSauce04
5bc58c78ed OpenBSD build fixes
- Specify OpenBSD's X11 include directory
- Add OpenBSD-specific linker flag to allow W|X
2026-04-19 13:37:26 +01:00
PabloMK7
0fe6a8c7df
video_core: Properly handle non RGBA8 shadow textures (#2047)
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
2026-04-17 21:45:50 +02:00
Cobalt
d4b5633cf0
qt Fix compilation issues in status LED code (#2045)
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
solves a build issue a ***lot*** of [L4T Megascript](https://github.com/cobalt2727/L4T-Megascript) users were reporting to me on Linux. C++ is definitely not my strong suit, but per https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/pow-powf-powl?view=msvc-170#remarks `pow` has identical behavior in C++ projects. no clue why `powf` worked fine on your environment when developing this but not other people's.

```cmake
[ 98%] Building CXX object src/citra_qt/CMakeFiles/citra_qt.dir/qt_image_interface.cpp.o
/home/runner/azahar/src/citra_qt/notification_led.cpp:56:15: error: no member named 'powf' in namespace 'std'; did you mean simply 'powf'?
   56 |     float t = std::powf(pwm, 1.f / gamma);
      |               ^~~~~~~~~
      |               powf
/usr/include/aarch64-linux-gnu/bits/mathcalls.h:140:1: note: 'powf' declared here
  140 | __MATHCALL_VEC (pow,, (_Mdouble_ __x, _Mdouble_ __y));
      | ^
/usr/include/math.h:280:3: note: expanded from macro '__MATHCALL_VEC'
  280 |   __MATHCALL (function, suffix, args)
      |   ^
/usr/include/math.h:287:3: note: expanded from macro '__MATHCALL'
  287 |   __MATHDECL (_Mdouble_,function,suffix, args)
      |   ^
/usr/include/math.h:289:3: note: expanded from macro '__MATHDECL'
  289 |   __MATHDECL_1(type, function,suffix, args); \
      |   ^
note: (skipping 1 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/usr/include/math.h:297:15: note: expanded from macro '__MATHDECL_1_IMPL'
  297 |   extern type __MATH_PRECNAME(function,suffix) args __THROW
      |               ^
/usr/include/math.h:326:34: note: expanded from macro '__MATH_PRECNAME'
  326 | # define __MATH_PRECNAME(name,r) name##f##r
      |                                  ^
<scratch space>:97:1: note: expanded from here
   97 | powf
      | ^
1 error generated.
make[2]: *** [src/citra_qt/CMakeFiles/citra_qt.dir/build.make:1573: src/citra_qt/CMakeFiles/citra_qt.dir/notification_led.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [CMakeFiles/Makefile2:3481: src/citra_qt/CMakeFiles/citra_qt.dir/all] Error 2
make: *** [Makefile:166: all] Error 2
```
2026-04-16 09:53:08 +02:00
OpenSauce04
afbaf8e485 android: Fixed incorrect location for Latin American Spanish locale files
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux-x86_64 (appimage) (push) Has been cancelled
citra-build / linux-x86_64 (appimage-wayland) (push) Has been cancelled
citra-build / linux-x86_64 (gcc-nopch) (push) Has been cancelled
citra-build / linux-arm64 (clang) (push) Has been cancelled
citra-build / linux-arm64 (gcc-nopch) (push) Has been cancelled
citra-build / macos (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (googleplay) (push) Has been cancelled
citra-build / android (vanilla) (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-libretro / android (push) Has been cancelled
citra-libretro / linux (push) Has been cancelled
citra-libretro / windows (push) Has been cancelled
citra-libretro / macos (arm64) (push) Has been cancelled
citra-libretro / macos (x86_64) (push) Has been cancelled
citra-libretro / ios (push) Has been cancelled
citra-libretro / tvos (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
2026-04-14 21:17:40 +01:00
OpenSauce04
52b1e01a6f Updated translations via Transifex 2026-04-14 20:15:51 +01:00
PabloMK7
f1cd5f5ff4
video_core: fix color blend min/max mode in OpenGL (#2038)
* video_core: fix check for fragment color blend emulation

* video_core: Fix typo in gl fragment shader gen
2026-04-14 19:26:22 +02:00
bug
1edc5de18e
android: Stop emulation state if activity destroyed and fix relaunching from intents (#2000)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-13 16:49:31 +02:00
PabloMK7
727377c012 discord rpc: Change how info is displayed
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-13 14:22:58 +02:00
PabloMK7
4dbe0fd497 qt: Properly fix discord rich presence 2026-04-13 14:22:58 +02:00
PabloMK7
6d230d28da Revert "qt: Try to fix Discord Rich Presence not updating correctly (#2013)"
This reverts commit 5983a23d38.
2026-04-13 14:22:58 +02:00
PabloMK7
336d871a7f
core: Add CMAKE option to disable built-in keyblob (#2024)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
* core: Add CMAKE option to disable built-in keyblob

* Additional assurance against accidental inclusion of default_keys.h

* default_keys.h: Make default_keys_enc constexpr

* default_keys.h: Make default_keys_enc_size constexpr

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-04-12 18:57:55 +01:00
Eric Warmenhoven
e8c75b4107
libretro: vulkan: wait before ticking (#2004)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
Ensure the scheduler worker thread has finished processing all dispatched command chunks before the rasterizer cache's garbage collector destroys sentenced surfaces.
2026-04-11 22:56:09 +02:00
PabloMK7
0fc3d692b9
android: Allow deleting per title disk shader cache (#2032)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-11 20:22:20 +02:00
jbm11208
5983a23d38
qt: Try to fix Discord Rich Presence not updating correctly (#2013) 2026-04-11 17:19:13 +02:00
PabloMK7
599069eb33
android: Block activity recreation due to orientation changes on boot (#2030) 2026-04-11 16:40:07 +02:00
PabloMK7
6eca4afac5
core: Fix typo in InfoLedPattern (#2029)
Some checks are pending
citra-build / source (push) Waiting to run
citra-build / linux-x86_64 (appimage) (push) Waiting to run
citra-build / linux-x86_64 (appimage-wayland) (push) Waiting to run
citra-build / linux-x86_64 (gcc-nopch) (push) Waiting to run
citra-build / linux-arm64 (clang) (push) Waiting to run
citra-build / linux-arm64 (gcc-nopch) (push) Waiting to run
citra-build / macos (push) Waiting to run
citra-build / windows (msvc) (push) Waiting to run
citra-build / windows (msys2) (push) Waiting to run
citra-build / android (googleplay) (push) Waiting to run
citra-build / android (vanilla) (push) Waiting to run
citra-build / docker (push) Waiting to run
citra-format / clang-format (push) Waiting to run
citra-libretro / android (push) Waiting to run
citra-libretro / linux (push) Waiting to run
citra-libretro / windows (push) Waiting to run
citra-libretro / macos (arm64) (push) Waiting to run
citra-libretro / macos (x86_64) (push) Waiting to run
citra-libretro / ios (push) Waiting to run
citra-libretro / tvos (push) Waiting to run
citra-transifex / transifex (push) Waiting to run
2026-04-11 13:23:42 +02:00
Eric Warmenhoven
b2faa299d5 libretro: fix linker error with tests 2026-04-09 22:00:49 +01:00
PabloMK7
3d69741076 qt: Show emulated notification LED 2026-04-09 19:19:59 +02:00
PabloMK7
d29e15f219 core: Add notification LED emulation 2026-04-09 19:19:59 +02:00
OpenSauce04
c650473fdc Default to Vulkan renderer on Android 2026-04-06 18:58:52 +01:00
SiniKraft
000530c028 android : Fix navigation bar overlapping the Show Home Menu apps button 2026-04-06 11:25:14 +02:00
SiniKraft
df05b5f3db android : Fix emulation exit showing an Invalid Rom Format error 2026-04-06 11:25:14 +02:00
Wunkolo
06a535f50e shader_jit: Add SETEMIT unit test 2026-04-05 23:02:56 +02:00
Wunkolo
4cbd75b413 shader_jit: Optimize GeometryEmitter SETEMIT state
The `SETEMIT`/`SETE` instruction only actually encodes 4 bits of possible state,
but this is currently expanded into three separate bytes of
data(four with padding) and requires three separate byte-writes for the x64 and
a64 JITs to write into. These 4 bits from the instruction can instead be
compacted into a singular 1-byte write from the JIT by encoding these 4 bits of
state into a singular byte at JIT-time, and unpacking this data is instead done
by `GeometryEmitter::Emit`. This also allows the serializer to use a singular
byte for all 3 fields now as well.
2026-04-05 23:02:56 +02:00
Wunk
60f331b43b
cmake: Allow Catch test discovery (#1997)
Allows individual unit-tests to be discovered, tested, and debugged by IDEs without having to run _all_ of the unit-tests just to debug one specific test.
2026-04-05 23:01:05 +02:00
OpenSauce04
ba6f8cb744 Disable Vulkan renderer on NetBSD because OS Vulkan support doesn't exist 2026-04-05 13:11:40 +01:00
OpenSauce04
118579adb3 Fixed launch failures on NetBSD due to PaX MPROTECT restrictions 2026-04-05 13:11:40 +01:00
OpenSauce04
23393904e0 Fixed NetBSD build issues
- Added missing include
- Fixed X11 include directory not being included
2026-04-05 13:11:40 +01:00
GasInfinity
3066887ff4 fix: don't crash when getaddrinfo gets a small or empty buffer in soc:U
* those are valid in hw!
2026-03-29 20:08:41 +02:00
GasInfinity
901f010913 fix: properly handle getaddrinfo/getnameinfo return values in soc:U 2026-03-29 20:08:41 +02:00
Why? You Don't Know?
5fc9732f05
android: Convert bgColor default values to Int (#1959) 2026-03-29 20:05:54 +02:00
OpenSauce04
39363cd435 ci: Merge standalone macOS CI/CD jobs into single runner 2026-03-28 15:32:24 +00:00
GasInfinity
60661c3b8b fix: correct the response of SendToOther in soc:U 2026-03-28 14:14:25 +00:00
PabloMK7
be0f096f48
core: Set boss as a online LLE module (#1952) 2026-03-28 12:43:26 +01:00
Marcin Serwin
6201256e15
cmake: Add option to use system oaknut (#1947)
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
2026-03-28 12:21:42 +01:00
PabloMK7
af188bb7b7
core: kernel: Implement thread cpu time limit for core1 (#1934) 2026-03-28 12:20:33 +01:00
RedBlackAka
0862e5e98a
Qt: Remove Vulkan warning and OpenGL Mesa override (#1938) 2026-03-28 12:17:15 +01:00
PabloMK7
49b0bef17d
android: Fix visibility of hidden system titles (#1935) 2026-03-28 12:04:43 +01:00
PabloMK7
f14f095e72
core: svc: Add better logging to svc failures (#1948) 2026-03-28 12:03:16 +01:00
PabloMK7
7e58ac5bcf android: Handle surface lost during swapchain creation 2026-03-27 18:31:13 +00:00
OpenSauce04
7220bd2edd externals: Updated to boost 1.90 + LLVM 22 workaround 2026-03-27 18:30:41 +00:00
PabloMK7
d4e9daa739
android: Fix compression and decompression on vanilla build (#1939) 2026-03-24 18:58:56 +01:00
OpenSauce04
9b045bf837 libretro: Replace render_touchscreen setting with enable_touch_pointer_timeout
Touch pointer rendering is now always enabled, but unless a controller is being used to move the touchscreen cursor, it will be hidden due to the timeout which is also enabled by default.
2026-03-23 13:07:39 +00:00
PabloMK7
7a600e28d2 android: Fix icon not showing if update title fails to load 2026-03-22 22:57:32 +01:00
PabloMK7
5a07260e1b loader: Fix identifying zcci files when system files are not set up 2026-03-22 22:57:32 +01:00
OpenSauce04
2c8297c34c android: Fixed native path intent URIs not launching apps correctly 2026-03-21 22:01:00 +00:00
OpenSauce
7f9f1e90ca
Added prerelease badge to readme 2026-03-20 18:42:21 +00:00
OpenSauce04
04a543290a Added AI policy document 2026-03-20 14:15:36 +00:00
OpenSauce04
8bcb8a225a Updated translations via Transifex 2026-03-20 12:42:47 +00:00
David Griswold
64cb0b57fb nullptr check on update_surface 2026-03-20 12:38:41 +00:00
OpenSauce04
7a60160f68 Updated translations via Transifex 2026-03-19 14:50:43 +00:00
OpenSauce04
dc91e8803e Updated compatibility data 2026-03-19 14:36:28 +00:00
PabloMK7
c55435b78d
android: Fix lifecycle bugs on SetupFragment (#1902)
* android: Attempt fixing lifecycle bugs on SetupFragment

* android: Fixed setup page number being lost on recreation

* Move the registerForActivityResult to MainActivity

* Code cleanup

* ViewUtils.kt: Added missing guard clause in showView

* Fixed permission buttons appearing to duplicate during setup

* ViewUtils.kt: Updated license header

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-03-19 13:48:58 +01:00
David Griswold
f721a474e4
force android emu_window to update height and width on surface change, solving aspect ratio issues on some screens (#1907) 2026-03-19 13:46:56 +01:00
PabloMK7
ab39df3ff0
android: Handle asynchronous screen disconnects (#1903) 2026-03-17 19:24:30 +01:00
OpenSauce04
2ff04dccba Removed confusing punctuation from "Failed to obtain loader" log message 2026-03-17 12:25:54 +00:00
PabloMK7
3d5ba09eb1
android: Fix launching applications through intent data in vanilla build (#1896)
* android: Fix launching applications through intent data in vanilla build

* GameHelper.kt: Use Uri.scheme where applicable

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-03-17 12:15:33 +00:00
Cobalt
ae9972b6be
Qt compat fix (again) (#1895)
* fix compilation on older QT6

* fix indent

my C++ is very rusty

* fix indents again

* Fixed formatting

* fix capitalization error

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-03-15 18:29:30 +00:00
OpenSauce04
e677f72bda android: Fixed onResume attempting to pause instead of unpause 2026-03-15 15:50:05 +00:00
OpenSauce04
4109bb200b android: Show Azahar version in toast when double-clicking on Applications 2026-03-15 14:51:53 +00:00
PabloMK7
6ad642a984
android: camera: Fix still image camera input (#1892) 2026-03-15 15:17:50 +01:00
Cobalt
ccd61d0134
qt: fix compilation on older QT6 (#1886)
* fix compilation on older QT6

* fix indent

my C++ is very rusty

* fix indents again

* Fixed formatting

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-03-15 13:46:50 +01:00
OpenSauce04
d97da17263 android: Fixed installed app shortcut creation failing on vanilla 2026-03-14 19:47:00 +00:00
OpenSauce04
0ff2aebdf1 android: Fixed Amiibo files failing to load on vanilla 2026-03-14 17:57:03 +00:00
Lillie
100b00b3b5 Fix typo "cartidges" 2026-03-13 10:02:38 +00:00
OpenSauce04
9e162705f4 Updated translations via Transifex 2026-03-12 20:54:03 +00:00
PabloMK7
b3f82618d7 android: Use StorageManager to get removable media path 2026-03-12 20:49:20 +00:00
OpenSauce04
fc6a410dfa android: Separate package IDs for build variants 2026-03-12 20:45:19 +00:00
OpenSauce04
56a563c239 macos: Add warning dialog when launching azahar executable directly 2026-03-12 18:14:12 +00:00
OpenSauce04
6715959382 shader_jit_a64_compiler: Added missing include
Fixes a build issue on ARM64 Linux w/ GCC

Fix proposed by PabloMK7
2026-03-12 17:18:29 +00:00
OpenSauce04
75cd4ce649 ci: Add build tests for ARM Linux
No binaries yet
2026-03-12 16:14:43 +00:00
Eric Warmenhoven
463db8ffe4 Move libretro ci file to .ci 2026-03-12 15:18:44 +00:00
Eric Warmenhoven
ecaebc54ff Rename libretro ci file 2026-03-12 15:01:29 +00:00
David Griswold
1febb83942
qt: Add controller touchpad support (#777) 2026-03-12 00:21:17 +01:00
PabloMK7
a3db3be4a6
video_core: vulkan: Fix Framebuffer move behaviour (#1865) 2026-03-11 23:36:03 +01:00
PabloMK7
909e4b7e1f
core: apt: Fix GetStartupArgument operation order (#1862) 2026-03-11 18:48:15 +01:00
PabloMK7
e92272ce31
core: fs: Implement NAND archives (#1861) 2026-03-11 15:06:28 +01:00
PabloMK7
51170ea85d
core: ac: Implement GetNZoneBeaconNotFoundEvent (#1860) 2026-03-11 13:49:56 +01:00
PabloMK7
9a7cc43d81
core: kernel: Set debug thread name based on process name (#1859) 2026-03-11 13:49:39 +01:00
OpenSauce04
e351fa56ce Updated translations via Transifex 2026-03-10 19:25:21 +00:00
OpenSauce04
845fadf49e android: Fix IOFile::GetFd not functioning as expected in vanilla
This fixes the cheats menu not loading correctly
2026-03-10 19:17:37 +00:00
Eric Warmenhoven
98910fed1c default libretro "touch support" option on 2026-03-10 16:28:22 +00:00
RedBlackAka
d9f28c5b2a
Qt: Improve update checker system to prevent downgrades #1749 (#1768)
* Qt: Improve update checker system to prevent downgrades

* Code cleanup

* Return 0 as fallback

* Satisfy clang-format

---------

Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
2026-03-10 16:01:40 +00:00
OpenSauce04
784fc8cca9 Updated translations via Transifex 2026-03-08 19:45:39 +00:00
OpenSauce04
a35a619903 ci: Add version suffix to libretro archive filenames 2026-03-08 19:43:38 +00:00
Eric Warmenhoven
3a5fa35449 libretro: draw cursor in vulkan 2026-03-08 19:04:10 +00:00
OpenSauce04
0407568006 android: Fix long-press menu for games in app dir displaying no file error 2026-03-08 18:46:09 +00:00
PabloMK7
30779d35cd Fix 3DSX being treated as invalid applications 2026-03-08 18:46:09 +00:00
PabloMK7
32da5ea0ae Read media type and pass it to UninstallProgram 2026-03-08 18:46:09 +00:00
OpenSauce04
e878174df8 android: Fixed games located in an application directory not being accessible 2026-03-08 18:46:09 +00:00
OpenSauce04
7ad6621f91 android: Fixed CIA installation failure in vanilla variant
This introduces a very hacky way of telling TranslateFilePath that a path is already native and doesn't need translating. I don't like this very much, but addressing this in any other way is very much outside of the scope of this PR.
2026-03-08 18:46:09 +00:00
OpenSauce04
8e1ffc1bdc Fixed a possible app crash when calling AndroidStorage::GetUserDirectory 2026-03-08 18:46:09 +00:00
OpenSauce04
96485a22f8 android: Split path resolution logic of getUserDirectory into seperate function 2026-03-08 18:46:09 +00:00
OpenSauce04
97c9a51015 android: Re-implement title uninstallation via Service::AM::UninstallProgram 2026-03-08 18:46:09 +00:00
OpenSauce04
a8ebd0f551 DocumentsTree: Put resolvePath under a strict directory whitelist 2026-03-08 18:46:09 +00:00
OpenSauce04
fac63ce6b1 DocumentsTree: Re-implement getFilename without resolvePath 2026-03-08 18:46:09 +00:00
OpenSauce04
c71b2dc822 Move AndroidCanUseRawFS and AndroidTranslateFilename into AndroidStorage namespace 2026-03-08 18:46:09 +00:00
PabloMK7
e87635095a android: Fully use raw FS access on vanilla builds 2026-03-08 18:46:09 +00:00
PabloMK7
0c624f16a7
core: Stub AC::CancelConnectAsync (#1846) 2026-03-08 19:26:39 +01:00
PabloMK7
d1d14cef79
core: Disable BOSS for enabled LLE online services (#1847) 2026-03-08 19:24:52 +01:00
PabloMK7
7d5da9eaeb
core: Fix application jump parameters (#1845) 2026-03-08 18:41:31 +01:00
lannoene
abc1980418
Add DLP:SRVR + misc bug fixes (#1828)
* Add DLP:SRVR + add friend code seed hack for LM1 + add multiple filters in IPC debugger + fix cia_container smdh offset not being applied, possible IF statement underflowing + default initialize boss variables + fix IPC header asserts in AM functions + add extra debug info to IPC param assert

* Make server & client more resistant to high ping conditions

* Remove DLP from list of online recommended modules

* Fix license headers + fix clang formatting + fix server create network assert

* Fix recorder.cpp license header
2026-03-08 15:48:09 +01:00
Fausto Núñez Alberro
70c9e18eea
libretro: enable VK_EXT_custom_border_color extension (#1825)
Fixes crash on startup with Vulkan renderer. The extension and its
features must be enabled during device creation for samplers using
custom border colors to work.

Fixes #1824
2026-03-07 21:56:44 +01:00
PabloMK7
46ca83cc36
core: Enable LLE CECD and BOSS when online LLE modules are enabled (#1842) 2026-03-07 21:11:45 +01:00
PabloMK7
1e0df67cc4
file_util: Fix file behaviour on Windows (#1841) 2026-03-07 20:33:29 +01:00
PabloMK7
ced1ec0112
core: nwm: Implement NWM_SOC::GetMACAddress (#1840) 2026-03-07 17:53:10 +01:00
Fausto Núñez Alberro
1b41c78afc
libretro: expose large_screen_proportion as a core option (#1833)
Adds the large_screen_proportion setting to libretro core options,
allowing users to adjust the ratio between the large and small screens
in the "Large Screen, Small Screen" layout.

This is useful on devices like the Steam Deck where the default 4x ratio
makes the small screen too tiny to be practical.

Values range from 1.00x to 6.00x in 0.25 increments.

Closes #1832
2026-03-07 16:44:27 +01:00
PabloMK7
748b97ac5e
core: ndm: Implement suspend daemons count (#1839) 2026-03-07 14:26:40 +01:00
PabloMK7
2207be30a9
core: Add "toggle unique data console type" option (#1826) 2026-03-06 01:23:35 +01:00
PabloMK7
8d284aeccf
video_core: Fix a few vulkan validation issues (#1818) 2026-03-04 21:05:22 +01:00
jbm11208
d49aa070fd
qt: Always receive camera data from UI thread (#1812)
* Make camera functions thread-safe

* Revert redundant changes to qt_multimedia_camera.h and add comments to qt_camera_base.cpp
2026-03-04 16:58:20 +01:00
OpenSauce04
92cd488754 Move version numbers to end of release file filenames 2026-03-04 14:48:04 +00:00
OpenSauce04
efccedbbd2 cmake: Version info generation improvements
- Allow GIT-COMMIT and GIT-TAG files to override real git info (useful for testing version-related functionality such as update checks)
- Always re-configure scm_rev.cpp when configuring with CMake (fixes an issue where the version number would just not update in incremental builds)
2026-03-04 11:45:55 +00:00
OpenSauce04
93e831decb android: Fixed invalid default config content, resulting in a crash 2026-03-03 18:34:53 +00:00
340 changed files with 33080 additions and 12369 deletions

View file

@ -12,6 +12,9 @@ fi
echo "Tag name is: $TAG_NAME"
docker build -f docker/azahar-room/Dockerfile -t azahar-room:$TAG_NAME .
docker build --no-cache -f docker/azahar-room/Dockerfile -t azahar-room:$TAG_NAME .
mkdir -p build
docker save azahar-room:$TAG_NAME > build/azahar-room-$TAG_NAME.dockerimage
FILENAME="azahar-room-$TARGET-$TAG_NAME.dockerimage"
docker save azahar-room:$TAG_NAME > build/$FILENAME
echo "DOCKER_IMAGE_PATH=artifacts/$FILENAME" >> $GITHUB_ENV

13
.ci/libretro-pack.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/bash -ex
# Determine the full revision name.
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`"
REV_NAME="azahar-libretro-$OS-$TARGET-$GITDATE-$GITREV"
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
REV_NAME="azahar-libretro-$OS-$TARGET-$GITHUB_REF_NAME"
fi
# Create .zip
zip -j -9 $REV_NAME.zip $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*

View file

@ -1,6 +1,6 @@
#!/bin/bash -ex
if [[ "$TARGET" == "appimage"* ]]; then
if [[ "$TARGET" == "appimage"* ]] || [[ "$TARGET" == "clang"* ]]; then
# Compile the AppImage we distribute with Clang.
export EXTRA_CMAKE_FLAGS=(-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_C_COMPILER=clang

View file

@ -2,13 +2,14 @@
ARTIFACTS_LIST=($ARTIFACTS)
BUNDLE_DIR=build/bundle
mkdir build
BUILD_DIR=build
UNIVERSAL_DIR=$BUILD_DIR/universal
BUNDLE_DIR=$UNIVERSAL_DIR/bundle
OTHER_BUNDLE_DIR=$BUILD_DIR/x86_64/bundle
# Set up the base artifact to combine into.
BASE_ARTIFACT=${ARTIFACTS_LIST[0]}
BASE_ARTIFACT_ARCH="${BASE_ARTIFACT##*-}"
mv $BASE_ARTIFACT $BUNDLE_DIR
# Set up the base bundle to combine into.
mkdir $UNIVERSAL_DIR
cp -a $BUILD_DIR/arm64/bundle $UNIVERSAL_DIR
# Executable binary paths that need to be combined.
BIN_PATHS=(Azahar.app/Contents/MacOS/azahar)
@ -19,21 +20,18 @@ DYLIB_PATHS=($(cd $BUNDLE_DIR && find . -name '*.dylib'))
unset IFS
# Combine all of the executable binaries and dylibs.
for OTHER_ARTIFACT in "${ARTIFACTS_LIST[@]:1}"; do
OTHER_ARTIFACT_ARCH="${OTHER_ARTIFACT##*-}"
for BIN_PATH in "${BIN_PATHS[@]}"; do
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_BUNDLE_DIR/$BIN_PATH
done
for BIN_PATH in "${BIN_PATHS[@]}"; do
lipo -create -output $BUNDLE_DIR/$BIN_PATH $BUNDLE_DIR/$BIN_PATH $OTHER_ARTIFACT/$BIN_PATH
done
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
for DYLIB_PATH in "${DYLIB_PATHS[@]}"; do
# Only merge if the libraries do not have conflicting arches, otherwise it will fail.
DYLIB_INFO=`file $BUNDLE_DIR/$DYLIB_PATH`
OTHER_DYLIB_INFO=`file $OTHER_ARTIFACT/$DYLIB_PATH`
if ! [[ "$DYLIB_INFO" =~ "$OTHER_ARTIFACT_ARCH" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "$BASE_ARTIFACT_ARCH" ]]; then
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_ARTIFACT/$DYLIB_PATH
fi
done
OTHER_DYLIB_INFO=`file $OTHER_BUNDLE_DIR/$DYLIB_PATH`
if ! [[ "$DYLIB_INFO" =~ "x86_64" ]] && ! [[ "$OTHER_DYLIB_INFO" =~ "arm64" ]]; then
lipo -create -output $BUNDLE_DIR/$DYLIB_PATH $BUNDLE_DIR/$DYLIB_PATH $OTHER_BUNDLE_DIR/$DYLIB_PATH
fi
done
# Remove leftover libs so that they aren't distributed

View file

@ -4,12 +4,10 @@ if [ "$GITHUB_REF_TYPE" == "tag" ]; then
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
fi
mkdir build && cd build
cmake .. -GNinja \
mkdir -p build/$BUILD_ARCH && cd build/$BUILD_ARCH
cmake ../.. -GNinja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES="$TARGET" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OSX_ARCHITECTURES="$BUILD_ARCH" \
-DENABLE_QT_TRANSLATION=ON \
-DENABLE_ROOM_STANDALONE=OFF \
-DUSE_DISCORD_PRESENCE=ON \
@ -18,9 +16,8 @@ ninja
ninja bundle
mv ./bundle/azahar.app ./bundle/Azahar.app # TODO: Can this be done in CMake?
ccache -s -v
CURRENT_ARCH=`arch`
if [ "$TARGET" = "$CURRENT_ARCH" ]; then
if [ "$BUILD_ARCH" = "$CURRENT_ARCH" ]; then
ctest -VV -C Release
fi

26
.ci/mxe.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/bash -ex
# TODO: Why doesn't the CI environment use the PATH set in the Dockerimage?
# It works fine when using the image locally.
export PATH="/mxe/usr/bin:${PATH}"
mkdir build && cd build
if [ "$GITHUB_REF_TYPE" == "tag" ]; then
export EXTRA_CMAKE_FLAGS=(-DENABLE_QT_UPDATE_CHECKER=ON)
fi
x86_64-w64-mingw32.shared-cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_DISCORD_PRESENCE=ON \
-DUSE_SYSTEM_BOOST=ON \
-DUSE_SYSTEM_CRYPTOPP=ON \
"${EXTRA_CMAKE_FLAGS[@]}"
x86_64-w64-mingw32.shared-cmake --build . -- -j$(nproc)
x86_64-w64-mingw32.shared-strip -s bin/Release/*.exe
make bundle
ccache -s -v

View file

@ -3,20 +3,21 @@
# Determine the full revision name.
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`"
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
# Determine the name of the release being built.
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
RELEASE_NAME=azahar-$GITHUB_REF_NAME
REV_NAME="azahar-$GITHUB_REF_NAME-$OS-$TARGET"
else
RELEASE_NAME=azahar-head
fi
# Archive and upload the artifacts.
mkdir -p artifacts
function pack_artifacts() {
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
# Determine the name of the release being built.
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
RELEASE_NAME=azahar-$GITHUB_REF_NAME
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
else
RELEASE_NAME=azahar-head
fi
ARTIFACTS_PATH="$1"
# Set up root directory for archive.
@ -35,10 +36,10 @@ function pack_artifacts() {
fi
# Create .zip/.tar.gz
if [ "$OS" = "windows" ]; then
if [ "$OS" = "windows" ] && [ "$TARGET" != "mxe" ]; then
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
powershell Compress-Archive "$REV_NAME" "$ARCHIVE_FULL_NAME"
elif [ "$OS" = "android" ] || [ "$OS" = "macos" ]; then
elif [ "$OS" = "android" ] || [ "$OS" = "macos" ] || [ "$TARGET" = "mxe" ]; then
ARCHIVE_FULL_NAME="$ARCHIVE_NAME.zip"
zip -r "$ARCHIVE_FULL_NAME" "$REV_NAME"
else
@ -56,11 +57,23 @@ if [ -n "$UNPACKED" ]; then
FILENAME=$(basename "$ARTIFACT")
EXTENSION="${FILENAME##*.}"
# TODO: Deduplicate
REV_NAME="azahar-$OS-$TARGET-$GITDATE-$GITREV"
# Determine the name of the release being built.
if [ "$GITHUB_REF_TYPE" = "tag" ]; then
RELEASE_NAME=azahar-$GITHUB_REF_NAME
REV_NAME="azahar-$OS-$TARGET-$GITHUB_REF_NAME"
else
RELEASE_NAME=azahar-head
fi
mv "$ARTIFACT" "artifacts/$REV_NAME.$EXTENSION"
done
elif [ -n "$PACK_INDIVIDUALLY" ]; then
# Pack and upload the artifacts one-by-one.
for ARTIFACT in build/bundle/*; do
TARGET=$(basename "$ARTIFACT")
pack_artifacts "$ARTIFACT"
done
else

View file

@ -1,3 +1,6 @@
- [ ] I have read the [Azahar AI Policy document](https://github.com/azahar-emu/azahar/blob/master/AI-POLICY.md) and have disclosed any use of AI if applicable under those terms.
---------
---
<!--
If you are contributing to Azahar for the first time please

View file

@ -7,28 +7,48 @@ on:
pull_request:
branches: [ master ]
permissions:
id-token: write
contents: read
attestations: write
jobs:
source:
if: ${{ !github.head_ref }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Pack
run: ./.ci/source.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: ./
format: spdx-json
output-file: artifacts/source.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: source
path: artifacts/
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
artifacts/*.tar.xz
sbom-path: artifacts/source.spdx.json
linux:
linux-x86_64:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: ["appimage", "appimage-wayland", "fresh"]
target: ["appimage", "appimage-wayland", "gcc-nopch"]
container:
image: opensauce04/azahar-build-environment:latest
options: -u 1001
@ -39,19 +59,20 @@ jobs:
OS: linux
TARGET: ${{ matrix.target }}
SHOULD_RUN: ${{ (matrix.target != 'appimage-wayland' || github.ref_type == 'tag') }}
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
if: ${{ env.SHOULD_RUN == 'true' }}
with:
submodules: recursive
- name: Set up cache
if: ${{ env.SHOULD_RUN == 'true' }}
uses: actions/cache@v4
if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }}
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-
${{ github.job }}-${{ matrix.target }}-
- name: Build
if: ${{ env.SHOULD_RUN == 'true' }}
run: ./.ci/linux.sh
@ -64,93 +85,132 @@ jobs:
if: ${{ matrix.target == 'appimage-wayland' && env.SHOULD_RUN == 'true' }}
run: |
mv artifacts/azahar.AppImage artifacts/azahar-wayland.AppImage
- name: Generate SBOM
if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json
upload-artifact: false
- name: Upload
if: ${{ contains(matrix.target, 'appimage') && env.SHOULD_RUN == 'true' }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
name: ${{ github.job }}-${{ matrix.target }}
path: artifacts/
- name: Attest artifacts
if: ${{ contains(matrix.target, 'appimage') && github.ref_type == 'tag' && env.SHOULD_RUN == 'true' }}
uses: actions/attest@v4
with:
subject-path: |
artifacts/*.AppImage
sbom-path: artifacts/linux-x86_64-${{ matrix.target }}.spdx.json
macos:
runs-on: ${{ (matrix.target == 'x86_64' && 'macos-26-intel') || 'macos-26' }}
linux-arm64:
runs-on: ubuntu-24.04-arm
strategy:
fail-fast: false
matrix:
target: ["x86_64", "arm64"]
target: ["clang", "gcc-nopch"]
container:
image: opensauce04/azahar-build-environment:latest
options: -u 1001
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: macos
OS: linux
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
key: ${{ github.job }}-${{ matrix.target }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-
${{ github.job }}-${{ matrix.target }}-
- name: Build
run: ./.ci/linux.sh
macos:
runs-on: 'macos-26'
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
OS: macos
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up cache
if: ${{ env.CACHE_ENABLED == 'true' }}
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-
- name: Install tools
run: brew install ccache ninja spirv-tools
- name: Build
run: ./.ci/macos.sh
- name: Prepare outputs for caching
run: cp -R build/bundle $OS-$TARGET
- name: Cache outputs for universal build
uses: actions/cache/save@v4
with:
path: ${{ env.OS }}-${{ env.TARGET }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
- name: Pack
run: ./.ci/pack.sh
- name: Upload
uses: actions/upload-artifact@v4
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
macos-universal:
runs-on: macos-26
needs: macos
env:
OS: macos
TARGET: universal
steps:
- uses: actions/checkout@v4
- name: Download x86_64 build from cache
uses: actions/cache/restore@v4
with:
path: ${{ env.OS }}-x86_64
key: ${{ runner.os }}-x86_64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Download ARM64 build from cache
uses: actions/cache/restore@v4
with:
path: ${{ env.OS }}-arm64
key: ${{ runner.os }}-arm64-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}
fail-on-cache-miss: true
- name: Build (x86_64)
run: BUILD_ARCH=x86_64 ./.ci/macos.sh
- name: Build (arm64)
run: BUILD_ARCH=arm64 ./.ci/macos.sh
- name: Create universal app
run: ./.ci/macos-universal.sh
env:
ARTIFACTS: ${{ env.OS }}-x86_64 ${{ env.OS }}-arm64
- name: Prepare for packing
run: |
mkdir build/bundle
cp -r build/x86_64/bundle build/bundle/x86_64
cp -r build/arm64/bundle build/bundle/arm64
cp -r build/universal/bundle build/bundle/universal
- name: Pack
env:
PACK_INDIVIDUALLY: 1
run: ./.ci/pack.sh
- name: Upload
uses: actions/upload-artifact@v4
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: build/
format: spdx-json
output-file: artifacts/macos.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}
path: artifacts/
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
artifacts/*.zip
sbom-path: artifacts/macos.spdx.json
windows:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
target: ["msvc", "msys2"]
include:
- target: msvc
os: windows-latest
- target: msys2
os: windows-latest
- target: mxe
os: ubuntu-latest
container:
image: opensauce04/azahar-build-environment:latest
options: -u 1001
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
defaults:
run:
shell: ${{ (matrix.target == 'msys2' && 'msys2') || 'bash' }} {0}
@ -158,14 +218,16 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
OS: windows
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up cache
uses: actions/cache@v4
if: ${{ env.CACHE_ENABLED == 'true' }}
uses: actions/cache@v5
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ matrix.target }}-${{ github.sha }}
@ -173,7 +235,7 @@ jobs:
${{ runner.os }}-${{ matrix.target }}-
- name: Set up MSVC
if: ${{ matrix.target == 'msvc' }}
uses: ilammy/msvc-dev-cmd@v1
uses: azahar-emu/msvc-dev-cmd@v1
- name: Install extra tools (MSVC)
if: ${{ matrix.target == 'msvc' }}
run: choco install ccache ninja ptime wget
@ -194,34 +256,62 @@ jobs:
qt6-base:p qt6-multimedia:p qt6-multimedia-wmf:p qt6-tools:p qt6-translations:p
- name: Install extra tools (MSYS2)
if: ${{ matrix.target == 'msys2' }}
uses: crazy-max/ghaction-chocolatey@v3
uses: crazy-max/ghaction-chocolatey@v4
with:
args: install ptime wget
- name: Install NSIS
if: ${{ github.ref_type == 'tag' }}
if: ${{ github.ref_type == 'tag' && matrix.target != 'mxe' }}
run: |
wget https://download.sourceforge.net/project/nsis/NSIS%203/3.11/nsis-3.11-setup.exe -O D:/a/_temp/nsis-setup.exe
ptime D:/a/_temp/nsis-setup.exe /S
shell: pwsh
- name: Disable line ending translation
run: git config --global core.autocrlf input
- name: Build
- name: Build (Native)
if: ${{ matrix.target != 'mxe' }}
run: ./.ci/windows.sh
- name: Generate installer
if: ${{ github.ref_type == 'tag' }}
- name: Build (MXE)
if: ${{ matrix.target == 'mxe' }}
run: ./.ci/mxe.sh
- name: Generate installer (Native)
if: ${{ github.ref_type == 'tag' && matrix.target != 'mxe' }}
run: |
cd src\installer
"C:\Program Files (x86)\NSIS\makensis.exe" /DPRODUCT_VARIANT=${{ matrix.target }} /DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi
mkdir ..\..\artifacts 2> NUL
move /y *.exe ..\..\artifacts\
shell: cmd
- name: Generate installer (MXE)
if: ${{ github.ref_type == 'tag' && matrix.target == 'mxe' }}
run: |
export PATH="/mxe/usr/bin:${PATH}" # TODO: Why do we have to do this if it's in the image?
cd src/installer
x86_64-w64-mingw32.shared-makensis -DPRODUCT_VARIANT=${{ matrix.target }} -DPRODUCT_VERSION=${{ github.ref_name }} citra.nsi
mkdir -p ../../artifacts
mv ./*.exe ../../artifacts/
- name: Pack
run: ./.ci/pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: artifacts/windows-${{ matrix.target }}.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: artifacts/
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
artifacts/*.zip
artifacts/*.exe
sbom-path: artifacts/windows-${{ matrix.target }}.spdx.json
android:
runs-on: ubuntu-latest
@ -233,17 +323,18 @@ jobs:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
CACHE_ENABLED: ${{ github.ref_type != 'tag' }}
OS: android
TARGET: ${{ matrix.target }}
SHOULD_RUN: ${{ (matrix.target == 'vanilla' || github.ref_type == 'tag') }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
if: ${{ env.SHOULD_RUN == 'true' }}
with:
submodules: recursive
- name: Set up cache
if: ${{ env.SHOULD_RUN == 'true' }}
uses: actions/cache@v4
if: ${{ env.SHOULD_RUN == 'true' && env.CACHE_ENABLED == 'true' }}
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
@ -281,23 +372,48 @@ jobs:
working-directory: src/android/app
env:
UNPACKED: 1
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: src/android
format: spdx-json
output-file: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json
upload-artifact: false
- name: Upload
if: ${{ env.SHOULD_RUN == 'true' }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: src/android/app/artifacts/
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
src/android/app/artifacts/*.apk
src/android/app/artifacts/*.aab
sbom-path: src/android/app/artifacts/android-${{ matrix.target }}.spdx.json
docker:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- target: x86_64
os: ubuntu-24.04
- target: arm64
os: ubuntu-24.04-arm
runs-on: ${{ matrix.os }}
container:
image: docker:dind
# Can't use docker:dind for ARM64 because it's Alpine-based, see https://github.com/actions/upload-artifact/issues/739
image: earthbuild/dind:ubuntu-24.04-docker-28.5.2-1
env:
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install tools
run: apk add bash
- name: Fix git ownership
run: git config --global --add safe.directory .
- name: Build Docker image
@ -306,8 +422,23 @@ jobs:
run: |
mkdir -p artifacts
mv build/*.dockerimage artifacts/
- name: Upload
uses: actions/upload-artifact@v4
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
name: docker
path: artifacts/
image: ${{ env.DOCKER_IMAGE_PATH }}
format: spdx-json
output-file: artifacts/docker-room.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v7
with:
name: docker-${{ env.TARGET }}
path: artifacts/
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
artifacts/*.dockerimage
sbom-path: artifacts/docker-room.spdx.json

View file

@ -20,7 +20,7 @@ jobs:
(github.event.pull_request.author_association != 'OWNER')
steps:
- name: Detect PR if author is first-time contributor
uses: actions/github-script@v7
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View file

@ -14,7 +14,7 @@ jobs:
if: github.event.issue.pull_request && contains(github.event.issue.labels.*.name, 'needs verification')
steps:
- name: Verify and reopen PR
uses: actions/github-script@v7
uses: actions/github-script@v9
with:
script: |
const { owner, repo } = context.repo;

View file

@ -13,7 +13,7 @@ jobs:
image: opensauce04/azahar-build-environment:latest
options: -u 1001
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Build

View file

@ -11,6 +11,11 @@ on:
env:
CORE_ARGS: -DENABLE_LIBRETRO=ON
permissions:
id-token: write
contents: read
attestations: write
jobs:
android:
runs-on: ubuntu-22.04
@ -23,7 +28,7 @@ jobs:
BUILD_DIR: build/android-arm64-v8a
EXTRA_PATH: bin/Release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Set tag name
@ -32,6 +37,10 @@ jobs:
echo "GIT_TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV
fi
echo $GIT_TAG_NAME
- name: Install tools
run: |
sudo apt-get update -y
sudo apt-get install -y llvm
- name: Update Android SDK CMake version
run: |
echo "y" | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "ndk;$ANDROID_NDK_VERSION"
@ -41,12 +50,32 @@ jobs:
export NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/$ANDROID_NDK_VERSION
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI . -B $BUILD_DIR
${ANDROID_SDK_ROOT}/cmake/3.30.3/bin/cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
zip -j -9 azahar-libretro-android-arm64.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-android.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-android.spdx.json
linux:
runs-on: ubuntu-22.04
env:
@ -56,19 +85,43 @@ jobs:
EXTRA_PATH: bin/Release
EXTRA_CORE_ARGS: -DCMAKE_C_COMPILER=gcc-12 -DCMAKE_CXX_COMPILER=g++-12 -DENABLE_LTO=OFF
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install tools
run: |
sudo apt-get update -y
sudo apt-get install -y llvm
- name: Build
run: |
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
cmake --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)
zip -j -9 azahar-libretro-linux-x86_64.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
llvm-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-linux.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-linux.spdx.json
windows:
runs-on: ubuntu-latest
env:
@ -77,10 +130,10 @@ jobs:
BUILD_DIR: build/windows-x86_64
EXTRA_CORE_ARGS: -DENABLE_LTO=OFF -G Ninja
CMAKE: x86_64-w64-mingw32.static-cmake
IMAGE: git.libretro.com:5050/libretro-infrastructure/libretro-build-mxe-win-cross-cores:mingw12
IMAGE: reallibretroretroarch/libretro-build-mxe-win-cross-cores:mingw12
EXTRA_PATH: bin/Release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Build in cross-container
@ -92,16 +145,36 @@ jobs:
$IMAGE \
bash -lc "\
${CMAKE} $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR && \
${CMAKE} --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc)"
zip -j -9 azahar-libretro-windows-x86_64.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
${CMAKE} --build $BUILD_DIR --target azahar_libretro --config Release -j $(nproc) && \
x86_64-w64-mingw32.static-strip -s $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*"
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-windows.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-windows.spdx.json
macos:
runs-on: macos-26
strategy:
fail-fast: false
matrix:
target: ["x86_64", "arm64"]
env:
@ -111,7 +184,7 @@ jobs:
BUILD_DIR: build/osx-${{ matrix.target }}
EXTRA_PATH: bin/Release
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Install tools
@ -120,12 +193,32 @@ jobs:
run: |
cmake $CORE_ARGS -DCMAKE_OSX_ARCHITECTURES=$TARGET . -B $BUILD_DIR
cmake --build $BUILD_DIR --target azahar_libretro --config Release
zip -j -9 azahar-libretro-macos-$TARGET.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-macos-${{ matrix.target }}.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-macos-${{ matrix.target }}.spdx.json
ios:
runs-on: macos-26
env:
@ -135,19 +228,39 @@ jobs:
EXTRA_PATH: bin/Release
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Build
run: |
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
cmake --build $BUILD_DIR --target azahar_libretro --config Release
zip -j -9 azahar-libretro-ios.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-ios.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-ios.spdx.json
tvos:
runs-on: macos-26
env:
@ -157,16 +270,35 @@ jobs:
EXTRA_PATH: bin/Release
EXTRA_CORE_ARGS: -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=-DIOS -DCMAKE_CXX_FLAGS=-DIOS -DIOS=ON -DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 -DCITRA_USE_PRECOMPILED_HEADERS=OFF -DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64 -DENABLE_OPT=OFF
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
- name: Build
run: |
cmake $CORE_ARGS $EXTRA_CORE_ARGS . -B $BUILD_DIR
cmake --build $BUILD_DIR --target azahar_libretro --config Release
zip -j -9 azahar-libretro-tvos.zip ${{ env.BUILD_DIR }}/${{ env.EXTRA_PATH }}/azahar_libretro.*
strip -x $BUILD_DIR/$EXTRA_PATH/azahar_libretro.*
- name: Pack
run: ./.ci/libretro-pack.sh
- name: Generate SBOM
if: ${{ github.ref_type == 'tag' }}
uses: anchore/sbom-action@v0
with:
path: build/
format: spdx-json
output-file: libretro-tvos.spdx.json
upload-artifact: false
- name: Upload
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ${{ env.OS }}-${{ env.TARGET }}
path: ./*.zip
path: |
./*.zip
./*.spdx.json
- name: Attest artifacts
if: ${{ github.ref_type == 'tag' }}
uses: actions/attest@v4
with:
subject-path: |
./*.zip
sbom-path: libretro-tvos.spdx.json

View file

@ -11,7 +11,7 @@ jobs:
image: opensauce04/azahar-build-environment:latest
options: -u 1001
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Fetch master branch

View file

@ -10,7 +10,7 @@ jobs:
permissions:
issues: write
steps:
- uses: actions/stale@v9.1.0
- uses: actions/stale@v10.2.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 90

View file

@ -7,10 +7,10 @@ on:
jobs:
transifex:
runs-on: ubuntu-latest
container: opensauce04/azahar-build-environment:transifex
container: opensauce04/azahar-build-environment:latest
if: ${{ github.repository == 'azahar-emu/azahar' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0

7
.gitignore vendored
View file

@ -56,3 +56,10 @@ repo/
.ccache/
node_modules/
VULKAN_SDK/
# Version info files
GIT-COMMIT
GIT-TAG
# verify-release.sh downloads
verify/

14
.gitmodules vendored
View file

@ -55,18 +55,12 @@
[submodule "sdl2"]
path = externals/sdl2/SDL
url = https://github.com/libsdl-org/SDL
[submodule "cryptopp-cmake"]
path = externals/cryptopp-cmake
url = https://github.com/abdes/cryptopp-cmake.git
[submodule "cryptopp"]
path = externals/cryptopp
url = https://github.com/weidai11/cryptopp.git
[submodule "dds-ktx"]
path = externals/dds-ktx
url = https://github.com/septag/dds-ktx
[submodule "openal-soft"]
path = externals/openal-soft
url = https://github.com/kcat/openal-soft
url = https://github.com/azahar-emu/openal-soft
[submodule "glslang"]
path = externals/glslang
url = https://github.com/KhronosGroup/glslang
@ -106,3 +100,9 @@
[submodule "externals/libretro-common"]
path = externals/libretro-common/libretro-common
url = https://github.com/libretro/libretro-common.git
[submodule "dllwalker"]
path = externals/dllwalker
url = https://github.com/azahar-emu/dllwalker
[submodule "externals/cryptopp"]
path = externals/cryptopp
url = https://github.com/cryptopp-modern/cryptopp-modern.git

20
AI-POLICY.md Normal file
View file

@ -0,0 +1,20 @@
# Azahar Emulator AI Use Policy
The following document outlines the acceptable and unacceptable uses of AI within the Azahar codebase.
It describes whether or not submissions which were exposed to large language models (LLMs) such as ChatGPT, Claude, DeepSeek, and similar models would be capable of being merged in a pull request or otherwise utilized.
- ✅ Use of AI to help developers discover or understand problems in the codebase is acceptable **under the condition that any discovered issue is independently verified by a human**.
- ✅ Use of AI to write code snippets of a sufficiently small size that they aren't reasonably copyrightable **with disclosure in the PR description** is acceptable.
- This will be handled on a case-by-case basis and is up to the interpretation of the maintainer, but generic algorithm snippets up to a maximum of approximately 5 lines of code are acceptable.
- ❌ Use of AI to write code for submission without disclosure is prohibited.
- ❌ Use of AI to write the entirety/ a significant portion of a contribution is prohibited.
- ❌ Use of AI to write snippets of code which are of a size such that they could reasonably be copyrightable is prohibited.
- ❌ Use of AI to rewrite incompatibly-licensed code for submission to Azahar is prohibited.
- ❌ Use of AI to autonomously submit pull requests or issues is prohibited.
Pull requests which violate these rules will be closed. Previously accepted submissions which are found to violate these rules will be retroactively removed from the codebase.
This document may be updated in the future if further clarification is required.
This policy is effective for code submitted on or after the 20th of March 2026.

View file

@ -13,6 +13,9 @@ cmake_policy(SET CMP0063 NEW)
cmake_policy(SET CMP0127 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
# Prefer building bundled dependencies as static instead of shared
set(BUILD_SHARED_LIBS OFF)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
include(DownloadExternals)
@ -25,6 +28,15 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
enable_language(OBJC OBJCXX)
endif()
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux" AND MINGW)
string(TOLOWER ${LIBTYPE} LIBTYPE_LOWER)
set(CMAKE_AR x86_64-w64-mingw32.${LIBTYPE_LOWER}-gcc-ar)
endif()
if (BSD STREQUAL "OpenBSD")
add_link_options(-z wxneeded)
endif()
option(ENABLE_LIBRETRO "Build as a LibRetro core" OFF)
# Some submodules like to pick their own default build type if not specified.
@ -95,7 +107,7 @@ endif()
# Track which options were explicitly set by the user (for libretro conflict detection)
set(_LIBRETRO_INCOMPATIBLE_OPTIONS
ENABLE_SDL2 ENABLE_QT ENABLE_WEB_SERVICE ENABLE_SCRIPTING
ENABLE_SDL2 ENABLE_QT ENABLE_WEB_SERVICE ENABLE_SCRIPTING ENABLE_GDBSTUB
ENABLE_OPENAL ENABLE_ROOM ENABLE_ROOM_STANDALONE ENABLE_CUBEB ENABLE_LIBUSB)
set(_USER_SET_OPTIONS "")
foreach(_opt IN LISTS _LIBRETRO_INCOMPATIBLE_OPTIONS)
@ -118,6 +130,7 @@ CMAKE_DEPENDENT_OPTION(ENABLE_ROOM_STANDALONE "Enable generating a standalone de
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(ENABLE_SCRIPTING "Enable RPC server for scripting" ON)
option(ENABLE_GDBSTUB "Enable GDB stub for emulated applications" ON)
CMAKE_DEPENDENT_OPTION(ENABLE_CUBEB "Enables the cubeb audio backend" ON "NOT IOS" OFF)
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
@ -126,7 +139,8 @@ CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support
CMAKE_DEPENDENT_OPTION(ENABLE_SOFTWARE_RENDERER "Enables the software renderer" ON "NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(ENABLE_OPENGL "Enables the OpenGL renderer" ${DEFAULT_ENABLE_OPENGL} "NOT APPLE" OFF)
option(ENABLE_VULKAN "Enables the Vulkan renderer" ON)
# NetBSD doesn't support Vulkan yet, remove this check when it does.
CMAKE_DEPENDENT_OPTION(ENABLE_VULKAN "Enables the Vulkan renderer" ON "NOT (BSD MATCHES \"NetBSD\")" OFF)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
@ -136,6 +150,8 @@ option(ENABLE_SSE42 "Enable SSE4.2 optimizations on x86_64" ON)
option(ENABLE_DEVELOPER_OPTIONS "Enable functionality targeted at emulator developers" OFF)
option(ENABLE_BUILTIN_KEYBLOB "Enable the inclusion of the default crypto keys blob" ON)
# Compile options
CMAKE_DEPENDENT_OPTION(COMPILE_WITH_DWARF "Add DWARF debugging information" ${IS_DEBUG_BUILD} "MINGW" OFF)
option(ENABLE_LTO "Enable link time optimization" ${DEFAULT_ENABLE_LTO})
@ -395,13 +411,21 @@ if (APPLE)
endif()
find_library(AVFOUNDATION_LIBRARY AVFoundation REQUIRED)
find_library(IOSURFACE_LIBRARY IOSurface REQUIRED)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY} ${MOLTENVK_LIBRARY})
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${AVFOUNDATION_LIBRARY} ${IOSURFACE_LIBRARY})
if (ENABLE_VULKAN AND NOT ENABLE_LIBRETRO)
if (NOT USE_SYSTEM_MOLTENVK)
if (USE_SYSTEM_MOLTENVK)
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
else()
download_moltenvk()
if (IOS)
set(MOLTENVK_RELATIVE_LIBPATH "static/MoltenVK.xcframework/ios-arm64/libMoltenVK.a")
else()
set(MOLTENVK_RELATIVE_LIBPATH "dynamic/dylib/macOS/libMoltenVK.dylib")
endif()
set(MOLTENVK_LIBRARY "${CMAKE_BINARY_DIR}/externals/MoltenVK/MoltenVK/${MOLTENVK_RELATIVE_LIBPATH}")
endif()
find_library(MOLTENVK_LIBRARY MoltenVK REQUIRED)
message(STATUS "Using MoltenVK at ${MOLTENVK_LIBRARY}.")
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} ${MOLTENVK_LIBRARY})
endif()

View file

@ -198,6 +198,10 @@ if (BUNDLE_TARGET_EXECUTE)
# On Linux, always bundle an AppImage.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
if (IS_MINGW)
return()
endif()
if (IN_PLACE)
message(FATAL_ERROR "Cannot bundle for Linux in-place.")
endif()
@ -273,15 +277,23 @@ else()
# On Linux, add a command to prepare linuxdeploy and any required plugins before any bundling occurs.
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_TARGET_DOWNLOAD_LINUXDEPLOY=1"
"-DLINUXDEPLOY_PATH=${CMAKE_BINARY_DIR}/externals/linuxdeploy"
"-DLINUXDEPLOY_ARCH=${CMAKE_HOST_SYSTEM_PROCESSOR}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
POST_BUILD)
if (MINGW)
add_custom_command(
TARGET bundle
# The target here is arbitrary
COMMAND cp -r "$<TARGET_FILE_DIR:citra_meta>/*" "${CMAKE_BINARY_DIR}/bundle/"
POST_BUILD)
else()
add_custom_command(
TARGET bundle
COMMAND ${CMAKE_COMMAND}
"-DBUNDLE_TARGET_DOWNLOAD_LINUXDEPLOY=1"
"-DLINUXDEPLOY_PATH=${CMAKE_BINARY_DIR}/externals/linuxdeploy"
"-DLINUXDEPLOY_ARCH=${CMAKE_HOST_SYSTEM_PROCESSOR}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
POST_BUILD)
endif()
endif()
endfunction()
@ -293,6 +305,11 @@ else()
create_base_bundle_target()
endif()
if (CMAKE_HOST_SYSTEM STREQUAL "Linux" AND MINGW)
# We don't really need to "bundle" MXE builds, so don't do anything
return()
endif()
set(bundle_executable_path "$<TARGET_FILE:${target_name}>")
if (bundle_qt AND APPLE)
# For Qt targets on Apple, expect an app bundle.
@ -331,6 +348,7 @@ else()
"-DBUNDLE_LIBRARY_PATHS=\"${bundle_library_paths}\""
"-DBUNDLE_QT=${bundle_qt}"
"-DIN_PLACE=${in_place}"
"-DIS_MINGW=${MINGW}"
"-DLINUXDEPLOY=${CMAKE_BINARY_DIR}/externals/linuxdeploy/squashfs-root/AppRun"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/BundleTarget.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")

View file

@ -0,0 +1,11 @@
function(disable_pax_mprotect target)
if (BSD STREQUAL "NetBSD")
add_custom_command(TARGET ${target} POST_BUILD
COMMAND paxctl +m "$<TARGET_FILE:${target}>"
COMMENT "Disabling PaX MPROTECT restrictions for '${target}'"
VERBATIM
)
else()
message(FATAL_ERROR "disable_pax_mprotect only applies on NetBSD.")
endif()
endfunction()

View file

@ -171,15 +171,8 @@ function(download_qt target)
endfunction()
function(download_moltenvk)
if (IOS)
set(MOLTENVK_PLATFORM "static/MoltenVK.xcframework/ios-arm64")
else()
set(MOLTENVK_PLATFORM "dynamic/dylib/macOS")
endif()
set(MOLTENVK_DIR "${CMAKE_BINARY_DIR}/externals/MoltenVK")
set(MOLTENVK_TAR "${CMAKE_BINARY_DIR}/externals/MoltenVK.tar")
if (NOT EXISTS ${MOLTENVK_DIR})
if (NOT EXISTS "${CMAKE_BINARY_DIR}/externals/MoltenVK")
if (NOT EXISTS ${MOLTENVK_TAR})
file(DOWNLOAD https://github.com/KhronosGroup/MoltenVK/releases/download/v1.2.9/MoltenVK-all.tar
${MOLTENVK_TAR} SHOW_PROGRESS)
@ -188,10 +181,6 @@ function(download_moltenvk)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${MOLTENVK_TAR}"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
endif()
# Add the MoltenVK library path to the prefix so find_library can locate it.
list(APPEND CMAKE_PREFIX_PATH "${MOLTENVK_DIR}/MoltenVK/${MOLTENVK_PLATFORM}")
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} PARENT_SCOPE)
endfunction()
function(get_external_prefix lib_name prefix_var)

View file

@ -1,4 +1,6 @@
macro(generate_build_info)
find_package(Git QUIET)
# Gets a UTC timstamp and sets the provided variable to it
function(get_timestamp _var)
string(TIMESTAMP timestamp UTC)
@ -6,9 +8,14 @@ macro(generate_build_info)
endfunction()
get_timestamp(BUILD_DATE)
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/externals/cmake-modules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/cmake-modules")
if (EXISTS "${SRC_DIR}/.git/objects")
if (EXISTS "${CMAKE_SOURCE_DIR}/GIT-COMMIT" AND EXISTS "${CMAKE_SOURCE_DIR}/GIT-TAG")
file(READ "${CMAKE_SOURCE_DIR}/GIT-COMMIT" GIT_REV_RAW LIMIT 64)
string(STRIP "${GIT_REV_RAW}" GIT_REV)
string(SUBSTRING "${GIT_REV_RAW}" 0 9 GIT_DESC)
set(GIT_BRANCH "HEAD")
elseif (EXISTS "${CMAKE_SOURCE_DIR}/.git/objects")
# Find the package here with the known path so that the GetGit commands can find it as well
find_package(Git QUIET PATHS "${GIT_EXECUTABLE}")
@ -17,12 +24,6 @@ macro(generate_build_info)
get_git_head_revision(GIT_REF_SPEC GIT_REV)
git_describe(GIT_DESC --always --long --dirty)
git_branch_name(GIT_BRANCH)
elseif (EXISTS "${SRC_DIR}/GIT-COMMIT" AND EXISTS "${SRC_DIR}/GIT-TAG")
# unified source archive
file(READ "${SRC_DIR}/GIT-COMMIT" GIT_REV_RAW LIMIT 64)
string(STRIP "${GIT_REV_RAW}" GIT_REV)
string(SUBSTRING "${GIT_REV_RAW}" 0 9 GIT_DESC)
set(GIT_BRANCH "HEAD")
else()
# self-packed archive?
set(GIT_REV "UNKNOWN")
@ -39,8 +40,8 @@ macro(generate_build_info)
if ($ENV{GITHUB_REF_TYPE} STREQUAL "tag")
set(GIT_TAG $ENV{GITHUB_REF_NAME})
endif()
elseif (EXISTS "${SRC_DIR}/GIT-COMMIT" AND EXISTS "${SRC_DIR}/GIT-TAG")
file(READ "${SRC_DIR}/GIT-TAG" GIT_TAG)
elseif (EXISTS "${CMAKE_SOURCE_DIR}/GIT-COMMIT" AND EXISTS "${CMAKE_SOURCE_DIR}/GIT-TAG")
file(READ "${CMAKE_SOURCE_DIR}/GIT-TAG" GIT_TAG)
string(STRIP ${GIT_TAG} GIT_TAG)
endif()

View file

@ -1,9 +1,10 @@
list(APPEND CMAKE_MODULE_PATH "${SRC_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules")
include(GenerateBuildInfo)
generate_build_info()
# The variable SRC_DIR must be passed into the script (since it uses the current build directory for all values of CMAKE_*_DIR)
set(VIDEO_CORE "${SRC_DIR}/src/video_core")
set(VIDEO_CORE "${CMAKE_SOURCE_DIR}/src/video_core")
set(HASH_FILES
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.cpp"
"${VIDEO_CORE}/renderer_opengl/gl_shader_disk_cache.h"
@ -47,4 +48,4 @@ foreach (F IN LISTS HASH_FILES)
set(COMBINED "${COMBINED}${TMP}")
endforeach()
string(MD5 SHADER_CACHE_VERSION "${COMBINED}")
configure_file("${SRC_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)
configure_file("${CMAKE_SOURCE_DIR}/src/common/scm_rev.cpp.in" "scm_rev.cpp" @ONLY)

View file

@ -17,6 +17,7 @@ foreach(KEY IN ITEMS
"use_virtual_sd"
"use_custom_storage"
"compress_cia_installs"
"async_fs_operations"
"region_value"
"init_clock"
"init_time"
@ -48,6 +49,7 @@ foreach(KEY IN ITEMS
"texture_filter"
"texture_sampling"
"delay_game_render_thread_us"
"simulate_3ds_gpu_timings"
"layout_option"
"swap_screen"
"upright_screen"
@ -107,6 +109,7 @@ foreach(KEY IN ITEMS
"output_device"
"input_type"
"input_device"
"simulate_headphones_plugged"
"delay_start_for_lle_modules"
"use_gdbstub"
"gdbstub_port"
@ -114,6 +117,8 @@ foreach(KEY IN ITEMS
"enable_rpc_server"
"log_filter"
"log_regex_filter"
"toggle_unique_data_console_type"
"break_on_unmapped_memory_access"
"use_integer_scaling"
"layouts_to_cycle"
"camera_inner_flip"
@ -198,6 +203,8 @@ if (ENABLE_QT)
"name"
"bind"
"profile"
"use_touchpad"
"controller_touch_device"
"use_touch_from_button"
"touch_from_button_map"
"touch_from_button_maps" # Why are these two so similar? Basically typo bait
@ -250,7 +257,7 @@ if (ENABLE_LIBRETRO)
"analog_deadzone"
"enable_mouse_touchscreen"
"enable_touch_touchscreen"
"render_touchscreen"
"enable_touch_pointer_timeout"
"enable_motion"
"motion_sensitivity"
)

View file

@ -1 +0,0 @@
**The Contributor's Guide has moved to [the wiki](https://github.com/citra-emu/citra/wiki/Contributing).**

View file

@ -1,6 +1,8 @@
![Azahar Emulator](https://azahar-emu.org/resources/images/logo/azahar-name-and-logo.svg)
![GitHub Release](https://img.shields.io/github/v/release/azahar-emu/azahar?label=Current%20Release)
![Current Release](https://img.shields.io/github/v/release/azahar-emu/azahar?label=Current%20Release)
![Current Prerelease](https://img.shields.io/github/v/release/azahar-emu/azahar?include_prereleases&label=Current%20Prerelease)
![GitHub Downloads](https://img.shields.io/github/downloads/azahar-emu/azahar/total?logo=github&label=GitHub%20Downloads)
![Google Play Downloads](https://playbadges.pavi2410.com/badge/downloads?id=io.github.lime3ds.android&pretty&label=Play%20Store%20Downloads)
![Flathub Downloads](https://img.shields.io/flathub/downloads/org.azahar_emu.Azahar?logo=flathub&label=Flathub%20Downloads)

@ -1 +1 @@
Subproject commit eadcdfb84b6f3b95734e867d99fe16a9e8db717f
Subproject commit d9f1126e42b606d02ecc89b10cb9a336a3b2f5a3

View file

@ -12,4 +12,4 @@ lang_map = ca@valencia:ca_ES_valencia
file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
source_file = ../../src/android/app/src/main/res/values/strings.xml
type = ANDROID
lang_map = es_ES:b+es+ES, hu_HU:b+hu+HU, ru_RU:b+ru+RU, pt_BR:b+pt+BR, zh_CN:b+zh+CN, pl_PL:b+pl+PL, ca@valencia:b+ca+ES+valencia, ko_KR:b+ko+KR, da_DK:b+da+DK, ja_JP:b+ja+JP, lt_LT:b+lt+LT, ro_RO:b+ro+RO, tr_TR:b+tr+TR, vi_VN:b+vi+VN, zh_TW:b+zh+TW
lang_map = ca@valencia:b+ca+ES+valencia, es_419:b+es+419, pt_BR:b+pt+BR, zh_CN:b+zh+CN, zh_TW:b+zh+TW

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1120
dist/languages/de.ts vendored

File diff suppressed because it is too large Load diff

1923
dist/languages/el.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

7928
dist/languages/es_419.ts vendored Normal file

File diff suppressed because it is too large Load diff

916
dist/languages/fi.ts vendored

File diff suppressed because it is too large Load diff

941
dist/languages/fr.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

916
dist/languages/id.ts vendored

File diff suppressed because it is too large Load diff

945
dist/languages/it.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1338
dist/languages/nb.ts vendored

File diff suppressed because it is too large Load diff

916
dist/languages/nl.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

957
dist/languages/sv.ts vendored

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -4,23 +4,19 @@
# --- Builder ----------------
FROM opensauce04/azahar-build-environment:latest AS builder
COPY . /var/azahar-src
RUN mkdir /var/azahar-src/
COPY ./ /var/azahar-src/
RUN mkdir builddir && cd builddir && \
cmake /var/azahar-src -G Ninja \
RUN mkdir ./builddir/
WORKDIR ./builddir/
RUN cmake /var/azahar-src -G Ninja \
-DENABLE_QT=OFF \
-DENABLE_GDBSTUB=OFF \
-DENABLE_TESTS=OFF \
-DENABLE_ROOM=ON \
-DENABLE_ROOM_STANDALONE=ON \
-DENABLE_OPENGL=OFF $( : "TODO: Can we disable these automatically when there's no frontend?") \
-DENABLE_VULKAN=OFF \
-DENABLE_SDL2=OFF \
-DENABLE_LIBUSB=OFF \
-DENABLE_CUBEB=OFF \
-DENABLE_OPENAL=OFF && \
ninja && \
mv bin/Release/azahar-room /usr/local/bin/ && \
cd .. && rm -rf builddir
-DENABLE_ROOM_STANDALONE=ON
RUN ninja
RUN mv ./bin/Release/azahar-room /usr/local/bin/
# --- Final ------------------
FROM debian:trixie AS final

View file

@ -57,9 +57,10 @@ if (ENABLE_TESTS)
else()
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
add_subdirectory(catch2)
add_subdirectory(catch2 EXCLUDE_FROM_ALL)
endif()
target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
include(Catch)
endif()
# Crypto++
@ -68,17 +69,9 @@ if(USE_SYSTEM_CRYPTOPP)
add_library(cryptopp INTERFACE)
target_link_libraries(cryptopp INTERFACE cryptopp::cryptopp)
else()
if (WIN32 AND NOT MSVC AND "arm64" IN_LIST ARCHITECTURE)
# TODO: CryptoPP ARM64 ASM does not seem to support Windows unless compiled with MSVC.
# TODO: See https://github.com/weidai11/cryptopp/issues/1260
set(CRYPTOPP_DISABLE_ASM ON CACHE BOOL "")
endif()
set(CRYPTOPP_BUILD_DOCUMENTATION OFF CACHE BOOL "")
set(CRYPTOPP_BUILD_TESTING OFF CACHE BOOL "")
set(CRYPTOPP_INSTALL OFF CACHE BOOL "")
set(CRYPTOPP_SOURCES "${CMAKE_SOURCE_DIR}/externals/cryptopp" CACHE STRING "")
add_subdirectory(cryptopp-cmake)
add_subdirectory(cryptopp EXCLUDE_FROM_ALL)
endif()
# dds-ktx
@ -111,7 +104,13 @@ endif()
# Oaknut
if ("arm64" IN_LIST ARCHITECTURE)
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
if(USE_SYSTEM_OAKNUT)
find_package(oaknut REQUIRED)
add_library(oaknut INTERFACE)
target_link_libraries(oaknut INTERFACE merry::oaknut)
else()
add_subdirectory(oaknut EXCLUDE_FROM_ALL)
endif()
endif()
# Dynarmic
@ -135,7 +134,7 @@ endif()
# getopt
if (MSVC)
add_subdirectory(getopt)
add_subdirectory(getopt EXCLUDE_FROM_ALL)
endif()
# inih
@ -144,7 +143,7 @@ if(USE_SYSTEM_INIH)
add_library(inih INTERFACE)
target_link_libraries(inih INTERFACE inih::inih inih::inir)
else()
add_subdirectory(inih)
add_subdirectory(inih EXCLUDE_FROM_ALL)
endif()
# MicroProfile
@ -167,7 +166,7 @@ if (NOT MSVC)
endif()
# Open Source Archives
add_subdirectory(open_source_archives)
add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
# faad2
add_subdirectory(faad2 EXCLUDE_FROM_ALL)
@ -206,12 +205,12 @@ add_subdirectory(teakra EXCLUDE_FROM_ALL)
# SDL2
if (ENABLE_SDL2 AND NOT USE_SYSTEM_SDL2)
add_subdirectory(sdl2)
add_subdirectory(sdl2 EXCLUDE_FROM_ALL)
endif()
# libusb
if (ENABLE_LIBUSB AND NOT USE_SYSTEM_LIBUSB)
add_subdirectory(libusb)
add_subdirectory(libusb EXCLUDE_FROM_ALL)
set(LIBUSB_INCLUDE_DIR "" PARENT_SCOPE)
set(LIBUSB_LIBRARIES usb PARENT_SCOPE)
endif()
@ -255,7 +254,7 @@ if(USE_SYSTEM_ENET)
add_library(enet INTERFACE)
target_link_libraries(enet INTERFACE libenet::libenet)
else()
add_subdirectory(enet)
add_subdirectory(enet EXCLUDE_FROM_ALL)
target_include_directories(enet INTERFACE ./enet/include)
endif()
@ -320,12 +319,8 @@ endif()
# OpenSSL
if (USE_SYSTEM_OPENSSL)
find_package(OpenSSL 1.1)
if (OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
endif()
endif()
if (NOT OPENSSL_FOUND)
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
else()
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
set(OPENSSLDIR "/etc/ssl/")
@ -367,7 +362,7 @@ target_compile_options(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
if (UNIX AND NOT APPLE)
add_subdirectory(gamemode)
add_subdirectory(gamemode EXCLUDE_FROM_ALL)
endif()
# cpp-jwt
@ -390,13 +385,13 @@ if(USE_SYSTEM_LODEPNG)
find_package(lodepng REQUIRED)
target_link_libraries(lodepng INTERFACE lodepng::lodepng)
else()
add_subdirectory(lodepng)
add_subdirectory(lodepng EXCLUDE_FROM_ALL)
endif()
# (xperia64): Only use libyuv on Android b/c of build issues on Windows and mandatory JPEG
if(ANDROID)
# libyuv
add_subdirectory(libyuv)
add_subdirectory(libyuv EXCLUDE_FROM_ALL)
target_include_directories(yuv INTERFACE ./libyuv/include)
endif()
@ -407,6 +402,9 @@ if (ENABLE_OPENAL)
find_package(OpenAL REQUIRED)
target_link_libraries(OpenAL INTERFACE OpenAL::OpenAL)
else()
if (BSD STREQUAL "OpenBSD")
set(ALSOFT_BACKEND_SOLARIS OFF CACHE BOOL "")
endif()
set(ALSOFT_EMBED_HRTF_DATA OFF CACHE BOOL "")
set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
set(ALSOFT_INSTALL OFF CACHE BOOL "")
@ -422,7 +420,7 @@ endif()
# OpenGL dependencies
if (ENABLE_OPENGL)
# Glad
add_subdirectory(glad)
add_subdirectory(glad EXCLUDE_FROM_ALL)
endif()
# Vulkan dependencies
@ -462,7 +460,7 @@ if (ENABLE_VULKAN)
set(ENABLE_CTEST OFF CACHE BOOL "")
set(ENABLE_HLSL OFF CACHE BOOL "")
set(BUILD_EXTERNAL OFF CACHE BOOL "")
add_subdirectory(glslang)
add_subdirectory(glslang EXCLUDE_FROM_ALL)
endif()
# sirit
@ -493,11 +491,24 @@ if (ENABLE_VULKAN)
else()
target_include_directories(vulkan-headers INTERFACE ./vulkan-headers/include)
target_disable_warnings(vulkan-headers)
if (BSD STREQUAL "NetBSD")
# There may be a better way to do this with
# find_package(X11), but I couldn't get
# CMake to do it, so we're depending on
# the x11-links package and assuming the
# prefix location. -OS
target_include_directories(vulkan-headers INTERFACE
/usr/pkg/share/x11-links/include)
elseif (BSD STREQUAL "OpenBSD")
# This is fine to hardcode because it'll never change
target_include_directories(vulkan-headers INTERFACE
/usr/X11R6/include)
endif()
endif()
# adrenotools
if (ANDROID AND "arm64" IN_LIST ARCHITECTURE)
add_subdirectory(libadrenotools)
add_subdirectory(libadrenotools EXCLUDE_FROM_ALL)
endif()
endif()
@ -513,4 +524,4 @@ elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64|armv8")
else()
target_compile_definitions(xxhash PRIVATE XXH_VECTOR=XXH_SCALAR)
message(STATUS "Disabling SIMD for xxHash")
endif()
endif()

2
externals/boost vendored

@ -1 +1 @@
Subproject commit f9b15f673a688982f78a5f63a49a27275b318e5f
Subproject commit 6a85c3100499e886e11c87a5c2109eedacea0a61

View file

@ -14,6 +14,7 @@ option(USE_SYSTEM_JSON "Use the system JSON (nlohmann-json3) package (instead of
option(USE_SYSTEM_DYNARMIC "Use the system dynarmic (instead of the bundled one)" OFF)
option(USE_SYSTEM_FMT "Use the system fmt (instead of the bundled one)" OFF)
option(USE_SYSTEM_XBYAK "Use the system xbyak (instead of the bundled one)" OFF)
option(USE_SYSTEM_OAKNUT "Use the system oaknut (instead of the bundled one)" OFF)
option(USE_SYSTEM_INIH "Use the system inih (instead of the bundled one)" OFF)
option(USE_SYSTEM_FFMPEG_HEADERS "Use the system FFmpeg headers (instead of the bundled one)" OFF)
option(USE_SYSTEM_GLSLANG "Use the system glslang and SPIR-V libraries (instead of the bundled ones)" OFF)
@ -40,6 +41,7 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_JSON "Disable system JSON" OFF "USE_SYSTEM
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_DYNARMIC "Disable system Dynarmic" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FMT "Disable system fmt" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_XBYAK "Disable system xbyak" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OAKNUT "Disable system oaknut" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_INIH "Disable system inih" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_FFMPEG_HEADERS "Disable system ffmpeg" OFF "USE_SYSTEM_LIBS" OFF)
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_GLSLANG "Disable system glslang" OFF "USE_SYSTEM_LIBS" OFF)
@ -66,6 +68,7 @@ set(LIB_VAR_LIST
DYNARMIC
FMT
XBYAK
OAKNUT
INIH
FFMPEG_HEADERS
GLSLANG

2
externals/cryptopp vendored

@ -1 +1 @@
Subproject commit 60f81a77e0c9a0e7ffc1ca1bc438ddfa2e43b78e
Subproject commit 8d92d788421483a43e09acf1cd4a2861cb2b8cab

@ -1 +0,0 @@
Subproject commit 00a151f8489daaa32434ab1f340e6750793ddf0c

1
externals/dllwalker vendored Submodule

@ -0,0 +1 @@
Subproject commit 2f8b349c26832cae612aa7082154c0697a9cbc8e

@ -1 +1 @@
Subproject commit 90191edd20bb877c5cbddfdac7ec0fe49ad93727
Subproject commit e399840fc6aba5f7bc3f0633e8ff10bba0640906

View file

@ -183,6 +183,9 @@ endif()
if(ENABLE_DEVELOPER_OPTIONS)
add_compile_definitions(ENABLE_DEVELOPER_OPTIONS)
endif()
if(ENABLE_BUILTIN_KEYBLOB)
add_compile_definitions(ENABLE_BUILTIN_KEYBLOB)
endif()
add_subdirectory(common)
add_subdirectory(core)
@ -200,6 +203,7 @@ if (ENABLE_QT)
endif()
if (ENABLE_QT) # Or any other hypothetical future frontends
add_subdirectory(citra_cli)
add_subdirectory(citra_meta)
endif()

View file

@ -24,12 +24,11 @@ val abiFilter = listOf("arm64-v8a", "x86_64")
val downloadedJniLibsPath = "${layout.buildDirectory.get().asFile.path}/downloadedJniLibs"
@Suppress("UnstableApiUsage")
android {
namespace = "org.citra.citra_emu"
compileSdkVersion = "android-35"
ndkVersion = "27.1.12297006"
ndkVersion = "27.3.13750724"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
@ -63,7 +62,7 @@ android {
defaultConfig {
// The application ID refers to Lime3DS to allow for
// the Play Store listing, which was originally set up for Lime3DS, to still be used.
applicationId = "io.github.lime3ds.android"
applicationId = "org.azahar_emu.azahar"
minSdk = 29
targetSdk = 35
versionCode = autoVersion
@ -80,7 +79,8 @@ android {
"-DENABLE_QT=0", // Don't use QT
"-DENABLE_SDL2=0", // Don't use SDL
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" // Support Android 15 16KiB page sizes
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", // Support Android 15 16KiB page sizes
"-DENABLE_GDBSTUB=OFF", // Disable GDB stub
)
}
}
@ -173,6 +173,7 @@ android {
register("googlePlay") {
dimension = "version"
versionNameSuffix = "-googleplay"
applicationId = "io.github.lime3ds.android"
}
}

View file

@ -19,12 +19,14 @@ import android.view.Surface
import android.view.View
import android.widget.TextView
import androidx.annotation.Keep
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.citra.citra_emu.activities.EmulationActivity
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.BuildUtil
import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.Log
@ -132,7 +134,27 @@ object NativeLibrary {
* If not set, it auto-detects a location
*/
external fun setUserDirectory(directory: String)
external fun getInstalledGamePaths(): Array<String?>
data class InstalledGame(
val path: String,
val mediaType: Game.MediaType
)
fun getInstalledGamePaths(): Array<InstalledGame> {
val games = getInstalledGamePathsImpl()
return games.mapNotNull { entry ->
entry?.let {
val sep = it.lastIndexOf('|')
if (sep == -1) return@mapNotNull null
val path = it.substring(0, sep)
val mediaType = Game.MediaType.fromInt(it.substring(sep + 1).toInt())
InstalledGame(path, mediaType!!)
}
}.toTypedArray()
}
private external fun getInstalledGamePathsImpl(): Array<String?>
// Create the config.ini file.
external fun createConfigFile()
@ -230,6 +252,16 @@ object NativeLibrary {
external fun playTimeManagerGetPlayTime(titleId: Long): Long
external fun playTimeManagerGetCurrentTitleId(): Long
private external fun uninstallTitle(titleId: Long, mediaType: Int): Boolean
fun uninstallTitle(titleId: Long, mediaType: Game.MediaType): Boolean {
return uninstallTitle(titleId, mediaType.value)
}
external fun nativeFileExists(path: String): Boolean
external fun deleteOpenGLShaderCache(titleId: Long)
external fun deleteVulkanShaderCache(titleId: Long)
private var coreErrorAlertResult = false
private val coreErrorAlertLock = Object()
@ -286,6 +318,12 @@ object NativeLibrary {
canContinue = false
}
CoreError.ErrorCoreExceptionRaised -> {
title = emulationActivity.getString(R.string.fatal_error)
message = emulationActivity.getString(R.string.fatal_error_message)
canContinue = false
}
CoreError.ErrorUnknown -> {
title = emulationActivity.getString(R.string.fatal_error)
message = emulationActivity.getString(R.string.fatal_error_message)
@ -408,7 +446,7 @@ object NativeLibrary {
return
}
if (resultCode == EmulationErrorDialogFragment.ShutdownRequested) {
if (resultCode == CoreError.ShutdownRequested.value) {
emulationActivity.finish()
return
}
@ -427,23 +465,51 @@ object NativeLibrary {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
emulationActivity = requireActivity() as EmulationActivity
var captionId = R.string.loader_error_invalid_format
val result = requireArguments().getInt(RESULT_CODE)
if (result == ErrorLoader_ErrorEncrypted) {
captionId = R.string.loader_error_encrypted
}
if (result == ErrorArticDisconnected) {
captionId = R.string.artic_base
var coreError = CoreError.fromInt(requireArguments().getInt(RESULT_CODE))
val title: String
val message: String
when (coreError) {
CoreError.ErrorGetLoader, CoreError.ErrorLoader_ErrorInvalidFormat, CoreError.ErrorSystemMode -> {
title = getString(R.string.loader_error_invalid_format)
message = getString(R.string.loader_error_invalid_format_description)
}
CoreError.ErrorLoader_ErrorEncrypted -> {
title = getString(R.string.loader_error_encrypted)
message = getString(R.string.loader_error_encrypted_description)
}
CoreError.ErrorArticDisconnected -> {
title = getString(R.string.artic_base)
message = getString(R.string.artic_server_comm_error)
}
CoreError.ErrorN3DSApplication -> {
title = getString(R.string.loader_error_invalid_system_mode)
message = getString(R.string.loader_error_invalid_system_mode_description)
}
CoreError.ErrorLoader_ErrorPatches -> {
title = getString(R.string.loader_error_applying_patches)
message = getString(R.string.loader_error_applying_patches_description)
}
CoreError.ErrorLoader_ErrorPatchesInvalidTitle -> {
title = getString(R.string.loader_error_applying_patches)
message = getString(R.string.loader_error_patch_wrong_application)
}
else -> {
title = getString(R.string.loader_error_generic_title)
message = getString(R.string.loader_error_generic,
getString(coreError.stringRes), coreError.value)
}
}
val alert = MaterialAlertDialogBuilder(requireContext())
.setTitle(captionId)
.setTitle(title)
.setMessage(
Html.fromHtml(
if (result == ErrorArticDisconnected)
CitraApplication.appContext.resources.getString(R.string.artic_server_comm_error)
else
CitraApplication.appContext.resources.getString(R.string.redump_games),
Html.fromHtml(message,
Html.FROM_HTML_MODE_LEGACY
)
)
@ -465,20 +531,6 @@ object NativeLibrary {
const val RESULT_CODE = "resultcode"
const val Success = 0
const val ErrorNotInitialized = 1
const val ErrorGetLoader = 2
const val ErrorSystemMode = 3
const val ErrorLoader = 4
const val ErrorLoader_ErrorEncrypted = 5
const val ErrorLoader_ErrorInvalidFormat = 6
const val ErrorLoader_ErrorGBATitle = 7
const val ErrorSystemFiles = 8
const val ErrorSavestate = 9
const val ErrorArticDisconnected = 10
const val ShutdownRequested = 11
const val ErrorUnknown = 12
fun newInstance(resultCode: Int): EmulationErrorDialogFragment {
val args = Bundle()
args.putInt(RESULT_CODE, resultCode)
@ -691,34 +743,47 @@ object NativeLibrary {
@Keep
@JvmStatic
fun getUserDirectory(uriOverride: Uri? = null): String {
fun getNativePath(uri: Uri): String {
BuildUtil.assertNotGooglePlay()
val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
val dirSep = "/"
val udUri = uriOverride ?:
preferences.getString("CITRA_DIRECTORY", "")!!.toUri()
val udPathSegment = udUri.lastPathSegment!!
val udVirtualPath = udPathSegment.substringAfter(":")
if (udPathSegment.startsWith("primary:")) { // User directory is located in primary storage
val uriString = uri.toString()
if (!uriString.contains(":")) { // These raw URIs happen when generating the game list. Why?
return uriString
}
if (uri.scheme == "file") {
return uri.path!!
}
val pathSegment = uri.lastPathSegment ?: return ""
val virtualPath = pathSegment.substringAfter(":")
if (pathSegment.startsWith("primary:")) { // User directory is located in primary storage
val primaryStoragePath = Environment.getExternalStorageDirectory().absolutePath
return primaryStoragePath + dirSep + udVirtualPath + dirSep
return primaryStoragePath + dirSep + virtualPath
} else { // User directory probably located on a removable storage device
val storageIdString = udPathSegment.substringBefore(":")
val udRemovablePath = RemovableStorageHelper.getRemovableStoragePath(storageIdString)
val storageIdString = pathSegment.substringBefore(":")
val removablePath = RemovableStorageHelper.getRemovableStoragePath(CitraApplication.appContext, storageIdString)
if (udRemovablePath == null) {
if (removablePath == null) {
android.util.Log.e("NativeLibrary",
"Unknown mount location for storage device '$storageIdString' (URI: $udUri)"
"Unknown mount location for storage device '$storageIdString' (URI: $uri)"
)
return ""
}
return udRemovablePath + dirSep + udVirtualPath + dirSep
return removablePath + dirSep + virtualPath
}
}
@Keep
@JvmStatic
fun getUserDirectory(): String {
val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
val userDirectoryUri = preferences.getString("CITRA_DIRECTORY", "")!!.toUri()
return getNativePath(userDirectoryUri)
}
@Keep
@ -812,12 +877,31 @@ object NativeLibrary {
FileUtil.deleteDocument(path)
}
enum class CoreError {
ErrorSystemFiles,
ErrorSavestate,
ErrorArticDisconnected,
ErrorN3DSApplication,
ErrorUnknown
enum class CoreError(val value: Int, @StringRes val stringRes: Int) {
Success(0, R.string.core_error_success),
ErrorNotInitialized(1, R.string.core_error_not_initialized),
ErrorGetLoader(2, R.string.core_error_get_loader),
ErrorSystemMode(3, R.string.core_error_system_mode),
ErrorLoader(4, R.string.core_error_loader),
ErrorLoader_ErrorEncrypted(5, R.string.core_error_loader_encrypted),
ErrorLoader_ErrorInvalidFormat(6, R.string.core_error_loader_invalid_format),
ErrorLoader_ErrorGBATitle(7, R.string.core_error_loader_gba_title),
ErrorLoader_ErrorPatches(8, R.string.core_error_loader_error_patches),
ErrorLoader_ErrorPatchesInvalidTitle(9, R.string.core_error_loader_patches_invalid_title),
ErrorSystemFiles(10, R.string.core_error_system_files),
ErrorSavestate(11, R.string.core_error_savestate),
ErrorArticDisconnected(12, R.string.core_error_artic_disconnected),
ErrorN3DSApplication(13, R.string.core_error_n3ds_application),
ErrorCoreExceptionRaised(14, R.string.core_error_core_exception_raised),
ErrorMemoryExceptionRaised(15, R.string.core_error_memory_exception_raised),
ShutdownRequested(16, R.string.core_error_shutdown_requested),
ErrorUnknown(17, R.string.core_error_unknown);
companion object {
fun fromInt(value: Int): CoreError {
return entries.find { it.value == value } ?: ErrorUnknown
}
}
}
enum class InstallStatus {

View file

@ -8,9 +8,9 @@ import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.InputDevice
import android.view.KeyEvent
@ -21,6 +21,7 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri
import androidx.core.os.BundleCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@ -43,6 +44,7 @@ import org.citra.citra_emu.features.settings.model.view.InputBindingSetting
import org.citra.citra_emu.fragments.EmulationFragment
import org.citra.citra_emu.fragments.MessageDialogFragment
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.BuildUtil
import org.citra.citra_emu.utils.ControllerMappingHelper
import org.citra.citra_emu.utils.FileBrowserHelper
import org.citra.citra_emu.utils.EmulationLifecycleUtil
@ -79,7 +81,9 @@ class EmulationActivity : AppCompatActivity() {
return navHostFragment.getChildFragmentManager().fragments.last() as EmulationFragment
}
private var isRotationBlocked: Boolean = true
private var isEmulationRunning: Boolean = false
private var isEmulationReady: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
@ -88,12 +92,20 @@ class EmulationActivity : AppCompatActivity() {
ThemeUtil.setTheme(this)
settingsViewModel.settings.loadSettings()
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
// Block orientation until emulation is ready to prevent unneccesary
// surface recreation until the renderer is ready.
isRotationBlocked = true
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
super.onCreate(savedInstanceState)
secondaryDisplay = SecondaryDisplay(this)
secondaryDisplay.updateDisplay()
binding = ActivityEmulationBinding.inflate(layoutInflater)
screenAdjustmentUtil = ScreenAdjustmentUtil(this, windowManager, settingsViewModel.settings)
hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this)
setContentView(binding.root)
@ -118,8 +130,6 @@ class EmulationActivity : AppCompatActivity() {
isEmulationRunning = true
instance = this
applyOrientationSettings() // Check for orientation settings at startup
val game = try {
intent.extras?.let { extras ->
BundleCompat.getParcelable(extras, "game", Game::class.java)
@ -135,13 +145,46 @@ class EmulationActivity : AppCompatActivity() {
NativeLibrary.playTimeManagerStart(game.titleId)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
NativeLibrary.stopEmulation()
NativeLibrary.playTimeManagerStop()
isEmulationReady = false
isRotationBlocked = true
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
emulationViewModel.setEmulationStarted(false)
val game = intent.extras?.let { extras ->
BundleCompat.getParcelable(extras, "game", Game::class.java)
}
if (game != null) {
NativeLibrary.playTimeManagerStart(game.titleId)
}
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
}
// On some devices, the system bars will not disappear on first boot or after some
// rotations. Here we set full screen immersive repeatedly in onResume and in
// onWindowFocusChanged to prevent the unwanted status bar state.
override fun onResume() {
super.onResume()
enableFullscreenImmersive()
applyOrientationSettings() // Check for orientation settings changes on runtime
if (isEmulationReady) {
// If emulation is ready then unblock rotation
isRotationBlocked = false
applyOrientationSettings()
emulationViewModel.setEmulationStarted(true)
} else {
if (!isRotationBlocked) {
applyOrientationSettings()
}
}
super.onResume()
}
override fun onStop() {
@ -150,8 +193,8 @@ class EmulationActivity : AppCompatActivity() {
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
enableFullscreenImmersive()
super.onWindowFocusChanged(hasFocus)
}
public override fun onRestart() {
@ -163,11 +206,15 @@ class EmulationActivity : AppCompatActivity() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean("isEmulationRunning", isEmulationRunning)
outState.putBoolean("isEmulationReady", isEmulationReady)
outState.putBoolean("isRotationBlocked", isRotationBlocked)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
isEmulationRunning = savedInstanceState.getBoolean("isEmulationRunning", false)
isEmulationReady = savedInstanceState.getBoolean("isEmulationReady", false)
isRotationBlocked = savedInstanceState.getBoolean("isRotationBlocked", isRotationBlocked)
}
override fun onDestroy() {
@ -221,6 +268,11 @@ class EmulationActivity : AppCompatActivity() {
fun onEmulationStarted() {
emulationViewModel.setEmulationStarted(true)
isEmulationReady = true
if (isRotationBlocked) {
isRotationBlocked = false
applyOrientationSettings()
}
Toast.makeText(
applicationContext,
getString(R.string.emulation_menu_help),
@ -294,6 +346,7 @@ class EmulationActivity : AppCompatActivity() {
private fun onAmiiboSelected(selectedFile: String) {
val success = NativeLibrary.loadAmiibo(selectedFile)
if (!success) {
Log.error("[EmulationActivity] Failed to load Amiibo file: $selectedFile")
MessageDialogFragment.newInstance(
R.string.amiibo_load_error,
R.string.amiibo_load_error_message
@ -516,13 +569,19 @@ class EmulationActivity : AppCompatActivity() {
return true
}
val openFileLauncher =
val openAmiiboFileLauncher =
registerForActivityResult(OpenFileResultContract()) { result: Intent? ->
if (result == null) return@registerForActivityResult
val selectedFiles = FileBrowserHelper.getSelectedFiles(
result, applicationContext, listOf<String>("bin")
) ?: return@registerForActivityResult
onAmiiboSelected(selectedFiles[0])
if (BuildUtil.isGooglePlayBuild) {
onAmiiboSelected(selectedFiles[0])
} else {
val fileUri = selectedFiles[0].toUri()
val nativePath = "!" + NativeLibrary.getNativePath(fileUri)
onAmiiboSelected(nativePath)
}
}
val openImageLauncher =

View file

@ -36,6 +36,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import android.widget.PopupMenu
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
@ -54,8 +55,10 @@ import org.citra.citra_emu.databinding.DialogShortcutBinding
import org.citra.citra_emu.features.cheats.ui.CheatsFragmentDirections
import org.citra.citra_emu.fragments.IndeterminateProgressDialogFragment
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.BuildUtil
import org.citra.citra_emu.utils.FileUtil
import org.citra.citra_emu.utils.GameIconUtils
import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.viewmodel.GamesViewModel
class GameAdapter(
@ -136,7 +139,7 @@ class GameAdapter(
val holder = view.tag as GameViewHolder
gameExists(holder)
if (holder.game.titleId == 0L) {
if (!holder.game.valid) {
MaterialAlertDialogBuilder(context)
.setTitle(R.string.properties)
.setMessage(R.string.properties_not_loaded)
@ -153,12 +156,21 @@ class GameAdapter(
if (holder.game.isInstalled) {
return true
}
val gameExists = DocumentFile.fromSingleUri(
CitraApplication.appContext,
Uri.parse(holder.game.path)
)?.exists() == true
val path = holder.game.path
val pathUri = path.toUri()
var gameExists: Boolean
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(path)) {
gameExists =
DocumentFile.fromSingleUri(
CitraApplication.appContext,
pathUri
)?.exists() == true
} else {
val nativePath = NativeLibrary.getNativePath(pathUri)
gameExists = NativeLibrary.nativeFileExists(nativePath)
}
return if (!gameExists) {
Log.error("[GameAdapter] ROM file does not exist: $path")
Toast.makeText(
CitraApplication.appContext,
R.string.loader_error_file_not_found,
@ -323,14 +335,16 @@ class GameAdapter(
}
}
val titleId = game.titleId
val dlcTitleId = titleId or 0x8C00000000L
val updateTitleId = titleId or 0xE00000000L
popup.setOnMenuItemClickListener { menuItem ->
val uninstallAction: () -> Unit = {
when (menuItem.itemId) {
R.id.game_context_uninstall -> CitraApplication.documentsTree.deleteDocument(dirs.gameDir)
R.id.game_context_uninstall_dlc -> FileUtil.deleteDocument(CitraApplication.documentsTree.folderUriHelper(dirs.dlcDir)
.toString())
R.id.game_context_uninstall_updates -> FileUtil.deleteDocument(CitraApplication.documentsTree.folderUriHelper(dirs.updatesDir)
.toString())
R.id.game_context_uninstall -> NativeLibrary.uninstallTitle(titleId, game.mediaType)
R.id.game_context_uninstall_dlc -> NativeLibrary.uninstallTitle(dlcTitleId, Game.MediaType.SDMC)
R.id.game_context_uninstall_updates -> NativeLibrary.uninstallTitle(updateTitleId, Game.MediaType.SDMC)
}
ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
bottomSheetDialog.dismiss()
@ -501,6 +515,63 @@ class GameAdapter(
showUninstallContextMenu(it, game, bottomSheetDialog)
}
bottomSheetView.findViewById<MaterialButton>(R.id.delete_cache).setOnClickListener {
val options = arrayOf(context.getString(R.string.vulkan), context.getString(R.string.opengles))
var selectedIndex = -1
val dialog = MaterialAlertDialogBuilder(context)
.setTitle(R.string.delete_cache_select_backend)
.setSingleChoiceItems(options, -1) { dialog, which ->
selectedIndex = which
}
.setPositiveButton(android.R.string.ok) {_, _ ->
val progToast = Toast.makeText(
CitraApplication.appContext,
R.string.deleting_shader_cache,
Toast.LENGTH_LONG
)
progToast.show()
activity.lifecycleScope.launch(Dispatchers.IO) {
when (selectedIndex) {
0 -> {
NativeLibrary.deleteVulkanShaderCache(game.titleId)
}
1 -> {
NativeLibrary.deleteOpenGLShaderCache(game.titleId)
}
}
activity.runOnUiThread {
progToast.cancel()
Toast.makeText(
CitraApplication.appContext,
R.string.shader_cache_deleted,
Toast.LENGTH_SHORT
).show()
}
}
}
.setNegativeButton(android.R.string.cancel) { dialog, _ ->
dialog.dismiss()
}
.create()
dialog.setOnShowListener {
val positiveButton = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE)
positiveButton.isEnabled = false
val listView = dialog.listView
listView.setOnItemClickListener { _, _, position, _ ->
selectedIndex = position
positiveButton.isEnabled = true
}
}
dialog.show()
}
val bottomSheetBehavior = bottomSheetDialog.getBehavior()
bottomSheetBehavior.skipCollapsed = true
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
@ -556,7 +627,9 @@ class GameAdapter(
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem.titleId == newItem.titleId
// The title is taken into account to support 3DSX, which all have the titleID 0.
// This only works now because we always return the English title, adjust if that changes.
return oldItem.titleId == newItem.titleId && oldItem.title == newItem.title
}
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -57,6 +57,7 @@ object StillImageCameraHelper {
val request = ImageRequest.Builder(context)
.data(uri)
.size(width, height)
.allowHardware(false)
.build()
return context.imageLoader.executeBlocking(request).drawable?.toBitmap(
width,

View file

@ -6,18 +6,15 @@ package org.citra.citra_emu.display
import android.app.Presentation
import android.content.Context
import android.graphics.SurfaceTexture
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Bundle
import android.view.Display
import android.view.MotionEvent
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.WindowManager
import org.citra.citra_emu.features.settings.model.IntSetting
import org.citra.citra_emu.display.SecondaryDisplayLayout
import org.citra.citra_emu.NativeLibrary
class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
@ -66,6 +63,11 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
}
fun updateDisplay() {
// return early if the parent context is dead or dying
if (context is android.app.Activity && (context.isFinishing || context.isDestroyed)) {
return
}
// decide if we are going to the external display or the internal one
var display = getExternalDisplay(context)
if (display == null ||
@ -78,12 +80,25 @@ class SecondaryDisplay(val context: Context) : DisplayManager.DisplayListener {
// otherwise, make a new presentation
releasePresentation()
pres = SecondaryDisplayPresentation(context, display!!, this)
pres?.show()
try {
pres = SecondaryDisplayPresentation(context, display!!, this)
pres?.show()
}
// catch BadTokenException and InvalidDisplayException,
// the display became invalid asynchronously, so we can assign to null
// until onDisplayAdded/Removed/Changed is called and logic retriggered
catch (_: WindowManager.BadTokenException) {
pres = null
} catch (_: WindowManager.InvalidDisplayException) {
pres = null
}
}
fun releasePresentation() {
pres?.dismiss()
try {
pres?.dismiss()
} catch (_: Exception) { }
pres = null
}

View file

@ -18,6 +18,7 @@ object SettingKeys {
external fun enable_required_online_lle_modules(): String
external fun use_virtual_sd(): String
external fun compress_cia_installs(): String
external fun async_fs_operations(): String
external fun region_value(): String
external fun init_clock(): String
external fun init_time(): String
@ -45,6 +46,7 @@ object SettingKeys {
external fun texture_filter(): String
external fun texture_sampling(): String
external fun delay_game_render_thread_us(): String
external fun simulate_3ds_gpu_timings(): String
external fun layout_option(): String
external fun swap_screen(): String
external fun upright_screen(): String
@ -92,6 +94,7 @@ object SettingKeys {
external fun audio_emulation(): String
external fun enable_audio_stretching(): String
external fun enable_realtime_audio(): String
external fun simulate_headphones_plugged(): String
external fun volume(): String
external fun output_type(): String
external fun output_device(): String
@ -102,6 +105,7 @@ object SettingKeys {
external fun gdbstub_port(): String
external fun instant_debug_log(): String
external fun enable_rpc_server(): String
external fun toggle_unique_data_console_type(): String
external fun log_filter(): String
external fun log_regex_filter(): String
external fun use_integer_scaling(): String

View file

@ -20,6 +20,7 @@ enum class BooleanSetting(
SWAP_SCREEN(SettingKeys.swap_screen(), Settings.SECTION_LAYOUT, false),
INSTANT_DEBUG_LOG(SettingKeys.instant_debug_log(), Settings.SECTION_DEBUG, false),
ENABLE_RPC_SERVER(SettingKeys.enable_rpc_server(), Settings.SECTION_DEBUG, false),
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE(SettingKeys.toggle_unique_data_console_type(), Settings.SECTION_DEBUG, false),
SWAP_EYES_3D(SettingKeys.swap_eyes_3d(),Settings.SECTION_RENDERER, false),
PERF_OVERLAY_ENABLE(SettingKeys.performance_overlay_enable(), Settings.SECTION_LAYOUT, false),
PERF_OVERLAY_SHOW_FPS(SettingKeys.performance_overlay_show_fps(), Settings.SECTION_LAYOUT, true),
@ -43,6 +44,7 @@ enum class BooleanSetting(
PRELOAD_TEXTURES(SettingKeys.preload_textures(), Settings.SECTION_UTILITY, false),
ENABLE_AUDIO_STRETCHING(SettingKeys.enable_audio_stretching(), Settings.SECTION_AUDIO, true),
ENABLE_REALTIME_AUDIO(SettingKeys.enable_realtime_audio(), Settings.SECTION_AUDIO, false),
SIMULATE_HEADPHONES_PLUGGED(SettingKeys.simulate_headphones_plugged(), Settings.SECTION_AUDIO, false),
CPU_JIT(SettingKeys.use_cpu_jit(), Settings.SECTION_CORE, true),
HW_SHADER(SettingKeys.use_hw_shader(), Settings.SECTION_RENDERER, true),
SHADER_JIT(SettingKeys.use_shader_jit(), Settings.SECTION_RENDERER, true),
@ -53,9 +55,11 @@ enum class BooleanSetting(
USE_ARTIC_BASE_CONTROLLER(SettingKeys.use_artic_base_controller(), Settings.SECTION_CONTROLS, false),
UPRIGHT_SCREEN(SettingKeys.upright_screen(), Settings.SECTION_LAYOUT, false),
COMPRESS_INSTALLED_CIA_CONTENT(SettingKeys.compress_cia_installs(), Settings.SECTION_STORAGE, false),
ASYNC_FS_OPERATIONS(SettingKeys.async_fs_operations(), Settings.SECTION_STORAGE, true),
ANDROID_HIDE_IMAGES(SettingKeys.android_hide_images(), Settings.SECTION_MISC, false),
APPLY_REGION_FREE_PATCH(SettingKeys.apply_region_free_patch(), Settings.SECTION_SYSTEM, true),
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false);
USE_INTEGER_SCALING(SettingKeys.use_integer_scaling(), Settings.SECTION_RENDERER, false),
SIMULATE_3DS_GPU_TIMINGS(SettingKeys.simulate_3ds_gpu_timings(), Settings.SECTION_RENDERER, true);
override var boolean: Boolean = defaultValue
@ -82,6 +86,7 @@ enum class BooleanSetting(
REQUIRED_ONLINE_LLE_MODULES,
NEW_3DS,
LLE_APPLETS,
TOGGLE_UNIQUE_DATA_CONSOLE_TYPE,
VSYNC,
DEBUG_RENDERER,
CPU_JIT,
@ -89,6 +94,7 @@ enum class BooleanSetting(
SHADERS_ACCURATE_MUL,
USE_ARTIC_BASE_CONTROLLER,
COMPRESS_INSTALLED_CIA_CONTENT,
ASYNC_FS_OPERATIONS,
ANDROID_HIDE_IMAGES,
PERF_OVERLAY_ENABLE, // Works in overlay options, but not from the settings menu
APPLY_REGION_FREE_PATCH

View file

@ -4,6 +4,8 @@
package org.citra.citra_emu.features.settings.model
import org.citra.citra_emu.features.settings.SettingKeys
enum class IntListSetting(
override val key: String,
override val section: String,
@ -11,7 +13,7 @@ enum class IntListSetting(
val canBeEmpty: Boolean = true
) : AbstractListSetting<Int> {
LAYOUTS_TO_CYCLE("layouts_to_cycle", Settings.SECTION_LAYOUT, listOf(0, 1, 2, 3, 4, 5), canBeEmpty = false);
LAYOUTS_TO_CYCLE(SettingKeys.layouts_to_cycle(), Settings.SECTION_LAYOUT, listOf(0, 1, 2, 3, 4, 5), canBeEmpty = false);
private var backingList: List<Int> = defaultValue
private var lastValidList : List<Int> = defaultValue

View file

@ -17,7 +17,7 @@ enum class IntSetting(
CAMERA_INNER_FLIP(SettingKeys.camera_inner_flip(), Settings.SECTION_CAMERA, 0),
CAMERA_OUTER_LEFT_FLIP(SettingKeys.camera_outer_left_flip(), Settings.SECTION_CAMERA, 0),
CAMERA_OUTER_RIGHT_FLIP(SettingKeys.camera_outer_right_flip(), Settings.SECTION_CAMERA, 0),
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 1),
GRAPHICS_API(SettingKeys.graphics_api(), Settings.SECTION_RENDERER, 2),
RESOLUTION_FACTOR(SettingKeys.resolution_factor(), Settings.SECTION_RENDERER, 1),
STEREOSCOPIC_3D_MODE(SettingKeys.render_3d(), Settings.SECTION_RENDERER, 2),
STEREOSCOPIC_3D_DEPTH(SettingKeys.factor_3d(), Settings.SECTION_RENDERER, 0),

View file

@ -599,6 +599,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
BooleanSetting.COMPRESS_INSTALLED_CIA_CONTENT.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.ASYNC_FS_OPERATIONS,
R.string.async_fs_operations,
R.string.async_fs_operations_description,
BooleanSetting.ASYNC_FS_OPERATIONS.key,
BooleanSetting.ASYNC_FS_OPERATIONS.defaultValue
)
)
}
}
@ -1275,7 +1284,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue
override val defaultValue = FloatSetting.BACKGROUND_RED.defaultValue.toInt()
}
add(
SliderSetting(
@ -1298,7 +1307,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue
override val defaultValue = FloatSetting.BACKGROUND_GREEN.defaultValue.toInt()
}
add(
SliderSetting(
@ -1321,7 +1330,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
override val section = null
override val isRuntimeEditable = false
override val valueAsString = int.toString()
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue
override val defaultValue = FloatSetting.BACKGROUND_BLUE.defaultValue.toInt()
}
add(
SliderSetting(
@ -1704,6 +1713,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
BooleanSetting.ENABLE_REALTIME_AUDIO.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.SIMULATE_HEADPHONES_PLUGGED,
R.string.simulate_headphones_plugged,
R.string.simulate_headphones_plugged_description,
BooleanSetting.SIMULATE_HEADPHONES_PLUGGED.key,
BooleanSetting.SIMULATE_HEADPHONES_PLUGGED.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.AUDIO_INPUT_TYPE,
@ -1790,6 +1808,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
BooleanSetting.VSYNC.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.SIMULATE_3DS_GPU_TIMINGS,
R.string.simulate_3ds_gpu_timings,
R.string.simulate_3ds_gpu_timings_description,
BooleanSetting.SIMULATE_3DS_GPU_TIMINGS.key,
BooleanSetting.SIMULATE_3DS_GPU_TIMINGS.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.DEBUG_RENDERER,
@ -1817,6 +1844,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
BooleanSetting.ENABLE_RPC_SERVER.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.TOGGLE_UNIQUE_DATA_CONSOLE_TYPE,
R.string.toggle_unique_data_console_type,
R.string.toggle_unique_data_console_type_desc,
BooleanSetting.TOGGLE_UNIQUE_DATA_CONSOLE_TYPE.key,
BooleanSetting.TOGGLE_UNIQUE_DATA_CONSOLE_TYPE.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.DELAY_START_LLE_MODULES,

View file

@ -18,6 +18,7 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ParcelFileDescriptor
import android.os.SystemClock
import android.text.Editable
import android.text.TextWatcher
@ -73,6 +74,7 @@ import org.citra.citra_emu.features.settings.model.SettingsViewModel
import org.citra.citra_emu.features.settings.ui.SettingsActivity
import org.citra.citra_emu.features.settings.utils.SettingsFile
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.BuildUtil
import org.citra.citra_emu.utils.DirectoryInitialization
import org.citra.citra_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.citra.citra_emu.utils.EmulationMenuSettings
@ -108,6 +110,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
private val onPause = Runnable{ togglePause() }
private val onShutdown = Runnable{ emulationState.stop() }
// Only used if a game is passed through intent on google play variant
private var gameFd: Int? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@ -125,25 +130,37 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onCreate(savedInstanceState)
val intent = requireActivity().intent
val intentUri: Uri? = intent.data
var intentUri: Uri? = intent.data
val oldIntentInfo = Pair(
intent.getStringExtra("SelectedGame"),
intent.getStringExtra("SelectedTitle")
)
var intentGame: Game? = null
intentUri = if (intentUri == null && oldIntentInfo.first != null) {
Uri.parse(oldIntentInfo.first)
} else {
intentUri
}
if (intentUri != null) {
intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
GameHelper.getGame(intentUri, isInstalled = false, addedToLibrary = false)
} else {
null
}
} else if (oldIntentInfo.first != null) {
val gameUri = Uri.parse(oldIntentInfo.first)
intentGame = if (Game.extensions.contains(FileUtil.getExtension(gameUri))) {
GameHelper.getGame(gameUri, isInstalled = false, addedToLibrary = false)
} else {
null
if (!BuildUtil.isGooglePlayBuild) {
val intentUriString = intentUri.toString()
// We need to build a special path as the incoming URI may be SAF exclusive
Log.warning("[EmulationFragment] Cannot determine native path of URI \"" +
intentUriString + "\", using file descriptor instead.")
if (!intentUriString.startsWith("!")) {
gameFd = requireContext().contentResolver.openFileDescriptor(intentUri, "r")?.detachFd()
intentUri = if (gameFd != null) {
Uri.parse("fd://" + gameFd.toString())
} else {
null
}
}
}
intentGame =
intentUri?.let {
// isInstalled, addedToLibrary and mediaType do not matter here
GameHelper.getGame(it, isInstalled = false, addedToLibrary = false, mediaType = Game.MediaType.GAME_CARD)
}
}
val insertedCartridge = preferences.getString("insertedCartridge", "")
@ -161,6 +178,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
return
}
Log.info("[EmulationFragment] Starting application " + game.path)
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
emulationState = EmulationState(game.path)
@ -486,7 +505,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onResume()
Choreographer.getInstance().postFrameCallback(this)
if (NativeLibrary.isRunning()) {
emulationState.pause()
emulationState.unpause()
// If the overlay is enabled, we need to update the position if changed
val position = IntSetting.PERFORMANCE_OVERLAY_POSITION.int
@ -524,8 +543,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
}
override fun onDestroy() {
if (::emulationState.isInitialized && requireActivity().isFinishing) {
emulationState.stop()
}
EmulationLifecycleUtil.removeHook(onPause)
EmulationLifecycleUtil.removeHook(onShutdown)
if (gameFd != null) {
ParcelFileDescriptor.adoptFd(gameFd!!).close()
gameFd = null
}
super.onDestroy()
}
@ -852,7 +878,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_emulation_amiibo_load -> {
emulationActivity.openFileLauncher.launch(false)
emulationActivity.openAmiiboFileLauncher.launch(false)
true
}

View file

@ -36,6 +36,7 @@ import org.citra.citra_emu.adapters.GameAdapter
import org.citra.citra_emu.databinding.FragmentGamesBinding
import org.citra.citra_emu.features.settings.model.Settings
import org.citra.citra_emu.model.Game
import org.citra.citra_emu.utils.BuildUtil
import org.citra.citra_emu.viewmodel.CompressProgressDialogViewModel
import org.citra.citra_emu.viewmodel.GamesViewModel
import org.citra.citra_emu.viewmodel.HomeViewModel
@ -60,8 +61,14 @@ class GamesFragment : Fragment() {
companion object {
fun doCompression(fragment: Fragment, gamesViewModel: GamesViewModel, inputPath: String?, outputUri: Uri?, shouldCompress: Boolean) {
if (outputUri != null) {
val outputPath: String =
if (!BuildUtil.isGooglePlayBuild) {
"!" + NativeLibrary.getNativePath(outputUri)
} else {
outputUri.toString()
}
CompressProgressDialogViewModel.reset()
val dialog = CompressProgressDialogFragment.newInstance(shouldCompress, outputUri.toString())
val dialog = CompressProgressDialogFragment.newInstance(shouldCompress, outputPath)
dialog.showNow(
fragment.requireActivity().supportFragmentManager,
CompressProgressDialogFragment.TAG
@ -69,9 +76,9 @@ class GamesFragment : Fragment() {
fragment.lifecycleScope.launch(Dispatchers.IO) {
val status = if (shouldCompress) {
NativeLibrary.compressFile(inputPath, outputUri.toString())
NativeLibrary.compressFile(inputPath, outputPath)
} else {
NativeLibrary.decompressFile(inputPath, outputUri.toString())
NativeLibrary.decompressFile(inputPath, outputPath)
}
fragment.requireActivity().runOnUiThread {

View file

@ -23,7 +23,6 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -32,7 +31,6 @@ import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.transition.MaterialFadeThrough
import org.citra.citra_emu.BuildConfig
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.R
@ -92,23 +90,20 @@ class SetupFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
homeViewModel.setNavigationVisibility(visible = false, animated = false)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (binding.viewPager2.currentItem > 0) {
pageBackward()
} else {
requireActivity().finish()
}
}
homeViewModel.selectedCitraDirectoryLiveData.observe(viewLifecycleOwner) { uri ->
if (uri == null) {
return@observe
}
)
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
onOpenCitraDirectory(uri)
homeViewModel.selectedCitraDirectory = null
}
homeViewModel.selectedGamesDirectoryLiveData.observe(viewLifecycleOwner) { uri ->
if (uri == null) {
return@observe
}
onGetGamesDirectory(uri)
homeViewModel.selectedGamesDirectory = null
}
pages = mutableListOf()
pages.apply {
@ -320,7 +315,7 @@ class SetupFragment : Fragment() {
R.string.select_citra_user_folder_description,
buttonAction = {
pageButtonCallback = it
PermissionsHandler.compatibleSelectDirectory(openCitraDirectory)
PermissionsHandler.compatibleSelectDirectory(mainActivity.setupOpenCitraDirectory)
},
buttonState = {
if (PermissionsHandler.hasWriteAccess(requireContext())) {
@ -342,9 +337,9 @@ class SetupFragment : Fragment() {
R.drawable.ic_controller,
R.string.games,
R.string.games_description,
buttonAction = {
buttonAction = {
pageButtonCallback = it
getGamesDirectory.launch(
mainActivity.setupGetGamesDirectory.launch(
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
)
},
@ -409,27 +404,33 @@ class SetupFragment : Fragment() {
}
binding.viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
var previousPosition: Int = 0
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position == 1 && previousPosition == 0) {
ViewUtils.showView(binding.buttonNext)
ViewUtils.showView(binding.buttonBack)
} else if (position == 0 && previousPosition == 1) {
ViewUtils.hideView(binding.buttonBack)
ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 1 && previousPosition == pages.size - 2) {
ViewUtils.hideView(binding.buttonNext)
} else if (position == pages.size - 2 && previousPosition == pages.size - 1) {
ViewUtils.showView(binding.buttonNext)
}
previousPosition = position
updateNavigationButtons(position)
}
})
homeViewModel.setNavigationVisibility(visible = false, animated = false)
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (binding.viewPager2.currentItem > 0) {
pageBackward()
} else {
requireActivity().finish()
}
}
}
)
binding.viewPager2.currentItem = homeViewModel.setupCurrentPage
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
binding.buttonNext.setOnClickListener {
val index = binding.viewPager2.currentItem
val currentPage = pages[index]
@ -479,29 +480,23 @@ class SetupFragment : Fragment() {
}
binding.buttonBack.setOnClickListener { pageBackward() }
if (savedInstanceState != null) {
val nextIsVisible = savedInstanceState.getBoolean(KEY_NEXT_VISIBILITY)
val backIsVisible = savedInstanceState.getBoolean(KEY_BACK_VISIBILITY)
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED)!!
if (nextIsVisible) {
binding.buttonNext.visibility = View.VISIBLE
}
if (backIsVisible) {
binding.buttonBack.visibility = View.VISIBLE
}
} else {
if (savedInstanceState == null) {
hasBeenWarned = BooleanArray(pages.size)
} else {
hasBeenWarned = savedInstanceState.getBooleanArray(KEY_HAS_BEEN_WARNED) ?: BooleanArray(pages.size)
}
updateNavigationButtons(binding.viewPager2.currentItem)
setInsets()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(KEY_NEXT_VISIBILITY, binding.buttonNext.isVisible)
outState.putBoolean(KEY_BACK_VISIBILITY, binding.buttonBack.isVisible)
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
if (::hasBeenWarned.isInitialized) {
outState.putBooleanArray(KEY_HAS_BEEN_WARNED, hasBeenWarned)
}
}
override fun onDestroyView() {
@ -510,15 +505,39 @@ class SetupFragment : Fragment() {
}
private lateinit var pageButtonCallback: SetupCallback
private val checkForButtonState: () -> Unit = {
val page = pages[binding.viewPager2.currentItem]
page.pageButtons?.forEach {
if (it.buttonState() == ButtonState.BUTTON_ACTION_COMPLETE) {
pageButtonCallback.onStepCompleted(it.titleId, pageFullyCompleted = false)
}
if (page.pageSteps() == PageState.PAGE_STEPS_COMPLETE) {
pageButtonCallback.onStepCompleted(0, pageFullyCompleted = true)
private fun updateNavigationButtons(position: Int) {
if (position == 0) {
ViewUtils.hideView(binding.buttonBack)
} else {
ViewUtils.showView(binding.buttonBack)
}
if (position == 0 || position == pages.size - 1) {
ViewUtils.hideView(binding.buttonNext)
} else {
ViewUtils.showView(binding.buttonNext)
}
}
private val checkForButtonState: () -> Unit = {
val currentIndex = binding.viewPager2.currentItem
val page = pages[currentIndex]
val isPageComplete = page.pageSteps() == PageState.PAGE_STEPS_COMPLETE
if (isPageComplete) {
binding.viewPager2.adapter?.notifyItemChanged(currentIndex)
ViewUtils.showView(binding.buttonNext)
} else {
page.pageButtons?.forEach {
if (it.buttonState() == ButtonState.BUTTON_ACTION_COMPLETE) {
if (this::pageButtonCallback.isInitialized) {
pageButtonCallback.onStepCompleted(it.titleId, pageFullyCompleted = false)
} else {
binding.viewPager2.adapter?.notifyItemChanged(currentIndex)
}
}
}
}
}
@ -559,48 +578,38 @@ class SetupFragment : Fragment() {
showPermissionDeniedSnackbar()
}
private val openCitraDirectory = registerForActivityResult<Uri, Uri>(
ActivityResultContracts.OpenDocumentTree()
) { result: Uri? ->
if (result == null) {
return@registerForActivityResult
}
private fun onOpenCitraDirectory(result: Uri) {
if (!BuildUtil.isGooglePlayBuild) {
if (NativeLibrary.getUserDirectory(result) == "") {
if (NativeLibrary.getNativePath(result) == "") {
SelectUserDirectoryDialogFragment.newInstance(
mainActivity,
R.string.invalid_selection,
R.string.invalid_user_directory
).show(mainActivity.supportFragmentManager, SelectUserDirectoryDialogFragment.TAG)
return@registerForActivityResult
return
}
}
CitraDirectoryHelper(requireActivity(), true).showCitraDirectoryDialog(result, pageButtonCallback, checkForButtonState)
CitraDirectoryHelper(requireActivity(), true).showCitraDirectoryDialog(result,
null, checkForButtonState)
}
private val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result == null) {
return@registerForActivityResult
}
private fun onGetGamesDirectory(result: Uri) {
requireActivity().contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
requireActivity().contentResolver.takePersistableUriPermission(
result,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
preferences.edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
// When a new directory is picked, we currently will reset the existing games
// database. This effectively means that only one game directory is supported.
preferences.edit()
.putString(GameHelper.KEY_GAME_PATH, result.toString())
.apply()
homeViewModel.setGamesDir(requireActivity(), result.path!!)
homeViewModel.setGamesDir(requireActivity(), result.path!!)
checkForButtonState.invoke()
}
checkForButtonState.invoke()
}
private fun finishSetup() {
preferences.edit()
@ -611,10 +620,12 @@ class SetupFragment : Fragment() {
fun pageForward() {
binding.viewPager2.currentItem = binding.viewPager2.currentItem + 1
homeViewModel.setupCurrentPage = binding.viewPager2.currentItem
}
fun pageBackward() {
binding.viewPager2.currentItem = binding.viewPager2.currentItem - 1
homeViewModel.setupCurrentPage = binding.viewPager2.currentItem
}
fun setPageWarned(page: Int) {

View file

@ -7,19 +7,26 @@ package org.citra.citra_emu.model
import android.os.Parcelable
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
import java.io.File
import java.io.IOException
import java.util.HashSet
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.activities.EmulationActivity
import org.citra.citra_emu.utils.BuildUtil
@Parcelize
@Serializable
class Game(
val valid: Boolean = false,
val title: String = "",
val description: String = "",
val path: String = "",
val titleId: Long = 0L,
val mediaType: MediaType = MediaType.GAME_CARD,
val company: String = "",
val regions: String = "",
val isInstalled: Boolean = false,
@ -35,12 +42,25 @@ class Game(
val keyLastPlayedTime get() = "${filename}_LastPlayed"
val launchIntent: Intent
get() = Intent(CitraApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = if (isInstalled) {
CitraApplication.documentsTree.getUri(path)
get() {
var appUri: Uri
if (isInstalled) {
if (BuildUtil.isGooglePlayBuild) {
appUri = CitraApplication.documentsTree.getUri(path)
} else {
val nativePath = NativeLibrary.getUserDirectory() + "/" + path
val nativeFile = File(nativePath)
if (!nativeFile.exists()) {
throw IOException("Attempting to create shortcut for an executable that doesn't exist: $nativePath")
}
appUri = Uri.fromFile(nativeFile)
}
} else {
Uri.parse(path)
appUri = path.toUri()
}
return Intent(CitraApplication.appContext, EmulationActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = appUri
}
}
@ -58,10 +78,23 @@ class Game(
result = 31 * result + regions.hashCode()
result = 31 * result + path.hashCode()
result = 31 * result + titleId.hashCode()
result = 31 * result + mediaType.hashCode()
result = 31 * result + company.hashCode()
return result
}
enum class MediaType(val value: Int) {
NAND(0),
SDMC(1),
GAME_CARD(2);
companion object {
fun fromInt(value: Int): MediaType? {
return MediaType.entries.find { it.value == value }
}
}
}
companion object {
val allExtensions: Set<String> get() = extensions + badExtensions

View file

@ -39,6 +39,8 @@ import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationBarView
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.TimeSource
import kotlinx.coroutines.launch
import org.citra.citra_emu.BuildConfig
import org.citra.citra_emu.NativeLibrary
@ -74,6 +76,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
override var themeId: Int = 0
companion object {
const val KEY_SETUP_CURRENT_PAGE = "SetupCurrentPage"
}
override fun onCreate(savedInstanceState: Bundle?) {
RefreshRateUtil.enforceRefreshRate(this)
@ -130,12 +136,22 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
)
}
var applicationsClickTimestamp = TimeSource.Monotonic.markNow()
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
setUpNavigation(navHostFragment.navController)
setUpNavigation(savedInstanceState, navHostFragment.navController)
(binding.navigationView as NavigationBarView).setOnItemReselectedListener {
when (it.itemId) {
R.id.gamesFragment -> gamesViewModel.setShouldScrollToTop(true)
R.id.gamesFragment -> {
if (applicationsClickTimestamp.elapsedNow() < 300.milliseconds) {
Toast.makeText(this, BuildConfig.VERSION_NAME, Toast.LENGTH_LONG)
.show()
}
applicationsClickTimestamp = TimeSource.Monotonic.markNow()
gamesViewModel.setShouldScrollToTop(true)
}
R.id.searchFragment -> gamesViewModel.setSearchFocused(true)
R.id.homeSettingsFragment -> SettingsActivity.launch(
this,
@ -176,6 +192,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
setInsets()
}
override fun onSaveInstanceState(outState: Bundle) {
// Save the user's current game state.
outState.putInt(KEY_SETUP_CURRENT_PAGE, homeViewModel.setupCurrentPage)
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(outState)
}
override fun onResume() {
checkUserPermissions()
@ -251,11 +275,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
}
private fun setUpNavigation(navController: NavController) {
private fun setUpNavigation(savedInstanceState: Bundle?, navController: NavController) {
val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
.getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
if (firstTimeSetup && !homeViewModel.navigatedToSetup) {
homeViewModel.setupCurrentPage = savedInstanceState?.getInt(KEY_SETUP_CURRENT_PAGE) ?: 0
navController.navigate(R.id.firstTimeSetupFragment)
homeViewModel.navigatedToSetup = true
} else {
@ -367,7 +392,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
if (!BuildUtil.isGooglePlayBuild) {
if (NativeLibrary.getUserDirectory(result) == "") {
if (NativeLibrary.getNativePath(result) == "") {
SelectUserDirectoryDialogFragment.newInstance(
this,
R.string.invalid_selection,
@ -412,4 +437,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
.build()
)
}
val setupOpenCitraDirectory = registerForActivityResult(
ActivityResultContracts.OpenDocumentTree(),
) { result: Uri? ->
homeViewModel.selectedCitraDirectory = result
}
val setupGetGamesDirectory = registerForActivityResult(
ActivityResultContracts.OpenDocumentTree()
) { result: Uri? ->
homeViewModel.selectedGamesDirectory = result
}
}

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -12,9 +12,11 @@ import androidx.core.app.NotificationCompat
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.NativeLibrary.InstallStatus
import org.citra.citra_emu.R
import org.citra.citra_emu.utils.FileUtil.getFilename
import androidx.core.net.toUri
class CiaInstallWorker(
val context: Context,
@ -131,7 +133,7 @@ class CiaInstallWorker(
installProgressBuilder.setOngoing(true)
setProgressCallback(100, 0)
selectedFiles.forEachIndexed { i, file ->
val filename = getFilename(Uri.parse(file))
val filename = getFilename(file.toUri())
installProgressBuilder.setContentText(
context.getString(
R.string.cia_install_notification_installing,
@ -140,7 +142,13 @@ class CiaInstallWorker(
selectedFiles.size
)
)
val res = installCIA(file)
var fileFinal: String
if (BuildUtil.isGooglePlayBuild) {
fileFinal = file
} else {
fileFinal = "!" + NativeLibrary.getNativePath(file.toUri())
}
val res = installCIA(fileFinal)
notifyInstallStatus(filename, res)
}
notificationManager.cancel(PROGRESS_NOTIFICATION_ID)

View file

@ -10,6 +10,8 @@ import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import org.citra.citra_emu.CitraApplication
import org.citra.citra_emu.model.CheapDocument
import org.citra.citra_emu.utils.BuildUtil
import java.io.IOException
import java.net.URLDecoder
import java.nio.file.Paths
import java.util.StringTokenizer
@ -77,8 +79,9 @@ class DocumentsTree {
@Synchronized
fun getFilename(filepath: String): String {
val node = resolvePath(filepath) ?: return ""
return node.name
val components = filepath.split(DELIMITER).filter { it.isNotEmpty() }
val filename = components.last()
return filename
}
@Synchronized
@ -260,6 +263,17 @@ class DocumentsTree {
@Synchronized
private fun resolvePath(filepath: String): DocumentsNode? {
if (!BuildUtil.isGooglePlayBuild) {
var isLegalPath = false
kotlinDirectoryAccessWhitelist.forEach {
if (filepath.startsWith(it)) {
isLegalPath = true
}
}
if (!isLegalPath) {
throw IOException("Attempted to resolve forbidden path: " + filepath)
}
}
root ?: return null
val tokens = StringTokenizer(filepath, DELIMITER, false)
var iterator = root
@ -351,5 +365,10 @@ class DocumentsTree {
companion object {
const val DELIMITER = "/"
val kotlinDirectoryAccessWhitelist = arrayOf(
"/config/",
"/log/",
"/gpu_drivers/",
)
}
}

View file

@ -219,10 +219,20 @@ object FileUtil {
*/
@JvmStatic
fun getFilename(uri: Uri): String {
val columns = arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
var filename = ""
var c: Cursor? = null
try {
if (uri.scheme == "fd") {
return ""
}
if (uri.scheme == "file") {
BuildUtil.assertNotGooglePlay()
val file = File(uri.path!!);
return file.name
}
val columns = arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME)
c = context.contentResolver.query(
uri,
columns,
@ -537,7 +547,7 @@ object FileUtil {
}
@JvmStatic
fun isNativePath(path: String): Boolean =
fun isNativePath(path: String): Boolean = // FIXME: This function name is bullshit -OS
try {
path[0] == '/'
} catch (e: StringIndexOutOfBoundsException) {

View file

@ -32,7 +32,7 @@ object GameHelper {
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
NativeLibrary.getInstalledGamePaths().forEach {
games.add(getGame(Uri.parse(it), isInstalled = true, addedToLibrary = true))
games.add(getGame(Uri.parse(it.path), isInstalled = true, addedToLibrary = true, it.mediaType))
}
// Cache list of games found on disk
@ -62,27 +62,46 @@ object GameHelper {
addGamesRecursive(games, FileUtil.listFiles(it.uri), depth - 1)
} else {
if (Game.allExtensions.contains(FileUtil.getExtension(it.uri))) {
games.add(getGame(it.uri, isInstalled = false, addedToLibrary = true))
games.add(getGame(it.uri, isInstalled = false, addedToLibrary = true, Game.MediaType.GAME_CARD))
}
}
}
}
fun getGame(uri: Uri, isInstalled: Boolean, addedToLibrary: Boolean): Game {
fun getGame(uri: Uri, isInstalled: Boolean, addedToLibrary: Boolean, mediaType: Game.MediaType): Game {
val filePath = uri.toString()
var gameInfo: GameInfo? = GameInfo(filePath)
var nativePath: String? = null
var gameInfo: GameInfo?
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) || filePath.startsWith("!")) {
gameInfo = GameInfo(filePath)
} else {
nativePath = if (uri.scheme == "fd") {
uri.toString()
} else {
"!" + NativeLibrary.getNativePath(uri)
};
gameInfo = GameInfo(nativePath)
}
if (gameInfo?.isValid() == false) {
val valid = gameInfo.isValid()
if (!valid) {
gameInfo = null
}
val isEncrypted = gameInfo?.isEncrypted() == true
val newGame = Game(
valid,
(gameInfo?.getTitle() ?: FileUtil.getFilename(uri)).replace("[\\t\\n\\r]+".toRegex(), " "),
filePath.replace("\n", " "),
filePath,
// TODO: This next line can be deduplicated but I don't want to right now -OS
if (BuildUtil.isGooglePlayBuild || FileUtil.isNativePath(filePath) || filePath.startsWith("!")) {
filePath
} else {
nativePath!!
},
gameInfo?.getTitleID() ?: 0,
mediaType,
gameInfo?.getCompany() ?: "",
if (isEncrypted) { CitraApplication.appContext.getString(R.string.unsupported_encrypted) } else { gameInfo?.getRegions() ?: "" },
isInstalled,

View file

@ -4,28 +4,43 @@
package org.citra.citra_emu.utils
import org.citra.citra_emu.utils.BuildUtil
import java.io.File
import android.content.Context
import android.os.storage.StorageManager
object RemovableStorageHelper {
// This really shouldn't be necessary, but the Android API seemingly
// doesn't have a way of doing this?
fun getRemovableStoragePath(idString: String): String? {
BuildUtil.assertNotGooglePlay()
// On certain Android flavours the external storage mount location can
// vary, so add extra cases here if we discover them.
val possibleMountPaths = listOf("/mnt/media_rw/$idString", "/storage/$idString")
private val pathCache = mutableMapOf<String, String?>()
private var scanned = false
for (mountPath in possibleMountPaths) {
val pathFile = File(mountPath);
if (pathFile.exists()) {
// TODO: Cache which mount location is being used for the remainder of the
// session, as it should never change. -OS
return pathFile.absolutePath
}
private fun scanVolumes(context: Context) {
if (scanned) {
return
}
return null
val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
for (volume in storageManager.storageVolumes) {
if (!volume.isRemovable) {
continue
}
val uuid = volume.uuid ?: continue
val dir = volume.directory ?: continue
pathCache[uuid.uppercase()] = dir.absolutePath
}
scanned = true
}
fun getRemovableStoragePath(context: Context, idString: String): String? {
BuildUtil.assertNotGooglePlay()
val key = idString.uppercase()
if (!scanned) {
scanVolumes(context)
}
return pathCache[key]
}
}

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -9,6 +9,10 @@ import android.view.ViewGroup
object ViewUtils {
fun showView(view: View, length: Long = 300) {
if (view.visibility == View.VISIBLE) {
return
}
view.apply {
alpha = 0f
visibility = View.VISIBLE

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -78,8 +78,9 @@ class GamesViewModel : ViewModel() {
val filteredList = sortedList.filter {
if (it.isSystemTitle) {
it.isVisibleSystemTitle
} else {
true
}
true
}
_games.value = filteredList

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -7,6 +7,8 @@ package org.citra.citra_emu.viewmodel
import android.content.res.Resources
import android.net.Uri
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager
@ -62,6 +64,20 @@ class HomeViewModel : ViewModel() {
var navigatedToSetup = false
var setupCurrentPage = 0
private val _selectedCitraDirectory = MutableLiveData<Uri?>()
val selectedCitraDirectoryLiveData: LiveData<Uri?> = _selectedCitraDirectory
var selectedCitraDirectory: Uri?
get() = _selectedCitraDirectory.value
set(value) { _selectedCitraDirectory.value = value }
private val _selectedGamesDirectory = MutableLiveData<Uri?>()
val selectedGamesDirectoryLiveData: LiveData<Uri?> = _selectedGamesDirectory
var selectedGamesDirectory: Uri?
get() = _selectedGamesDirectory.value
set(value) { _selectedGamesDirectory.value = value }
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
if (_navigationVisible.value.first == visible) {
return

View file

@ -179,6 +179,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.bg_blue);
ReadSetting("Renderer", Settings::values.custom_second_layer_opacity);
ReadSetting("Renderer", Settings::values.delay_game_render_thread_us);
ReadSetting("Renderer", Settings::values.simulate_3ds_gpu_timings);
ReadSetting("Renderer", Settings::values.disable_right_eye_render);
ReadSetting("Renderer", Settings::values.swap_eyes_3d);
ReadSetting("Renderer", Settings::values.render_3d_which_display);
@ -231,6 +232,7 @@ void Config::ReadValues() {
// Storage
ReadSetting("Storage", Settings::values.compress_cia_installs);
ReadSetting("Storage", Settings::values.async_fs_operations);
// Utility
ReadSetting("Utility", Settings::values.dump_textures);
@ -242,6 +244,7 @@ void Config::ReadValues() {
ReadSetting("Audio", Settings::values.audio_emulation);
ReadSetting("Audio", Settings::values.enable_audio_stretching);
ReadSetting("Audio", Settings::values.enable_realtime_audio);
ReadSetting("Audio", Settings::values.simulate_headphones_plugged);
ReadSetting("Audio", Settings::values.volume);
ReadSetting("Audio", Settings::values.output_type);
ReadSetting("Audio", Settings::values.output_device);
@ -315,6 +318,8 @@ void Config::ReadValues() {
ReadSetting("Debugging", Settings::values.gdbstub_port);
ReadSetting("Debugging", Settings::values.instant_debug_log);
ReadSetting("Debugging", Settings::values.enable_rpc_server);
ReadSetting("Debugging", Settings::values.toggle_unique_data_console_type);
ReadSetting("Debugging", Settings::values.break_on_unmapped_memory_access);
for (const auto& service_module : Service::service_module_map) {
bool use_lle =
@ -334,14 +339,13 @@ void Config::Reload() {
for (auto key = Settings::Keys::keys_array.begin(); key != Settings::Keys::keys_array.end();
++key) {
const auto key_declaration_string = std::string(*key) + " =";
// FIXME: This code looks so ass when formatted by clang-format -OS
if (std::ranges::find(DefaultINI::android_config_omitted_keys, *key) ==
std::end(DefaultINI::android_config_omitted_keys) &&
std::string(DefaultINI::android_config_default_file_content)
.find(key_declaration_string) == std::string::npos) {
if ((std::ranges::find(DefaultINI::android_config_omitted_keys, *key) ==
std::end(DefaultINI::android_config_omitted_keys)) &&
(std::string(DefaultINI::android_config_default_file_content)
.find(key_declaration_string) == std::string::npos)) {
ASSERT_MSG(false,
"Validation of default content config failed: Missing or malformed key "
"declaration {}",
"Validation of default config content (jni/default_ini.h) failed: Missing "
"declaration for key '{}'",
*key);
}
}

View file

@ -35,7 +35,10 @@ constexpr std::array android_config_omitted_keys = {
Keys::audio_encoder,
Keys::audio_encoder_options,
Keys::audio_bitrate,
Keys::last_artic_base_addr, // On Android, this value is stored as a "preference"
Keys::last_artic_base_addr, // On Android, this value is stored as a "preference"
Keys::break_on_unmapped_memory_access, // Does nothing as the error is ignored
Keys::use_gdbstub, // GDB functionality disabled by deafult on Android
Keys::gdbstub_port,
};
// clang-format off
@ -90,7 +93,7 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
[Renderer]
# Whether to render using OpenGL
# 1: OpenGL ES (default), 2: Vulkan
# 1: OpenGL ES, 2: Vulkan (default)
)") DECLARE_KEY(graphics_api) BOOST_HANA_STRING(R"(
# Whether to compile shaders on multiple worker threads (Vulkan only)
@ -196,6 +199,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# Set to 0 for no delay, only useful in dynamic-fps games to simulate GPU delay.
)") DECLARE_KEY(delay_game_render_thread_us) BOOST_HANA_STRING(R"(
# Delays GPU completion events based on measurements taken from real hardware
# 0: No delay, 1 (default): Enable delay
)") DECLARE_KEY(simulate_3ds_gpu_timings) BOOST_HANA_STRING(R"(
# Disables rendering the right eye image
# Greatly improves performance in some games, but can cause flickering in others.
# 0 : Enable right eye rendering, 1: Disable right eye rendering
@ -271,6 +278,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# 0 (default): Do not compress, 1: Compress
)") DECLARE_KEY(compress_cia_installs) BOOST_HANA_STRING(R"(
# Whether to enable async filesystem operations
# 0: Disabled, 1 (default): Enabled
)") DECLARE_KEY(async_fs_operations) BOOST_HANA_STRING(R"(
# Position of the performance overlay
# 0: Top Left
# 1: Center Top
@ -404,6 +415,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# 0 (default): No, 1: Yes
)") DECLARE_KEY(enable_realtime_audio) BOOST_HANA_STRING(R"(
# Simulates whether headphones are plugged in to the emulated 3DS system
# 0 (default): No, 1: Yes
)") DECLARE_KEY(simulate_headphones_plugged) BOOST_HANA_STRING(R"(
# Output volume.
# 1.0 (default): 100%, 0.0; mute
)") DECLARE_KEY(volume) BOOST_HANA_STRING(R"(
@ -527,9 +542,6 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# 0 (default): Off, 1: On
)") DECLARE_KEY(renderer_debug) BOOST_HANA_STRING(R"(
# Port for listening to GDB connections.
)") DECLARE_KEY(use_gdbstub) BOOST_HANA_STRING(R"(
# Flush log output on every message
# Immediately commits the debug log to file. Use this if Azahar crashes and the log output is being cut.
)") DECLARE_KEY(instant_debug_log) BOOST_HANA_STRING(R"(
@ -538,6 +550,10 @@ static const char* android_config_default_file_content = (BOOST_HANA_STRING(R"(
# 0 (default): Off, 1: On
)") DECLARE_KEY(enable_rpc_server) BOOST_HANA_STRING(R"(
# Enables toggling the unique data console type (Old 3DS <-> New 3DS) to be able to download the opposite system firmware type from system settings.
# 0 (default): Off, 1: On
)") DECLARE_KEY(toggle_unique_data_console_type) BOOST_HANA_STRING(R"(
# Delay the start of apps when LLE modules are enabled
# 0: Off, 1 (default): On
)") DECLARE_KEY(delay_start_for_lle_modules) BOOST_HANA_STRING(R"(

View file

@ -25,7 +25,10 @@ bool EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) {
render_window = surface;
window_info.type = Frontend::WindowSystemType::Android;
window_info.render_surface = surface;
if (surface != nullptr) {
window_width = ANativeWindow_getWidth(surface);
window_height = ANativeWindow_getHeight(surface);
}
StopPresenting();
OnFramebufferSizeChanged();
return true;

View file

@ -75,7 +75,7 @@ GameInfoData* GetNewGameInfoData(const std::string& path) {
result = update_loader->ReadIcon(update_smdh);
if (result != Loader::ResultStatus::Success) {
is_encrypted = result == Loader::ResultStatus::ErrorEncrypted;
return {};
return original_smdh;
}
return update_smdh;
}();

View file

@ -652,34 +652,38 @@ void Java_org_citra_citra_1emu_NativeLibrary_setUserDirectory(JNIEnv* env,
FileUtil::SetCurrentDir(GetJString(env, j_directory));
}
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getInstalledGamePaths(
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_getInstalledGamePathsImpl(
JNIEnv* env, [[maybe_unused]] jclass clazz) {
std::vector<std::string> games;
const FileUtil::DirectoryEntryCallable ScanDir =
[&games, &ScanDir](u64*, const std::string& directory, const std::string& virtual_name) {
std::string path = directory + virtual_name;
if (FileUtil::IsDirectory(path)) {
path += '/';
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
} else {
if (!FileUtil::Exists(path))
return false;
auto loader = Loader::GetLoader(path);
if (loader) {
bool executable{};
const Loader::ResultStatus result = loader->IsExecutable(executable);
if (Loader::ResultStatus::Success == result && executable) {
games.emplace_back(path);
}
Service::FS::MediaType media_type;
const FileUtil::DirectoryEntryCallable ScanDir = [&games, &ScanDir, &media_type](
u64*, const std::string& directory,
const std::string& virtual_name) {
std::string path = directory + virtual_name;
if (FileUtil::IsDirectory(path)) {
path += '/';
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
} else {
if (!FileUtil::Exists(path))
return false;
auto loader = Loader::GetLoader(path);
if (loader) {
bool executable{};
const Loader::ResultStatus result = loader->IsExecutable(executable);
if (Loader::ResultStatus::Success == result && executable) {
games.emplace_back(path + "|" + std::to_string(static_cast<int>(media_type)));
}
}
return true;
};
}
return true;
};
media_type = Service::FS::MediaType::SDMC;
ScanDir(nullptr, "",
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
"Nintendo "
"3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000");
media_type = Service::FS::MediaType::NAND;
ScanDir(nullptr, "",
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"00000000000000000000000000000000/title/00040010");
@ -1107,4 +1111,63 @@ void Java_org_citra_citra_1emu_NativeLibrary_setInsertedCartridge(JNIEnv* env, j
inserted_cartridge = GetJString(env, path);
}
} // extern "C"
jboolean Java_org_citra_citra_1emu_NativeLibrary_uninstallTitle(JNIEnv* env, jobject obj,
jlong j_titleid, jint j_mediatype) {
const auto titleid = static_cast<u64>(j_titleid);
const auto result =
Service::AM::UninstallProgram(static_cast<Service::FS::MediaType>(j_mediatype), titleid);
if (result.IsError()) {
LOG_ERROR(Frontend, "Failed to uninstall '{}': 0x{:08X}", std::to_string(titleid),
result.raw);
return false;
}
return true;
}
jboolean Java_org_citra_citra_1emu_NativeLibrary_nativeFileExists(JNIEnv* env, jobject obj,
jstring j_path) {
const auto path = GetJString(env, j_path);
return FileUtil::Exists(path);
}
void Java_org_citra_citra_1emu_NativeLibrary_deleteOpenGLShaderCache(JNIEnv* env, jobject obj,
jlong title_id) {
for (const std::string_view cache_type : {"separable", "conventional"}) {
const std::string path =
fmt::format("{}opengl/precompiled/{}/{:016X}.bin",
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), cache_type, title_id);
LOG_INFO(Frontend, "Deleting shader file: {}", path);
FileUtil::Delete(path);
}
const std::string path =
fmt::format("{}opengl/transferable/{:016X}.bin",
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id);
LOG_INFO(Frontend, "Deleting shader file: {}", path);
FileUtil::Delete(path);
}
void Java_org_citra_citra_1emu_NativeLibrary_deleteVulkanShaderCache(JNIEnv* env, jobject obj,
jlong title_id) {
for (const std::string_view cache_type : {"vs", "fs", "gs", "pl"}) {
const std::string path =
fmt::format("{}vulkan/transferable/{:016X}_{}.vkch",
FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), title_id, cache_type);
LOG_INFO(Frontend, "Deleting shader file: {}", path);
FileUtil::Delete(path);
}
FileUtil::ForeachDirectoryEntry(
nullptr,
fmt::format("{}vulkan/pipeline", FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)),
[title_id]([[maybe_unused]] u64* num_entries_out, const std::string& directory,
const std::string& virtual_name) {
if (virtual_name.starts_with(fmt::format("{:016X}", title_id))) {
std::string path = directory + DIR_SEP + virtual_name;
LOG_INFO(Frontend, "Deleting shader file: {}", path);
FileUtil::Delete(path);
}
return true;
});
}
} // extern "C"

View file

@ -161,7 +161,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/game_button_tray"
android:id="@+id/horizontal_layout_2"
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -199,19 +199,24 @@
</LinearLayout>
<LinearLayout
android:id="@+id/compress_tray"
android:id="@+id/horizontal_layout_3"
style="@style/ThemeOverlay.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="start|center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/game_button_tray">
app:layout_constraintTop_toBottomOf="@+id/horizontal_layout_2">
<com.google.android.material.button.MaterialButton
android:id="@+id/delete_cache"
style="@style/Widget.Material3.Button.TonalButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/delete_shader_cache"
android:text="@string/delete_shader_cache" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -5,13 +5,13 @@
android:id="@+id/coordinator_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_system_files"

View file

@ -66,10 +66,14 @@
<string name="give_permission">Concedir permís</string>
<string name="notification_warning">Saltar-se la concessió del permís de notificacions?</string>
<string name="notification_warning_description">Azahar no et podrà notificar sobre informació important.</string>
<string name="filesystem_permission_warning">Falten permisos</string>
<string name="filesystem_permission_warning_description">Azahar necessita permís per a administrar fitxers en este dispositiu per a poder emmagatzemar i administrar les dades d\'usuari de 3DS.\n\nPer favor, atorgue el permís abans de continuar.</string>
<string name="camera_permission">Càmera</string>
<string name="camera_permission_description">Concedix el permís de càmera per a emular la càmera de la 3DS.</string>
<string name="microphone_permission">Micròfon</string>
<string name="microphone_permission_description">Concedix el permís de micròfon per a emular el micròfon de la 3DS.</string>
<string name="filesystem_permission">Sistema de fitxers</string>
<string name="filesystem_permission_description">Concedix el permís de sistema d\'arxius a baix per a permetre que Azahar emmagatzeme arxius.</string>
<string name="permission_denied">Permís denegat</string>
<string name="add_games_warning">Vols saltar la selecció de la carpeta d\'aplicacions?</string>
<string name="add_games_warning_description">El software no es mostrarà a la llista d\'aplicacions si no se selecciona cap carpeta.</string>
@ -89,6 +93,9 @@
<string name="cannot_skip">No pots saltar la configuració de la carpeta d\'usuari</string>
<string name="cannot_skip_directory_description">Aquest pas és necessari perquè Azahar funcione. Selecciona un directori i després podràs continuar.</string>
<string name="selecting_user_directory_without_write_permissions">Has perdut els permisos d\'escriptura en el teu <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">directori de dades d\'usuari</a>, on es guarden les partides guardades i altra informació. Això pot ocórrer després d\'algunes actualitzacions d\'aplicacions o d\'Android. Torna a seleccionar el directori per a recuperar els permisos i poder continuar.</string>
<string name="invalid_selection">Selecció no vàlida</string>
<string name="invalid_user_directory">La selecció del directori d\'usuari no és vàlida.\nTorna a seleccionar el directori d\'usuari, assegurant-te de navegar fins ell des de l\'arrel de l\'emmagatzematge del dispositiu.</string>
<string name="filesystem_permission_lost">Azahar ha perdut el permís per a administrar fitxers en este dispositiu. Això pot ocórrer després d\'alguna actualització de l\'aplicació o d\'Android. Torna a concedir este permís en la següent pantalla per a continuar usant l\'aplicació.</string>
<string name="set_up_theme_settings">Configuració de tema</string>
<string name="setup_theme_settings_description">Configura les teues preferències de temes per a Azahar.</string>
<string name="setup_set_theme">Establir tema</string>
@ -100,9 +107,18 @@
<string name="search_recently_added">Acabats d\'afegir</string>
<string name="search_installed">Instal·lats</string>
<!-- Input related strings -->
<string name="controller_auto_map">Mapatge de Comandament Automàtic</string>
<string name="controller_auto_map_description">Aplica l\'assignació de comandament estàndard per a tots els botons i eixos</string>
<string name="auto_map_prompt">Pressiona este botó en el teu comandament!</string>
<string name="auto_map_image_description">Botons de 3DS amb el botó A ressaltat</string>
<string name="controller_clear_all">Netejar Totes les Assignacions</string>
<string name="controller_clear_all_confirm">Això eliminarà totes els assignacions del comandament actual.</string>
<string name="controller_circlepad">Pad circular</string>
<string name="controller_c">Palanca C</string>
<string name="controller_hotkeys">Tecles de drecera</string>
<string name="controller_hotkeys_description">Si la tecla \"Habilitar tecles d\'accés ràpid\" està assignada, s\'ha de pressionar eixa tecla a més de la tecla d\'accés ràpid assignada</string>
<string name="controller_hotkey_enable_button">Habilitar tecles d\'accés ràpid</string>
<string name="controller_triggers">Botons de darrere</string>
<string name="controller_trigger">Botó de darrere</string>
<string name="controller_dpad">Pad de control</string>
@ -110,6 +126,8 @@
<string name="controller_dpad_axis_description">És possible que alguns controladors no puguen assignar el D-pad com un eix. Si aquest és el cas, utilitza la secció D-Pad (botons).</string>
<string name="controller_dpad_button">D-Pad (Botó)</string>
<string name="controller_dpad_button_description">Assigna només el D-pad a aquests si tens problemes amb les assignacions de botons del D-Pad (Eix).</string>
<string name="controller_axis_vertical">Eix Vertical</string>
<string name="controller_axis_horizontal">Eix Horitzontal</string>
<string name="direction_up">Amunt</string>
<string name="direction_down">Avall</string>
<string name="direction_left">Esquerra</string>
@ -118,6 +136,8 @@
<string name="input_dialog_description">Pulsa o mou un botó/palanca.</string>
<string name="input_binding">Assignació de botons</string>
<string name="input_binding_description">Prem o mou una entrada per enllaçar-la a %1$s.</string>
<string name="input_binding_description_vertical_axis">Pressiona A DALT en el teu joystick.</string>
<string name="input_binding_description_horizontal_axis">Pressiona DRETA en el teu joystick.</string>
<string name="button_home">HOME</string>
<string name="button_swap">Intercanviar Pantalles</string>
<string name="button_turbo">Turbo</string>
@ -156,6 +176,8 @@
<string name="username">Nom d\'usuari/a</string>
<string name="new_3ds">Mode New 3DS</string>
<string name="lle_applets">Usar Applets LLE (si están instal·lades)</string>
<string name="apply_region_free_patch">Aplicar modificació de regió lliure a les aplicacions instal·lades.</string>
<string name="apply_region_free_patch_desc">Modifica la regió de les aplicacions instal·lades perquè siguen de regió lliure, de manera que sempre apareguen en el menú Home.</string>
<string name="enable_required_online_lle_modules">Habilite els mòduls LLE necessaris per a les funcions en línia (si estan instal·lats)</string>
<string name="enable_required_online_lle_modules_desc">Habilita els mòduls LLE necessaris per al mode multijugador en línia, accés a la eShop, etc.</string>
<string name="clock">Rellotge</string>
@ -190,7 +212,6 @@
<string name="storage">Emmagatzematge</string>
<string name="compress_cia_installs">Comprimir el contingut de CIAs instal·lats</string>
<string name="compress_cia_installs_description">Comprimix el contingut de fitxers CIA quan són instal·lats a la SD emulada. Només afecta contingut CIA instal·lat amb esta opció activada.</string>
<!-- Camera settings strings -->
<string name="inner_camera">Càmera interior</string>
<string name="outer_left_camera">Càmera esquerra externa</string>
@ -211,23 +232,30 @@
<string name="spirv_shader_gen_description">Usa SPIR-V en vez de GLSL per a emetre el fragment de ombrejador utilitzat per a emular PICA.</string>
<string name="disable_spirv_optimizer">Desativar l\'optimitzador SPIR-V</string>
<string name="disable_spirv_optimizer_description">Desactiva la passada d\'optimització SPIR-V reduint considerablement el quequeig i afectant poc el rendiment.</string>
<string name="async_shaders">Activar compilació de ombrejadors asíncrona</string>
<string name="async_shaders_description">Compila els ombrejats en segón pla per a reduir les aturades durant la partida.
S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="linear_filtering">Filtre Linear</string>
<string name="linear_filtering_description">Activa el filtre linear, que fa que els gràfics del joc es vegen més suaus.</string>
<string name="use_integer_scaling">Escalat a múltiples sencers</string>
<string name="use_integer_scaling_description">Escala les pantalles amb un multiplicador enter de la pantalla original de 3DS. Per a dissenys amb dos grandàries de pantalla diferents, la pantalla més gran s\'escala amb un multiplicador enter.</string>
<string name="texture_filter_name">Filtre de Textures</string>
<string name="texture_filter_description">Millora l\'aspecte visual de les aplicacions aplicant un filtre a les textures. Els filtres compatibles són Anime4K, Ultrafast, Bicubic, ScaleForce, xBRZ Freescale i MMPX.</string>
<string name="delay_render_thread">Endarrerir fil de renderitzat del joc</string>
<string name="delay_render_thread_description">Retarda el fil de renderitzat del joc en enviar dades a la GPU. Ajuda a solucionar problemes de rendiment en les (poques) aplicacions amb velocitats de fotogrames dinàmiques.</string>
<string name="advanced">Avançat</string>
<string name="texture_sampling_name">Mostreig de Textures</string>
<string name="texture_sampling_description">Sobreescriu el filtre de mostreig usat en jocs. Pot ser útil en uns certs casos de jocs amb baix rendiment en pujar la resolució. Si no estàs segur, possa\'l en Controlat per Joc.</string>
<string name="shaders_accurate_mul">Multiplicació Precisa</string>
<string name="shaders_accurate_mul_description">Usa multiplicacions més precises en els ombrejos de Hardware, que podrien arreglar uns certs problemes gràfics. Quan s\'active, el rendiment es reduirà.</string>
<string name="asynchronous_gpu">Activar Emulació Asíncrona de la GPU</string>
<string name="asynchronous_gpu_description">Usa un fil separat per a emular la GPU de manera asíncrona. Quan s\'active, el rendiment millorarà.</string>
<string name="frame_limit_enable">Límit de velocitat</string>
<string name="frame_limit_enable_description">Quan s\'active, la velocitat d\'emulació estarà limitada a un percentatge determinat de la velocitat normal. Quan es desactive, la velocitat d\'emulació no tindrà límit i la tecla d\'accés ràpid de velocitat turbo no funcionarà.</string>
<string name="frame_limit_slider">Limitar percentatge de velocitat</string>
<string name="frame_limit_slider_description">Especifica el valor al qual es limita la velocitat d\'emulació. Amb el valor per defecte del 100%, l\'emulació es limitarà a la velocitat normal. Els valors alts o baixos incrementaran o reduiran el límit de velocitat.</string>
<string name="android_hide_images">Ocultar les imatges de 3DS en Android</string>
<string name="android_hide_images_description">Evita que Android indexe les imatges de la càmera, captures de pantalla i textures personalitzades de la 3DS i les mostre en la galeria. És possible que hages de reiniciar el dispositiu després de canviar esta configuració perquè tinga efecte.</string>
<string name="turbo_limit">Límit de Velocitat Turbo</string>
<string name="turbo_limit_description">Límit de velocitat d\'emulació utilitzat mentres la tecla d\'accés ràpid turbo està activa.</string>
<string name="expand_to_cutout_area">Expandir a l\'àrea de retallada</string>
@ -249,10 +277,18 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="debug_warning">Avís: Modificar estes configuracions reduiran la velocitat d\'emulació.</string>
<string name="stereoscopy">Estereoscopia</string>
<string name="render3d">Mode 3D Estereoscòpic</string>
<string name="render3d_description">Seleccione el mode 3D estereoscòpic per a renderitzat 3D. Els modes costat a costat són els més comuns en l\'actualitat. Els modes Anaglifo i Entrellaçat sempre s\'apliquen a totes les pantalles connectades.</string>
<string name="factor3d">Profunditat</string>
<string name="factor3d_description">Especifica el valor del regulador 3D. Hauria d\'estar posat a més enllà del 0% quan el Mode 3D Estereoscòpic està activat.\nNota: Els valors de profunditat superiors al 100% no són possibles en hardware real i poden causar problemes gràfics.</string>
<string name="disable_right_eye_render">Desactivar Renderitzat d\'Ull Dret</string>
<string name="disable_right_eye_render_description">Millora enormement el rendiment en algunes aplicacions, però pot provocar parpellejos en unes altres.</string>
<string name="swap_eyes_3d">Intercanviar Ulls</string>
<string name="swap_eyes_3d_description">Intercanvia quin ull es mostra en cada costat. Combinat amb el mode Costat a Costat, permet veure en 3D creuant els ulls!</string>
<string name="render_3d_which_display">Renderitzat 3D Estereoscòpic</string>
<string name="render_3d_which_display_description">Decidix si s\'activa el 3D estereoscòpic i en quines pantalles. Les opcions de pantalla única només són rellevants quan es connecten diverses pantalles.</string>
<string name="render_3d_which_display_both">Activat (totes les pantalles)</string>
<string name="render_3d_which_display_primary">Activat (només pantalla principal)</string>
<string name="render_3d_which_display_secondary">Activat (només pantalla secundària)</string>
<string name="cardboard_vr">Cardboard VR</string>
<string name="cardboard_screen_size">Grandària de la pantalla Cardboard</string>
<string name="cardboard_screen_size_description">Escala la pantalla a un percentatge de la seua grandària original.</string>
@ -277,6 +313,7 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="audio_volume">Volum</string>
<string name="audio_stretch">Extensió d\'Àudio</string>
<string name="audio_stretch_description">Estén l\'àudio per a reduir les aturades. Quan s\'active, la latència d\'àudio s\'incrementarà i reduirà un poc el rendiment.</string>
<string name="realtime_audio">Activar àudio en temps real</string>
<string name="realtime_audio_description">Ajusta la velocitat de reproducció d\'àudio per a compensar les caigudes en la velocitat d\'emulació de quadres. Això significa que l\'àudio es reproduirà a velocitat completa fins i tot quan la velocitat de quadres del joc siga baixa. Pot causar problemes de desincronització d\'àudio.</string>
<string name="audio_input_type">Dispositiu d\'entrada d\'àudio</string>
<string name="sound_output_mode">Mode d\'eixida de l\'àudio</string>
@ -288,14 +325,19 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="hw_shaders_description">Usa el hardware per a emular els ombrejadors de 3DS. Quan s\'active, el rendiment millorarà notablement.</string>
<string name="cpu_clock_speed">Velocitat de rellotge de la CPU</string>
<string name="vsync">Activar Sincronització Vertical</string>
<string name="vsync_description">Sincronitza la freqüència de fotogrames del joc amb la freqüència d\'actualització del teu dispositiu. Pot causar latència d\'entrada addicional, però pot reduir el tearing en alguns casos.</string>
<string name="renderer_debug">Renderitzador de depuració</string>
<string name="renderer_debug_description">Arxiva informació addicional gràfica relacionada amb la depuració. Quan està activada, el rendiment dels jocs serà reduït considerablement</string>
<string name="instant_debug_log">Guardar l\'eixida del registre en cada missatge</string>
<string name="instant_debug_log_description">Envia immediatament el registre de depuració a un arxiu. Usa-ho si Azahar falla i es talla l\'eixida del registre.</string>
<string name="delay_start_lle_modules">Inici diferit amb mòduls LLE</string>
<string name="delay_start_lle_modules_description">Retarda l\'inici de l\'aplicació quan els mòduls LLE estan habilitats.</string>
<string name="deterministic_async_operations">Operacions asíncrones deterministes</string>
<string name="deterministic_async_operations_description">Fa que les operacions asíncrones siguen deterministes per a la depuració. Habilitar esta opció pot causar bloquejos.</string>
<string name="enable_rpc_server">Activar Servidor RPC</string>
<string name="enable_rpc_server_desc">Activa el servidor RPC en el port 45987. Això permet llegir/escriure de manera remota la memòria emulada.</string>
<string name="toggle_unique_data_console_type">Canviar tipus de consola en les dades úniques de consola</string>
<string name="toggle_unique_data_console_type_desc">Permet alternar el tipus de consola (Old 3DS &#8596; New 3DS) per a poder descarregar el firmware del sistema oposat des de la configuració del sistema.</string>
<string name="shader_jit">Activar Ombreig JIT</string>
<string name="shader_jit_description">Usar el motor JIT en lloc de l\'intèrpret per a l\'emulació del ombrejador de software.</string>
@ -306,6 +348,8 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="layout_screen_orientation_landscape_reverse">Horitzontal invertida</string>
<string name="layout_screen_orientation_portrait">Vertical</string>
<string name="layout_screen_orientation_portrait_reverse">Vertical invertida</string>
<string name="layouts_to_cycle">Cicle d\'Estils</string>
<string name="layouts_to_cycle_description">Selecciona quins estils es poden ciclar amb la tecla d\'accés ràpid</string>
<string name="aspect_ratio_default">Per omissió</string>
<string name="aspect_ratio_16_9">16:9</string>
<string name="aspect_ratio_4_3">4:3</string>
@ -326,7 +370,6 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="learn_more">Més Informació</string>
<string name="close">Tancar</string>
<string name="reset_to_default">Restablir valors de fàbrica</string>
<string name="redump_games"><![CDATA[Per favor, seguix les guies per a bolcar els teus <a href=\"https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/dumping-game-cartridges/\">cartutxos de joc</a> i/o <a href=\"https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/dumping-installed-titles/\">títols instal·lats</a>.]]></string>
<string name="option_default">Per omissió</string>
<string name="none">Cap</string>
<string name="auto">Auto</string>
@ -368,9 +411,7 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="preferences_theme">Tema i Color</string>
<string name="preferences_layout">Estil</string>
<!-- ROM loading errors -->
<string name="loader_error_encrypted">La teua ROM està encriptada</string>
<string name="loader_error_invalid_format">Format de ROM no vàlid</string>
<string name="loader_error_file_not_found">El fitxer ROM no existix</string>
<string name="no_game_present">No hi ha cap joc iniciable!</string>
@ -413,6 +454,13 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="emulation_portrait_layout_top_full">Per omissió</string>
<string name="emulation_secondary_display_default">Per defecte del sistema (espill)</string>
<string name="emulation_screen_layout_custom">Estil Personalitzat</string>
<string name="bg_color">Color de fons</string>
<string name="bg_color_description">El color que apareix darrere de les pantalles durant l\'emulació, representat com un valor RGB.</string>
<string name="bg_red">Roig</string>
<string name="bg_green">Verd</string>
<string name="bg_blue">Azul</string>
<string name="second_screen_opacity">Opacitat personalitzada de la segona pantalla</string>
<string name="second_screen_opacity_description">L\'opacitat de la segona pantalla de 3DS en usar la pantalla personalitzada. Útil si la segona pantalla esta posicionada en la part superior de la primera pantalla.</string>
<string name="emulation_small_screen_position">Posició de Pantalla Xicoteta</string>
<string name="small_screen_position_description">On hauria d\'aparéixer la pantalla xicoteta en relació amb la gran en Proporció de Pantalla Gran?</string>
<string name="small_screen_position_top_right">Amunt a la dreta</string>
@ -508,8 +556,17 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="fatal_error">Error Fatal</string>
<string name="fatal_error_message">Ha ocorregut un error fatal. Mira el registre per a més detalls.\nSeguir amb l\'emulació podria resultar en diversos penges i problemes.</string>
<string name="unsupported_encrypted">Aplicació cifrada no suportada</string>
<string name="invalid_system_mode">Mode de sistema no vàlid</string>
<string name="invalid_system_mode_message">Les aplicacions exclusives de New 3DS no es poden carregar sense activar el mode New 3DS.</string>
<!-- Disk Shader Cache -->
<string name="preparing_shaders">Preparant ombrejadors</string>
<string name="building_shaders">Construint%s</string>
<string name="delete_shader_cache">Eliminar cache d\'ombrejadors</string>
<string name="delete_cache_select_backend">Selecciona l\'API gràfica per a eliminar la cache d\'ombrejadors</string>
<string name="deleting_shader_cache">Eliminant la caché d\'ombrejadors, per favor espere…</string>
<string name="shader_cache_deleted">Caché d\'ombrejadors eliminada</string>
<!-- About Game Dialog -->
<string name="play">Jugar</string>
<string name="uninstall_cia">Desinstal·lar Aplicació</string>
@ -532,11 +589,37 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="game_context_id">ID:</string>
<string name="game_context_file">Fitxer:</string>
<string name="game_context_type">Tipus:</string>
<string name="game_context_insert">Inserir Cartutx</string>
<string name="game_context_eject">Expulsar Cartutx</string>
<!-- Performance Overlay settings -->
<string name="performance_overlay_show">Mostrar informació de rendiment</string>
<string name="performance_overlay_options">Informació de rendiment</string>
<string name="performance_overlay_enable">Activar informació de rendiment</string>
<string name="performance_overlay_options_description">Configura la informació de rendiment</string>
<string name="performance_overlay_show_fps">Mostrar FPS</string>
<string name="performance_overlay_show_fps_description">Mostra els fotogrames per segon actuals.</string>
<string name="performance_overlay_show_frametime">Mostrar duració de fotogrames</string>
<string name="performance_overlay_show_frametime_description">Mostra la duració actual de cada fotograma.</string>
<string name="performance_overlay_show_speed">Mostrar velocitat</string>
<string name="performance_overlay_show_speed_description">Mostra el percentatge de velocitat d\'emulació actual.</string>
<string name="performance_overlay_show_app_ram_usage">Mostrar l\'ús de memòria de l\'aplicació</string>
<string name="performance_overlay_show_app_ram_usage_description">Mostra la quantitat de memòria RAM que esta usant l\'emulador.</string>
<string name="performance_overlay_show_available_ram">Mostrar memòria disponible</string>
<string name="performance_overlay_show_available_ram_description">Mostra la quantitat de memòria RAM que esta disponible.</string>
<string name="performance_overlay_show_battery_temp">Mostrar la temperatura de la bateria</string>
<string name="performance_overlay_show_battery_temp_description">Mostra la temperatura actual de la bateria en Celsius i Fahrenheit.</string>
<string name="performance_overlay_position">Posició de la informació</string>
<string name="performance_overlay_position_description">Tria on la informació de rendiment serà mostrada en la pantalla.</string>
<string name="performance_overlay_position_top_left">Dalt a l\'esquerra</string>
<string name="performance_overlay_position_top_right">Dalt a la dreta</string>
<string name="performance_overlay_position_bottom_left">Avall a l\'esquerra</string>
<string name="performance_overlay_position_bottom_right">Avall a la dreta</string>
<string name="performance_overlay_position_center_top">Dalt al centre</string>
<string name="performance_overlay_position_center_bottom">Avall al centre</string>
<string name="performance_overlay_background">Fons de la informació</string>
<string name="performance_overlay_background_description">Agrega un fons darrere de la informació per a fer-la més llegible.</string>
<!-- Cheats -->
<string name="cheats">Trucs</string>
<string name="cheats_add">Afegir trucs</string>
@ -639,6 +722,7 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<!-- Render 3D modes -->
<string name="side_by_side">De costat a costat</string>
<string name="side_by_side_full">De costat a costat ample complet</string>
<string name="anaglyph">Anàglifo</string>
<string name="interlaced">Entrellaçat</string>
<string name="reverse_interlaced">Entrellaçat invers</string>
@ -827,4 +911,19 @@ S\'esperen errors gràfics temporals quan estigue activat.</string>
<string name="emulation_occupied_quicksave_slot">Guardat ràpid - %1$tF %1$tR</string>
<string name="quickload_not_found">Guardat ràpid no disponible.</string>
</resources>
<!-- File Compression -->
<string name="compress">Comprimir</string>
<string name="compressing">Comprimint...</string>
<string name="decompress">Descomprimir</string>
<string name="decompressing">Descomprimint...</string>
<string name="compress_success">Compressió completada amb èxit.</string>
<string name="compress_unsupported">Compressió no suportada amb este fitxer.</string>
<string name="compress_already">Este fitxer ja està comprimit.</string>
<string name="compress_failed">Va fallar la compressió.</string>
<string name="decompress_success">Descompressió completada amb èxit.</string>
<string name="decompress_unsupported">Descompressió no suportada amb este fitxer.</string>
<string name="decompress_not_compressed">Este fitxer no està comprimit.</string>
<string name="decompress_failed">Va fallar la descompressió.</string>
<string name="compress_decompress_installed_app">Les aplicacions ja instal·lades no es poden comprimir ni descomprimir.</string>
</resources>

View file

@ -0,0 +1,332 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_disclaimer">Este software ejecutará aplicaciones para la consola portátil Nintendo 3DS. No se incluyen juegos.\n\nAntes de comenzar con la emulación, seleccione una carpeta para almacenar los datos de usuario de Azahar en.\n\nQué es ésto:\n<a href='https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage'>Wiki - Datos de usuario de Azahar Android y almacenamiento</a></string>
<string name="app_notification_channel_description">Notificaciones del emulador 3DS Azahar</string>
<string name="app_notification_running">Azahar está ejecutándose</string>
<string name="app_game_install_description">A continuación, deberás seleccionar una Carpeta de Aplicaciones. Azahar mostrará todas las ROM de 3DS de la carpeta seleccionada en la aplicación.\n\nLas ROM CIA, actualizaciones y los DLC deben instalarse por separado haciendo clic en el icono de la carpeta y seleccionando Instalar CIA.</string>
<!-- Home Strings -->
<string name="grid_menu_core_settings">Configuración</string>
<string name="home_options">Opciones</string>
<string name="home_search">Buscar</string>
<string name="home_games">Aplicaciones</string>
<string name="settings_description">Configurar opciones del emulador</string>
<string name="install_game_content">Instalar archivo CIA</string>
<string name="install_game_content_description">Instalar aplicaciones, actualizaciones o DLC</string>
<string name="share_log">Compartir Registro</string>
<string name="share_log_description">Compartir el archivo de registro de Azahar para depurar problemas</string>
<string name="gpu_driver_manager">Administrador de drivers de GPU</string>
<string name="install_gpu_driver">Instalar drivers de GPU</string>
<string name="install_gpu_driver_description">Instala drivers alternativos para intentar mejorar el rendimiento o la precisión</string>
<string name="driver_already_installed">Driver ya instalado</string>
<string name="custom_driver_not_supported">Drivers personalizados no compatibles</string>
<string name="custom_driver_not_supported_description">La carga de drivers personalizados no es compatible en este dispositivo.\n¡Comprueba esta opción otra vez en el futuro para ver si ya está disponible!</string>
<string name="share_log_not_found">No se encontró ningún archivo de registro</string>
<string name="select_games_folder">Selecciona el directorio de aplicaciones</string>
<string name="select_games_folder_description">Permite que Azahar llene la lista de aplicaciones</string>
<string name="about">Acerca de</string>
<string name="citra_description">Un emulador de 3DS de código abierto</string>
<string name="about_description">Versión de compilación, créditos y más</string>
<string name="games_dir_selected">Directorio de aplicaciones seleccionado</string>
<string name="select_citra_user_folder_home_description">Cambia los archivos que Azahar usa para cargar aplicaciones</string>
<string name="theme_and_color_description">Cambia la apariencia de la app</string>
<string name="install_cia_title">Instalar CIA</string>
<!-- GPU driver installation -->
<string name="select_gpu_driver">Seleccionar driver de GPU</string>
<string name="select_gpu_driver_title">¿Quieres reemplazar tu driver actual de GPU?</string>
<string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Por defecto</string>
<string name="select_gpu_driver_install_success">Instalado %s</string>
<string name="select_gpu_driver_use_default">Usando el driver por defecto de la GPU</string>
<string name="select_gpu_driver_error">¡El driver seleccionado no es válido, se usará el driver por defecto del sistema!</string>
<string name="system_gpu_driver">Driver de la GPU del sistema</string>
<string name="installing_driver">Instalando el driver...</string>
<!-- About screen strings -->
<string name="copied_to_clipboard">Copiado al portapapeles</string>
<string name="contributors">Colaboradores</string>
<string name="contributors_description">Colaboradores que hicieron posible Azahar</string>
<string name="licenses_description">Proyectos utilizados por Azahar para Android</string>
<string name="build">Compilación</string>
<string name="licenses">Licencias</string>
<!-- Setup strings -->
<string name="welcome">¡Te damos la bienvenida!</string>
<string name="welcome_description">Aprende a configurar &lt;b>Azahar&lt;/b> y disfruta de la emulación.</string>
<string name="get_started">Comenzar</string>
<string name="step_complete">¡Completado!</string>
<string name="games">Aplicaciones</string>
<string name="games_description">Selecciona la carpeta de &lt;b>Aplicaciones&lt;/b> con el botón de abajo.</string>
<string name="done">Terminado</string>
<string name="done_description">¡Ya estás listo!\n¡Disfruta del emulador!</string>
<string name="text_continue">Continuar</string>
<string name="notifications">Notificaciones</string>
<string name="notifications_description">Concede el permiso de notificaciones con el botón de abajo.</string>
<string name="give_permission">Conceder permiso</string>
<string name="notification_warning">¿Omitir el permiso de notificaciones?</string>
<string name="notification_warning_description">Azahar no podrá notificarte sobre información importante.</string>
<string name="filesystem_permission_warning">Faltan permisos</string>
<string name="filesystem_permission_warning_description">Azahar necesita permiso para administrar archivos en este dispositivo para poder almacenar y administrar los datos de usuario.\n\nPor favor, concede el permiso antes de continuar.</string>
<string name="camera_permission">Cámara</string>
<string name="camera_permission_description">Concede el permiso de cámara para emular la cámara de la 3DS.</string>
<string name="microphone_permission">Micrófono</string>
<string name="microphone_permission_description">Concede el permiso de micrófono para emular el micrófono de la 3DS.</string>
<string name="filesystem_permission">Sistema de archivos</string>
<string name="filesystem_permission_description">Concede el permiso de sistema de archivos para permitir que Azahar almacene archivos.</string>
<string name="permission_denied">Permiso denegado</string>
<string name="add_games_warning">¿Omitir la selección de la carpeta de aplicaciones?</string>
<string name="add_games_warning_description">No se mostrará nada en la lista de aplicaciones si no se selecciona una carpeta.</string>
<string name="permissions">Permisos</string>
<string name="select_emulator_data_folders">Carpetas de datos</string>
<string name="select_emulator_data_folders_description"><![CDATA[Seleccionar carpetas de datos<br/>(Carpeta de usuario es necesaria)]]></string>
<string name="permissions_description">Concede permisos opcionales para usar funciones específicas del emulador</string>
<string name="warning_help">Ayuda</string>
<string name="warning_skip">Omitir</string>
<string name="warning_cancel">Cancelar</string>
<string name="select_citra_user_folder">Selecciona la carpeta de usuario</string>
<string name="select_citra_user_folder_description"><![CDATA[Selecciona tu directorio de <a href=\"https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage\">datos de usuario</a> con el botón de abajo.]]></string>
<string name="select_which_user_directory_to_use">Parece que tienes directorios de usuario configurados tanto para Lime3DS como para Azahar. Probablemente se deba a que actualizaste a Azahar y, cuando se te pidió, elegiste un directorio de usuario diferente al que usabas para Lime3DS.\n\nEsto puede haberte hecho pensar que perdiste partidas guardadas u otras configuraciones; te pedimos disculpas si eso ocurrió.\n\n¿Prefieres volver a usar tu directorio de usuario original de Lime3DS, restaurando la configuración y las partidas guardadas de Lime3DS, o conservar tu directorio de usuario actual de Azahar?\n\nNinguno de los directorios se eliminará, independientemente de tu elección, y puedes cambiar libremente entre ellos usando la opción \"Selecciona la carpeta de usuario\".</string>
<string name="keep_current_azahar_directory">Mantener el directorio actual de Azahar</string>
<string name="use_prior_lime3ds_directory">Usar el directorio de Lime3DS anterior</string>
<string name="select">Seleccionar</string>
<string name="cannot_skip">No puedes omitir configurar la carpeta de usuario</string>
<string name="cannot_skip_directory_description">Este paso es necesario para permitir que Azahar funcione. Por favor, selecciona un directorio y luego puedes continuar.</string>
<string name="selecting_user_directory_without_write_permissions">Has perdido los permisos de escritura en tu <a href="https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage">directorio de datos de usuario</a>, donde se guardan las partidas guardadas y otra información. Esto puede ocurrir después de actualizar Android o algunas aplicaciones. Vuelve a seleccionar el directorio para recuperar los permisos y poder continuar.</string>
<string name="invalid_selection">Selección no válida</string>
<string name="invalid_user_directory">La selección del directorio de usuario no es válida.\nVuelve a seleccionar el directorio de usuario, asegurándote de navegar hasta él desde la raíz del almacenamiento del dispositivo.</string>
<string name="filesystem_permission_lost">Azahar ha perdido el permiso para administrar archivos en este dispositivo. Esto puede ocurrir después de actualizar Android o algunas aplicaciones. Vuelve a conceder este permiso en la siguiente pantalla para seguir usando la aplicación.</string>
<string name="set_up_theme_settings">Configuración del tema</string>
<string name="setup_theme_settings_description">Configura tus preferencias de tema de Azahar.</string>
<string name="setup_set_theme">Establecer tema</string>
<!-- Search Strings -->
<string name="search_and_filter_games">Buscar y filtrar aplicaciones</string>
<string name="home_search_games">Buscar aplicaciones</string>
<string name="search_recently_played">Jugado recientemente</string>
<string name="search_recently_added">Añadido recientemente</string>
<string name="search_installed">Instalados</string>
<!-- Input related strings -->
<string name="controller_auto_map">Asignar control automáticamente</string>
<string name="controller_auto_map_description">Aplica la asignación de control estándar para todos los botones y ejes</string>
<string name="auto_map_prompt">¡Presiona este botón en tu control!</string>
<string name="auto_map_image_description">Botones frontales de 3DS en rombo con el botón A resaltado</string>
<string name="controller_clear_all">Eliminar todas las asignaciones</string>
<string name="controller_clear_all_confirm">Esto eliminará todas los asignaciones del control actual.</string>
<string name="controller_circlepad">Circle Pad</string>
<string name="controller_c">Palanca C</string>
<string name="controller_hotkeys">Teclas de atajo</string>
<string name="controller_hotkeys_description">Si la tecla \"Habilitar teclas de acceso rápido\" está asignada, se debe presionar esa tecla además de la tecla de acceso rápido asignada</string>
<string name="controller_hotkey_enable_button">Habilitar teclas de acceso rápido</string>
<string name="controller_triggers">Gatillos</string>
<string name="controller_trigger">Gatillo</string>
<string name="controller_dpad">Cruceta</string>
<string name="controller_dpad_axis">Cruceta (eje)</string>
<string name="controller_dpad_axis_description">Es posible que algunos controles no puedan asignar la Cruceta como un eje. Si ese es el caso, utilice la sección Cruceta (botones).</string>
<string name="controller_dpad_button">Cruceta (botón)</string>
<string name="controller_dpad_button_description">Solamente asigne la Cruceta a éstos si tiene problemas con las asignaciones de botones de la Cruceta (eje).</string>
<string name="controller_axis_vertical">Eje vertical</string>
<string name="controller_axis_horizontal">Eje horizontal</string>
<string name="direction_up">Arriba</string>
<string name="direction_down">Abajo</string>
<string name="direction_left">Izquierda</string>
<string name="direction_right">Derecha</string>
<string name="input_dialog_title">Asigna %1$s %2$s</string>
<string name="input_dialog_description">Presiona un botón o mueve una palanca</string>
<string name="input_binding">Asignación de botones</string>
<string name="input_binding_description">Pulsa un botón o mueve una palanca para asignarlo a %1$s.</string>
<string name="input_binding_description_vertical_axis">Presiona ARRIBA en tu joystick.</string>
<string name="input_binding_description_horizontal_axis">Presiona DERECHA en tu joystick.</string>
<string name="button_home">HOME</string>
<string name="button_swap">Intercambiar pantallas</string>
<string name="button_turbo">Turbo</string>
<string name="input_message_analog_only">¡Este control debe asignarse a una palanca analógica o a un eje de la Cruceta!</string>
<string name="input_message_button_only">¡Este dispositivo debe asignarse a un botón del control!</string>
<string name="turbo_limit_hotkey">Velocidad turbo</string>
<string name="turbo_enabled_toast">Velocidad turbo activada</string>
<string name="turbo_disabled_toast">Velocidad turbo desactivada</string>
<!-- System files strings -->
<string name="setup_system_files">Archivos del sistema</string>
<string name="setup_system_files_description">Realizar operaciones de archivos del sistema, como instalar archivos del sistema o iniciar el Menú Home</string>
<string name="setup_tool_connect">Conectar con la herramienta de configuración Artic</string>
<string name="setup_system_files_preamble"><![CDATA[Azahar necesita archivos de una consola real para poder utilizar algunas de sus funciones. Puedes obtener los archivos con la <a href=https://github.com/azahar-emu/ArticSetupTool>herramienta de configuración Artic</a>.<br>Notas:<ul><li><b>Esta operación instalará archivos únicos de la consola en Azahar, ¡no compartas las carpetas de usuario ni nand después de realizar el proceso de configuración!</b></li><li>Tras la configuración, Azahar se vinculará a la consola que ha ejecutado la herramienta de configuración. Puedes desvincular la consola más tarde desde la pestaña \"Archivos de sistema\" del menú de opciones del emulador.</li><li>No te conectes en línea con Azahar y la consola 3DS al mismo tiempo después de configurar los archivos del sistema, ya que esto podría causar problemas.</li><li>Se necesita la configuración de Old 3DS para que la New 3DS funcione (se recomienda configurar ambos modos).</li><li>Ambos modos de configuración funcionarán independientemente del modelo de la consola donde se ejecute la herramienta.</li></ul>]]></string>
<string name="setup_system_files_detect">Obteniendo el estado actual de los archivos del sistema, por favor espere...</string>
<string name="delete_system_files">Desvincular datos únicos de la consola</string>
<string name="delete_system_files_description"><![CDATA[Esta acción desvinculará tu consola real de Azahar, con las siguientes consecuencias:<br><ul><li>OTP, SecureInfo y LocalFriendCodeSeed serán eliminados de Azahar.</li><li>Tu lista de amigos se restablecerá y se cerrará la sesión de tu cuenta NNID/PNID.</li><li>Los archivos del sistema y los títulos de la eShop obtenidos a través de Azahar obtenidos a través de Azahar se volverán inaccesibles hasta que la misma consola se vincule nuevamente usando la herramienta de configuración (los datos guardados no se perderán).</li></ul><br>¿Continuar?]]></string>
<string name="setup_system_files_o3ds">Configuración de Old 3DS</string>
<string name="setup_system_files_n3ds">Configuración de New 3DS</string>
<string name="setup_system_files_possible">La configuración es posible.</string>
<string name="setup_system_files_o3ds_needed">La configuración Old 3DS es necesaria primero.</string>
<string name="setup_system_files_completed">Configuración completa.</string>
<string name="setup_system_files_enter_address">Ingresa la dirección de la herramienta de configuración Artic</string>
<string name="setup_system_files_preparing">Preparando configuración, por favor espere...</string>
<string name="boot_home_menu">Cargar el Menú HOME</string>
<string name="show_home_apps">Mostrar las aplicaciones del menú HOME en la lista de aplicaciones</string>
<string name="run_system_setup">Ejecutar la configuración de la consola cuando se ejecute el Menú HOME</string>
<string name="home_menu">Menú HOME</string>
<!-- Generic buttons (Shared with lots of stuff) -->
<string name="generic_buttons">Botones</string>
<string name="button">Botón</string>
<!-- System settings strings -->
<string name="emulation_settings">Opciones de emulación</string>
<string name="username">Nombre de usuario</string>
<string name="new_3ds">Modo New 3DS</string>
<string name="lle_applets">Usar Applets LLE (si están instaladas)</string>
<string name="apply_region_free_patch">Aplicar parche de región libre a las aplicaciones instaladas</string>
<string name="apply_region_free_patch_desc">Parchea la región de las aplicaciones instaladas para que estén libres de región, de modo que siempre aparezcan en el menú home.</string>
<string name="enable_required_online_lle_modules">Habilitar los módulos LLE necesarios para las funciones en línea (si están instalados)</string>
<string name="enable_required_online_lle_modules_desc">Habilita los módulos LLE necesarios para el modo multijugador en línea, acceso a la eShop, etc.</string>
<string name="clock">Reloj</string>
<string name="init_clock">Reloj</string>
<string name="init_clock_description">Configura el reloj emulado de la 3DS para que tenga la misma fecha y hora de tu dispositivo o para simular una distinta.</string>
<string name="device_clock">Reloj del sistema</string>
<string name="simulated_clock">Reloj simulado</string>
<string name="simulated_clock_description">Si el reloj está en \"Reloj simulado\", esto cambia la fecha y hora de inicio.</string>
<string name="profile_settings">Opciones de perfil</string>
<string name="emulated_region">Región</string>
<string name="emulated_language">Idioma</string>
<string name="birthday">Cumpleaños</string>
<string name="birthday_month">Mes</string>
<string name="birthday_day">Día</string>
<string name="country">País</string>
<string name="play_coins">Monedas de juego</string>
<string name="steps_per_hour">Pasos por hora del podómetro</string>
<string name="steps_per_hour_description">Número de pasos por hora reportados por el podómetro. Rango de 0 a 65.535.</string>
<string name="console_id">ID de la Consola</string>
<string name="regenerate_console_id">Regenerar ID de la consola</string>
<string name="regenerate_console_id_description">Esto reemplazará tu ID de consola de 3DS virtual por una nueva. Tu ID virtual actual será irrecuperable. Esto puede tener efectos inesperados en algunas aplicaciones. Si usas un archivo de configuración obsoleto esto podría fallar. ¿Quieres continuar?</string>
<string name="mac_address">Dirección MAC</string>
<string name="regenerate_mac_address">Regenerar dirección MAC</string>
<string name="regenerate_mac_address_description">Esto reemplazará tu dirección MAC actual por una nueva. No se recomienda hacerlo si obtuviste la dirección MAC de tu consola real con la herramienta de configuración. ¿Quieres continuar?</string>
<string name="plugin_loader">Cargador de complementos 3GX</string>
<string name="plugin_loader_description">Carga los complementos 3GX de la SD emulada si están disponibles.</string>
<string name="allow_plugin_loader">Permite que las aplicaciones cambien el estado del cargador de complementos.</string>
<string name="allow_plugin_loader_description">Permite que las apps de homebrew activen el cargador de complementos incluso cuando está desactivado.</string>
<string name="region_mismatch">Advertencia de región no válida</string>
<string name="region_mismatch_emulated">La configuración del país no es válida para la región emulada seleccionada.</string>
<string name="region_mismatch_console">La configuración del país no es válida para la consola vinculada actual.</string>
<string name="storage">Almacenamiento</string>
<string name="compress_cia_installs">Comprimir contenido CIA instalado</string>
<string name="compress_cia_installs_description">Comprime el contenido de archivos CIA cuando son instalados a la SD emulada. Solo afecta contenido CIA instalado con esta opción activada.</string>
<!-- Camera settings strings -->
<string name="inner_camera">Cámara interior</string>
<string name="outer_left_camera">Cámara izquierda externa</string>
<string name="outer_right_camera">Cámara derecha externa</string>
<string name="image_source">Fuente de la imagen de la cámara</string>
<string name="image_source_description">Configura la fuente de imagen de la cámara virtual. Puedes usar un archivo de imagen o una cámara si es compatible.</string>
<string name="camera_device">Cámara</string>
<string name="camera_device_description">Si la \"Fuente de imagen\" está en \"Cámara del dispositivo\", se usará la cámara del propio dispositivo.</string>
<string name="camera_facing_front">Frontal</string>
<string name="camera_facing_back">Trasera</string>
<string name="camera_facing_external">Externa</string>
<string name="image_flip">Rotación</string>
<!-- Graphics settings strings -->
<string name="renderer">Motor gráfico</string>
<string name="graphics_api">API gráfica</string>
<string name="spirv_shader_gen">Activar generación de shaders SPIR-V</string>
<string name="spirv_shader_gen_description">Usa SPIR-V en vez de GLSL para emitir el shader de fragmentos usado para emular PICA.</string>
<string name="disable_spirv_optimizer">Desactivar el optimizador de SPIR-V</string>
<string name="disable_spirv_optimizer_description">Desactiva el paso de optimización SPIR-V reduciendo considerablemente los tirones y afectando muy poco el rendimiento.</string>
<string name="async_shaders">Activar compilación asíncrona de shaders</string>
<string name="async_shaders_description">Compila los shaders en segundo plano para reducir los tirones durante la partida. Habrá fallos gráficos temporales cuando esté activado.</string>
<string name="linear_filtering">Filtrado lineal</string>
<string name="linear_filtering_description">Activa el filtrado linear, que hace que los gráficos del juego se vean más suaves.</string>
<string name="use_integer_scaling">Escalado de enteros</string>
<string name="use_integer_scaling_description">Escala las pantallas usando un multiplicador entero basado en la resolución original de 3DS. En diseños con dos tamaños de pantalla diferentes, la más grande usará el escalado de enteros.</string>
<string name="texture_filter_name">Filtrado de texturas</string>
<string name="texture_filter_description">Mejora los gráficos visuales de las aplicaciones aplicando un filtro a las texturas. Los filtros compatibles son Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale y MMPX.</string>
<string name="delay_render_thread">Atrasar hilo del motor gráfico del juego</string>
<string name="delay_render_thread_description">Retrasa el hilo del motor gráfico del juego cuando envía datos a la GPU. Ayuda con los problemas de rendimiento en las (muy pocas) aplicaciones con tasas de fotogramas dinámicas.</string>
<string name="advanced">Avanzado</string>
<string name="texture_sampling_name">Muestreo de texturas</string>
<string name="texture_sampling_description">Sobrescribe el filtro de muestreo original del juego. Puede ser útil en ciertos casos con juegos con problemas gráficos al aumentar la resolución. Si no estás seguro, déjalo en Controlado por juego.</string>
<string name="shaders_accurate_mul">Multiplicación precisa</string>
<string name="shaders_accurate_mul_description">Usa una multiplicación más precisa en los shaders por hardware, lo cual puede solucionar ciertos errores gráficos. Al activarse, el rendimiento disminuirá.</string>
<string name="asynchronous_gpu">Activar emulación asíncrona de la GPU</string>
<string name="asynchronous_gpu_description">Usa un hilo separado para emular la GPU de manera asíncrona. Al activarse, el rendimiento mejorará.</string>
<string name="frame_limit_enable">Límite de velocidad</string>
<string name="frame_limit_enable_description">Si se activa, la velocidad de emulación se limitará a un porcentaje determinado de velocidad normal. Si se desactiva, la velocidad no tendrá límite y el atajo para la velocidad velocidad turbo no funcionará.</string>
<string name="frame_limit_slider">Limitar porcentaje de velocidad</string>
<string name="frame_limit_slider_description">Especifica el valor al que se limita la velocidad de emulación. Con el valor por defecto del 100%, la emulación se limitará a la velocidad normal. Los valores altos o bajos aumentarán o reducirán el límite de velocidad.</string>
<string name="android_hide_images">Ocultar las imágenes de 3DS de la galería de Android</string>
<string name="android_hide_images_description">Evita que Android indexe las imágenes de la cámara, capturas de pantalla y texturas personalizadas de la 3DS y las muestre en la galería. Es posible que tengas que reiniciar el dispositivo después de cambiar esta configuración para que surta efecto.</string>
<string name="turbo_limit">Límite de velocidad turbo</string>
<string name="turbo_limit_description">Límite de velocidad de emulación que se aplica al activar el turbo.</string>
<string name="expand_to_cutout_area">Expandir al área de recorte</string>
<string name="expand_to_cutout_area_description">Amplía la pantalla para incluir el recorte de la cámara (o notch).</string>
<string name="internal_resolution">Resolución interna</string>
<string name="internal_resolution_description">Especifica la resolución de renderizado. Una alta resolución mejorará considerablemente la calidad visual, pero tendrá un gran impacto en el rendimiento y puede causar fallos en ciertas aplicaciones.</string>
<string name="internal_resolution_setting_auto">Auto (Tamaño pantalla)</string>
<string name="internal_resolution_setting_1x">Nativa (400x240)</string>
<string name="internal_resolution_setting_2x">2x Nativa (800x480)</string>
<string name="internal_resolution_setting_3x">3x Nativa (1200x720)</string>
<string name="internal_resolution_setting_4x">4x Nativa (1600x960)</string>
<string name="internal_resolution_setting_5x">5x Nativa (2000x1200)</string>
<string name="internal_resolution_setting_6x">6x Nativa (2400x1440)</string>
<string name="internal_resolution_setting_7x">7x Nativa (2800x1680)</string>
<string name="internal_resolution_setting_8x">8x Nativa (3200x1920)</string>
<string name="internal_resolution_setting_9x">9x Nativa (3600x2160)</string>
<string name="internal_resolution_setting_10x">10x Nativa (4000x2400)</string>
<string name="performance_warning">¡Desactivar esta opción reducirá notablemente el rendimiento de la emulación! Para obtener la mejor experiencia, se recomienda dejarla activada.</string>
<string name="debug_warning">Aviso: Modificar estos ajustes reducirá la velocidad de emulación.</string>
<string name="stereoscopy">Estereoscopia</string>
<string name="render3d">Modo 3D estereoscópico</string>
<string name="render3d_description">Elige el modo 3D estereoscópico para el renderizado 3D. Los modos de lado a lado son los más comunes en la actualidad. Los modos anaglifo y entrelazado se aplicarán siempre a todas las pantallas conectadas.</string>
<string name="factor3d">Profundidad</string>
<string name="factor3d_description">Especifica el valor del regulador 3D. Debería ajustarse por encima del 0% cuando el 3D estereoscópico esté activado.\nNota: Los valores de profundidad superiores al 100% no son posibles en el hardware real y pueden causar problemas gráficos.</string>
<string name="disable_right_eye_render">Desactivar el renderizado de ojo derecho</string>
<string name="disable_right_eye_render_description">Mejora mucho el rendimiento en algunas aplicaciones, pero puede causar parpadeo en otras.</string>
<string name="swap_eyes_3d">Intercambiar ojos</string>
<string name="swap_eyes_3d_description">Intercambia qué ojo se muestra en cada lado. Combinado con el modo lado a lado, ¡permite ver en 3D cruzando los ojos!</string>
<string name="render_3d_which_display">Renderizado 3D estereoscópico</string>
<string name="render_3d_which_display_description">Si se enciende el 3D estereoscópico, y en que pantallas. Las opciones para una sola pantalla solo son relevantes si hay varias pantallas conectadas.</string>
<string name="render_3d_which_display_both">Activado (todas las pantallas)</string>
<string name="render_3d_which_display_primary">Encendido (Solo Pantalla Principal)</string>
<string name="render_3d_which_display_secondary">Encendido (Solo Pantalla Secundaria)</string>
<string name="cardboard_vr">Cardboard VR</string>
<string name="cardboard_screen_size">Tamaño de la pantalla para Cardboard</string>
<string name="cardboard_screen_size_description">Escala la pantalla a un porcentaje de su tamaño original.</string>
<string name="cardboard_x_shift">Desplazamiento horizontal</string>
<string name="cardboard_x_shift_description">Especifica el porcentaje de espacio vacío para desplazar las pantallas horizontalmente. Los valores positivos acercan los dos ojos al medio, mientras que los negativos los alejan.</string>
<string name="cardboard_y_shift">Desplazamiento vertical</string>
<string name="cardboard_y_shift_description">Especifica el porcentaje de espacio vacío para desplazar las pantallas verticalmente. Los valores positivos mueven los dos ojos hacia abajo, mientras que los negativos los mueve hacia arriba.</string>
<string name="use_shader_jit">Shader JIT</string>
<string name="use_disk_shader_cache">Caché de shader en disco</string>
<string name="use_disk_shader_cache_description">Reduce los tirones al guardar y cargar shaders generados en el disco. No se puede usar sin activar la opción shader por hardware.</string>
<string name="utility">Utilidades</string>
<string name="dump_textures">Volcar texturas</string>
<string name="dump_textures_description">Las texturas se vuelcan en dump/textures/[Title ID]/.</string>
<string name="custom_textures">Texturas personalizadas</string>
<string name="custom_textures_description">Las texturas se cargan desde load/textures/[Title ID]/.</string>
<string name="preload_textures">Precargar texturas personalizadas</string>
<string name="preload_textures_description">Carga todas las texturas personalizadas en la memoria. Esta función puede usar mucha memoria.</string>
<string name="async_custom_loading">Carga asíncrona de texturas personalizadas</string>
<string name="async_custom_loading_description">Carga las texturas personalizadas de manera asíncrona con hilos de fondo para reducir los tirones de carga.</string>
<!-- Audio settings strings -->
<string name="audio_volume">Volumen</string>
<string name="audio_stretch">Estiramiento de audio</string>
<string name="audio_stretch_description">Estira el audio para reducir los tirones. Al activarse, la latencia del audio se incrementará y reducirá un poco el rendimiento.</string>
<string name="realtime_audio">Activar audio en tiempo real</string>
<string name="realtime_audio_description">Escala la velocidad de reproducción de audio para compensar por perdidas en la velocidad de fotogramas durante la emulación. Esto significa que el audio se reproducirá a velocidad completa incluso cuando la velocidad de fotogramas del juego sea baja. Puede causar problemas de desincronización de audio.</string>
<string name="audio_input_type">Dispositivo de entrada de audio</string>
<string name="sound_output_mode">Modo de salida del audio</string>
<!-- Debug settings strings -->
<string name="cpu_jit">CPU JIT</string>
<string name="cpu_jit_description">Usa el compilador Just-in-Time (JIT) para emular la CPU. Cuando este encendido, el rendimiento de los juegos mejorará significativamente. </string>
<string name="hw_shaders">Activar shader por hardware</string>
<string name="hw_shaders_description">Usa el hardware para emular los shaders de la 3DS. Al activarse, el rendimiento mejorará notablemente.</string>
<string name="cpu_clock_speed">Velocidad de reloj de la CPU</string>
<string name="vsync">Activar V-Sync</string>
<string name="vsync_description">Sincroniza la velocidad de fotogramas con la tasa de actualización de tu dispositivo. Puede causar latencia de entrada adicional, pero puede reducir el rasgado de imagen en algunos casos.</string>
<string name="renderer_debug">Renderizador de depuración</string>
<string name="toggle_unique_data_console_type">Alternar tipo de consola.</string>
<string name="emulation_toggle_controls">Alternar Controles</string>
<string name="emulation_switch_secondary_layout_description">El diseño usado por una pantalla secundaria conectada, alámbrica o inalámbrica (Chromecast, Miracast)</string>
</resources>

View file

@ -3,7 +3,7 @@
<string name="app_disclaimer">Este software irá executar aplicativos feitos para o console portátil Nintendo 3DS. Nenhum jogo está incluído.\n\nAntes de começar a emulação, selecione uma pasta para armazenar os dados do usuário do Azahar.\n\nO que é isto:\n<a href='https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage'>Wiki - Dados e armazenamento do usuário do Azahar para Android</a></string>
<string name="app_notification_channel_description">Notificações do emulador Azahar 3DS</string>
<string name="app_notification_running">Azahar está Executando</string>
<string name="app_notification_running">Azahar em Execução</string>
<string name="app_game_install_description">Em seguida, você precisará selecionar uma pasta de Aplicativos. O Azahar exibirá todas as ROMs de 3DS dentro da pasta selecionada no aplicativo.\n\nROMs, atualizações e DLC no formato CIA precisarão ser instaladas separadamente clicando no ícone da pasta e selecionando Instalar CIA.</string>
<!-- Home Strings -->
@ -14,7 +14,7 @@
<string name="settings_description">Definir as configurações do emulador</string>
<string name="install_game_content">Instalar arquivo CIA</string>
<string name="install_game_content_description">Instalar aplicativos, atualizações ou DLC</string>
<string name="share_log">Compartilhar Registro</string>
<string name="share_log">Compartilhar Log</string>
<string name="share_log_description">Compartilhe o arquivo de log do Azahar para depurar problemas</string>
<string name="gpu_driver_manager">Gerenciador de Driver da GPU</string>
<string name="install_gpu_driver">Instalar driver da GPU</string>
@ -23,8 +23,8 @@
<string name="custom_driver_not_supported">Drivers personalizados não suportados</string>
<string name="custom_driver_not_supported_description">O carregamento de driver personalizado não é suportado atualmente para este dispositivo.\nVerifique esta opção novamente no futuro para ver se o suporte foi adicionado!</string>
<string name="share_log_not_found">Nenhum arquivo de log encontrado</string>
<string name="select_games_folder">Selecione a Pasta de Aplicativos</string>
<string name="select_games_folder_description">Permitir que o Azahar preencha a lista de aplicativos</string>
<string name="select_games_folder">Selecionar Pasta de Aplicativos</string>
<string name="select_games_folder_description">Permite que o Azahar preencha a lista de aplicativos</string>
<string name="about">Sobre</string>
<string name="citra_description">Um emulador de 3DS de código aberto</string>
<string name="about_description">Versão da compilação, créditos e mais</string>
@ -86,7 +86,7 @@
<string name="warning_cancel">Cancelar</string>
<string name="select_citra_user_folder">Selecione a Pasta do Usuário</string>
<string name="select_citra_user_folder_description"><![CDATA[Selecione o seu diretório de <a href=\"https://web.archive.org/web/20240304193549/https://github.com/citra-emu/citra/wiki/Citra-Android-user-data-and-storage\">dados de usuário</a>com o botão abaixo.]]></string>
<string name="select_which_user_directory_to_use">Parece que você tem diretórios de usuários configurados para ambos o Lime3DS e o Azahar. Isso ocorreu devido a você ter atualizado para o Azahar, e quando solicitado, escolha um diretório de usuário diferente daquele que foi usado pelo Lime3DS.\n\nIsso pode ter resultado em você pensar que tenha perdidos dados salvos ou outras configurações - pedimos desculpas caso isso tenha acontecido.\n\nGostaria de voltar a usar sua pasta de usuário original do Lime3DS, restaurar configurações e salvar os jogos do Lime3DS, ou manter o diretório de usuário do Azahar atual?\n\nNenhum dos diretórios serão excluídos, independente de sua escolha, sinta-se livre para trocar entre elas usando a opção Selecionar Pasta de Usuário.</string>
<string name="select_which_user_directory_to_use">Parece que você possui pastas de usuário configuradas tanto para o Lime3DS quanto para o Azahar. Isso provavelmente ocorreu porque você atualizou para o Azahar e, quando solicitado, escolheu uma pasta de usuário diferente da que era usada pelo Lime3DS.\n\nIsso pode ter feito você pensar que perdeu seus saves ou outras configurações — pedimos desculpas se isso aconteceu.\n\nVocê gostaria de voltar a usar sua pasta de usuário original do Lime3DS, restaurando as configurações e jogos salvos do Lime3DS, ou manter sua pasta de usuário atual do Azahar?\n\nNenhuma das pastas será excluída, independentemente da sua escolha, e você pode alternar livremente entre elas usando a opção Selecionar Pasta do Usuário.</string>
<string name="keep_current_azahar_directory">Manter diretório atual do Azahar</string>
<string name="use_prior_lime3ds_directory">Usar o diretório antigo do Lime3DS</string>
<string name="select">Selecionar</string>
@ -107,9 +107,18 @@
<string name="search_recently_added">Recentemente adicionado</string>
<string name="search_installed">Instalado</string>
<!-- Input related strings -->
<string name="controller_auto_map">Mapeamento Automático do Controle</string>
<string name="controller_auto_map_description">Aplicar mapeamento padrão do controle para todos os botões e eixos</string>
<string name="auto_map_prompt">Pressione este botão no seu controle!</string>
<string name="auto_map_image_description">Botões de face em diamante do 3DS com o botão A destacado</string>
<string name="controller_clear_all">Limpar Todos os Mapeamentos</string>
<string name="controller_clear_all_confirm">Isso removerá todos os mapeamentos de controle atuais.</string>
<string name="controller_circlepad">Analógico</string>
<string name="controller_c">Direcional C-Stick</string>
<string name="controller_hotkeys">Teclas de atalho</string>
<string name="controller_hotkeys_description">Se a tecla \"Ativar Atalho\" estiver mapeada, ela deve ser pressionada em conjunto com o atalho mapeado</string>
<string name="controller_hotkey_enable_button">Ativar Atalho</string>
<string name="controller_triggers">Gatilhos</string>
<string name="controller_trigger">Gatilho</string>
<string name="controller_dpad">Direcional D-Pad</string>
@ -132,7 +141,7 @@
<string name="button_home">Menu Principal</string>
<string name="button_swap">Trocar telas</string>
<string name="button_turbo">Turbo</string>
<string name="input_message_analog_only">Este controle tem de ser mapeado a um eixo analógico do gamepad ou um eixo de D-pad!</string>
<string name="input_message_analog_only">Este controle deve ser mapeado para um analógico ou eixo do D-pad!</string>
<string name="input_message_button_only">Este controle tem de ser mapeado a um botão do gamepad!</string>
<string name="turbo_limit_hotkey">Velocidade Turbo</string>
<string name="turbo_enabled_toast">Velocidade Turbo Ativada</string>
@ -203,6 +212,8 @@
<string name="storage">Armazenamento</string>
<string name="compress_cia_installs">Comprimir conteúdo CIA instalado</string>
<string name="compress_cia_installs_description">Comprime o conteúdo dos arquivos CIA ao serem instalados no cartão SD emulado. Afeta apenas o conteúdo CIA instalado enquanto a opção estiver ativada.</string>
<string name="async_fs_operations">Operações assíncronas do sistema de arquivos</string>
<string name="async_fs_operations_description">Torna assíncronos os acessos ao sistema de arquivos emulado. Reduz consideravelmente os engasgos relacionados ao sistema de arquivos, mas pode aumentar um pouco os tempos de carregamento.</string>
<!-- Camera settings strings -->
<string name="inner_camera">Câmera frontal</string>
@ -228,10 +239,14 @@
<string name="async_shaders_description">Compila shaders em segundo plano para reduzir travamentos durante o jogo. Quando ativado, espere falhas gráficas temporárias</string>
<string name="linear_filtering">Filtragem Linear</string>
<string name="linear_filtering_description">Ativa a filtragem linear, que suaviza o visual do jogo.</string>
<string name="use_integer_scaling">Escala Inteira</string>
<string name="use_integer_scaling_description">Garante que a tela maior em todos os layouts tenha uma escala inteira de 240px de altura, correspondente à tela original do 3DS.</string>
<string name="texture_filter_name">Filtro de texturas</string>
<string name="texture_filter_description">Aprimora o visual dos aplicativos ao aplicar filtros às texturas. Os filtros compatíveis são: Anime4K Ultrafast, Bicúbico, ScaleForce, xBRZ Freescale e MMPX.</string>
<string name="delay_render_thread">Atrasar Thread de Renderização do Aplicativo</string>
<string name="delay_render_thread_description">Atrasar thread de renderização do aplicativo quando for enviado dados para a GPU. Ajuda com problemas de desempenho em (muito poucos) aplicativos com taxa de quadros dinâmica.</string>
<string name="simulate_3ds_gpu_timings">Simular temporizações da GPU do 3DS</string>
<string name="simulate_3ds_gpu_timings_description">Atrasa os eventos de conclusão da GPU com base em medições feitas no hardware original, para que os jogos tenham medições de tempo de GPU mais realistas. Ajuda a estabilizar jogos com FPS dinâmico. Desativar este recurso pode melhorar o desempenho em alguns casos raros, à custa da estabilidade.</string>
<string name="advanced">Avançado</string>
<string name="texture_sampling_name">Amostragem de Texturas</string>
<string name="texture_sampling_description">Substitui o filtro de amostragem usado pelos jogos. Isso pode ser útil em certos casos com jogos que se comportem mal durante o upscaling. Em caso de dúvidas, defina como Controlado pelo Jogo.</string>
@ -304,6 +319,8 @@
<string name="audio_stretch_description">Estica o áudio para reduzir engasgos. Quando ativado, aumenta a latência do áudio e reduz levemente o desempenho.</string>
<string name="realtime_audio">Ativar Áudio em Tempo Real</string>
<string name="realtime_audio_description">Dimensiona a velocidade de reprodução de áudio para compensar quedas na taxa de quadros da emulação. Isso significa que o áudio será reproduzido em velocidade máxima mesmo quando a taxa de quadros do jogo estiver baixa. Pode causar problemas de dessincronização de áudio.</string>
<string name="simulate_headphones_plugged">Simular fones de ouvido conectados</string>
<string name="simulate_headphones_plugged_description">Simula se os fones de ouvido estão conectados ao sistema emulado do 3DS.</string>
<string name="audio_input_type">Dispositivo de entrada de áudio</string>
<string name="sound_output_mode">Modo de Saída de Som</string>
@ -325,6 +342,8 @@
<string name="deterministic_async_operations_description">Torna as operações assíncronas determinísticas para depuração. Ativar essa opção pode causar congelamentos.</string>
<string name="enable_rpc_server">Ativar servidor RPC</string>
<string name="enable_rpc_server_desc">Ativa o servidor RPC na porta 45987. Isso permite ler e escrever remotamente a memória do sistema emulado.</string>
<string name="toggle_unique_data_console_type">Alternar tipo de console de dados únicos</string>
<string name="toggle_unique_data_console_type_desc">Alterna o tipo de console de dados únicos (Old 3DS &#8596; New 3DS) para permitir o download do firmware do sistema oposto nas configurações do sistema. </string>
<string name="shader_jit">Ativar Shader JIT</string>
<string name="shader_jit_description">Usa o mecanismo JIT em vez do interpretador para a emulação de shaders por software.</string>
@ -335,6 +354,8 @@
<string name="layout_screen_orientation_landscape_reverse">Paisagem Reversa</string>
<string name="layout_screen_orientation_portrait">Retrato</string>
<string name="layout_screen_orientation_portrait_reverse">Retrato Reverso</string>
<string name="layouts_to_cycle">Layouts para Alternar</string>
<string name="layouts_to_cycle_description">Quais layouts são percorridos pela tecla de atalho \"Alternar Layout\"</string>
<string name="aspect_ratio_default">Padrão</string>
<string name="aspect_ratio_16_9">16:9</string>
<string name="aspect_ratio_4_3">4:3</string>
@ -355,7 +376,6 @@
<string name="learn_more">Saber mais</string>
<string name="close">Fechar</string>
<string name="reset_to_default">Redefinir para o Padrão</string>
<string name="redump_games"><![CDATA[Por favor, siga os tutoriais para recarregar seus <a href=\"https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/dumping-game-cartridges/\">cartuchos de jogos</a> ou <a href=\"https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/dumping-installed-titles/\">títulos instalados</a>.]]></string>
<string name="option_default">Padrão</string>
<string name="none">Nenhum</string>
<string name="auto">Automático</string>
@ -398,11 +418,39 @@
<string name="preferences_layout">Disposição</string>
<!-- ROM loading errors -->
<string name="loader_error_generic_title">Erro ao carregar o aplicativo</string>
<string name="loader_error_invalid_format">Formato de aplicativo inválido</string>
<string name="loader_error_invalid_format_description"><![CDATA[Formato de arquivo do aplicativo não suportado.<br>Certifique-se de estar usando um dos formatos de arquivo compatíveis: <ul><li>Imagens de cartucho: <b>.cci/.zcci/.3ds</b></li><li>Arquivos instaláveis: <b>.cia/.zcia</b></li><li> Títulos homebrew: <b>.3dsx/.z3dsx</b></li><li>Contêineres NCCH: <b>.cxi/.zcxi/.app</b></li><li>Arquivos ELF: <b>.elf/.axf</b></li></ul>]]></string>
<string name="loader_error_invalid_system_mode">Modo de sistema inválido</string>
<string name="loader_error_invalid_system_mode_description">Aplicativos exclusivos do New 3DS não podem ser carregados sem ativar o modo New 3DS.</string>
<string name="loader_error_applying_patches">Erro ao aplicar patches</string>
<string name="loader_error_applying_patches_description">Ocorreu um erro genérico ao aplicar um patch ao aplicativo. Verifique o log para mais detalhes.</string>
<string name="loader_error_patch_wrong_application">Falha ao aplicar um patch porque ele foi projetado para um aplicativo diferente. Certifique-se de estar usando os patches para o aplicativo, região e versão corretos.</string>
<string name="loader_error_encrypted">Sua ROM está Criptografada</string>
<string name="loader_error_invalid_format">Formato inválido de ROM</string>
<string name="loader_error_encrypted_description"><![CDATA[O Azahar não suporta aplicativos criptografados. Leia nosso <a href=\"https://azahar-emu.org/blog/game-loading-changes/\">post no blog</a> para mais informações.]]></string>
<string name="loader_error_file_not_found">O arquivo ROM não existe</string>
<string name="no_game_present">Nenhum jogo inicializável presente!</string>
<string name="loader_error_generic">Ocorreu um erro ao carregar a ROM: \"%s (%d)\"</string>
<string name="core_error_success">Sucesso</string>
<string name="core_error_not_initialized">Não inicializado</string>
<string name="core_error_get_loader">Carregador do arquivo não encontrado, tipo de arquivo incompatível</string>
<string name="core_error_system_mode">Falha ao analisar o arquivo</string>
<string name="core_error_loader">Erro genérico do carregador</string>
<string name="core_error_loader_encrypted">Arquivo criptografado</string>
<string name="core_error_loader_invalid_format">Arquivo corrompido</string>
<string name="core_error_loader_gba_title">O arquivo é um título de GBA</string>
<string name="core_error_loader_error_patches">Erro ao aplicar patches</string>
<string name="core_error_loader_patches_invalid_title">Os patches são para um aplicativo diferente</string>
<string name="core_error_system_files">Arquivos de sistema ausentes</string>
<string name="core_error_savestate">Falha no savestate</string>
<string name="core_error_artic_disconnected">Artic Base desconectado</string>
<string name="core_error_n3ds_application">O arquivo é um aplicativo do New 3DS</string>
<string name="core_error_core_exception_raised">Exceção de núcleo disparada</string>
<string name="core_error_memory_exception_raised">Exceção de memória disparada</string>
<string name="core_error_shutdown_requested">Desligamento solicitado</string>
<string name="core_error_unknown">Erro desconhecido</string>
<!-- Emulation Menu -->
<string name="emulation_menu_help">Pressione Voltar para acessar o menu.</string>
<string name="emulation_save_state">Salvar estado</string>
@ -472,7 +520,7 @@
<string name="emulation_custom_layout_y">Posição Y</string>
<string name="emulation_custom_layout_width">Largura</string>
<string name="emulation_custom_layout_height">Altura</string>
<string name="emulation_cycle_landscape_layouts">Trocar Disposições</string>
<string name="emulation_cycle_landscape_layouts">Alternar Layout</string>
<string name="emulation_swap_screens">Trocar telas</string>
<string name="emulation_rotate_upright">Girar Tela para Posição Vertical</string>
<string name="emulation_touch_overlay_reset">Redefinir sobreposição</string>
@ -550,6 +598,10 @@
<!-- Disk Shader Cache -->
<string name="preparing_shaders">Preparando Shaders</string>
<string name="building_shaders">Construindo %s</string>
<string name="delete_shader_cache">Excluir Cache de Shaders</string>
<string name="delete_cache_select_backend">Selecione a API gráfica para excluir o cache de shaders</string>
<string name="deleting_shader_cache">Excluindo cache de shaders do título, aguarde...</string>
<string name="shader_cache_deleted">Cache de shaders excluído</string>
<!-- About Game Dialog -->
<string name="play">Jogar</string>

View file

@ -190,7 +190,6 @@
<string name="storage">存储</string>
<string name="compress_cia_installs">压缩已安装的 CIA 内容</string>
<string name="compress_cia_installs_description">安装到模拟 SD 卡时,压缩 CIA 文件的内容。仅影响启用此设置时安装的 CIA 内容。</string>
<!-- Camera settings strings -->
<string name="inner_camera">内置摄像头</string>
<string name="outer_left_camera">外置左摄像头</string>
@ -334,7 +333,6 @@
<string name="learn_more">了解更多</string>
<string name="close">关闭</string>
<string name="reset_to_default">恢复默认</string>
<string name="redump_games"><![CDATA[请按照指南以转储您的<a href=\"https://web.archive.org/web/20240304210021/https://citra-emu.org/wiki/dumping-game-cartridges/\">游戏卡带</a>或<a href=\"https://web.archive.org/web/20240304210011/https://citra-emu.org/wiki/dumping-installed-titles/\">已安装的应用</a>。]]></string>
<string name="option_default">默认</string>
<string name="none"></string>
<string name="auto">自动</string>
@ -376,9 +374,7 @@
<string name="preferences_theme">主题和色彩</string>
<string name="preferences_layout">布局</string>
<!-- ROM loading errors -->
<string name="loader_error_encrypted">您的 ROM 是加密的</string>
<string name="loader_error_invalid_format">无效的 ROM 格式</string>
<string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="no_game_present">目前没有可启动的游戏!</string>

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