Compare commits

...

132 Commits

Author SHA1 Message Date
d2df38c8c3 Android #119 2023-11-01 00:57:53 +00:00
7e284809de Merge pull request #11931 from t895/applet-launcher
android: Applet launcher UI
2023-10-31 16:55:57 -04:00
324c93e4aa Merge pull request #11929 from dima-xd/swkbd-applet
service: am: Add support for LLE Software Keyboard Applet
2023-10-31 16:55:49 -04:00
133788d0d4 android: Initialize filesystem components during application start 2023-10-31 14:41:40 -04:00
e8cb8b2668 android: Implement applet launcher 2023-10-31 14:41:40 -04:00
361dbdddcc service: am: Add support for LLE Software Keyboard Applet 2023-10-31 21:14:37 +03:00
ab3e3c11af Merge pull request #11925 from t895/controller-fix
android: Fix controllers stuck on player 2
2023-10-31 09:47:42 -04:00
db5c24eb66 Merge pull request #11892 from german77/pkm_screenshot
service: am: Implement ISelfController::SaveCurrentScreenshot
2023-10-31 09:47:19 -04:00
f7755df2af android: Reorder controller indexes and only use controllers
Before we could ignore controller inputs by forwarding them to player two if a non-controller was connected before and recognized as an input device.
2023-10-30 21:38:51 -04:00
c60204e255 Merge pull request #11922 from t895/simplify-card-layout
android: Simplify game card layout
2023-10-30 15:32:45 -04:00
5e69769356 Merge pull request #11903 from Macj0rdan/scrollable-volume-button
Implemented wheel event for volume control in VolumeButton
2023-10-30 15:32:39 -04:00
22cac3a5e3 Merge pull request #11728 from liushuyu/update-deps
Update external dependencies
2023-10-30 15:32:31 -04:00
e867768316 android: Simplify game card layout
Using a material card view to shape the image was just a waste of a layout pass. A shapeable image view does what we want and does it faster.
2023-10-30 13:28:52 -04:00
07276cf62a Merge pull request #11908 from t895/log-spam
android: Fix URI parsing in native code
2023-10-30 13:28:11 -04:00
f04bc172ae android: FileUtil: Add option to suppress log for native exists() calls
We often check for the existence of files that only exist in ExeFS so this can spam logcat with useless messages when scanning for games.
2023-10-30 11:38:10 -04:00
585b6e9d46 android: Fix resolving android URIs in native code 2023-10-30 11:38:10 -04:00
a9e29a3972 android: Refactor game metadata collection to new file
This also removes irrelevant data and adds new information from/to the Game data class and RomMetadata struct
2023-10-30 11:38:09 -04:00
1e61c3e1e7 android: Use header for EmulationSession 2023-10-30 11:28:23 -04:00
79d3cef8db Merge pull request #11920 from Termynat0r/master
Fix macOS build
2023-10-30 10:01:03 -04:00
3e0da4f698 Merge pull request #11916 from t895/focus-fix
android: Release touch on input overlay when opening in-game menu
2023-10-30 09:59:53 -04:00
789c16305d Merge pull request #11915 from t895/startup-freeze
android: Move game deserialization to another thread
2023-10-30 09:59:45 -04:00
1836e62d33 Merge pull request #11910 from liamwhite/surface-lost-on-creation
renderer_vulkan: ensure exception on surface loss
2023-10-30 09:59:37 -04:00
0bbbe80f75 Fix macOS build
Added missing preprocessor macros for macOS analog to linux and freebsd
2023-10-30 10:49:39 +01:00
70be45c992 android: InputHandler: Convert to object
This doesn't need to be an instance of a class because it doesn't hold any data. It's just all helper functions.
2023-10-30 01:20:27 -04:00
9b3c64f4a4 android: Removed unused ControllerMappingHelper 2023-10-30 01:20:27 -04:00
eec3d356b6 Merge pull request #11689 from liamwhite/breakpad
qt: implement automatic crash dump support
2023-10-29 23:41:13 -04:00
2c1d850b46 android: Release touch on input overlay when opening in-game menu 2023-10-29 21:42:47 -04:00
2581590023 android: Move game deserialization to another thread
Deserializing games from the cache in shared preferences was done on the main thread and could cause a stutter on startup.
2023-10-29 21:29:32 -04:00
adb0900906 Merge pull request #11911 from german77/leak_event
core: Close all KEvents
2023-10-29 19:46:47 -04:00
2d608cd625 Merge pull request #11909 from t895/card-grid
android: Break home settings into grid with large screens
2023-10-29 19:46:41 -04:00
29955de767 Merge pull request #11904 from ameerj/gl_threaded_opts_on
nvidia_flags: Enable GL Threaded optimizations
2023-10-29 19:46:34 -04:00
b0f62d8f24 Merge pull request #11898 from hebo6/patch-1
Adding StartupWmClass for .desktop file
2023-10-29 19:46:27 -04:00
ed2d77ddbc Merge pull request #11893 from liamwhite/swizzle
renderer_vulkan: fix viewport swizzle dirty state tracking
2023-10-29 19:46:20 -04:00
6e883a26da core: Close all KEvents 2023-10-29 13:52:12 -06:00
8427b9d49d renderer_vulkan: ensure exception on surface loss 2023-10-29 15:31:05 -04:00
0bb1c7c804 Implemented wheel event for volume control in VolumeButton 2023-10-29 20:29:17 +01:00
a5aa5876b4 android: Break home settings into grid with large screens 2023-10-29 13:47:41 -04:00
911d2216be Merge pull request #11866 from liamwhite/more-qt-nonsense
qt: fix game list shutdown crash
2023-10-29 11:25:22 -04:00
4da2105a32 Merge pull request #11862 from liamwhite/pascal-robust
Manually robust on Pascal and earlier
2023-10-29 11:25:15 -04:00
1f9684eaf9 Merge pull request #11859 from Kelebek1/compute_findbuffer
Add missing loop around compute FindBuffer calls
2023-10-29 11:25:09 -04:00
40c97c0549 Merge pull request #11852 from german77/async_brr
input_common: joycon: Move vibrations to a queue
2023-10-29 11:25:02 -04:00
6aee148b17 Merge pull request #11843 from liamwhite/sync-process
kernel: update KProcess
2023-10-29 11:24:52 -04:00
b5b93e6741 Merge pull request #11827 from liamwhite/preallocated
nvnflinger: fix reporting and freeing of preallocated buffers
2023-10-29 11:24:44 -04:00
18a4529851 Merge pull request #11803 from flodavid/improve-controller-applet-click
yuzu: Improve behavior when clicking on controller box in Controller applet
2023-10-29 09:13:07 -06:00
9e4d606c4c nvidia_flags: Enable GL Threaded optimizations 2023-10-28 21:26:22 -04:00
64f60f0acb Adding StartupWmClass for .desktop file 2023-10-28 13:45:35 +08:00
21c631b33b renderer_vulkan: fix viewport swizzle dirty state tracking 2023-10-27 14:23:47 -04:00
f26dddf3b5 service: am: Implement ISelfController::SaveCurrentScreenshot 2023-10-26 22:29:52 -06:00
43be2bfe33 Merge pull request #11880 from abouvier/unbundle-stb
cmake: prefer system stb headers
2023-10-25 17:21:37 -04:00
79ba5d9c26 cmake: prefer system stb headers 2023-10-25 21:47:32 +02:00
008d7e8c5f Merge pull request #11876 from liamwhite/apiversion
vulkan_common: use highest API version
2023-10-25 12:22:21 -04:00
19e9bde9e0 kernel: make sure new process is in list 2023-10-25 10:05:45 -04:00
6eb3a583cb Merge pull request #11812 from german77/save_capture
service: caps: Implement SaveScreenShotEx0 and variants
2023-10-24 21:43:51 -04:00
e0834ee50b vulkan_common: use highest API version 2023-10-24 17:04:17 -04:00
79894152a8 qt: fix game list shutdown crash 2023-10-23 23:06:07 -04:00
9274eaecd0 Merge pull request #11863 from german77/buffer
service: ipc: Add third read buffer index
2023-10-23 17:04:09 -04:00
c733620024 service: ipc: Add third read buffer index 2023-10-23 10:33:01 -06:00
897b411ae7 service: caps: Implement SaveScreenShotEx0 and variants 2023-10-23 10:18:22 -06:00
94836ba3b1 externals: stb: Add image write 2023-10-23 10:18:14 -06:00
b1909b0435 Merge pull request #11841 from german77/halp
yuzu: fix restore shortcuts button
2023-10-23 10:36:40 -04:00
1cc764988f Merge pull request #11846 from german77/cheats
cheats: Clamp cheat names without failing
2023-10-23 10:33:37 -04:00
da5c49f22d Merge pull request #11847 from ameerj/glsl-shfl-fix
emit_glsl_warp: Fix shfl_in_bounds conditional
2023-10-23 10:33:24 -04:00
6b93b0b08c Merge pull request #11854 from german77/vibration-ui
yuzu: Fix vibration reseting to 1%
2023-10-23 10:33:03 -04:00
68f25217b8 Add missing dowhile loops around FindBuffer calls 2023-10-23 15:08:56 +01:00
0604b14263 Manually robust on Pascal and earlier 2023-10-23 09:08:57 -04:00
a065dcdcd9 externals/opus: use CMakeLists shipped with Opus itself 2023-10-22 14:21:33 -06:00
3d4a064674 yuzu: Fix vibration reseting to 1% 2023-10-22 13:39:45 -06:00
e4dfd51337 input_common: joycon: Move vibrations to a queue 2023-10-22 11:30:59 -06:00
cfe73af6f2 emit_glsl_warp: Fix shfl_in_bounds conditional 2023-10-22 00:45:23 -04:00
d6bd16b2c0 externals/libusb: remove the GUID override workaround ...
... on Windows MSVC, it seems to have been fixed
2023-10-21 22:29:32 -06:00
a49b146ccc externals: update libusb to c060e9ce30ac2e3ffb49d94209c4dae77b6642f7 ...
... this fixes an issue when compiling with newer MSVC
2023-10-21 22:29:19 -06:00
fd9e157184 externals: update VulkanMemoryAllocator to 2f382df218d7e8516dee3b3caccb819a62b571a2 2023-10-21 22:29:19 -06:00
6cbd4020e8 externals: update Vulkan-Headers to 1.3.265 2023-10-21 22:29:19 -06:00
3558b236cd externals: update ffmpeg to 9c1294eaddb88cb0e044c675ccae059a85fc9c6c
... to fix build with binutils 2.41+
2023-10-21 22:29:19 -06:00
48e82c4138 externals: update vcpkg to ef2eef17340f3fbd679327d286fad06dd6e838ed 2023-10-21 22:29:19 -06:00
9eb70aea1d externals: update SDL to 2.28.4 2023-10-21 22:29:19 -06:00
0460fbacc9 externals: update cpp-jwt to 10ef5735d842b31025f1257ae78899f50a40fb14 2023-10-21 22:29:19 -06:00
c73297e840 externals: update cpp-httplib to 0.14.1 2023-10-21 22:29:19 -06:00
633d869ff4 externals: update libusb to 1.0.26 2023-10-21 22:29:19 -06:00
e03f86cc54 externals: update inih to r57 2023-10-21 22:29:19 -06:00
a0a3566977 externals: update opus to 1.4 2023-10-21 22:29:19 -06:00
77fb9d415b yuzu: Fix restore shortcuts button 2023-10-21 21:16:20 -06:00
bbdaa62175 cheats: Clamp cheat names without failing 2023-10-21 21:04:03 -06:00
31bffc7299 kernel: fix extraneous ref 2023-10-21 22:16:41 -04:00
5f8f09d750 kernel: shutdown app before gpu 2023-10-21 20:35:18 -04:00
dcfe674ed4 kernel: signal thread on termination completed 2023-10-21 20:03:41 -04:00
bb195c2c2b kernel: add missing TLR clear 2023-10-21 20:03:41 -04:00
8c59543ee3 kernel: update KProcess 2023-10-21 20:03:41 -04:00
db37e583ff Merge pull request #11831 from liamwhite/hosversionbetween
set: return version info from system archive
2023-10-21 18:22:20 -04:00
d28e826e47 Merge pull request #11830 from liamwhite/ts-session
ts: add OpenSession
2023-10-21 18:22:13 -04:00
13beb85514 Merge pull request #11828 from liamwhite/setthreadescription
common: use SetThreadDescription API for thread names
2023-10-21 18:22:04 -04:00
4b06bcc82c Merge pull request #11789 from Kelebek1/spirv_shift_right
Manually robust on Maxwell and earlier
2023-10-21 18:21:53 -04:00
12ebc8d9d1 set: return version info from system archive 2023-10-20 13:29:52 -04:00
2b85e9e997 ts: add OpenSession 2023-10-20 13:29:32 -04:00
59b62c6507 common: use SetThreadDescription API for thread names 2023-10-20 11:41:29 -04:00
2e760a9833 Merge pull request #11748 from liamwhite/kern_1700
kernel: update for 17.0.0
2023-10-20 17:08:00 +02:00
bab4a13a41 Merge pull request #11825 from liamwhite/system-resource
kernel: fix incorrect calculation of used non system memory value
2023-10-20 16:40:15 +02:00
b56c7397ad Merge pull request #11806 from liamwhite/needs-more-locking
renderer_vulkan: add locks to avoid scheduler flushes from CPU
2023-10-20 10:26:03 -04:00
689f346e97 nvnflinger: fix reporting and freeing of preallocated buffers
Co-authored-by: Kelebek1 <eeeedddccc@hotmail.co.uk>
2023-10-20 10:17:32 -04:00
249db0a59b kernel: fix incorrect calculation of used non system memory value 2023-10-20 09:12:10 -04:00
9526ce95dd gdbstub: add PermissionLocked to mappings table 2023-10-20 02:53:31 -04:00
687158fe00 kernel: fix format string error 2023-10-20 02:41:32 -04:00
d8507332c1 kernel: make check fully constexpr for broken msvc constant folding 2023-10-20 02:34:15 -04:00
67e983a354 codespell: allow 'VAs' 2023-10-20 02:34:15 -04:00
f21058a6c0 k_page_table: add MapFirstGroup 2023-10-20 02:34:15 -04:00
b456af31e6 kernel: update KMemoryRegionType values 2023-10-20 02:34:15 -04:00
0441853d0f k_page_table: implement PermissionLocked 2023-10-20 02:34:15 -04:00
60a1c6b95b k_page_table: add new CheckMemoryState helper 2023-10-20 02:34:15 -04:00
794e6c7a96 kernel: split Io memory state, add PermissionLocked attribute 2023-10-20 02:34:15 -04:00
22afa2c7a3 kernel: reshuffle ini1 size, add slab clear note 2023-10-20 02:34:15 -04:00
85a89ca3e3 Merge pull request #11822 from german77/no-name
service: mii: Create random mii with name
2023-10-19 16:54:05 -04:00
26776c0e60 service: mii: Create random mii with name 2023-10-19 13:35:02 -06:00
e02ee8e59d Manually robust on Maxwell and earlier 2023-10-19 19:54:31 +01:00
134ecca9b0 Merge pull request #11810 from liamwhite/clang-17
general: fix build failure on clang 17
2023-10-18 19:30:29 -04:00
c5f1ec8040 Merge pull request #11795 from Squall-Leonhart/D32FToOther
[Vulkan]Implement missing copy formats for D32, ARGB8_SRGB and BGRA8_Unorm/SRGB
2023-10-18 09:22:14 -04:00
765ea9b79d Merge pull request #11791 from german77/bufferx
service: hle: Allow to access read buffer A and X directly
2023-10-18 09:21:58 -04:00
c5bdc0054c general: fix build failure on clang 17 2023-10-17 22:44:21 -04:00
0b7593d352 yuzu: Improve behavior when clicking on controller box in Controller applet
- Apply changes on Controller configuration of commit 9524d70 to Controller applet
  - Fix regression of this previous commit:
  Enabling a controller in its tab did not activate previous controllers

Signed-off-by: flodavid <fl.david.53@gmail.com>
2023-10-17 23:19:11 +02:00
d9dde7e6f3 renderer_vulkan: add locks to avoid scheduler flushes from CPU 2023-10-17 10:00:25 -04:00
c73bb33ff1 service: hle: Allow to access read buffer A and X directly 2023-10-16 23:36:46 -06:00
326ebbb2fa Changes based on hardware tests
Removes unnecessary d32f to bgra shader and blit functions,
update vk_texture_cache to use abgr shader for d32f to BGRA formats
updates  abgr to d32f shader to comply with hardware tests
2023-10-17 02:42:40 +11:00
07143ce15c Make Clang happy. 2023-10-17 00:26:19 +11:00
dbc73c6c6c Added missing BuildShader line
Adds `convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),`
2023-10-17 00:15:31 +11:00
90c56f5dc1 added missing trailing line. 2023-10-16 06:07:26 +11:00
4b0291172e meant to add the unorms as well 2023-10-16 04:29:24 +11:00
12e4757cf3 use texelfetch instead of texturelod 2023-10-16 04:20:45 +11:00
144c0734f5 appease the format gods 2023-10-16 03:24:44 +11:00
f40f65f5d2 Another missing copy connected to Bravely Default II
adds blit_image_helper.ConvertABGR8ToD32F and fragment shader for performing ABGR and BGRA to D32F copies
2023-10-16 03:17:53 +11:00
03c3f936cf missed this line when editing the copypasta 2023-10-15 20:58:50 +11:00
66f41da365 moved line to appease the format gods 2023-10-15 20:54:25 +11:00
7a986d731b Implement missing formats for Bravely Default 2 2023-10-15 20:43:48 +11:00
d3997bad9b qt: implement automatic crash dump support 2023-10-08 11:35:53 -04:00
206 changed files with 16550 additions and 12821 deletions

View File

@ -19,6 +19,7 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_DISCORD_PRESENCE=ON \
-DYUZU_CRASH_DUMPS=ON \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON \
-GNinja

View File

@ -23,6 +23,7 @@ cmake .. \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON \
-DYUZU_ENABLE_LTO=ON \
-DYUZU_CRASH_DUMPS=ON \
-GNinja
ninja

View File

@ -17,7 +17,6 @@ cmake .. \
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
-DENABLE_QT_TRANSLATION=ON \
-DUSE_CCACHE=ON \
-DYUZU_CRASH_DUMPS=ON \
-DYUZU_USE_BUNDLED_SDL2=OFF \
-DYUZU_USE_EXTERNAL_SDL2=OFF \
-DYUZU_TESTS=OFF \

View File

@ -3,4 +3,4 @@
[codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink

5
.gitmodules vendored
View File

@ -32,7 +32,7 @@
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "opus"]
path = externals/opus/opus
path = externals/opus
url = https://github.com/xiph/opus.git
[submodule "SDL"]
path = externals/SDL
@ -58,3 +58,6 @@
[submodule "VulkanMemoryAllocator"]
path = externals/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
[submodule "breakpad"]
path = externals/breakpad
url = https://github.com/yuzu-emu/breakpad.git

View File

@ -147,3 +147,7 @@ License: GPL-3.0-or-later
Files: src/android/gradle/wrapper/*
Copyright: 2023 yuzu Emulator Project
License: GPL-3.0-or-later
Files: externals/stb/*
Copyright: Sean Barrett
License: MIT

View File

@ -52,7 +52,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
@ -139,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif()
if (YUZU_CRASH_DUMPS)
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
endif()
if (ENABLE_WEB_SERVICE)
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
endif()
@ -294,6 +291,7 @@ find_package(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE)
find_package(RenderDoc MODULE)
find_package(stb MODULE)
find_package(VulkanMemoryAllocator CONFIG)
find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED)
@ -550,6 +548,18 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
endif()
if (WIN32 AND YUZU_CRASH_DUMPS)
set(BREAKPAD_VER "breakpad-c89f9dd")
download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX)
set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include")
set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib")
add_library(libbreakpad_client INTERFACE IMPORTED)
target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}")
target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}")
endif()
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
@ -569,13 +579,6 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
if (YUZU_CRASH_DUMPS)
find_library(DBGHELP_LIBRARY dbghelp)
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
endif()
endif()
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()

View File

@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
#
# SPDX-License-Identifier: GPL-3.0-or-later
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
find_path(stb_image_resize_INCLUDE_DIR stb_image_resize.h PATH_SUFFIXES stb)
find_path(stb_image_write_INCLUDE_DIR stb_image_write.h PATH_SUFFIXES stb)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(stb
REQUIRED_VARS
stb_image_INCLUDE_DIR
stb_image_resize_INCLUDE_DIR
stb_image_write_INCLUDE_DIR
)
if (stb_FOUND AND NOT TARGET stb::headers)
add_library(stb::headers INTERFACE IMPORTED)
set_property(TARGET stb::headers PROPERTY
INTERFACE_INCLUDE_DIRECTORIES
"${stb_image_INCLUDE_DIR}"
"${stb_image_resize_INCLUDE_DIR}"
"${stb_image_write_INCLUDE_DIR}"
)
endif()
mark_as_advanced(
stb_image_INCLUDE_DIR
stb_image_resize_INCLUDE_DIR
stb_image_write_INCLUDE_DIR
)

View File

@ -1,3 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
End of merge log. You can find the original README.md below the break.
-----
<!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later

View File

@ -13,3 +13,4 @@ Exec=yuzu %f
Categories=Game;Emulator;Qt;
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
Keywords=Nintendo;Switch;
StartupWMClass=yuzu

View File

@ -134,6 +134,10 @@ endif()
# Opus
if (NOT TARGET Opus::opus)
set(OPUS_BUILD_TESTING OFF)
set(OPUS_BUILD_PROGRAMS OFF)
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
add_subdirectory(opus)
endif()
@ -168,9 +172,13 @@ if (NOT TARGET LLVM::Demangle)
add_library(LLVM::Demangle ALIAS demangle)
endif()
add_library(stb stb/stb_dxt.cpp stb/stb_image.cpp stb/stb_image_resize.cpp)
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
if (NOT TARGET stb::headers)
add_library(stb::headers ALIAS stb)
endif()
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
@ -185,3 +193,105 @@ if (ANDROID)
add_subdirectory(libadrenotools)
endif()
endif()
# Breakpad
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
set(BREAKPAD_WIN32_DEFINES
NOMINMAX
UNICODE
WIN32_LEAN_AND_MEAN
_CRT_SECURE_NO_WARNINGS
_CRT_SECURE_NO_DEPRECATE
_CRT_NONSTDC_NO_DEPRECATE
)
# libbreakpad
add_library(libbreakpad STATIC)
file(GLOB_RECURSE LIBBREAKPAD_SOURCES breakpad/src/processor/*.cc)
file(GLOB_RECURSE LIBDISASM_SOURCES breakpad/src/third_party/libdisasm/*.c)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "_unittest|_selftest|synth_minidump|/tests|/testdata|/solaris|microdump_stackwalk|minidump_dump|minidump_stackwalk")
if (WIN32)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/mac|/android")
target_compile_definitions(libbreakpad PRIVATE ${BREAKPAD_WIN32_DEFINES})
target_include_directories(libbreakpad PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
elseif (APPLE)
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/windows|/android")
else()
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/mac|/windows|/android")
endif()
target_sources(libbreakpad PRIVATE ${LIBBREAKPAD_SOURCES} ${LIBDISASM_SOURCES})
target_include_directories(libbreakpad
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src/third_party/libdisasm
)
# libbreakpad_client
add_library(libbreakpad_client STATIC)
file(GLOB LIBBREAKPAD_COMMON_SOURCES breakpad/src/common/*.cc breakpad/src/common/*.c breakpad/src/client/*.cc)
if (WIN32)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/windows/*.cc breakpad/src/common/windows/*.cc)
list(FILTER LIBBREAKPAD_COMMON_SOURCES EXCLUDE REGEX "language.cc|path_helper.cc|stabs_to_module.cc|stabs_reader.cc|minidump_file_writer.cc")
target_include_directories(libbreakpad_client PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
target_compile_definitions(libbreakpad_client PRIVATE ${BREAKPAD_WIN32_DEFINES})
elseif (APPLE)
target_compile_definitions(libbreakpad_client PRIVATE HAVE_MACH_O_NLIST_H)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/mac/*.cc breakpad/src/common/mac/*.cc)
list(APPEND LIBBREAKPAD_CLIENT_SOURCES breakpad/src/common/mac/MachIPC.mm)
else()
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/linux/*.cc breakpad/src/common/linux/*.cc)
endif()
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
list(FILTER LIBBREAKPAD_CLIENT_SOURCES EXCLUDE REGEX "/sender|/tests|/unittests|/testcases|_unittest|_test")
target_sources(libbreakpad_client PRIVATE ${LIBBREAKPAD_CLIENT_SOURCES})
target_include_directories(libbreakpad_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src)
if (WIN32)
target_link_libraries(libbreakpad_client PRIVATE wininet.lib)
elseif (APPLE)
find_library(CoreFoundation_FRAMEWORK CoreFoundation)
target_link_libraries(libbreakpad_client PRIVATE ${CoreFoundation_FRAMEWORK})
else()
find_library(PTHREAD_LIBRARIES pthread)
target_compile_definitions(libbreakpad_client PRIVATE HAVE_GETCONTEXT=1)
if (PTHREAD_LIBRARIES)
target_link_libraries(libbreakpad_client PRIVATE ${PTHREAD_LIBRARIES})
endif()
endif()
# Host tools for symbol processing
if (LINUX)
find_package(ZLIB REQUIRED)
add_executable(minidump_stackwalk breakpad/src/processor/minidump_stackwalk.cc)
target_link_libraries(minidump_stackwalk PRIVATE libbreakpad libbreakpad_client)
add_executable(dump_syms
breakpad/src/common/dwarf_cfi_to_module.cc
breakpad/src/common/dwarf_cu_to_module.cc
breakpad/src/common/dwarf_line_to_module.cc
breakpad/src/common/dwarf_range_list_handler.cc
breakpad/src/common/language.cc
breakpad/src/common/module.cc
breakpad/src/common/path_helper.cc
breakpad/src/common/stabs_reader.cc
breakpad/src/common/stabs_to_module.cc
breakpad/src/common/dwarf/bytereader.cc
breakpad/src/common/dwarf/dwarf2diehandler.cc
breakpad/src/common/dwarf/dwarf2reader.cc
breakpad/src/common/dwarf/elf_reader.cc
breakpad/src/common/linux/crc32.cc
breakpad/src/common/linux/dump_symbols.cc
breakpad/src/common/linux/elf_symbols_to_module.cc
breakpad/src/common/linux/elfutils.cc
breakpad/src/common/linux/file_id.cc
breakpad/src/common/linux/linux_libc_support.cc
breakpad/src/common/linux/memory_mapped_file.cc
breakpad/src/common/linux/safe_readlink.cc
breakpad/src/tools/linux/dump_syms/dump_syms.cc)
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
endif()
endif()

2
externals/SDL vendored

1
externals/breakpad vendored Submodule

Submodule externals/breakpad added at c89f9dddc7

View File

@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
if (NOT MINGW)
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
endif()
make_directory("${LIBUSB_PREFIX}")
add_custom_command(
@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
endif()
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
else()
target_include_directories(usb
# turns out other projects also have "config.h", so make sure the

1
externals/opus vendored Submodule

Submodule externals/opus added at 101a71e03b

View File

@ -1,259 +0,0 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
cmake_minimum_required(VERSION 3.8)
project(opus)
option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
include(opus/opus_functions.cmake)
if(OPUS_STACK_PROTECTOR)
if(NOT MSVC) # GC on by default on MSVC
check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
endif()
else()
if(MSVC)
check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
endif()
endif()
add_library(opus
# CELT sources
opus/celt/bands.c
opus/celt/celt.c
opus/celt/celt_decoder.c
opus/celt/celt_encoder.c
opus/celt/celt_lpc.c
opus/celt/cwrs.c
opus/celt/entcode.c
opus/celt/entdec.c
opus/celt/entenc.c
opus/celt/kiss_fft.c
opus/celt/laplace.c
opus/celt/mathops.c
opus/celt/mdct.c
opus/celt/modes.c
opus/celt/pitch.c
opus/celt/quant_bands.c
opus/celt/rate.c
opus/celt/vq.c
# SILK sources
opus/silk/A2NLSF.c
opus/silk/CNG.c
opus/silk/HP_variable_cutoff.c
opus/silk/LPC_analysis_filter.c
opus/silk/LPC_fit.c
opus/silk/LPC_inv_pred_gain.c
opus/silk/LP_variable_cutoff.c
opus/silk/NLSF2A.c
opus/silk/NLSF_VQ.c
opus/silk/NLSF_VQ_weights_laroia.c
opus/silk/NLSF_decode.c
opus/silk/NLSF_del_dec_quant.c
opus/silk/NLSF_encode.c
opus/silk/NLSF_stabilize.c
opus/silk/NLSF_unpack.c
opus/silk/NSQ.c
opus/silk/NSQ_del_dec.c
opus/silk/PLC.c
opus/silk/VAD.c
opus/silk/VQ_WMat_EC.c
opus/silk/ana_filt_bank_1.c
opus/silk/biquad_alt.c
opus/silk/bwexpander.c
opus/silk/bwexpander_32.c
opus/silk/check_control_input.c
opus/silk/code_signs.c
opus/silk/control_SNR.c
opus/silk/control_audio_bandwidth.c
opus/silk/control_codec.c
opus/silk/dec_API.c
opus/silk/decode_core.c
opus/silk/decode_frame.c
opus/silk/decode_indices.c
opus/silk/decode_parameters.c
opus/silk/decode_pitch.c
opus/silk/decode_pulses.c
opus/silk/decoder_set_fs.c
opus/silk/enc_API.c
opus/silk/encode_indices.c
opus/silk/encode_pulses.c
opus/silk/gain_quant.c
opus/silk/init_decoder.c
opus/silk/init_encoder.c
opus/silk/inner_prod_aligned.c
opus/silk/interpolate.c
opus/silk/lin2log.c
opus/silk/log2lin.c
opus/silk/pitch_est_tables.c
opus/silk/process_NLSFs.c
opus/silk/quant_LTP_gains.c
opus/silk/resampler.c
opus/silk/resampler_down2.c
opus/silk/resampler_down2_3.c
opus/silk/resampler_private_AR2.c
opus/silk/resampler_private_IIR_FIR.c
opus/silk/resampler_private_down_FIR.c
opus/silk/resampler_private_up2_HQ.c
opus/silk/resampler_rom.c
opus/silk/shell_coder.c
opus/silk/sigm_Q15.c
opus/silk/sort.c
opus/silk/stereo_LR_to_MS.c
opus/silk/stereo_MS_to_LR.c
opus/silk/stereo_decode_pred.c
opus/silk/stereo_encode_pred.c
opus/silk/stereo_find_predictor.c
opus/silk/stereo_quant_pred.c
opus/silk/sum_sqr_shift.c
opus/silk/table_LSF_cos.c
opus/silk/tables_LTP.c
opus/silk/tables_NLSF_CB_NB_MB.c
opus/silk/tables_NLSF_CB_WB.c
opus/silk/tables_gain.c
opus/silk/tables_other.c
opus/silk/tables_pitch_lag.c
opus/silk/tables_pulses_per_block.c
# Opus sources
opus/src/analysis.c
opus/src/mapping_matrix.c
opus/src/mlp.c
opus/src/mlp_data.c
opus/src/opus.c
opus/src/opus_decoder.c
opus/src/opus_encoder.c
opus/src/opus_multistream.c
opus/src/opus_multistream_decoder.c
opus/src/opus_multistream_encoder.c
opus/src/opus_projection_decoder.c
opus/src/opus_projection_encoder.c
opus/src/repacketizer.c
)
if (DEBUG)
target_sources(opus PRIVATE opus/silk/debug.c)
endif()
if (OPUS_FIXED_POINT)
target_sources(opus PRIVATE
opus/silk/fixed/LTP_analysis_filter_FIX.c
opus/silk/fixed/LTP_scale_ctrl_FIX.c
opus/silk/fixed/apply_sine_window_FIX.c
opus/silk/fixed/autocorr_FIX.c
opus/silk/fixed/burg_modified_FIX.c
opus/silk/fixed/corrMatrix_FIX.c
opus/silk/fixed/encode_frame_FIX.c
opus/silk/fixed/find_LPC_FIX.c
opus/silk/fixed/find_LTP_FIX.c
opus/silk/fixed/find_pitch_lags_FIX.c
opus/silk/fixed/find_pred_coefs_FIX.c
opus/silk/fixed/k2a_FIX.c
opus/silk/fixed/k2a_Q16_FIX.c
opus/silk/fixed/noise_shape_analysis_FIX.c
opus/silk/fixed/pitch_analysis_core_FIX.c
opus/silk/fixed/prefilter_FIX.c
opus/silk/fixed/process_gains_FIX.c
opus/silk/fixed/regularize_correlations_FIX.c
opus/silk/fixed/residual_energy16_FIX.c
opus/silk/fixed/residual_energy_FIX.c
opus/silk/fixed/schur64_FIX.c
opus/silk/fixed/schur_FIX.c
opus/silk/fixed/solve_LS_FIX.c
opus/silk/fixed/vector_ops_FIX.c
opus/silk/fixed/warped_autocorrelation_FIX.c
)
else()
target_sources(opus PRIVATE
opus/silk/float/LPC_analysis_filter_FLP.c
opus/silk/float/LPC_inv_pred_gain_FLP.c
opus/silk/float/LTP_analysis_filter_FLP.c
opus/silk/float/LTP_scale_ctrl_FLP.c
opus/silk/float/apply_sine_window_FLP.c
opus/silk/float/autocorrelation_FLP.c
opus/silk/float/burg_modified_FLP.c
opus/silk/float/bwexpander_FLP.c
opus/silk/float/corrMatrix_FLP.c
opus/silk/float/encode_frame_FLP.c
opus/silk/float/energy_FLP.c
opus/silk/float/find_LPC_FLP.c
opus/silk/float/find_LTP_FLP.c
opus/silk/float/find_pitch_lags_FLP.c
opus/silk/float/find_pred_coefs_FLP.c
opus/silk/float/inner_product_FLP.c
opus/silk/float/k2a_FLP.c
opus/silk/float/noise_shape_analysis_FLP.c
opus/silk/float/pitch_analysis_core_FLP.c
opus/silk/float/process_gains_FLP.c
opus/silk/float/regularize_correlations_FLP.c
opus/silk/float/residual_energy_FLP.c
opus/silk/float/scale_copy_vector_FLP.c
opus/silk/float/scale_vector_FLP.c
opus/silk/float/schur_FLP.c
opus/silk/float/sort_FLP.c
opus/silk/float/warped_autocorrelation_FLP.c
opus/silk/float/wrappers_FLP.c
)
endif()
target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
if(NOT MSVC)
if(MINGW)
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
else()
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
endif()
endif()
# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
# allocation If none is defined, then the fallback is a non-threadsafe global
# array
if(OPUS_USE_ALLOCA OR MSVC)
target_compile_definitions(opus PRIVATE USE_ALLOCA)
else()
target_compile_definitions(opus PRIVATE VAR_ARRAYS)
endif()
if(OPUS_CUSTOM_MODES)
target_compile_definitions(opus PRIVATE CUSTOM_MODES)
endif()
if(NOT OPUS_ENABLE_FLOAT_API)
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
endif()
target_compile_definitions(opus
PUBLIC
-DOPUS_VERSION="\\"1.3.1\\""
PRIVATE
# Use C99 intrinsics to speed up float-to-int conversion
HAVE_LRINTF
)
if (FIXED_POINT)
target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
endif()
target_include_directories(opus
PUBLIC
opus/include
PRIVATE
opus/celt
opus/silk
opus/silk/fixed
opus/silk/float
opus/src
)
add_library(Opus::opus ALIAS opus)

1
externals/opus/opus vendored

Submodule externals/opus/opus deleted from ad8fe90db7

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

1724
externals/stb/stb_image_write.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
import android.app.Dialog
import android.content.DialogInterface
import android.net.Uri
import android.os.Bundle
import android.text.Html
import android.text.method.LinkMovementMethod
@ -16,7 +17,7 @@ import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
@ -68,7 +69,7 @@ object NativeLibrary {
@Keep
@JvmStatic
fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else {
FileUtil.openContentUri(path, openmode)
@ -78,7 +79,7 @@ object NativeLibrary {
@Keep
@JvmStatic
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
} else {
FileUtil.getFileSize(path)
@ -88,23 +89,41 @@ object NativeLibrary {
@Keep
@JvmStatic
fun exists(path: String?): Boolean {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path)
} else {
FileUtil.exists(path)
FileUtil.exists(path, suppressLog = true)
}
}
@Keep
@JvmStatic
fun isDirectory(path: String?): Boolean {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path)
} else {
FileUtil.isDirectory(path)
}
}
@Keep
@JvmStatic
fun getParentDirectory(path: String): String =
if (DocumentsTree.isNativePath(path)) {
YuzuApplication.documentsTree!!.getParentDirectory(path)
} else {
path
}
@Keep
@JvmStatic
fun getFilename(path: String): String =
if (DocumentsTree.isNativePath(path)) {
YuzuApplication.documentsTree!!.getFilename(path)
} else {
FileUtil.getFilename(Uri.parse(path))
}
/**
* Returns true if pro controller isn't available and handheld is
*/
@ -215,32 +234,6 @@ object NativeLibrary {
external fun initGameIni(gameID: String?)
/**
* Gets the embedded icon within the given ROM.
*
* @param filename the file path to the ROM.
* @return a byte array containing the JPEG data for the icon.
*/
external fun getIcon(filename: String): ByteArray
/**
* Gets the embedded title of the given ISO/ROM.
*
* @param filename The file path to the ISO/ROM.
* @return the embedded title of the ISO/ROM.
*/
external fun getTitle(filename: String): String
external fun getDescription(filename: String): String
external fun getGameId(filename: String): String
external fun getRegions(filename: String): String
external fun getCompany(filename: String): String
external fun isHomebrew(filename: String): Boolean
external fun setAppDirectory(directory: String)
/**
@ -259,7 +252,7 @@ object NativeLibrary {
external fun reloadKeys(): Boolean
external fun initializeEmulation()
external fun initializeSystem()
external fun defaultCPUCore(): Int
@ -293,11 +286,6 @@ object NativeLibrary {
*/
external fun stopEmulation()
/**
* Resets the in-memory ROM metadata cache.
*/
external fun resetRomMetadata()
/**
* Returns true if emulation is running (or is paused).
*/
@ -517,6 +505,36 @@ object NativeLibrary {
*/
external fun initializeEmptyUserDirectory()
/**
* Gets the launch path for a given applet. It is the caller's responsibility to also
* set the system's current applet ID before trying to launch the nca given by this function.
*
* @param id The applet entry ID
* @return The applet's launch path
*/
external fun getAppletLaunchPath(id: Long): String
/**
* Sets the system's current applet ID before launching.
*
* @param appletId One of the ids in the Service::AM::Applets::AppletId enum
*/
external fun setCurrentAppletId(appletId: Int)
/**
* Sets the cabinet mode for launching the cabinet applet.
*
* @param cabinetMode One of the modes that corresponds to the enum in Service::NFP::CabinetMode
*/
external fun setCabinetMode(cabinetMode: Int)
/**
* Checks whether NAND contents are available and valid.
*
* @return 'true' if firmware is available
*/
external fun isFirmwareAvailable(): Boolean
/**
* Button type for use in onTouchEvent
*/

View File

@ -45,7 +45,6 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.MemoryUtil
@ -57,17 +56,16 @@ import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
private var controllerMappingHelper: ControllerMappingHelper? = null
var isActivityRecreated = false
private lateinit var nfcReader: NfcReader
private lateinit var inputHandler: InputHandler
private val gyro = FloatArray(3)
private val accel = FloatArray(3)
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
private var controllerIds = InputHandler.getGameControllerIds()
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private val actionMute = "ACTION_EMULATOR_MUTE"
@ -95,8 +93,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
isActivityRecreated = savedInstanceState != null
controllerMappingHelper = ControllerMappingHelper()
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive()
@ -105,8 +101,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader = NfcReader(this)
nfcReader.initialize()
inputHandler = InputHandler()
inputHandler.initialize()
InputHandler.initialize()
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
@ -162,6 +157,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
InputHandler.updateControllerIds()
buildPictureInPictureParams()
}
@ -195,7 +191,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super.dispatchKeyEvent(event)
}
return inputHandler.dispatchKeyEvent(event)
return InputHandler.dispatchKeyEvent(event)
}
override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
@ -210,7 +206,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return true
}
return inputHandler.dispatchGenericMotionEvent(event)
return InputHandler.dispatchGenericMotionEvent(event)
}
override fun onSensorChanged(event: SensorEvent) {

View File

@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.CardAppletOptionBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): AppletAdapter.AppletViewHolder {
CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@AppletAdapter) }
.also { return AppletViewHolder(it) }
}
override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
holder.bind(applets[position])
override fun getItemCount(): Int = applets.size
override fun onClick(view: View) {
val applet = (view.tag as AppletViewHolder).applet
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
if (appletPath.isEmpty()) {
Toast.makeText(
YuzuApplication.appContext,
R.string.applets_error_applet,
Toast.LENGTH_SHORT
).show()
return
}
if (applet.appletInfo == AppletInfo.Cabinet) {
view.findNavController()
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
return
}
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
val appletGame = Game(
title = YuzuApplication.appContext.getString(applet.titleId),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
view.findNavController().navigate(action)
}
inner class AppletViewHolder(val binding: CardAppletOptionBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var applet: Applet
init {
itemView.tag = this
}
fun bind(applet: Applet) {
this.applet = applet
binding.title.setText(applet.titleId)
binding.description.setText(applet.descriptionId)
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
applet.iconId,
binding.icon.context.theme
)
)
}
}
}

View File

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.DialogListItemBinding
import org.yuzu.yuzu_emu.model.CabinetMode
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.Game
class CabinetLauncherDialogAdapter(val fragment: Fragment) :
RecyclerView.Adapter<CabinetModeViewHolder>(),
View.OnClickListener {
private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
.apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
.also { return CabinetModeViewHolder(it) }
}
override fun getItemCount(): Int = cabinetModes.size
override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
holder.bind(cabinetModes[position])
override fun onClick(view: View) {
val mode = (view.tag as CabinetModeViewHolder).cabinetMode
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
NativeLibrary.setCabinetMode(mode.id)
val appletGame = Game(
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
path = appletPath
)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
fragment.findNavController().navigate(action)
}
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
RecyclerView.ViewHolder(binding.root) {
lateinit var cabinetMode: CabinetMode
init {
itemView.tag = this
}
fun bind(cabinetMode: CabinetMode) {
this.cabinetMode = cabinetMode
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
cabinetMode.iconId,
binding.icon.context.theme
)
)
binding.title.setText(cabinetMode.titleId)
}
}
}

View File

@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
return oldItem.gameId == newItem.gameId
return oldItem.programId == newItem.programId
}
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {

View File

@ -0,0 +1,113 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.AppletAdapter
import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
import org.yuzu.yuzu_emu.model.Applet
import org.yuzu.yuzu_emu.model.AppletInfo
import org.yuzu.yuzu_emu.model.HomeViewModel
class AppletLauncherFragment : Fragment() {
private var _binding: FragmentAppletLauncherBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentAppletLauncherBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarApplets.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
val applets = listOf(
Applet(
R.string.album_applet,
R.string.album_applet_description,
R.drawable.ic_album,
AppletInfo.PhotoViewer
),
Applet(
R.string.cabinet_applet,
R.string.cabinet_applet_description,
R.drawable.ic_nfc,
AppletInfo.Cabinet
),
Applet(
R.string.mii_edit_applet,
R.string.mii_edit_applet_description,
R.drawable.ic_mii,
AppletInfo.MiiEdit
)
)
binding.listApplets.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
adapter = AppletAdapter(requireActivity(), applets)
}
setInsets()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarApplets.layoutParams = mlpAppBar
val mlpListApplets =
binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
mlpListApplets.leftMargin = leftInsets
mlpListApplets.rightMargin = rightInsets
binding.listApplets.layoutParams = mlpListApplets
binding.listApplets.updatePadding(bottom = barInsets.bottom)
windowInsets
}
}

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter
import org.yuzu.yuzu_emu.databinding.DialogListBinding
class CabinetLauncherDialogFragment : DialogFragment() {
private lateinit var binding: DialogListBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogListBinding.inflate(layoutInflater)
binding.dialogList.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = CabinetLauncherDialogAdapter(this@CabinetLauncherDialogFragment)
}
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.cabinet_launcher)
.setView(binding.root)
.create()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return binding.root
}
}

View File

@ -15,6 +15,7 @@ import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.view.*
import android.widget.TextView
import android.widget.Toast
@ -25,6 +26,7 @@ import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
@ -156,6 +158,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
binding.drawerLayout.addDrawerListener(object : DrawerListener {
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
binding.surfaceInputOverlay.dispatchTouchEvent(
MotionEvent.obtain(
SystemClock.uptimeMillis(),
SystemClock.uptimeMillis() + 100,
MotionEvent.ACTION_UP,
0f,
0f,
0
)
)
}
override fun onDrawerOpened(drawerView: View) {
// No op
}
override fun onDrawerClosed(drawerView: View) {
// No op
}
override fun onDrawerStateChanged(newState: Int) {
// No op
}
})
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
game.title

View File

@ -26,10 +26,11 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
@ -131,6 +132,20 @@ class HomeSettingsFragment : Fragment() {
}
)
)
add(
HomeSetting(
R.string.applets,
R.string.applets_description,
R.drawable.ic_applet,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
},
{ NativeLibrary.isFirmwareAvailable() },
R.string.applets_error_firmware,
R.string.applets_error_description
)
)
add(
HomeSetting(
R.string.select_games_folder,
@ -186,7 +201,8 @@ class HomeSettingsFragment : Fragment() {
}
binding.homeSettingsList.apply {
layoutManager = LinearLayoutManager(requireContext())
layoutManager =
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
adapter = HomeSettingAdapter(
requireActivity() as AppCompatActivity,
viewLifecycleOwner,

View File

@ -8,6 +8,7 @@ import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.Html
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
@ -32,7 +33,9 @@ class MessageDialogFragment : DialogFragment() {
if (titleId != 0) dialog.setTitle(titleId)
if (titleString.isNotEmpty()) dialog.setTitle(titleString)
if (descriptionId != 0) dialog.setMessage(descriptionId)
if (descriptionId != 0) {
dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
}
if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString)
if (helpLinkId != 0) {

View File

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.R
data class Applet(
@StringRes val titleId: Int,
@StringRes val descriptionId: Int,
@DrawableRes val iconId: Int,
val appletInfo: AppletInfo,
val cabinetMode: CabinetMode = CabinetMode.None
)
// Combination of Common::AM::Applets::AppletId enum and the entry id
enum class AppletInfo(val appletId: Int, val entryId: Long = 0) {
None(0x00),
Application(0x01),
OverlayDisplay(0x02),
QLaunch(0x03),
Starter(0x04),
Auth(0x0A),
Cabinet(0x0B, 0x0100000000001002),
Controller(0x0C),
DataErase(0x0D),
Error(0x0E),
NetConnect(0x0F),
ProfileSelect(0x10),
SoftwareKeyboard(0x11),
MiiEdit(0x12, 0x0100000000001009),
Web(0x13),
Shop(0x14),
PhotoViewer(0x015, 0x010000000000100D),
Settings(0x16),
OfflineWeb(0x17),
LoginShare(0x18),
WebAuth(0x19),
MyPage(0x1A)
}
// Matches enum in Service::NFP::CabinetMode with extra metadata
enum class CabinetMode(
val id: Int,
@StringRes val titleId: Int = 0,
@DrawableRes val iconId: Int = 0
) {
None(-1),
StartNicknameAndOwnerSettings(0, R.string.cabinet_nickname_and_owner, R.drawable.ic_edit),
StartGameDataEraser(1, R.string.cabinet_game_data_eraser, R.drawable.ic_refresh),
StartRestorer(2, R.string.cabinet_restorer, R.drawable.ic_restore),
StartFormatter(3, R.string.cabinet_formatter, R.drawable.ic_clear)
}

View File

@ -11,16 +11,15 @@ import kotlinx.serialization.Serializable
@Parcelize
@Serializable
class Game(
val title: String,
val description: String,
val regions: String,
val title: String = "",
val path: String,
val gameId: String,
val company: String,
val isHomebrew: Boolean
val programId: String = "",
val developer: String = "",
val version: String = "",
val isHomebrew: Boolean = false
) : Parcelable {
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
val keyAddedToLibraryTime get() = "${programId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${programId}_LastPlayed"
override fun equals(other: Any?): Boolean {
if (other !is Game) {
@ -32,11 +31,9 @@ class Game(
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + description.hashCode()
result = 31 * result + regions.hashCode()
result = 31 * result + path.hashCode()
result = 31 * result + gameId.hashCode()
result = 31 * result + company.hashCode()
result = 31 * result + programId.hashCode()
result = 31 * result + developer.hashCode()
result = 31 * result + isHomebrew.hashCode()
return result
}

View File

@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.GameMetadata
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
val games: StateFlow<List<Game>> get() = _games
private val _games = MutableStateFlow(emptyList<Game>())
@ -49,26 +47,34 @@ class GamesViewModel : ViewModel() {
// Retrieve list of cached games
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getStringSet(GameHelper.KEY_GAMES, emptySet())
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: MissingFieldException) {
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
?.exists()
if (gameExists == true) {
deserializedGames.add(game)
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: Exception) {
// We don't care about any errors related to parsing the game cache
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(game.path)
)?.exists()
if (gameExists == true) {
deserializedGames.add(game)
}
}
setGames(deserializedGames.toList())
}
reloadGames(false)
}
setGames(deserializedGames.toList())
}
reloadGames(false)
}
fun setGames(games: List<Game>) {
@ -106,7 +112,7 @@ class GamesViewModel : ViewModel() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeLibrary.resetRomMetadata()
GameMetadata.resetMetadata()
setGames(GameHelper.getGames())
_isReloading.value = false

View File

@ -403,6 +403,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
} else {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
NativeLibrary.initializeSystem()
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
@ -648,7 +649,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
// Reinitialize relevant data
NativeLibrary.initializeEmulation()
NativeLibrary.initializeSystem()
gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success)

View File

@ -1,70 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
/**
* Some controllers have incorrect mappings. This class has special-case fixes for them.
*/
class ControllerMappingHelper {
/**
* Some controllers report extra button presses that can be ignored.
*/
fun shouldKeyBeIgnored(inputDevice: InputDevice, keyCode: Int): Boolean {
return if (isDualShock4(inputDevice)) {
// The two analog triggers generate analog motion events as well as a keycode.
// We always prefer to use the analog values, so throw away the button press
keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
} else {
false
}
}
/**
* Scale an axis to be zero-centered with a proper range.
*/
fun scaleAxis(inputDevice: InputDevice, axis: Int, value: Float): Float {
if (isDualShock4(inputDevice)) {
// Android doesn't have correct mappings for this controller's triggers. It reports them
// as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0]
// Scale them to properly zero-centered with a range of [0.0, 1.0].
if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) {
return (value + 1) / 2.0f
}
} else if (isXboxOneWireless(inputDevice)) {
// Same as the DualShock 4, the mappings are missing.
if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) {
return (value + 1) / 2.0f
}
if (axis == MotionEvent.AXIS_GENERIC_1) {
// This axis is stuck at ~.5. Ignore it.
return 0.0f
}
} else if (isMogaPro2Hid(inputDevice)) {
// This controller has a broken axis that reports a constant value. Ignore it.
if (axis == MotionEvent.AXIS_GENERIC_1) {
return 0.0f
}
}
return value
}
// Sony DualShock 4 controller
private fun isDualShock4(inputDevice: InputDevice): Boolean {
return inputDevice.vendorId == 0x54c && inputDevice.productId == 0x9cc
}
// Microsoft Xbox One controller
private fun isXboxOneWireless(inputDevice: InputDevice): Boolean {
return inputDevice.vendorId == 0x45e && inputDevice.productId == 0x2e0
}
// Moga Pro 2 HID
private fun isMogaPro2Hid(inputDevice: InputDevice): Boolean {
return inputDevice.vendorId == 0x20d6 && inputDevice.productId == 0x6271
}
}

View File

@ -15,7 +15,7 @@ object DirectoryInitialization {
fun start() {
if (!areDirectoriesReady) {
initializeInternalStorage()
NativeLibrary.initializeEmulation()
NativeLibrary.initializeSystem()
areDirectoriesReady = true
}
}

View File

@ -42,6 +42,23 @@ class DocumentsTree {
return node != null && node.isDirectory
}
fun getParentDirectory(filepath: String): String {
val node = resolvePath(filepath)!!
val parentNode = node.parent
if (parentNode != null && parentNode.isDirectory) {
return parentNode.uri!!.toString()
}
return node.uri!!.toString()
}
fun getFilename(filepath: String): String {
val node = resolvePath(filepath)
if (node != null) {
return node.name!!
}
return filepath
}
private fun resolvePath(filepath: String): DocumentsNode? {
val tokens = StringTokenizer(filepath, File.separator, false)
var iterator = root

View File

@ -144,7 +144,7 @@ object FileUtil {
* @param path Native content uri path
* @return bool
*/
fun exists(path: String?): Boolean {
fun exists(path: String?, suppressLog: Boolean = false): Boolean {
var c: Cursor? = null
try {
val mUri = Uri.parse(path)
@ -152,7 +152,9 @@ object FileUtil {
c = context.contentResolver.query(mUri, columns, null, null, null)
return c!!.count > 0
} catch (e: Exception) {
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
if (!suppressLog) {
Log.info("[FileUtil] Cannot find file from given path, error: " + e.message)
}
} finally {
closeQuietly(c)
}

View File

@ -71,27 +71,26 @@ object GameHelper {
fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
val filePath = uri.toString()
var name = NativeLibrary.getTitle(filePath)
var name = GameMetadata.getTitle(filePath)
// If the game's title field is empty, use the filename.
if (name.isEmpty()) {
name = FileUtil.getFilename(uri)
}
var gameId = NativeLibrary.getGameId(filePath)
var programId = GameMetadata.getProgramId(filePath)
// If the game's ID field is empty, use the filename without extension.
if (gameId.isEmpty()) {
gameId = name.substring(0, name.lastIndexOf("."))
if (programId.isEmpty()) {
programId = name.substring(0, name.lastIndexOf("."))
}
val newGame = Game(
name,
NativeLibrary.getDescription(filePath).replace("\n", " "),
NativeLibrary.getRegions(filePath),
filePath,
gameId,
NativeLibrary.getCompany(filePath),
NativeLibrary.isHomebrew(filePath)
programId,
GameMetadata.getDeveloper(filePath),
GameMetadata.getVersion(filePath),
GameMetadata.getIsHomebrew(filePath)
)
if (addedToLibrary) {

View File

@ -18,7 +18,6 @@ import coil.key.Keyer
import coil.memory.MemoryCache
import coil.request.ImageRequest
import coil.request.Options
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
@ -36,7 +35,7 @@ class GameIconFetcher(
}
private fun decodeGameIcon(uri: String): Bitmap? {
val data = NativeLibrary.getIcon(uri)
val data = GameMetadata.getIcon(uri)
return BitmapFactory.decodeByteArray(
data,
0,

View File

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
object GameMetadata {
external fun getTitle(path: String): String
external fun getProgramId(path: String): String
external fun getDeveloper(path: String): String
external fun getVersion(path: String): String
external fun getIcon(path: String): ByteArray
external fun getIsHomebrew(path: String): Boolean
external fun resetMetadata()
}

View File

@ -3,17 +3,24 @@
package org.yuzu.yuzu_emu.utils
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
class InputHandler {
object InputHandler {
private var controllerIds = getGameControllerIds()
fun initialize() {
// Connect first controller
NativeLibrary.onGamePadConnectEvent(getPlayerNumber(NativeLibrary.Player1Device))
}
fun updateControllerIds() {
controllerIds = getGameControllerIds()
}
fun dispatchKeyEvent(event: KeyEvent): Boolean {
val button: Int = when (event.device.vendorId) {
0x045E -> getInputXboxButtonKey(event.keyCode)
@ -35,7 +42,7 @@ class InputHandler {
}
return NativeLibrary.onGamePadButtonEvent(
getPlayerNumber(event.device.controllerNumber),
getPlayerNumber(event.device.controllerNumber, event.deviceId),
button,
action
)
@ -58,9 +65,14 @@ class InputHandler {
return true
}
private fun getPlayerNumber(index: Int): Int {
private fun getPlayerNumber(index: Int, deviceId: Int = -1): Int {
var deviceIndex = index
if (deviceId != -1) {
deviceIndex = controllerIds[deviceId]!!
}
// TODO: Joycons are handled as different controllers. Find a way to merge them.
return when (index) {
return when (deviceIndex) {
2 -> NativeLibrary.Player2Device
3 -> NativeLibrary.Player3Device
4 -> NativeLibrary.Player4Device
@ -238,7 +250,7 @@ class InputHandler {
}
private fun setGenericAxisInput(event: MotionEvent, axis: Int) {
val playerNumber = getPlayerNumber(event.device.controllerNumber)
val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@ -297,7 +309,7 @@ class InputHandler {
private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
// Joycon support is half dead. Right joystick doesn't work
val playerNumber = getPlayerNumber(event.device.controllerNumber)
val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@ -325,7 +337,7 @@ class InputHandler {
}
private fun setRazerAxisInput(event: MotionEvent, axis: Int) {
val playerNumber = getPlayerNumber(event.device.controllerNumber)
val playerNumber = getPlayerNumber(event.device.controllerNumber, event.deviceId)
when (axis) {
MotionEvent.AXIS_X, MotionEvent.AXIS_Y ->
@ -362,4 +374,33 @@ class InputHandler {
)
}
}
fun getGameControllerIds(): Map<Int, Int> {
val gameControllerDeviceIds = mutableMapOf<Int, Int>()
val deviceIds = InputDevice.getDeviceIds()
var controllerSlot = 1
deviceIds.forEach { deviceId ->
InputDevice.getDevice(deviceId)?.apply {
// Don't over-assign controllers
if (controllerSlot >= 8) {
return gameControllerDeviceIds
}
// Verify that the device has gamepad buttons, control sticks, or both.
if (sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD ||
sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK
) {
// This device is a game controller. Store its device ID.
if (deviceId and id and vendorId and productId != 0) {
// Additionally filter out devices that have no ID
gameControllerDeviceIds
.takeIf { !it.contains(deviceId) }
?.put(deviceId, controllerSlot)
controllerSlot++
}
}
}
}
return gameControllerDeviceIds
}
}

View File

@ -14,8 +14,10 @@ add_library(yuzu-android SHARED
id_cache.cpp
id_cache.h
native.cpp
native.h
native_config.cpp
uisettings.cpp
game_metadata.cpp
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <core/core.h>
#include <core/file_sys/patch_manager.h>
#include <core/loader/nro.h>
#include <jni.h>
#include "core/loader/loader.h"
#include "jni/android_common/android_common.h"
#include "native.h"
struct RomMetadata {
std::string title;
u64 programId;
std::string developer;
std::string version;
std::vector<u8> icon;
bool isHomebrew;
};
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file =
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadProgramId(entry.programId);
loader->ReadIcon(entry.icon);
const FileSys::PatchManager pm{
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
EmulationSession::GetInstance().System().GetContentProvider()};
const auto control = pm.GetControlMetadata();
if (control.first != nullptr) {
entry.developer = control.first->GetDeveloperName();
entry.version = control.first->GetVersionString();
} else {
FileSys::NACP nacp;
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
entry.developer = nacp.GetDeveloperName();
} else {
entry.developer = "";
}
entry.version = "1.0.0";
}
if (loader->GetFileType() == Loader::FileType::NRO) {
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
}
m_rom_metadata_cache[path] = entry;
return entry;
}
RomMetadata GetRomMetadata(const std::string& path) {
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
}
return CacheRomMetadata(path);
}
extern "C" {
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
jstring jpath) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).title);
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
jstring jpath) {
return ToJString(env, std::to_string(GetRomMetadata(GetJString(env, jpath)).programId));
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
jstring jpath) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).developer);
}
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
jstring jpath) {
return ToJString(env, GetRomMetadata(GetJString(env, jpath)).version);
}
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
jstring jpath) {
auto icon_data = GetRomMetadata(GetJString(env, jpath)).icon;
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
jstring jpath) {
return static_cast<jboolean>(GetRomMetadata(GetJString(env, jpath)).isHomebrew);
}
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
return m_rom_metadata_cache.clear();
}
} // extern "C"

View File

@ -33,7 +33,6 @@
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
@ -48,514 +47,419 @@
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "jni/android_common/android_common.h"
#include "jni/applets/software_keyboard.h"
#include "jni/config.h"
#include "jni/emu_window/emu_window.h"
#include "jni/id_cache.h"
#include "video_core/rasterizer_interface.h"
#include "jni/native.h"
#include "video_core/renderer_base.h"
#define jconst [[maybe_unused]] const auto
#define jauto [[maybe_unused]] auto
namespace {
static EmulationSession s_instance;
class EmulationSession final {
public:
EmulationSession() {
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
}
EmulationSession::EmulationSession() {
m_vfs = std::make_shared<FileSys::RealVfsFilesystem>();
}
~EmulationSession() = default;
EmulationSession& EmulationSession::GetInstance() {
return s_instance;
}
static EmulationSession& GetInstance() {
return s_instance;
}
const Core::System& EmulationSession::System() const {
return m_system;
}
const Core::System& System() const {
return m_system;
}
Core::System& EmulationSession::System() {
return m_system;
}
Core::System& System() {
return m_system;
}
const EmuWindow_Android& EmulationSession::Window() const {
return *m_window;
}
const EmuWindow_Android& Window() const {
return *m_window;
}
EmuWindow_Android& EmulationSession::Window() {
return *m_window;
}
EmuWindow_Android& Window() {
return *m_window;
}
ANativeWindow* EmulationSession::NativeWindow() const {
return m_native_window;
}
ANativeWindow* NativeWindow() const {
return m_native_window;
}
void EmulationSession::SetNativeWindow(ANativeWindow* native_window) {
m_native_window = native_window;
}
void SetNativeWindow(ANativeWindow* native_window) {
m_native_window = native_window;
}
int InstallFileToNand(std::string filename, std::string file_extension) {
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) {
return false;
}
if (!dest->Resize(src->GetSize())) {
return false;
}
using namespace Common::Literals;
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
jconst read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i);
}
return true;
};
enum InstallResult {
Success = 0,
SuccessFileOverwritten = 1,
InstallError = 2,
ErrorBaseGame = 3,
ErrorFilenameExtension = 4,
};
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (file_extension == "nsp") {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) {
return InstallError;
}
} else {
return ErrorFilenameExtension;
int EmulationSession::InstallFileToNand(std::string filename, std::string file_extension) {
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) {
return false;
}
if (!nsp) {
return InstallError;
}
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
return InstallError;
}
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
*nsp, true, copy_func);
switch (res) {
case FileSys::InstallResult::Success:
return Success;
case FileSys::InstallResult::OverwriteExisting:
return SuccessFileOverwritten;
case FileSys::InstallResult::ErrorBaseInstall:
return ErrorBaseGame;
default:
return InstallError;
}
}
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
const std::string& custom_driver_name,
const std::string& file_redirect_dir) {
#ifdef ARCHITECTURE_arm64
void* handle{};
const char* file_redirect_dir_{};
int featureFlags{};
// Enable driver file redirection when renderer debugging is enabled.
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
file_redirect_dir_ = file_redirect_dir.c_str();
}
// Try to load a custom driver.
if (custom_driver_name.size()) {
handle = adrenotools_open_libvulkan(
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
}
// Try to load the system driver.
if (!handle) {
handle =
adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
nullptr, nullptr, file_redirect_dir_, nullptr);
}
m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
#endif
}
bool IsRunning() const {
return m_is_running;
}
bool IsPaused() const {
return m_is_running && m_is_paused;
}
const Core::PerfStatsResults& PerfStats() const {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats;
}
void SurfaceChanged() {
if (!IsRunning()) {
return;
}
m_window->OnSurfaceChanged(m_native_window);
}
void ConfigureFilesystemProvider(const std::string& filepath) {
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
if (!file) {
return;
}
auto loader = Loader::GetLoader(m_system, file);
if (!loader) {
return;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return;
}
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
m_manual_provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
program_id, file);
} else if (res2 == Loader::ResultStatus::Success &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
const auto nsp = file_type == Loader::FileType::NSP
? std::make_shared<FileSys::NSP>(file)
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) {
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile());
}
}
}
}
Core::SystemResultStatus InitializeEmulation(const std::string& filepath) {
std::scoped_lock lock(m_mutex);
// Create the render window.
m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
m_vulkan_library);
m_system.SetFilesystem(m_vfs);
m_system.GetUserChannel().clear();
// Initialize system.
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetAppletFrontendSet({
nullptr, // Amiibo Settings
nullptr, // Controller Selector
nullptr, // Error Display
nullptr, // Mii Editor
nullptr, // Parental Controls
nullptr, // Photo Viewer
nullptr, // Profile Selector
std::move(android_keyboard), // Software Keyboard
nullptr, // Web Browser
});
// Initialize filesystem.
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
m_manual_provider.get());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
ConfigureFilesystemProvider(filepath);
// Initialize account manager
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
// Load the ROM.
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
if (m_load_result != Core::SystemResultStatus::Success) {
return m_load_result;
}
// Complete initialization.
m_system.GPU().Start();
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
return Core::SystemResultStatus::Success;
}
void ShutdownEmulation() {
std::scoped_lock lock(m_mutex);
m_is_running = false;
// Unload user input.
m_system.HIDCore().UnloadInputDevices();
// Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger();
m_system.ShutdownMainProcess();
m_detached_tasks.WaitForAllTasks();
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
m_window.reset();
OnEmulationStopped(Core::SystemResultStatus::Success);
return;
}
// Tear down the render window.
m_window.reset();
}
void PauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Pause();
m_is_paused = true;
}
void UnPauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Run();
m_is_paused = false;
}
void HaltEmulation() {
std::scoped_lock lock(m_mutex);
m_is_running = false;
m_cv.notify_one();
}
void RunEmulation() {
{
std::scoped_lock lock(m_mutex);
m_is_running = true;
}
// Load the disk shader cache.
if (Settings::values.use_disk_shader_cache.GetValue()) {
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
m_system.GetApplicationProcessProgramID(), std::stop_token{},
LoadDiskCacheProgress);
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
}
void(m_system.Run());
if (m_system.DebuggerEnabled()) {
m_system.InitializeDebugger();
}
OnEmulationStarted();
while (true) {
{
[[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
break;
}
}
{
// Refresh performance stats.
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
m_perf_stats = m_system.GetAndResetPerfStats();
}
}
}
std::string GetRomTitle(const std::string& path) {
return GetRomMetadata(path).title;
}
std::vector<u8> GetRomIcon(const std::string& path) {
return GetRomMetadata(path).icon;
}
bool GetIsHomebrew(const std::string& path) {
return GetRomMetadata(path).isHomebrew;
}
void ResetRomMetadata() {
m_rom_metadata_cache.clear();
}
bool IsHandheldOnly() {
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
if (npad_style_set.fullkey == 1) {
if (!dest->Resize(src->GetSize())) {
return false;
}
if (npad_style_set.handheld == 0) {
return false;
using namespace Common::Literals;
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
jconst read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i);
}
return !Settings::IsDockedMode();
}
void SetDeviceType([[maybe_unused]] int index, int type) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
}
void OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
// Ensure that player1 is configured correctly and handheld disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
jauto handheld =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
handheld->Disconnect();
}
}
// Ensure that handheld is configured correctly and player 1 disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
jauto player1 =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
player1->Disconnect();
}
}
if (!controller->IsConnected()) {
controller->Connect();
}
}
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->Disconnect();
}
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard() {
return m_software_keyboard;
}
private:
struct RomMetadata {
std::string title;
std::vector<u8> icon;
bool isHomebrew;
return true;
};
RomMetadata GetRomMetadata(const std::string& path) {
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
enum InstallResult {
Success = 0,
SuccessFileOverwritten = 1,
InstallError = 2,
ErrorBaseGame = 3,
ErrorFilenameExtension = 4,
};
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (file_extension == "nsp") {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) {
return InstallError;
}
return CacheRomMetadata(path);
} else {
return ErrorFilenameExtension;
}
RomMetadata CacheRomMetadata(const std::string& path) {
jconst file = Core::GetGameFileFromPath(m_vfs, path);
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
if (!nsp) {
return InstallError;
}
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon);
if (loader->GetFileType() == Loader::FileType::NRO) {
jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
return InstallError;
}
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true,
copy_func);
switch (res) {
case FileSys::InstallResult::Success:
return Success;
case FileSys::InstallResult::OverwriteExisting:
return SuccessFileOverwritten;
case FileSys::InstallResult::ErrorBaseInstall:
return ErrorBaseGame;
default:
return InstallError;
}
}
void EmulationSession::InitializeGpuDriver(const std::string& hook_lib_dir,
const std::string& custom_driver_dir,
const std::string& custom_driver_name,
const std::string& file_redirect_dir) {
#ifdef ARCHITECTURE_arm64
void* handle{};
const char* file_redirect_dir_{};
int featureFlags{};
// Enable driver file redirection when renderer debugging is enabled.
if (Settings::values.renderer_debug && file_redirect_dir.size()) {
featureFlags |= ADRENOTOOLS_DRIVER_FILE_REDIRECT;
file_redirect_dir_ = file_redirect_dir.c_str();
}
// Try to load a custom driver.
if (custom_driver_name.size()) {
handle = adrenotools_open_libvulkan(
RTLD_NOW, featureFlags | ADRENOTOOLS_DRIVER_CUSTOM, nullptr, hook_lib_dir.c_str(),
custom_driver_dir.c_str(), custom_driver_name.c_str(), file_redirect_dir_, nullptr);
}
// Try to load the system driver.
if (!handle) {
handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
nullptr, nullptr, file_redirect_dir_, nullptr);
}
m_vulkan_library = std::make_shared<Common::DynamicLibrary>(handle);
#endif
}
bool EmulationSession::IsRunning() const {
return m_is_running;
}
bool EmulationSession::IsPaused() const {
return m_is_running && m_is_paused;
}
const Core::PerfStatsResults& EmulationSession::PerfStats() const {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats;
}
void EmulationSession::SurfaceChanged() {
if (!IsRunning()) {
return;
}
m_window->OnSurfaceChanged(m_native_window);
}
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
if (!file) {
return;
}
auto loader = Loader::GetLoader(m_system, file);
if (!loader) {
return;
}
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return;
}
u64 program_id = 0;
const auto res2 = loader->ReadProgramId(program_id);
if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) {
m_manual_provider->AddEntry(FileSys::TitleType::Application,
FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()),
program_id, file);
} else if (res2 == Loader::ResultStatus::Success &&
(file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
const auto nsp = file_type == Loader::FileType::NSP
? std::make_shared<FileSys::NSP>(file)
: FileSys::XCI{file}.GetSecurePartitionNSP();
for (const auto& title : nsp->GetNCAs()) {
for (const auto& entry : title.second) {
m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first,
entry.second->GetBaseFile());
}
}
}
}
m_rom_metadata_cache[path] = entry;
void EmulationSession::InitializeSystem() {
// Initialize filesystem.
m_system.SetFilesystem(m_vfs);
m_system.GetUserChannel().clear();
m_manual_provider = std::make_unique<FileSys::ManualContentProvider>();
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual,
m_manual_provider.get());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
}
return entry;
Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string& filepath) {
std::scoped_lock lock(m_mutex);
// Create the render window.
m_window =
std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window, m_vulkan_library);
// Initialize system.
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetAppletFrontendSet({
nullptr, // Amiibo Settings
nullptr, // Controller Selector
nullptr, // Error Display
nullptr, // Mii Editor
nullptr, // Parental Controls
nullptr, // Photo Viewer
nullptr, // Profile Selector
std::move(android_keyboard), // Software Keyboard
nullptr, // Web Browser
});
// Initialize filesystem.
ConfigureFilesystemProvider(filepath);
// Initialize account manager
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
// Load the ROM.
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
if (m_load_result != Core::SystemResultStatus::Success) {
return m_load_result;
}
private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
static_cast<jint>(progress), static_cast<jint>(max));
// Complete initialization.
m_system.GPU().Start();
m_system.GetCpuManager().OnGpuReady();
m_system.RegisterExitCallback([&] { HaltEmulation(); });
return Core::SystemResultStatus::Success;
}
void EmulationSession::ShutdownEmulation() {
std::scoped_lock lock(m_mutex);
m_is_running = false;
// Unload user input.
m_system.HIDCore().UnloadInputDevices();
// Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger();
m_system.ShutdownMainProcess();
m_detached_tasks.WaitForAllTasks();
m_load_result = Core::SystemResultStatus::ErrorNotInitialized;
m_window.reset();
OnEmulationStopped(Core::SystemResultStatus::Success);
return;
}
static void OnEmulationStarted() {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetOnEmulationStarted());
// Tear down the render window.
m_window.reset();
}
void EmulationSession::PauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Pause();
m_is_paused = true;
}
void EmulationSession::UnPauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Run();
m_is_paused = false;
}
void EmulationSession::HaltEmulation() {
std::scoped_lock lock(m_mutex);
m_is_running = false;
m_cv.notify_one();
}
void EmulationSession::RunEmulation() {
{
std::scoped_lock lock(m_mutex);
m_is_running = true;
}
static void OnEmulationStopped(Core::SystemResultStatus result) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetOnEmulationStopped(), static_cast<jint>(result));
// Load the disk shader cache.
if (Settings::values.use_disk_shader_cache.GetValue()) {
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
m_system.Renderer().ReadRasterizer()->LoadDiskResources(
m_system.GetApplicationProcessProgramID(), std::stop_token{}, LoadDiskCacheProgress);
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
}
private:
static EmulationSession s_instance;
void(m_system.Run());
// Frontend management
std::unordered_map<std::string, RomMetadata> m_rom_metadata_cache;
if (m_system.DebuggerEnabled()) {
m_system.InitializeDebugger();
}
// Window management
std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{};
OnEmulationStarted();
// Core emulation
Core::System m_system;
InputCommon::InputSubsystem m_input_subsystem;
Common::DetachedTasks m_detached_tasks;
Core::PerfStatsResults m_perf_stats{};
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
std::atomic<bool> m_is_running = false;
std::atomic<bool> m_is_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
while (true) {
{
[[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
break;
}
}
{
// Refresh performance stats.
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
m_perf_stats = m_system.GetAndResetPerfStats();
}
}
}
// GPU driver parameters
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
bool EmulationSession::IsHandheldOnly() {
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
// Synchronization
std::condition_variable_any m_cv;
mutable std::mutex m_perf_stats_mutex;
mutable std::mutex m_mutex;
};
if (npad_style_set.fullkey == 1) {
return false;
}
/*static*/ EmulationSession EmulationSession::s_instance;
if (npad_style_set.handheld == 0) {
return false;
}
} // Anonymous namespace
return !Settings::IsDockedMode();
}
void EmulationSession::SetDeviceType([[maybe_unused]] int index, int type) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
}
void EmulationSession::OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
// Ensure that player1 is configured correctly and handheld disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
jauto handheld = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
handheld->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::ProController);
handheld->Disconnect();
}
}
// Ensure that handheld is configured correctly and player 1 disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
jauto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
player1->Disconnect();
}
}
if (!controller->IsConnected()) {
controller->Connect();
}
}
void EmulationSession::OnGamepadDisconnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->Disconnect();
}
SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
return m_software_keyboard;
}
void EmulationSession::LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress,
int max) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetDiskCacheProgressClass(),
IDCache::GetDiskCacheLoadProgress(), static_cast<jint>(stage),
static_cast<jint>(progress), static_cast<jint>(max));
}
void EmulationSession::OnEmulationStarted() {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStarted());
}
void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
JNIEnv* env = IDCache::GetEnvForThread();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnEmulationStopped(),
static_cast<jint>(result));
}
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize();
@ -657,10 +561,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass cla
EmulationSession::GetInstance().HaltEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().ResetRomMetadata();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
@ -766,51 +666,12 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
}
}
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
return env->NewStringUTF(title.c_str());
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
jstring j_filename) {
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz) {
// Create the default config.ini.
Config{};
// Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize();
EmulationSession::GetInstance().InitializeSystem();
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
@ -898,4 +759,49 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
}
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, jclass clazz,
jlong jid) {
auto bis_system =
EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return ToJString(env, "");
}
auto applet_nca =
bis_system->GetEntry(static_cast<u64>(jid), FileSys::ContentRecordType::Program);
if (!applet_nca) {
return ToJString(env, "");
}
return ToJString(env, applet_nca->GetFullPath());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
jint jappletId) {
EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId(
static_cast<Service::AM::Applets::AppletId>(jappletId));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz,
jint jcabinetMode) {
EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode(
static_cast<Service::NFP::CabinetMode>(jcabinetMode));
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isFirmwareAvailable(JNIEnv* env, jclass clazz) {
auto bis_system =
EmulationSession::GetInstance().System().GetFileSystemController().GetSystemNANDContents();
if (!bis_system) {
return false;
}
// Query an applet to see if it's available
auto applet_nca =
bis_system->GetEntry(0x010000000000100Dull, FileSys::ContentRecordType::Program);
if (!applet_nca) {
return false;
}
return true;
}
} // extern "C"

View File

@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <android/native_window_jni.h>
#include "common/detached_tasks.h"
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/perf_stats.h"
#include "jni/applets/software_keyboard.h"
#include "jni/emu_window/emu_window.h"
#include "video_core/rasterizer_interface.h"
#pragma once
class EmulationSession final {
public:
explicit EmulationSession();
~EmulationSession() = default;
static EmulationSession& GetInstance();
const Core::System& System() const;
Core::System& System();
const EmuWindow_Android& Window() const;
EmuWindow_Android& Window();
ANativeWindow* NativeWindow() const;
void SetNativeWindow(ANativeWindow* native_window);
void SurfaceChanged();
int InstallFileToNand(std::string filename, std::string file_extension);
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
const std::string& custom_driver_name,
const std::string& file_redirect_dir);
bool IsRunning() const;
bool IsPaused() const;
void PauseEmulation();
void UnPauseEmulation();
void HaltEmulation();
void RunEmulation();
void ShutdownEmulation();
const Core::PerfStatsResults& PerfStats() const;
void ConfigureFilesystemProvider(const std::string& filepath);
void InitializeSystem();
Core::SystemResultStatus InitializeEmulation(const std::string& filepath);
bool IsHandheldOnly();
void SetDeviceType([[maybe_unused]] int index, int type);
void OnGamepadConnectEvent([[maybe_unused]] int index);
void OnGamepadDisconnectEvent([[maybe_unused]] int index);
SoftwareKeyboard::AndroidKeyboard* SoftwareKeyboard();
private:
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
static void OnEmulationStarted();
static void OnEmulationStopped(Core::SystemResultStatus result);
private:
// Window management
std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{};
// Core emulation
Core::System m_system;
InputCommon::InputSubsystem m_input_subsystem;
Common::DetachedTasks m_detached_tasks;
Core::PerfStatsResults m_perf_stats{};
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
std::atomic<bool> m_is_running = false;
std::atomic<bool> m_is_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
// GPU driver parameters
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
// Synchronization
std::condition_variable_any m_cv;
mutable std::mutex m_perf_stats_mutex;
mutable std::mutex m_mutex;
};

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M17,16l-4,-4V8.82C14.16,8.4 15,7.3 15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6c0,1.3 0.84,2.4 2,2.82V12l-4,4H3v5h5v-3.05l4,-4.2 4,4.2V21h5v-5h-4z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" />
</vector>

View File

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M9,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" />
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M20.77,8.58l-0.92,2.01c0.09,0.46 0.15,0.93 0.15,1.41 0,4.41 -3.59,8 -8,8s-8,-3.59 -8,-8c0,-0.05 0.01,-0.1 0,-0.14 2.6,-0.98 4.69,-2.99 5.74,-5.55C11.58,8.56 14.37,10 17.5,10c0.45,0 0.89,-0.04 1.33,-0.1l-0.6,-1.32 -0.88,-1.93 -1.93,-0.88 -2.79,-1.27 2.79,-1.27 0.71,-0.32C14.87,2.33 13.47,2 12,2 6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10c0,-1.47 -0.33,-2.87 -0.9,-4.13l-0.33,0.71z" />
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M15,13m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0" />
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M20.6,5.6L19.5,8l-1.1,-2.4L16,4.5l2.4,-1.1L19.5,1l1.1,2.4L23,4.5z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" />
</vector>

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:padding="24dp">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="20dp"
android:layout_gravity="center_vertical"
app:tint="?attr/colorOnSurface" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
tools:text="@string/applets" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
tools:text="@string/applets_description" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

@ -1,63 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"
android:id="@+id/card_game"
style="?attr/materialCardViewElevatedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:clipToPadding="false"
android:focusable="true"
android:transitionName="card_game"
android:layout_gravity="center"
app:cardElevation="0dp"
app:cardCornerRadius="12dp">
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp">
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"
android:id="@+id/card_game_art"
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/image_game_screen"
android:layout_width="150dp"
android:layout_height="150dp"
app:cardCornerRadius="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/image_game_screen"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:src="@drawable/default_icon" />
</com.google.android.material.card.MaterialCardView>
app:layout_constraintTop_toTopOf="parent"
app:shapeAppearance="@style/ShapeAppearance.Material3.Corner.ExtraSmall"
tools:src="@drawable/default_icon" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:id="@+id/text_game_title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="center"
android:textSize="14sp"
android:singleLine="true"
android:marqueeRepeatLimit="marquee_forever"
android:ellipsize="none"
android:requiresFadingEdge="horizontal"
app:layout_constraintEnd_toEndOf="@+id/card_game_art"
app:layout_constraintStart_toStartOf="@+id/card_game_art"
app:layout_constraintTop_toBottomOf="@+id/card_game_art"
app:layout_constraintEnd_toEndOf="@+id/image_game_screen"
app:layout_constraintStart_toStartOf="@+id/image_game_screen"
app:layout_constraintTop_toBottomOf="@+id/image_game_screen"
tools:text="The Legend of Zelda: Skyward Sword" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -16,7 +16,8 @@
<LinearLayout
android:id="@+id/option_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<ImageView
android:id="@+id/option_icon"

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/dialog_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:fadeScrollbars="false"
android:paddingVertical="12dp"
android:scrollbars="vertical" />
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="horizontal"
android:paddingHorizontal="24dp"
android:paddingVertical="16dp">
<ImageView
android:id="@+id/icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
tools:src="@drawable/ic_nfc" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_gravity="center_vertical|start"
android:textAlignment="viewStart"
tools:text="List option" />
</LinearLayout>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_applets"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_applets"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_applets"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/applets" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_applets"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -25,6 +25,9 @@
<action
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
app:destination="@id/driverManagerFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_appletLauncherFragment"
app:destination="@id/appletLauncherFragment" />
</fragment>
<fragment
@ -102,5 +105,17 @@
android:id="@+id/driverManagerFragment"
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
android:label="DriverManagerFragment" />
<fragment
android:id="@+id/appletLauncherFragment"
android:name="org.yuzu.yuzu_emu.fragments.AppletLauncherFragment"
android:label="AppletLauncherFragment" >
<action
android:id="@+id/action_appletLauncherFragment_to_cabinetLauncherDialogFragment"
app:destination="@id/cabinetLauncherDialogFragment" />
</fragment>
<dialog
android:id="@+id/cabinetLauncherDialogFragment"
android:name="org.yuzu.yuzu_emu.fragments.CabinetLauncherDialogFragment"
android:label="CabinetLauncherDialogFragment" />
</navigation>

View File

@ -124,6 +124,24 @@
<string name="share_save_file">Share save file</string>
<string name="export_save_failed">Failed to export save</string>
<!-- Applet launcher strings -->
<string name="applets">Applet launcher</string>
<string name="applets_description">Launch system applets using installed firmware</string>
<string name="applets_error_firmware">Firmware not installed</string>
<string name="applets_error_applet">Applet not available</string>
<string name="applets_error_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file and <a href="https://yuzu-emu.org/help/quickstart/#dumping-system-firmware">firmware</a> are installed and try again.]]></string>
<string name="album_applet">Album</string>
<string name="album_applet_description">See images stored in the user screenshots folder with the system photo viewer</string>
<string name="mii_edit_applet">Mii edit</string>
<string name="mii_edit_applet_description">View and edit Miis with the system editor</string>
<string name="cabinet_applet">Cabinet</string>
<string name="cabinet_applet_description">Edit and delete data stored on amiibo</string>
<string name="cabinet_launcher">Cabinet launcher</string>
<string name="cabinet_nickname_and_owner">Nickname and owner settings</string>
<string name="cabinet_game_data_eraser">Game data eraser</string>
<string name="cabinet_restorer">Restorer</string>
<string name="cabinet_formatter">Formatter</string>
<!-- About screen strings -->
<string name="gaia_is_not_real">Gaia isn\'t real</string>
<string name="copied_to_clipboard">Copied to clipboard</string>

View File

@ -120,6 +120,8 @@ add_library(common STATIC
socket_types.h
spin_lock.cpp
spin_lock.h
stb.cpp
stb.h
steady_clock.cpp
steady_clock.h
stream.cpp
@ -208,6 +210,8 @@ if (MSVC)
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@ -223,7 +227,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID)

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/fs/fs_android.h"
#include "common/string_util.h"
namespace Common::FS::Android {
@ -28,28 +29,35 @@ void RegisterCallbacks(JNIEnv* env, jclass clazz) {
env->GetJavaVM(&g_jvm);
native_library = clazz;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
F(JMethodID, JMethodName, Signature)
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) \
F(JMethodID, JMethodName, Signature)
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) \
F(JMethodID, JMethodName, Signature)
#define F(JMethodID, JMethodName, Signature) \
JMethodID = env->GetStaticMethodID(native_library, JMethodName, Signature);
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
}
void UnRegisterCallbacks() {
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
#define F(JMethodID) JMethodID = nullptr;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
}
bool IsContentUri(const std::string& path) {
@ -95,4 +103,29 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
#undef F
#undef FR
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) \
F(FunctionName, JMethodID, Caller)
#define F(FunctionName, JMethodID, Caller) \
std::string FunctionName(const std::string& filepath) { \
if (JMethodID == nullptr) { \
return 0; \
} \
auto env = GetEnvForThread(); \
jstring j_filepath = env->NewStringUTF(filepath.c_str()); \
jstring j_return = \
static_cast<jstring>(env->Caller(native_library, JMethodID, j_filepath)); \
if (!j_return) { \
return {}; \
} \
const jchar* jchars = env->GetStringChars(j_return, nullptr); \
const jsize length = env->GetStringLength(j_return); \
const std::u16string_view string_view(reinterpret_cast<const char16_t*>(jchars), length); \
const std::string converted_string = Common::UTF16ToUTF8(string_view); \
env->ReleaseStringChars(j_return, jchars); \
return converted_string; \
}
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
#undef F
#undef FH
} // namespace Common::FS::Android

View File

@ -17,19 +17,28 @@
"(Ljava/lang/String;)Z") \
V(Exists, bool, file_exists, CallStaticBooleanMethod, "exists", "(Ljava/lang/String;)Z")
#define ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(V) \
V(GetParentDirectory, get_parent_directory, CallStaticObjectMethod, "getParentDirectory", \
"(Ljava/lang/String;)Ljava/lang/String;") \
V(GetFilename, get_filename, CallStaticObjectMethod, "getFilename", \
"(Ljava/lang/String;)Ljava/lang/String;")
namespace Common::FS::Android {
static JavaVM* g_jvm = nullptr;
static jclass native_library = nullptr;
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
#define FR(FunctionName, ReturnValue, JMethodID, Caller, JMethodName, Signature) F(JMethodID)
#define FS(FunctionName, ReturnValue, Parameters, JMethodID, JMethodName, Signature) F(JMethodID)
#define F(JMethodID) static jmethodID JMethodID = nullptr;
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
ANDROID_STORAGE_FUNCTIONS(FS)
#undef F
#undef FS
#undef FR
#undef FH
enum class OpenMode {
Read,
@ -62,4 +71,10 @@ ANDROID_SINGLE_PATH_DETERMINE_FUNCTIONS(FR)
#undef F
#undef FR
#define FH(FunctionName, JMethodID, Caller, JMethodName, Signature) F(FunctionName)
#define F(FunctionName) std::string FunctionName(const std::string& filepath);
ANDROID_SINGLE_PATH_HELPER_FUNCTIONS(FH)
#undef F
#undef FH
} // namespace Common::FS::Android

View File

@ -13,6 +13,7 @@
#define AMIIBO_DIR "amiibo"
#define CACHE_DIR "cache"
#define CONFIG_DIR "config"
#define CRASH_DUMPS_DIR "crash_dumps"
#define DUMP_DIR "dump"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"

View File

@ -119,6 +119,7 @@ public:
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
@ -400,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
}
std::string_view GetParentPath(std::string_view path) {
if (path.empty()) {
return path;
}
#ifdef ANDROID
if (path[0] != '/') {
std::string path_string{path};
return FS::Android::GetParentDirectory(path_string);
}
#endif
const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/');
std::size_t name_index;

View File

@ -15,6 +15,7 @@ enum class YuzuPath {
AmiiboDir, // Where Amiibo backups are stored.
CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored.
CrashDumpsDir, // Where crash dumps are stored.
DumpDir, // Where dumped data is stored.
KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored.

View File

@ -25,6 +25,7 @@ void ConfigureNvidiaEnvironmentFlags() {
void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str()));
void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"));
void(_putenv("__GL_THREADED_OPTIMIZATIONS=1"));
#endif
}

View File

@ -505,7 +505,6 @@ struct Values {
linkage, false, "use_auto_stub", Category::Debugging, Specialization::Default, false};
Setting<bool> enable_all_controllers{linkage, false, "enable_all_controllers",
Category::Debugging};
Setting<bool> create_crash_dumps{linkage, false, "create_crash_dumps", Category::Debugging};
Setting<bool> perform_vulkan_check{linkage, true, "perform_vulkan_check", Category::Debugging};
// Miscellaneous

8
src/common/stb.cpp Normal file
View File

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "common/stb.h"

8
src/common/stb.h Normal file
View File

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stb_image.h>
#include <stb_image_resize.h>
#include <stb_image_write.h>

View File

@ -14,6 +14,10 @@
#include <windows.h>
#endif
#ifdef ANDROID
#include <common/fs/fs_android.h>
#endif
namespace Common {
/// Make a string lowercase
@ -63,6 +67,14 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
if (full_path.empty())
return false;
#ifdef ANDROID
if (full_path[0] != '/') {
*_pPath = Common::FS::Android::GetParentDirectory(full_path);
*_pFilename = Common::FS::Android::GetFilename(full_path);
return true;
}
#endif
std::size_t dir_end = full_path.find_last_of("/"
// windows needs the : included for something like just "C:" to be considered a directory
#ifdef _WIN32

View File

@ -11,6 +11,7 @@
#include <mach/mach.h>
#elif defined(_WIN32)
#include <windows.h>
#include "common/string_util.h"
#else
#if defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h>
@ -82,29 +83,8 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
#ifdef _MSC_VER
// Sets the debugger-visible name of the current thread.
// Uses trick documented in:
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
void SetCurrentThreadName(const char* name) {
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
#pragma pack(push, 8)
struct THREADNAME_INFO {
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user addr space)
DWORD dwThreadID; // thread ID (-1=caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} info;
#pragma pack(pop)
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = std::numeric_limits<DWORD>::max();
info.dwFlags = 0;
__try {
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
} __except (EXCEPTION_CONTINUE_EXECUTION) {
}
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
}
#else // !MSVC_VER, so must be POSIX threads

View File

@ -86,9 +86,9 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
symbols.insert_or_assign(
module.second, Symbols::GetSymbols(module.first, system.ApplicationMemory(),
system.ApplicationProcess()->Is64BitProcess()));
symbols.insert_or_assign(module.second,
Symbols::GetSymbols(module.first, system.ApplicationMemory(),
system.ApplicationProcess()->Is64Bit()));
}
for (auto& entry : out) {

View File

@ -309,17 +309,10 @@ struct System::Impl {
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
// Create a resource limit for the process.
const auto physical_memory_size =
kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
// Create the process.
auto main_process = Kernel::KProcess::Create(system.Kernel());
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.AppendNewProcess(main_process);
kernel.MakeApplicationProcess(main_process);
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {
@ -418,6 +411,7 @@ struct System::Impl {
services->KillNVNFlinger();
}
kernel.CloseServices();
kernel.ShutdownCores();
services.reset();
service_manager.reset();
cheat_engine.reset();
@ -429,7 +423,6 @@ struct System::Impl {
gpu_core.reset();
host1x_core.reset();
perf_stats.reset();
kernel.ShutdownCores();
cpu_manager.Shutdown();
debugger.reset();
kernel.Shutdown();

View File

@ -258,20 +258,20 @@ private:
Kernel::KScopedSchedulerLock sl{system.Kernel()};
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
for (auto& thread : ThreadList()) {
thread.RequestSuspend(Kernel::SuspendType::Debug);
}
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
// Wake up all threads.
for (auto* thread : ThreadList()) {
if (thread == except) {
for (auto& thread : ThreadList()) {
if (std::addressof(thread) == except) {
continue;
}
thread->SetStepState(Kernel::StepState::NotStepping);
thread->Resume(Kernel::SuspendType::Debug);
thread.SetStepState(Kernel::StepState::NotStepping);
thread.Resume(Kernel::SuspendType::Debug);
}
}
@ -283,13 +283,17 @@ private:
}
void UpdateActiveThread() {
const auto& threads{ThreadList()};
if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
state->active_thread = threads.front();
auto& threads{ThreadList()};
for (auto& thread : threads) {
if (std::addressof(thread) == state->active_thread) {
// Thread is still alive, no need to update.
return;
}
}
state->active_thread = std::addressof(threads.front());
}
const std::list<Kernel::KThread*>& ThreadList() {
Kernel::KProcess::ThreadList& ThreadList() {
return system.ApplicationProcess()->GetThreadList();
}

View File

@ -109,7 +109,7 @@ static std::string EscapeXML(std::string_view data) {
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
: DebuggerFrontend(backend_), system{system_} {
if (system.ApplicationProcess()->Is64BitProcess()) {
if (system.ApplicationProcess()->Is64Bit()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@ -446,10 +446,10 @@ void GDBStub::HandleBreakpointRemove(std::string_view command) {
// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
const Kernel::KThread& thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read32(thread->GetTlsAddress() + 0x1fc)};
const VAddr argument_thread_type{thread->GetArgument()};
const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)};
const VAddr argument_thread_type{thread.GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
@ -477,10 +477,10 @@ static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory&
}
static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
const Kernel::KThread* thread) {
const Kernel::KThread& thread) {
// Read thread type from TLS
const VAddr tls_thread_type{memory.Read64(thread->GetTlsAddress() + 0x1f8)};
const VAddr argument_thread_type{thread->GetArgument()};
const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)};
const VAddr argument_thread_type{thread.GetArgument()};
if (argument_thread_type && tls_thread_type != argument_thread_type) {
// Probably not created by nnsdk, no name available.
@ -508,16 +508,16 @@ static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory&
}
static std::optional<std::string> GetThreadName(Core::System& system,
const Kernel::KThread* thread) {
if (system.ApplicationProcess()->Is64BitProcess()) {
const Kernel::KThread& thread) {
if (system.ApplicationProcess()->Is64Bit()) {
return GetNameFromThreadType64(system.ApplicationMemory(), thread);
} else {
return GetNameFromThreadType32(system.ApplicationMemory(), thread);
}
}
static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
switch (thread->GetWaitReasonForDebugging()) {
static std::string_view GetThreadWaitReason(const Kernel::KThread& thread) {
switch (thread.GetWaitReasonForDebugging()) {
case Kernel::ThreadWaitReasonForDebugging::Sleep:
return "Sleep";
case Kernel::ThreadWaitReasonForDebugging::IPC:
@ -535,8 +535,8 @@ static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
}
}
static std::string GetThreadState(const Kernel::KThread* thread) {
switch (thread->GetState()) {
static std::string GetThreadState(const Kernel::KThread& thread) {
switch (thread.GetState()) {
case Kernel::ThreadState::Initialized:
return "Initialized";
case Kernel::ThreadState::Waiting:
@ -604,7 +604,7 @@ void GDBStub::HandleQuery(std::string_view command) {
const auto& threads = system.ApplicationProcess()->GetThreadList();
std::vector<std::string> thread_ids;
for (const auto& thread : threads) {
thread_ids.push_back(fmt::format("{:x}", thread->GetThreadId()));
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
}
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
} else if (command.starts_with("sThreadInfo")) {
@ -616,14 +616,14 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += "<threads>";
const auto& threads = system.ApplicationProcess()->GetThreadList();
for (const auto* thread : threads) {
for (const auto& thread : threads) {
auto thread_name{GetThreadName(system, thread)};
if (!thread_name) {
thread_name = fmt::format("Thread {:d}", thread->GetThreadId());
thread_name = fmt::format("Thread {:d}", thread.GetThreadId());
}
buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
thread->GetThreadId(), thread->GetActiveCore(),
thread.GetThreadId(), thread.GetActiveCore(),
EscapeXML(*thread_name), GetThreadState(thread));
}
@ -822,11 +822,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
const char p =
True(mem_info.attribute & MemoryAttribute::PermissionLocked) ? 'P' : '-';
reply +=
fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
mem_info.base_address, mem_info.base_address + mem_info.size - 1,
perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
reply += fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{}{} [{}, {}]\n",
mem_info.base_address,
mem_info.base_address + mem_info.size - 1, perm, state, l, i,
d, u, p, mem_info.ipc_count, mem_info.device_count);
}
const uintptr_t next_address = mem_info.base_address + mem_info.size;
@ -848,10 +850,10 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
const auto& threads{system.ApplicationProcess()->GetThreadList()};
for (auto* thread : threads) {
if (thread->GetThreadId() == thread_id) {
return thread;
auto& threads{system.ApplicationProcess()->GetThreadList()};
for (auto& thread : threads) {
if (thread.GetThreadId() == thread_id) {
return std::addressof(thread);
}
}

View File

@ -104,16 +104,16 @@ Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
}
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
// Allow use of cores 0~3 and thread priorities 1~63.
constexpr u32 default_thread_info_capability = 0x30007F7;
// Allow use of cores 0~3 and thread priorities 16~63.
constexpr u32 default_thread_info_capability = 0x30043F7;
ProgramMetadata result;
result.LoadManual(
true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/,
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/,
0x1FE00000 /*system_resource_size*/, {default_thread_info_capability} /*capabilities*/);
0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x100000 /*main_thread_stack_size*/,
0 /*title_id*/, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, 0 /*system_resource_size*/,
{default_thread_info_capability} /*capabilities*/);
return result;
}

View File

@ -73,6 +73,9 @@ public:
u64 GetFilesystemPermissions() const;
u32 GetSystemResourceSize() const;
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
const std::array<u8, 0x10>& GetName() const {
return npdm_header.application_name;
}
void Print() const;
@ -164,14 +167,14 @@ private:
u32_le unk_size_2;
};
Header npdm_header;
AciHeader aci_header;
AcidHeader acid_header;
Header npdm_header{};
AciHeader aci_header{};
AcidHeader acid_header{};
FileAccessControl acid_file_access;
FileAccessHeader aci_file_access;
FileAccessControl acid_file_access{};
FileAccessHeader aci_file_access{};
KernelCapabilityDescriptors aci_kernel_capabilities;
KernelCapabilityDescriptors aci_kernel_capabilities{};
};
} // namespace FileSys

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/file_sys/system_archive/system_version.h"
#include "core/file_sys/vfs_vector.h"
#include "core/hle/api_version.h"
@ -12,6 +13,9 @@ std::string GetLongDisplayVersion() {
}
VirtualDir SystemVersion() {
LOG_WARNING(Common_Filesystem, "called - Using hardcoded firmware version '{}'",
GetLongDisplayVersion());
VirtualFile file = std::make_shared<VectorVfsFile>(std::vector<u8>(0x100), "file");
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MAJOR, 0);
file->WriteObject(HLE::ApiVersion::HOS_VERSION_MINOR, 1);

View File

@ -8,7 +8,11 @@
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_trace.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Board::Nintendo::Nx {
@ -30,6 +34,8 @@ constexpr const std::size_t RequiredNonSecureSystemMemorySize =
constexpr const std::size_t RequiredNonSecureSystemMemorySizeWithFatal =
RequiredNonSecureSystemMemorySize + impl::RequiredNonSecureSystemMemorySizeViFatal;
constexpr const std::size_t SecureAlignment = 128_KiB;
namespace {
using namespace Common::Literals;
@ -183,4 +189,57 @@ u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
return GenerateUniformRange(min, max, GenerateRandomU64);
}
size_t KSystemControl::CalculateRequiredSecureMemorySize(size_t size, u32 pool) {
if (pool == static_cast<u32>(KMemoryManager::Pool::Applet)) {
return 0;
} else {
// return KSystemControlBase::CalculateRequiredSecureMemorySize(size, pool);
return size;
}
}
Result KSystemControl::AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
u32 pool) {
// Applet secure memory is handled separately.
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
// Ensure the size is aligned.
const size_t alignment =
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
R_UNLESS(Common::IsAligned(size, alignment), ResultInvalidSize);
// Allocate the memory.
const size_t num_pages = size / PageSize;
const KPhysicalAddress paddr = kernel.MemoryManager().AllocateAndOpenContinuous(
num_pages, alignment / PageSize,
KMemoryManager::EncodeOption(static_cast<KMemoryManager::Pool>(pool),
KMemoryManager::Direction::FromFront));
R_UNLESS(paddr != 0, ResultOutOfMemory);
// Ensure we don't leak references to the memory on error.
ON_RESULT_FAILURE {
kernel.MemoryManager().Close(paddr, num_pages);
};
// We succeeded.
*out = KPageTable::GetHeapVirtualAddress(kernel.MemoryLayout(), paddr);
R_SUCCEED();
}
void KSystemControl::FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool) {
// Applet secure memory is handled separately.
UNIMPLEMENTED_IF(pool == static_cast<u32>(KMemoryManager::Pool::Applet));
// Ensure the size is aligned.
const size_t alignment =
(pool == static_cast<u32>(KMemoryManager::Pool::System) ? PageSize : SecureAlignment);
ASSERT(Common::IsAligned(GetInteger(address), alignment));
ASSERT(Common::IsAligned(size, alignment));
// Close the secure region's pages.
kernel.MemoryManager().Close(KPageTable::GetHeapPhysicalAddress(kernel.MemoryLayout(), address),
size / PageSize);
}
} // namespace Kernel::Board::Nintendo::Nx

View File

@ -4,6 +4,11 @@
#pragma once
#include "core/hle/kernel/k_typed_address.h"
#include "core/hle/result.h"
namespace Kernel {
class KernelCore;
}
namespace Kernel::Board::Nintendo::Nx {
@ -25,8 +30,16 @@ public:
static std::size_t GetMinimumNonSecureSystemPoolSize();
};
// Randomness.
static u64 GenerateRandomRange(u64 min, u64 max);
static u64 GenerateRandomU64();
// Secure Memory.
static size_t CalculateRequiredSecureMemorySize(size_t size, u32 pool);
static Result AllocateSecureMemory(KernelCore& kernel, KVirtualAddress* out, size_t size,
u32 pool);
static void FreeSecureMemory(KernelCore& kernel, KVirtualAddress address, size_t size,
u32 pool);
};
} // namespace Kernel::Board::Nintendo::Nx

View File

@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
/// memory.
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
KVirtualAddress slab_addr) {
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
slab_addr -= memory_layout.GetSlabRegion().GetAddress();
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
}
@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
// Get the start of the slab region, since that's where we'll be working.
KVirtualAddress address = memory_layout.GetSlabRegionAddress();
const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
KVirtualAddress address = slab_region.GetAddress();
// Clear the slab region.
// TODO: implement access to kernel VAs.
// std::memset(device_ptr, 0, slab_region.GetSize());
// Initialize slab type array to be in sorted order.
std::array<KSlabType, KSlabType_Count> slab_types;

View File

@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
MainMemoryAddress);
}
static inline size_t GetInitialProcessBinarySize() {
return InitialProcessBinarySizeMax;
}
} // namespace Kernel

View File

@ -200,8 +200,8 @@ private:
RawCapabilityValue raw;
BitField<0, 15, CapabilityType> id;
BitField<15, 4, u32> major_version;
BitField<19, 13, u32> minor_version;
BitField<15, 4, u32> minor_version;
BitField<19, 13, u32> major_version;
};
union HandleTable {

View File

@ -107,12 +107,12 @@ KConditionVariable::KConditionVariable(Core::System& system)
KConditionVariable::~KConditionVariable() = default;
Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
KThread* owner_thread = GetCurrentThreadPointer(m_kernel);
Result KConditionVariable::SignalToAddress(KernelCore& kernel, KProcessAddress addr) {
KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
KScopedSchedulerLock sl(m_kernel);
KScopedSchedulerLock sl(kernel);
// Remove waiter thread.
bool has_waiters{};
@ -133,7 +133,7 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
// Write the value to userspace.
Result result{ResultSuccess};
if (WriteToUser(m_kernel, addr, std::addressof(next_value))) [[likely]] {
if (WriteToUser(kernel, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
result = ResultInvalidCurrentMemory;
@ -148,28 +148,28 @@ Result KConditionVariable::SignalToAddress(KProcessAddress addr) {
}
}
Result KConditionVariable::WaitForAddress(Handle handle, KProcessAddress addr, u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(m_kernel);
Result KConditionVariable::WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
u32 value) {
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
KThread* owner_thread{};
{
KScopedSchedulerLock sl(m_kernel);
KScopedSchedulerLock sl(kernel);
// Check if the thread should terminate.
R_UNLESS(!cur_thread->IsTerminationRequested(), ResultTerminationRequested);
// Read the tag from userspace.
u32 test_tag{};
R_UNLESS(ReadFromUser(m_kernel, std::addressof(test_tag), addr),
ResultInvalidCurrentMemory);
R_UNLESS(ReadFromUser(kernel, std::addressof(test_tag), addr), ResultInvalidCurrentMemory);
// If the tag isn't the handle (with wait mask), we're done.
R_SUCCEED_IF(test_tag != (handle | Svc::HandleWaitMask));
// Get the lock owner thread.
owner_thread = GetCurrentProcess(m_kernel)
owner_thread = GetCurrentProcess(kernel)
.GetHandleTable()
.GetObjectWithoutPseudoHandle<KThread>(handle)
.ReleasePointerUnsafe();

View File

@ -24,11 +24,12 @@ public:
explicit KConditionVariable(Core::System& system);
~KConditionVariable();
// Arbitration
Result SignalToAddress(KProcessAddress addr);
Result WaitForAddress(Handle handle, KProcessAddress addr, u32 value);
// Arbitration.
static Result SignalToAddress(KernelCore& kernel, KProcessAddress addr);
static Result WaitForAddress(KernelCore& kernel, Handle handle, KProcessAddress addr,
u32 value);
// Condition variable
// Condition variable.
void Signal(u64 cv_key, s32 count);
Result Wait(KProcessAddress addr, u64 key, u32 value, s64 timeout);

View File

@ -22,7 +22,7 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
KScopedSchedulerLock sl{kernel};
// Pin the current thread.
process->PinCurrentThread(core_id);
process->PinCurrentThread();
// Set the interrupt flag for the thread.
GetCurrentThread(kernel).SetInterruptFlag();

View File

@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
FlagCanChangeAttribute = (1 << 24),
FlagCanCodeMemory = (1 << 25),
FlagLinearMapped = (1 << 26),
FlagCanPermissionLock = (1 << 27),
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
FlagLinearMapped,
Free = static_cast<u32>(Svc::MemoryState::Free),
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
FlagCanAlignedDeviceMap,
IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
FlagCanAlignedDeviceMap,
IoRegister =
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
FlagCanCodeMemory,
FlagCanCodeMemory | FlagCanPermissionLock,
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped,
@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
FlagCanCodeAlias,
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
FlagCanPermissionLock,
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
NonDeviceIpc =
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
FlagCanUseNonDeviceIpc,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
enum class KMemoryPermission : u8 {
None = 0,
@ -182,8 +190,9 @@ enum class KMemoryAttribute : u8 {
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
SetMask = Uncached,
SetMask = Uncached | PermissionLocked,
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
@ -261,6 +270,10 @@ struct KMemoryInfo {
return m_state;
}
constexpr Svc::MemoryState GetSvcState() const {
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
}
constexpr KMemoryPermission GetPermission() const {
return m_permission;
}
@ -326,6 +339,10 @@ public:
return this->GetEndAddress() - 1;
}
constexpr KMemoryState GetState() const {
return m_memory_state;
}
constexpr u16 GetIpcLockCount() const {
return m_ipc_lock_count;
}
@ -443,6 +460,13 @@ public:
}
}
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
m_attribute = (m_attribute & ~mask) | attr;
}
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
ASSERT(this->GetAddress() < addr);
ASSERT(this->Contains(addr));

View File

@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
}
// Update block state.
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
static_cast<u8>(clear_disable_attr));
it->Update(state, perm, attr, it->GetAddress() == address,
static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
}
@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
KProcessAddress address, size_t num_pages,
KMemoryState test_state, KMemoryPermission test_perm,
KMemoryAttribute test_attr, KMemoryState state,
KMemoryPermission perm, KMemoryAttribute attr) {
KMemoryPermission perm, KMemoryAttribute attr,
KMemoryBlockDisableMergeAttribute set_disable_attr,
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
}
// Update block state.
it->Update(state, perm, attr, false, 0, 0);
it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
static_cast<u8>(clear_disable_attr));
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
} else {
@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
this->CoalesceForUpdate(allocator, address, num_pages);
}
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
KProcessAddress address, size_t num_pages,
KMemoryAttribute mask, KMemoryAttribute attr) {
// Ensure for auditing that we never end up with an invalid tree.
KScopedMemoryBlockManagerAuditor auditor(this);
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
KProcessAddress cur_address = address;
size_t remaining_pages = num_pages;
iterator it = this->FindIterator(address);
while (remaining_pages > 0) {
const size_t remaining_size = remaining_pages * PageSize;
KMemoryInfo cur_info = it->GetMemoryInfo();
if ((it->GetAttribute() & mask) != attr) {
// If we need to, create a new block before and insert it.
if (cur_info.GetAddress() != GetInteger(cur_address)) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address);
it = m_memory_block_tree.insert(*new_block);
it++;
cur_info = it->GetMemoryInfo();
cur_address = cur_info.GetAddress();
}
// If we need to, create a new block after and insert it.
if (cur_info.GetSize() > remaining_size) {
KMemoryBlock* new_block = allocator->Allocate();
it->Split(new_block, cur_address + remaining_size);
it = m_memory_block_tree.insert(*new_block);
cur_info = it->GetMemoryInfo();
}
// Update block state.
it->UpdateAttribute(mask, attr);
cur_address += cur_info.GetSize();
remaining_pages -= cur_info.GetNumPages();
} else {
// If we already have the right attributes, just advance.
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
remaining_pages = 0;
cur_address += remaining_size;
} else {
remaining_pages =
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
cur_address = cur_info.GetEndAddress();
}
}
it++;
}
this->CoalesceForUpdate(allocator, address, num_pages);
}
// Debug.
bool KMemoryBlockManager::CheckState() const {
// Loop over every block, ensuring that we are sorted and coalesced.

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