Compare commits

..

307 Commits

Author SHA1 Message Date
Charles Lombardo b3a1f793c3 android: Update surface parameters on emulation start
This adds a quick update that notifies the render surface if there was a change between surface creation and emulation starting.
2023-11-03 13:31:06 -04:00
liamwhite eda403388a Merge pull request #11948 from german77/hard_ring
service: hid: Ensure GetNextEntryIndex can't fail
2023-11-03 09:14:17 -04:00
liamwhite 3032980478 Merge pull request #11947 from german77/battery
core: hid: Fix wrong battery values
2023-11-03 09:14:10 -04:00
liamwhite 7f96f4db3f Merge pull request #11943 from liamwhite/silence-logspam
renderer_vulkan: minimize transform feedback support log
2023-11-03 09:14:02 -04:00
liamwhite a0f9a3ab5b Merge pull request #11936 from liamwhite/romfs-nonsense
romfs: fix extraction of single-directory root
2023-11-03 09:13:46 -04:00
german77 b36fec486e service: hid: Ensure GetNextEntryIndex can't fail 2023-11-02 20:33:19 -06:00
german77 57cf830862 core: hid: Fix wrong battery values 2023-11-02 18:39:08 -06:00
Liam 41701052d3 renderer_vulkan: minimize transform feedback support log 2023-11-01 20:47:08 -04:00
liamwhite 57c8dcfd77 Merge pull request #11942 from t895/log-version
android: Adjust log lifecycle
2023-11-01 15:45:53 -04:00
Charles Lombardo 398e881428 android: Adjust log lifecycle
Now logging will start when the frontend starts like qt does. This also adjusts the share log button to follow where we share the current log if we just returned from a game or return the old log if we haven't started a game yet.
2023-11-01 14:41:19 -04:00
Charles Lombardo 92418e909f android: Use yuzu logging system
Now anything that's logged in the frontend will be printed into the log file
2023-11-01 14:41:19 -04:00
liamwhite 7b10ceda02 Merge pull request #11940 from t895/controller-null-check
android: Default to player number 0 if we get an input from an unreco…
2023-11-01 13:50:50 -04:00
Charles Lombardo 344162db75 android: Default to player number 0 if we get an input from an unrecognized controller 2023-11-01 13:10:51 -04:00
liamwhite 48f913b6e7 Merge pull request #11937 from t895/reorganize-options
android: Reorganize settings tab
2023-11-01 09:14:27 -04:00
liamwhite 0efda40b57 Merge pull request #11933 from t895/android-ci-agony
ci: android: Use signing key if available
2023-11-01 09:14:19 -04:00
Charles Lombardo 5872c7d420 android: Adjust driver manager source string 2023-11-01 00:18:20 -04:00
Charles Lombardo 2b6edd3efd android: Reorganize settings tab 2023-11-01 00:17:38 -04:00
Liam b0c6bf497a romfs: fix extraction of single-directory root 2023-10-31 23:26:51 -04:00
liamwhite bf16289d77 Merge pull request #11934 from zhaobot/tx-update-20231101020822
Update translations (2023-11-01)
2023-10-31 23:06:23 -04:00
Charles Lombardo 987b37798a Merge pull request #11932 from t895/translations-update
android: Update translations
2023-10-31 23:04:13 -04:00
Charles Lombardo 135b645b3d ci: android: Use signing key if available
Lets gradle handle apk signing when available
2023-10-31 22:23:57 -04:00
The yuzu Community 1e468eac94 Update translations (2023-11-01) 2023-11-01 02:08:34 +00:00
Charles Lombardo 97b4ca1d01 android: Auto-generate locale config 2023-10-31 22:07:44 -04:00
Charles Lombardo 1d7ff850d6 android: Update translations from transifex 2023-10-31 22:07:43 -04:00
liamwhite 7e284809de Merge pull request #11931 from t895/applet-launcher
android: Applet launcher UI
2023-10-31 16:55:57 -04:00
liamwhite 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
Charles Lombardo 133788d0d4 android: Initialize filesystem components during application start 2023-10-31 14:41:40 -04:00
Charles Lombardo e8cb8b2668 android: Implement applet launcher 2023-10-31 14:41:40 -04:00
Dzmitry Dubrova 361dbdddcc service: am: Add support for LLE Software Keyboard Applet 2023-10-31 21:14:37 +03:00
liamwhite ab3e3c11af Merge pull request #11925 from t895/controller-fix
android: Fix controllers stuck on player 2
2023-10-31 09:47:42 -04:00
liamwhite db5c24eb66 Merge pull request #11892 from german77/pkm_screenshot
service: am: Implement ISelfController::SaveCurrentScreenshot
2023-10-31 09:47:19 -04:00
Charles Lombardo 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
liamwhite c60204e255 Merge pull request #11922 from t895/simplify-card-layout
android: Simplify game card layout
2023-10-30 15:32:45 -04:00
liamwhite 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
liamwhite 22cac3a5e3 Merge pull request #11728 from liushuyu/update-deps
Update external dependencies
2023-10-30 15:32:31 -04:00
Charles Lombardo 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
liamwhite 07276cf62a Merge pull request #11908 from t895/log-spam
android: Fix URI parsing in native code
2023-10-30 13:28:11 -04:00
Charles Lombardo 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
Charles Lombardo 585b6e9d46 android: Fix resolving android URIs in native code 2023-10-30 11:38:10 -04:00
Charles Lombardo 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
Charles Lombardo 1e61c3e1e7 android: Use header for EmulationSession 2023-10-30 11:28:23 -04:00
liamwhite 79d3cef8db Merge pull request #11920 from Termynat0r/master
Fix macOS build
2023-10-30 10:01:03 -04:00
liamwhite 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
liamwhite 789c16305d Merge pull request #11915 from t895/startup-freeze
android: Move game deserialization to another thread
2023-10-30 09:59:45 -04:00
liamwhite 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
Termynat0r 0bbbe80f75 Fix macOS build
Added missing preprocessor macros for macOS analog to linux and freebsd
2023-10-30 10:49:39 +01:00
Charles Lombardo 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
Charles Lombardo 9b3c64f4a4 android: Removed unused ControllerMappingHelper 2023-10-30 01:20:27 -04:00
liamwhite eec3d356b6 Merge pull request #11689 from liamwhite/breakpad
qt: implement automatic crash dump support
2023-10-29 23:41:13 -04:00
Charles Lombardo 2c1d850b46 android: Release touch on input overlay when opening in-game menu 2023-10-29 21:42:47 -04:00
Charles Lombardo 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
liamwhite adb0900906 Merge pull request #11911 from german77/leak_event
core: Close all KEvents
2023-10-29 19:46:47 -04:00
liamwhite 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
liamwhite 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
liamwhite b0f62d8f24 Merge pull request #11898 from hebo6/patch-1
Adding StartupWmClass for .desktop file
2023-10-29 19:46:27 -04:00
liamwhite ed2d77ddbc Merge pull request #11893 from liamwhite/swizzle
renderer_vulkan: fix viewport swizzle dirty state tracking
2023-10-29 19:46:20 -04:00
german77 6e883a26da core: Close all KEvents 2023-10-29 13:52:12 -06:00
Liam 8427b9d49d renderer_vulkan: ensure exception on surface loss 2023-10-29 15:31:05 -04:00
FrozenAra 0bb1c7c804 Implemented wheel event for volume control in VolumeButton 2023-10-29 20:29:17 +01:00
Charles Lombardo a5aa5876b4 android: Break home settings into grid with large screens 2023-10-29 13:47:41 -04:00
liamwhite 911d2216be Merge pull request #11866 from liamwhite/more-qt-nonsense
qt: fix game list shutdown crash
2023-10-29 11:25:22 -04:00
liamwhite 4da2105a32 Merge pull request #11862 from liamwhite/pascal-robust
Manually robust on Pascal and earlier
2023-10-29 11:25:15 -04:00
liamwhite 1f9684eaf9 Merge pull request #11859 from Kelebek1/compute_findbuffer
Add missing loop around compute FindBuffer calls
2023-10-29 11:25:09 -04:00
liamwhite 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
liamwhite 6aee148b17 Merge pull request #11843 from liamwhite/sync-process
kernel: update KProcess
2023-10-29 11:24:52 -04:00
liamwhite b5b93e6741 Merge pull request #11827 from liamwhite/preallocated
nvnflinger: fix reporting and freeing of preallocated buffers
2023-10-29 11:24:44 -04:00
Narr the Reg 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
Ameer J 9e4d606c4c nvidia_flags: Enable GL Threaded optimizations 2023-10-28 21:26:22 -04:00
Bo He 64f60f0acb Adding StartupWmClass for .desktop file 2023-10-28 13:45:35 +08:00
Liam 21c631b33b renderer_vulkan: fix viewport swizzle dirty state tracking 2023-10-27 14:23:47 -04:00
Narr the Reg f26dddf3b5 service: am: Implement ISelfController::SaveCurrentScreenshot 2023-10-26 22:29:52 -06:00
liamwhite 43be2bfe33 Merge pull request #11880 from abouvier/unbundle-stb
cmake: prefer system stb headers
2023-10-25 17:21:37 -04:00
Alexandre Bouvier 79ba5d9c26 cmake: prefer system stb headers 2023-10-25 21:47:32 +02:00
liamwhite 008d7e8c5f Merge pull request #11876 from liamwhite/apiversion
vulkan_common: use highest API version
2023-10-25 12:22:21 -04:00
Liam 19e9bde9e0 kernel: make sure new process is in list 2023-10-25 10:05:45 -04:00
liamwhite 6eb3a583cb Merge pull request #11812 from german77/save_capture
service: caps: Implement SaveScreenShotEx0 and variants
2023-10-24 21:43:51 -04:00
Liam e0834ee50b vulkan_common: use highest API version 2023-10-24 17:04:17 -04:00
Liam 79894152a8 qt: fix game list shutdown crash 2023-10-23 23:06:07 -04:00
liamwhite 9274eaecd0 Merge pull request #11863 from german77/buffer
service: ipc: Add third read buffer index
2023-10-23 17:04:09 -04:00
Narr the Reg c733620024 service: ipc: Add third read buffer index 2023-10-23 10:33:01 -06:00
german77 897b411ae7 service: caps: Implement SaveScreenShotEx0 and variants 2023-10-23 10:18:22 -06:00
german77 94836ba3b1 externals: stb: Add image write 2023-10-23 10:18:14 -06:00
liamwhite b1909b0435 Merge pull request #11841 from german77/halp
yuzu: fix restore shortcuts button
2023-10-23 10:36:40 -04:00
liamwhite 1cc764988f Merge pull request #11846 from german77/cheats
cheats: Clamp cheat names without failing
2023-10-23 10:33:37 -04:00
liamwhite 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
liamwhite 6b93b0b08c Merge pull request #11854 from german77/vibration-ui
yuzu: Fix vibration reseting to 1%
2023-10-23 10:33:03 -04:00
Kelebek1 68f25217b8 Add missing dowhile loops around FindBuffer calls 2023-10-23 15:08:56 +01:00
Liam 0604b14263 Manually robust on Pascal and earlier 2023-10-23 09:08:57 -04:00
liushuyu a065dcdcd9 externals/opus: use CMakeLists shipped with Opus itself 2023-10-22 14:21:33 -06:00
german77 3d4a064674 yuzu: Fix vibration reseting to 1% 2023-10-22 13:39:45 -06:00
german77 e4dfd51337 input_common: joycon: Move vibrations to a queue 2023-10-22 11:30:59 -06:00
Ameer J cfe73af6f2 emit_glsl_warp: Fix shfl_in_bounds conditional 2023-10-22 00:45:23 -04:00
liushuyu 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
liushuyu a49b146ccc externals: update libusb to c060e9ce30ac2e3ffb49d94209c4dae77b6642f7 ...
... this fixes an issue when compiling with newer MSVC
2023-10-21 22:29:19 -06:00
liushuyu fd9e157184 externals: update VulkanMemoryAllocator to 2f382df218d7e8516dee3b3caccb819a62b571a2 2023-10-21 22:29:19 -06:00
liushuyu 6cbd4020e8 externals: update Vulkan-Headers to 1.3.265 2023-10-21 22:29:19 -06:00
liushuyu 3558b236cd externals: update ffmpeg to 9c1294eaddb88cb0e044c675ccae059a85fc9c6c
... to fix build with binutils 2.41+
2023-10-21 22:29:19 -06:00
liushuyu 48e82c4138 externals: update vcpkg to ef2eef17340f3fbd679327d286fad06dd6e838ed 2023-10-21 22:29:19 -06:00
liushuyu 9eb70aea1d externals: update SDL to 2.28.4 2023-10-21 22:29:19 -06:00
liushuyu 0460fbacc9 externals: update cpp-jwt to 10ef5735d842b31025f1257ae78899f50a40fb14 2023-10-21 22:29:19 -06:00
liushuyu c73297e840 externals: update cpp-httplib to 0.14.1 2023-10-21 22:29:19 -06:00
liushuyu 633d869ff4 externals: update libusb to 1.0.26 2023-10-21 22:29:19 -06:00
liushuyu e03f86cc54 externals: update inih to r57 2023-10-21 22:29:19 -06:00
liushuyu a0a3566977 externals: update opus to 1.4 2023-10-21 22:29:19 -06:00
Narr the Reg 77fb9d415b yuzu: Fix restore shortcuts button 2023-10-21 21:16:20 -06:00
german77 bbdaa62175 cheats: Clamp cheat names without failing 2023-10-21 21:04:03 -06:00
Liam 31bffc7299 kernel: fix extraneous ref 2023-10-21 22:16:41 -04:00
Liam 5f8f09d750 kernel: shutdown app before gpu 2023-10-21 20:35:18 -04:00
Liam dcfe674ed4 kernel: signal thread on termination completed 2023-10-21 20:03:41 -04:00
Liam bb195c2c2b kernel: add missing TLR clear 2023-10-21 20:03:41 -04:00
Liam 8c59543ee3 kernel: update KProcess 2023-10-21 20:03:41 -04:00
liamwhite db37e583ff Merge pull request #11831 from liamwhite/hosversionbetween
set: return version info from system archive
2023-10-21 18:22:20 -04:00
liamwhite d28e826e47 Merge pull request #11830 from liamwhite/ts-session
ts: add OpenSession
2023-10-21 18:22:13 -04:00
liamwhite 13beb85514 Merge pull request #11828 from liamwhite/setthreadescription
common: use SetThreadDescription API for thread names
2023-10-21 18:22:04 -04:00
liamwhite 4b06bcc82c Merge pull request #11789 from Kelebek1/spirv_shift_right
Manually robust on Maxwell and earlier
2023-10-21 18:21:53 -04:00
Liam 12ebc8d9d1 set: return version info from system archive 2023-10-20 13:29:52 -04:00
Liam 2b85e9e997 ts: add OpenSession 2023-10-20 13:29:32 -04:00
Liam 59b62c6507 common: use SetThreadDescription API for thread names 2023-10-20 11:41:29 -04:00
Fernando S 2e760a9833 Merge pull request #11748 from liamwhite/kern_1700
kernel: update for 17.0.0
2023-10-20 17:08:00 +02:00
Fernando S 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
liamwhite 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
Liam 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
Liam 249db0a59b kernel: fix incorrect calculation of used non system memory value 2023-10-20 09:12:10 -04:00
Liam 9526ce95dd gdbstub: add PermissionLocked to mappings table 2023-10-20 02:53:31 -04:00
Liam 687158fe00 kernel: fix format string error 2023-10-20 02:41:32 -04:00
Liam d8507332c1 kernel: make check fully constexpr for broken msvc constant folding 2023-10-20 02:34:15 -04:00
Liam 67e983a354 codespell: allow 'VAs' 2023-10-20 02:34:15 -04:00
Liam f21058a6c0 k_page_table: add MapFirstGroup 2023-10-20 02:34:15 -04:00
Liam b456af31e6 kernel: update KMemoryRegionType values 2023-10-20 02:34:15 -04:00
Liam 0441853d0f k_page_table: implement PermissionLocked 2023-10-20 02:34:15 -04:00
Liam 60a1c6b95b k_page_table: add new CheckMemoryState helper 2023-10-20 02:34:15 -04:00
Liam 794e6c7a96 kernel: split Io memory state, add PermissionLocked attribute 2023-10-20 02:34:15 -04:00
Liam 22afa2c7a3 kernel: reshuffle ini1 size, add slab clear note 2023-10-20 02:34:15 -04:00
liamwhite 85a89ca3e3 Merge pull request #11822 from german77/no-name
service: mii: Create random mii with name
2023-10-19 16:54:05 -04:00
Narr the Reg 26776c0e60 service: mii: Create random mii with name 2023-10-19 13:35:02 -06:00
Kelebek1 e02ee8e59d Manually robust on Maxwell and earlier 2023-10-19 19:54:31 +01:00
liamwhite 134ecca9b0 Merge pull request #11810 from liamwhite/clang-17
general: fix build failure on clang 17
2023-10-18 19:30:29 -04:00
liamwhite 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
liamwhite 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
Liam c5bdc0054c general: fix build failure on clang 17 2023-10-17 22:44:21 -04:00
flodavid 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
liamwhite bd05ace08d Merge pull request #11774 from liamwhite/refcount-issue
fsmitm_romfsbuild: avoid unnecessary copies of vfs pointers
2023-10-17 11:49:11 -04:00
liamwhite fa56518f20 Merge pull request #11747 from Kelebek1/image_alias_sample_names
Small things
2023-10-17 11:48:57 -04:00
liamwhite b577d7a55f Merge pull request #11349 from vonchenplus/buffer_cache_crash
video_core: Fix moltenvk crash on macos
2023-10-17 11:48:44 -04:00
Liam d9dde7e6f3 renderer_vulkan: add locks to avoid scheduler flushes from CPU 2023-10-17 10:00:25 -04:00
Fernando S 2244b613cf Merge pull request #11788 from Squall-Leonhart/IFREMOVED
[crash fix]brings back the removed if  statement in util.cpp and adds the  num_level test to it like previous discontinued PR
2023-10-17 14:36:36 +02:00
german77 c73bb33ff1 service: hle: Allow to access read buffer A and X directly 2023-10-16 23:36:46 -06:00
Narr the Reg bcce184e60 service: acc: Implement functions needed for profile select (#11653) 2023-10-17 05:12:55 +02:00
Squall-Leonhart 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
Squall Leonhart 07143ce15c Make Clang happy. 2023-10-17 00:26:19 +11:00
Squall Leonhart 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
liamwhite 8becf13e8b Merge pull request #11786 from v1993/cuda-on-linux
host1x/codecs: enable CUDA on Linux
2023-10-15 22:23:00 -04:00
liamwhite 9e2ebb24df Merge pull request #11794 from german77/linemot
input_common: udp: Avoid crash when trying to map motion before client is ready
2023-10-15 22:22:45 -04:00
Squall Leonhart 90c56f5dc1 added missing trailing line. 2023-10-16 06:07:26 +11:00
Squall Leonhart 4b0291172e meant to add the unorms as well 2023-10-16 04:29:24 +11:00
Squall-Leonhart 12e4757cf3 use texelfetch instead of texturelod 2023-10-16 04:20:45 +11:00
Squall Leonhart 144c0734f5 appease the format gods 2023-10-16 03:24:44 +11:00
Squall-Leonhart 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
Squall-Leonhart 03c3f936cf missed this line when editing the copypasta 2023-10-15 20:58:50 +11:00
Squall-Leonhart 66f41da365 moved line to appease the format gods 2023-10-15 20:54:25 +11:00
Squall-Leonhart 7a986d731b Implement missing formats for Bravely Default 2 2023-10-15 20:43:48 +11:00
german77 eae0570a1c input_common: udp: Avoid crash when trying to map motion before client is ready 2023-10-15 02:13:51 -06:00
Squall Leonhart b57d98f847 brings back the removed If statement and adds the num_level test
This resolves the out of bounds read/writes in the linear swizzler, it brings back the scaled TOTK Recall bug however, pending further work in the block size calculation.

Recall is not glitched in the Dynamic FPS resolution mod to the degree that it is in the native yuzu scaler, this can be a workaround for the time being.


The recall effect is constructed from multiple 320x180 texture slices, it breaking may have a similar origin to https://github.com/Ryujinx/Ryujinx/pull/5640

but it may also be connected to the other deficiencies identified in the Yuzu size calculations, such as no apparent implementation of slice testing for end of slce depth as opposed to full aligned size as implemented in https://github.com/Ryujinx/Ryujinx/pull/5220
2023-10-15 02:09:28 +11:00
Valeri 762ac5aa9f host1x/codecs: enable CUDA on Linux 2023-10-14 17:35:45 +03:00
liamwhite 1a4abd184f Merge pull request #11780 from Darkness4/master
qt: add network components when using discord
2023-10-14 09:58:33 -04:00
liamwhite 9524d7034c Merge pull request #11779 from flodavid/improve-player-config-click
yuzu: Improve behavior when clicking on controller box in Control configuration
2023-10-14 09:58:27 -04:00
liamwhite 36d18e457b Merge pull request #11778 from liamwhite/audren-shutdown-lock
audio: fix shutdown deadlock in audio renderer
2023-10-14 09:58:17 -04:00
liamwhite db562bc08d Merge pull request #11775 from Kelebek1/draw_vertex_array
Implement vertex array first and subsequent draws
2023-10-14 09:58:11 -04:00
liamwhite 18672e6a78 Merge pull request #11159 from flodavid/master_bis
Enable to use controller to close a game
2023-10-14 09:58:03 -04:00
Kelebek1 32ad99701d Implement vertex array first and subsequent draws 2023-10-14 12:09:35 +01:00
Nguyen Marc 63c5340cc4 Revert "cmake: only add network component if qt used"
This reverts commit a94371f67b.
2023-10-14 08:46:05 +02:00
Nguyen Marc a94371f67b cmake: only add network component if qt used 2023-10-14 01:46:20 +02:00
Nguyen Marc 22e4add562 qt: add missing target_link_libraries for discordrpc 2023-10-14 01:15:28 +02:00
Nguyen Marc b1a7bbd458 qt: add network components when using discord 2023-10-14 01:01:02 +02:00
flodavid 27ab2a6e13 yuzu: Improve behavior when clicking on controller box in Control Configuration
When reducing the number of Connecter Controllers, keep the one clicked if it was not the last one of the list
2023-10-14 00:46:11 +02:00
Liam 68ea0a2b72 audio: fix shutdown deadlock in audio renderer 2023-10-13 16:34:31 -04:00
liamwhite a8bd02acd8 Merge pull request #11772 from v1993/polyfill-thread-fixes
common/polyfill_thread: use std::forward where appropriate, qualify std::move calls
2023-10-13 15:15:25 -04:00
Charles Lombardo 3e4edbe007 Merge pull request #11767 from t895/gradle-stuff
android: Update dependencies
2023-10-13 15:08:47 -04:00
Charles Lombardo 4a9240599a Merge pull request #11773 from t895/manager-fix
android: Fix incorrect assumption for driver installation validation
2023-10-13 15:08:39 -04:00
Liam 053a16799e fsmitm_romfsbuild: avoid unnecessary copies of vfs pointers 2023-10-13 14:22:52 -04:00
Charles Lombardo 82c845dc2f android: Fix incorrect assumption for driver installation validation
The driver was assumed to be installed at this point before I made a refactor. Now we just check if the copy operation was successful and delete the file if it fails.
2023-10-13 13:50:38 -04:00
Charles Lombardo 3aa6d4d8ce android: Allow ANDROID_STL 2023-10-13 12:55:41 -04:00
Charles Lombardo 9b961dddb4 android: Remove unnecessary flag to extract native libs in AndroidManifest.xml 2023-10-13 12:55:41 -04:00
Charles Lombardo 224b6036a4 android: Update dependencies
Updates to androidx navigation, lifecycle, preference, fragment, recyclerview, and core
2023-10-13 12:55:41 -04:00
Charles Lombardo 2c3281c66b externals: Update LLVM to 17.0.2
Matches android ndk
2023-10-13 12:55:41 -04:00
Charles Lombardo 1591923f91 android: Update ndk to 26.1.10909125
The new ndk uses LLVM 17.0.2 so we can remove the LLVM download and libc++ options for the android builds
2023-10-13 12:55:41 -04:00
DanielSvoboda 56e5d99684 Improvement in Directory Path Detection for Shortcuts (#11749)
* Improvement in Directory Path Detection for Shortcuts

This pull request updates how the directory path for shortcuts is determined. The main changes are:

1. Replaced the use of environment variables to determine the path of the desktop and applications menu with `QStandardPaths::writableLocation`. This change addresses an issue where the desktop path was not correctly identified when its location was customized, as shown in the attached screenshot.

2. Added conversion from `QString` to `std::string` using `toUtf8()`, which correctly handles non-ASCII characters in directory paths. This change ensures that directory paths containing Portuguese words like "Área de trabalho" are supported.

3. Replaced directory checking using `Common::FS::IsDir()` with `QDir::exists()`.

These changes should improve cross-platform compatibility and code robustness. Because it couldn't locate my desktop, which wasn't on the C drive, but on the F, and even though localization wouldn't work because it was setting it to find the 'Desktop' folder and in the computer's language it says 'Área de trabalho', that will fix for other languages too.

* Update main.cpp

* formatting

* Update src/yuzu/main.cpp

Co-authored-by: Tobias <thm.frey@gmail.com>

* Update src/yuzu/main.cpp

Co-authored-by: Tobias <thm.frey@gmail.com>

* Update main.cpp

* Update main.cpp

* Update main.cpp

desktopPath > desktop_Path
applicationsPath > applications_Path

* Update main.cpp

* formatting

* Update main.cpp

This code will attempt to use QStandardPaths to find the applications directory. If that fails, it will resort to using the ~/.local/share/applications directory, which is a common location for application shortcuts in Linux.

* Update main.cpp

* formatting

---------

Co-authored-by: Tobias <thm.frey@gmail.com>
2023-10-13 09:57:49 -06:00
Valeri Ochinski ca75c9125d common/polyfill_thread: use std::forward where appropriate, qualify std::move calls 2023-10-13 18:51:11 +03:00
liamwhite 1a4874e178 Merge pull request #11769 from liamwhite/qt-ownership-issue
qt: ensure worker cancellation is complete before clearing
2023-10-13 09:29:13 -04:00
liamwhite c00b63b9e1 Merge pull request #11766 from liamwhite/open-sesame
k_page_table: add missing page group open when locking memory
2023-10-13 09:29:05 -04:00
liamwhite c8602e1b1f Merge pull request #11649 from t895/driver-manager
android: Driver manager
2023-10-13 09:28:53 -04:00
Liam faa6c35e78 qt: ensure worker cancellation is complete before clearing 2023-10-12 21:07:49 -04:00
Charles Lombardo 8b64878258 android: Update AGP to 8.1.2 2023-10-12 18:59:57 -04:00
liamwhite 519c12da15 Merge pull request #11746 from liamwhite/relr
jit: add support for relr-type relocations
2023-10-12 18:23:31 -04:00
liamwhite d74fd9e2fe Merge pull request #11763 from liamwhite/lto-noinline
kernel: mark TLS accessors as noinline for non-MSVC LTO
2023-10-12 18:23:25 -04:00
liamwhite d011d3ff0e Merge pull request #11765 from german77/cap_mac
service: caps: Remove ambiguous call
2023-10-12 18:23:18 -04:00
Charles Lombardo a5fb9de6fa android: Add GPU driver management fragment
Implements a GPU driver manager that saves all drivers to the user data directory and asynchronously installs drivers when they're needed.
2023-10-12 17:17:21 -04:00
Liam c4ec76edba k_page_table: add missing page group open when locking memory 2023-10-12 15:00:26 -04:00
Narr the Reg 3f05b8facd service: caps: Remove ambiguous call 2023-10-12 12:52:55 -06:00
liamwhite 65d3300875 Merge pull request #11753 from german77/timex2
service: caps: Fix GetAlbumFileList3AaeAruid and GetAlbumFileList0AafeAruidDeprecated
2023-10-12 11:17:35 -04:00
liamwhite 7b2ac196d2 Merge pull request #11751 from Kelebek1/transition_msaa_image
Transition MSAA images to general layout without uploading data
2023-10-12 11:17:20 -04:00
F David d9456f0a11 fix style 2023-10-12 16:06:44 +02:00
Liam 2212c9653d kernel: mark TLS accessors as noinline for non-MSVC LTO 2023-10-12 09:16:22 -04:00
Narr the Reg fe04a7523a service: caps: Fix GetAlbumFileList3AaeAruid and GetAlbumFileList0AafeAruidDeprecated 2023-10-11 20:01:33 -06:00
liamwhite 45a76637f5 Merge pull request #11752 from lat9nq/msvc-tz-2022g
externals/nx_tzdb: Update download version to 2022g
2023-10-11 21:45:22 -04:00
lat9nq bf7c45e560 externals/nx_tzdb: Update download version to 2022g
Mainly for MSVC, changes the time zone database version to latest.
2023-10-11 20:20:31 -04:00
flodavid 48b67fc4a0 yuzu: Enable to use controller to restart a game
- Show the right confirm dialog if wanted
  - Create generic method to ask close confirmation
- Add "R + Plus + Minus" default shortcut to Restart emulation
2023-10-12 01:53:54 +02:00
Florian 6c246f2ac5 yuzu: Use new setting method for stop emulation 2023-10-12 01:51:53 +02:00
flodavid a34565727b yuzu: Enable to use controller to close a game
- Add General setting to choose if a confirm dialog is shown when stopping
- Show the right confirm dialog if wanted
  - Reuse dialog window that ask to close the game
- Add "L + Plus + Minus" default shortcut to Stop emulation
- Create generic question dialog based on TAS dialog
  - It allows controller interaction on most dialogs
2023-10-12 01:51:52 +02:00
Kelebek1 6f4a080b98 Transition MSAA images to general layout without uploading data 2023-10-11 23:27:23 +01:00
liamwhite da6824d9fd Merge pull request #11720 from lat9nq/dbg-syms
ci/linux: Upload separated debug symbols
2023-10-11 17:55:17 -04:00
liamwhite b36f45b239 Merge pull request #11740 from german77/shorcuts
yuzu: Save multiple resolutions per icon
2023-10-11 17:55:08 -04:00
liamwhite 07ae6659e7 Merge pull request #11744 from Kelebek1/no_res_no_rescaled
Do not set rescaled flag when rescaling is disabled
2023-10-11 17:54:59 -04:00
liamwhite 880b004321 Merge pull request #11750 from lat9nq/2022g
externals/tzdb_to_nx: Update to 221202
2023-10-11 17:54:50 -04:00
lat9nq 21ebe3e462 externals/tzdb_to_nx: Update to 221202
This updates us to an eggert/tz commit downstream of 2022g that compiles. This
seems to be the revision Nintendo is using for 17.0.0, if the data checksums
are anything to go off of.
2023-10-11 16:42:31 -04:00
Kelebek1 98cac9410c Get out of render pass before query barriers, fix image names with samples > 1, remove image alias bit 2023-10-11 17:15:35 +01:00
Liam 7b5d234558 jit: add support for relr-type relocations 2023-10-11 11:13:19 -04:00
liamwhite 84b0e29b56 Merge pull request #11734 from Kelebek1/device_local_buffer_alloc
Do not allocate DeviceLocal buffers as mapped
2023-10-11 09:24:28 -04:00
liamwhite 5ecdcfa334 Merge pull request #11735 from Kelebek1/clear_command_buffer_post_dsp
Clear DSP buffer after each execution
2023-10-11 09:24:16 -04:00
liamwhite 5f4857691e Merge pull request #11683 from Kelebek1/do_not_sync_on_written_buffer
Do not double sync written buffers, move mark written to binding
2023-10-11 09:24:05 -04:00
liamwhite b50ce645ac Merge pull request #11144 from flodavid/master
Enable controller interaction in Controller Applet
2023-10-11 09:23:52 -04:00
Fernando S b6d19329ac Merge pull request #11743 from Squall-Leonhart/IFREMOVED
Fix mistaken usage of info.block instead of level_info.block
2023-10-11 11:56:47 +02:00
Kelebek1 8c769b71a1 Do not set rescaled flag when rescaling is disabled 2023-10-11 10:29:19 +01:00
Squall-Leonhart 9512992fe2 Fix mistaken usage of info.block instead of level_info.block
Fixed an error on my part, in the last change I had mistakenly passed unadjusted block info into FullUploadSwizzles and UnswizzleImage

Revert (my mistaken changing of) the construction of SwizzleParameters in UnswizzleImage and FullUploadSwizzles to use level_info.block instead of info.block. This ensures that the block information used in the swizzling process is correctly adjusted for each mip level.
2023-10-11 19:12:33 +11:00
Narr the Reg 6b10f04322 yuzu: Save multiple resolutions per icon 2023-10-10 17:24:49 -06:00
toast2903 c206a04747 ci/linux: Fix find parameter order
Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
2023-10-10 13:50:50 -04:00
Kelebek1 ec6ddaf766 Clear DSP buffer after each execution 2023-10-10 18:22:08 +01:00
liamwhite 36ea7565fa Merge pull request #11534 from Squall-Leonhart/IFREMOVED
Partial revert of #10433 (Texture Cache Util: Fix block depth adjustment on slices)
2023-10-10 12:44:48 -04:00
lat9nq 00b0938f10 ci/linux: Upload separated debug symbols
Creates a new archive with a debug suffix that contains the debug symbols from
compiling yuzu for mainline. The yuzu executable also gets a GNU debug link to the symbols file.
ci/linux: Compile with debug symbols and upload separately

Currently only uploads for yuzu but yuzu-cmd or other future executables can be
added to the for-loop's parameters.
2023-10-10 11:55:55 -04:00
Kelebek1 ed58445111 Not not allocate DeviceLocal buffers as mapped 2023-10-10 12:49:07 +01:00
Fernando S b28b05e2aa Merge pull request #11718 from liamwhite/arm64-native-clock
common: add arm64 native clock
2023-10-10 11:48:06 +02:00
Fernando S 8151a4d301 Merge pull request #11650 from german77/lle_album
service: am: Add support for LLE Album Applet
2023-10-10 11:47:13 +02:00
Fernando S 8ac8d703b9 Merge pull request #11686 from liamwhite/trmem
kernel: implement transfer memory
2023-10-10 11:45:56 +02:00
liamwhite 1c1959eaeb Merge pull request #11716 from Squall-Leonhart/Z327444
add Z32, FLOAT, UINT, UINT, UINT, LINEAR to format lookup table
2023-10-08 17:12:00 -04:00
liamwhite c0d152affa Merge pull request #11705 from FearlessTobi/windows-sc
yuzu: Add desktop shortcut support for Windows (continuation of #11344)
2023-10-08 17:11:52 -04:00
liamwhite 85d99f873f Merge pull request #10519 from mdmrk/master
yuzu-qt: Track play time
2023-10-08 17:11:34 -04:00
Liam 21bc2c14bc common: add arm64 native clock 2023-10-08 12:54:23 -04:00
Liam d3997bad9b qt: implement automatic crash dump support 2023-10-08 11:35:53 -04:00
Squall Leonhart 54fa1115a6 add Z32, FLOAT, UINT, UINT, UINT, LINEAR to format lookup table
Should fix and close #11711
2023-10-09 02:13:17 +11:00
Narr the Reg 8347e5cdb9 service: caps: Implement album manager and reorganize service 2023-10-07 20:57:20 -06:00
Narr the Reg dac53b4ba0 externals: stb: Split library into cpp file 2023-10-07 20:57:19 -06:00
Narr the Reg 0bb7990c49 service: Stub multiple functions to increase stability of album applet 2023-10-07 20:57:10 -06:00
FearlessTobi 9ef9ca0927 yuzu: Add desktop shortcut support for Windows
Allows creating desktop shortcuts with icons for yuzu games.

Co-Authored-By: Jeroen van Schijndel <13182141+roenyroeny@users.noreply.github.com>
2023-10-07 21:24:11 -04:00
liamwhite bd42bba71c Merge pull request #11656 from liamwhite/recreate-surface-automatically
vk_present_manager: recreate surface on any surface loss
2023-10-07 12:49:54 -04:00
liamwhite a27f94830a Merge pull request #11677 from Squall-Leonhart/D32FTOABGR8
Implements D32_Float to A8B8G8R8_UNORM format copy
2023-10-07 12:49:48 -04:00
liamwhite bd6f9f1d91 Merge pull request #11630 from Kelebek1/clear_stencil_requires_depth_test
Enable depth test on depthstencil clear path
2023-10-07 12:49:37 -04:00
liamwhite bf15aa093c Merge pull request #11639 from liamwhite/no-program-id-change
loader: don't reassign program ID on npdm reparse
2023-10-07 12:49:32 -04:00
liamwhite 0e9b839b6f Merge pull request #11648 from liamwhite/unicode-nonsense
gdbserver: use numeric character references for unicode
2023-10-07 12:49:27 -04:00
liamwhite 15a5bdd979 Merge pull request #11544 from Kelebek1/reduce_stream_buffer_renderdoc
Allow GPUs without rebar to open multiple RenderDoc captures
2023-10-07 12:49:19 -04:00
liamwhite fc4cde7513 Merge pull request #11669 from german77/settings2
yuzu: Fix custom rtc and mute audio settings
2023-10-07 10:55:21 -04:00
liamwhite ff3859d482 Merge pull request #11688 from Kelebek1/x8d42
Implement X8_D24 pixel format
2023-10-07 10:55:14 -04:00
liamwhite 10de8f2c60 Merge pull request #11684 from Kelebek1/disable_push_descriptor_maxwell
Disable push descriptor for Pascal and older nVidia architectures
2023-10-07 10:54:52 -04:00
Squall Leonhart 51b89fddd0 update shader to confirmed format copy 2023-10-07 18:28:09 +11:00
Kelebek1 f585dec48d Allow GPUs without rebar to open multiple RenderDoc captures 2023-10-06 07:52:06 +01:00
Kelebek1 ad1a9f3d3a Implement X8_D24 format 2023-10-06 00:58:30 +01:00
Liam e797a917a9 kernel: implement transfer memory 2023-10-04 22:32:27 -04:00
Kelebek1 a764f49910 Mark a buffer GPU modified after the buffers are confirmed, do not double synch them 2023-10-05 00:19:11 +01:00
Squall-Leonhart a17cde7b2c lets not convert depth to greyscale since this makes the exhaust and tire smoke light gray/white
tiresmoke should be a darker gray.
2023-10-05 03:14:53 +11:00
Squall-Leonhart a84c928827 Fix CI Formatting check 2023-10-04 19:12:08 +11:00
Squall-Leonhart 9568d3bc60 Implements D32_Float to A8B8G8R8_UNORM format copy
Corrects some visual issues in games such as Disney SpeedStorm
2023-10-04 19:07:05 +11:00
Narr the Reg 0fe935a5de core: Update clocks when settings are saved 2023-10-03 20:20:26 -06:00
Narr the Reg c84c35ac74 yuzu: Fix mute when in background setting 2023-10-03 20:12:06 -06:00
Liam b32940d3ea vk_present_manager: recreate surface on any surface loss 2023-10-02 19:07:18 -04:00
liamwhite 7a0da729b4 Merge pull request #11657 from liamwhite/new-codespell
ci: fix new codespell errors
2023-10-02 18:05:55 -04:00
Liam 0448eb6f0f ci: fix new codespell errors 2023-10-02 18:03:05 -04:00
Narr the Reg c334959440 service: caps: Partially implement IAlbumAccessorService 2023-10-02 12:38:03 -06:00
Narr the Reg 2fa53ec1d9 yuzu: Allow to launch album applet from firmware 2023-10-02 11:29:12 -06:00
Narr the Reg e37ad99f22 externals: Add stb_image and stb_image_resize 2023-10-02 11:29:12 -06:00
liamwhite ff57c66773 Merge pull request #11652 from liamwhite/shutdown-goes-brrr
k_page_table: skip page table clearing on finalization
2023-10-02 11:28:24 -04:00
liamwhite c6d552f29b Merge pull request #11655 from liamwhite/additional-dump-targets
qt: add additional romfs dump targets
2023-10-02 11:28:14 -04:00
Narr the Reg 7f9b64519d Merge pull request #11651 from liamwhite/fsc-creation
fsp-srv: enable auto save data creation on init
2023-10-02 09:22:41 -06:00
Liam d89ef6280c qt: add additional romfs dump targets 2023-10-02 09:23:25 -04:00
Liam 8fb13372c2 k_page_table: skip page table clearing on finalization 2023-10-01 23:38:56 -04:00
Liam f9521f5bd4 fsp-srv: enable auto save data creation on init 2023-10-01 23:33:19 -04:00
liamwhite 53f904b740 Merge pull request #11642 from zhaobot/tx-update-20231001021119
Update translations (2023-10-01)
2023-10-01 19:24:54 -04:00
liamwhite 99e2568304 Merge pull request #11632 from german77/hle_cabinet
service: am: Add support for LLE Cabinet Applet
2023-10-01 19:24:46 -04:00
Liam 38394f36d7 gdbserver: use numeric character references for unicode 2023-10-01 19:22:08 -04:00
Charles Lombardo 26f9d1f122 android: Use application context for all FileUtil functions 2023-10-01 15:56:02 -04:00
Narr the Reg 9cd5c44019 service: nfc: Implement SetRegisterInfoPrivate mii support 2023-10-01 11:38:30 -06:00
Narr the Reg 2687a83f6a service: am: Implement shared buffer
Co-authored-by: Liam <byteslice@airmail.cc>
2023-10-01 11:38:30 -06:00
Narr the Reg 35f25882e0 service: nvnflinger: Implement shared buffer
Co-authored-by: Liam <byteslice@airmail.cc>
2023-10-01 11:38:30 -06:00
Narr the Reg f1f3d490ef service: ldn: Implement lp2p:m and stub IMonitorService 2023-10-01 11:38:30 -06:00
Narr the Reg ae5c134ac6 service: am: Set push in arguments according to the launched applet 2023-10-01 11:38:30 -06:00
Narr the Reg 03d4fffc70 yuzu: Allow to launch cabinet applet from firmware 2023-10-01 11:38:25 -06:00
Fernando S a0d56c855c Merge pull request #11646 from FernandoS27/stop-ignoring-your-mental-health
Query Cache: Fix memory leak.
2023-10-01 17:40:13 +02:00
Fernando Sahmkow 9007d8c8d4 Query Cache: Fix memory leak. 2023-10-01 11:47:14 +02:00
The yuzu Community 65bbf708a8 Update translations (2023-10-01) 2023-10-01 02:11:44 +00:00
Liam 2f0db2708c loader: don't reassign program ID on npdm reparse 2023-09-30 11:35:42 -04:00
Fernando S 184ee2d890 Merge pull request #11493 from merryhime/evt
core_timing: Replace queue with a fibonacci heap
2023-09-29 13:37:19 +02:00
Fernando S d6b3e7f195 Merge pull request #11546 from Kelebek1/core_timing_mutex
Reduce core timing mutex contention
2023-09-29 13:36:57 +02:00
Fernando S 926e24c642 Merge pull request #11622 from liamwhite/qcr-reg1
renderer_vulkan: fix query cache for homebrew
2023-09-29 06:01:18 +02:00
liamwhite 18a396b53f Merge pull request #11631 from Kelebek1/double_focus_change
Don't send a double focus change message
2023-09-28 22:16:47 -04:00
Kelebek1 c62e089260 Don't send a double focus change message 2023-09-28 23:47:10 +01:00
Kelebek1 1a246bf135 Enable depth test on stencil clear path 2023-09-28 21:19:51 +01:00
Liam 7507a7f89f renderer_vulkan: fix query cache for homebrew 2023-09-27 19:11:47 -04:00
Kelebek1 8992a62da4 Reduce core timing mutex contention 2023-09-19 23:10:03 +01:00
Squall-Leonhart 02b897ce27 Reuse part of my previous idea to to use num_levels to check within AdjustMipBlockSize
The partial revert was not enough for Tsukihime, this might do the trick
2023-09-20 03:27:13 +10:00
Squall Leonhart 79f0202045 Partial revert of #10433
The If block in this change was causing some 2D textures to be treated as if their mip 0 was a 3D Slice, this could be ascertained as the same texture viewed from different distances would render fine, but then close up would look like a decoding failure.

It also resulted in some 3D ASTC textures not being scaled appropriate leading to broken graphical effects such as the jagged TOTK recall animation being a circle, as the If block was only accepting the image based on its original info without any adjustments applied.
2023-09-18 23:28:53 +10:00
Merry f70bafff1a core_timing: Attempt to reduce heap sifting 2023-09-16 07:42:45 +01:00
Merry 3ad7eec9de core_timing: Use a fibonacci heap 2023-09-16 07:42:45 +01:00
Merry 7135bdc3bd vcpkg: Add boost.heap 2023-09-16 07:42:45 +01:00
Feng Chen e69eebb14a video_core: Fix d24r8/s8d24 convert shader build error in moltenvk 2023-09-07 18:01:36 +08:00
Feng Chen 0145c89879 video_core: Add missing scissor update when viewport scale offset disable 2023-09-07 18:01:30 +08:00
Liam 667ec28697 Address review comments 2023-08-27 19:45:25 -04:00
Mario 5464423667 yuzu-qt: Track play time 2023-08-26 22:20:19 -04:00
Feng Chen cc4736fa58 video_core: set vertex buffer num to 16, because mvk have when using more than 16 2023-08-23 23:22:55 +08:00
flodavid b881949b6d yuzu: Enable controller interaction in Controller Applet 2023-07-25 19:03:12 +02:00
403 changed files with 56032 additions and 15939 deletions
+9
View File
@@ -8,8 +8,17 @@ ccache -s
BUILD_FLAVOR=mainline
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
fi
cd src/android
chmod +x ./gradlew
./gradlew "assemble${BUILD_FLAVOR}Release" "bundle${BUILD_FLAVOR}Release"
ccache -s
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
rm "${ANDROID_KEYSTORE_FILE}"
fi
-12
View File
@@ -13,15 +13,3 @@ cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/release/app-${BUILD_FLAVOR
"artifacts/${REV_NAME}.apk"
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}Release"/"app-${BUILD_FLAVOR}-release.aab" \
"artifacts/${REV_NAME}.aab"
if [ -n "${ANDROID_KEYSTORE_B64}" ]
then
echo "Signing apk..."
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > ks.jks
apksigner sign --ks ks.jks \
--ks-key-alias "${ANDROID_KEY_ALIAS}" \
--ks-pass env:ANDROID_KEYSTORE_PASS "artifacts/${REV_NAME}.apk"
else
echo "No keystore specified, not signing the APK files."
fi
+1
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
+1 -1
View File
@@ -5,6 +5,6 @@
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`"
ARTIFACTS_DIR="artifacts"
ARTIFACTS_DIR="$PWD/artifacts"
mkdir -p "${ARTIFACTS_DIR}/"
+15 -1
View File
@@ -11,7 +11,7 @@ ccache -s
mkdir build || true && cd build
cmake .. \
-DBoost_USE_STATIC_LIBS=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
@@ -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
@@ -31,6 +32,19 @@ ccache -s
ctest -VV -C Release
# Separate debug symbols from specified executables
for EXE in yuzu; do
EXE_PATH="bin/$EXE"
# Copy debug symbols out
objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
# Add debug link and strip debug symbols
objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
# Overwrite original with stripped copy
mv $EXE_PATH.out $EXE_PATH
done
# Strip debug symbols from all executables
find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
DESTDIR="$PWD/AppDir" ninja install
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
+5
View File
@@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
fi
# Copy debug symbols to artifacts
cd build/bin
tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
cd -
. .ci/scripts/common/post-upload.sh
-1
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 \
+1 -1
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
+4 -1
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
+4
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
+18 -52
View File
@@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
include(DownloadExternals)
include(CMakeDependentOption)
include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
@@ -53,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}")
@@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
DESTINATION "${vvl_lib_path}")
endif()
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON)
set(LLVM_VERSION "15.0.6")
# Note: even though libcxx and libcxxabi have separate releases on the project page,
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
# must fetch the source release for the entire llvm tree.
FetchContent_Declare(llvm
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
TLS_VERIFY TRUE
)
FetchContent_MakeAvailable(llvm)
# libcxx has support for most of the range library, but it's gated behind a flag:
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
# Disable standard header inclusion
set(ANDROID_STL "none")
# libcxxabi
set(LIBCXXABI_INCLUDE_TESTS OFF)
set(LIBCXXABI_ENABLE_SHARED FALSE)
set(LIBCXXABI_ENABLE_STATIC TRUE)
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
link_libraries(cxxabi_static)
# libcxx
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
set(LIBCXX_CXX_ABI "libcxxabi")
set(LIBCXX_INCLUDE_TESTS OFF)
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
set(LIBCXX_INCLUDE_DOCS OFF)
set(LIBCXX_ENABLE_SHARED FALSE)
set(LIBCXX_ENABLE_STATIC TRUE)
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
link_libraries(cxx_static cxx-headers)
endif()
if (YUZU_USE_BUNDLED_VCPKG)
@@ -179,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()
@@ -329,11 +286,12 @@ find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader)
find_package(LLVM 17 MODULE COMPONENTS Demangle)
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
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)
@@ -400,6 +358,9 @@ function(set_yuzu_qt_components)
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif()
if (USE_DISCORD_PRESENCE)
list(APPEND YUZU_QT_COMPONENTS2 Network)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components)
@@ -587,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)
@@ -606,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()
+31
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
)
+1 -1
View File
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# Gets a UTC timstamp and sets the provided variable to it
# Gets a UTC timestamp and sets the provided variable to it
function(get_timestamp _var)
string(TIMESTAMP timestamp UTC)
set(${_var} "${timestamp}" PARENT_SCOPE)
+7273
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+671 -494
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+625 -457
View File
File diff suppressed because it is too large Load Diff
+690 -522
View File
File diff suppressed because it is too large Load Diff
+7288
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+613 -445
View File
File diff suppressed because it is too large Load Diff
+654 -486
View File
File diff suppressed because it is too large Load Diff
+608 -442
View File
File diff suppressed because it is too large Load Diff
+612 -444
View File
File diff suppressed because it is too large Load Diff
+640 -471
View File
File diff suppressed because it is too large Load Diff
+608 -442
View File
File diff suppressed because it is too large Load Diff
+697 -525
View File
File diff suppressed because it is too large Load Diff
+695 -524
View File
File diff suppressed because it is too large Load Diff
+608 -442
View File
File diff suppressed because it is too large Load Diff
+607 -441
View File
File diff suppressed because it is too large Load Diff
+608 -442
View File
File diff suppressed because it is too large Load Diff
+608 -442
View File
File diff suppressed because it is too large Load Diff
+611 -443
View File
File diff suppressed because it is too large Load Diff
+611 -443
View File
File diff suppressed because it is too large Load Diff
+618 -450
View File
File diff suppressed because it is too large Load Diff
+610 -442
View File
File diff suppressed because it is too large Load Diff
+1
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
+4
View File
@@ -120,6 +120,10 @@ QWidget#connectedControllers {
background: transparent;
}
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
+4
View File
@@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
background: transparent;
}
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
+4
View File
@@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
background: transparent;
}
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported,
QWidget#controllersSupported,
QWidget#controllerSupported1,
+110
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()
@@ -171,6 +175,10 @@ endif()
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()
+1 -1
Vendored Submodule
+1
Submodule externals/breakpad added at c89f9dddc7
-7
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 -1
View File
@@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
set(CAN_BUILD_NX_TZDB false)
endif()
set(NX_TZDB_VERSION "220816")
set(NX_TZDB_VERSION "221202")
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
Vendored Submodule
+1
Submodule externals/opus added at 101a71e03b
-259
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)
Submodule externals/opus/opus deleted from ad8fe90db7
+7987
View File
File diff suppressed because it is too large Load Diff
+2634
View File
File diff suppressed because it is too large Load Diff
+1724
View File
File diff suppressed because it is too large Load Diff
+12 -8
View File
@@ -27,7 +27,7 @@ android {
namespace = "org.yuzu.yuzu_emu"
compileSdkVersion = "android-34"
ndkVersion = "25.2.9519653"
ndkVersion = "26.1.10909125"
buildFeatures {
viewBinding = true
@@ -47,6 +47,10 @@ android {
jniLibs.useLegacyPackaging = true
}
androidResources {
generateLocaleConfig = true
}
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
@@ -203,23 +207,23 @@ ktlint {
}
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.recyclerview:recyclerview:1.3.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.6.0")
implementation("androidx.fragment:fragment-ktx:1.6.1")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference:1.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}
@@ -26,9 +26,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:supportsRtl="true"
android:isGame="true"
android:appCategory="game"
android:localeConfig="@xml/locales_config"
android:banner="@drawable/tv_banner"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true">
@@ -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
@@ -15,13 +16,9 @@ import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.FileUtil.exists
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
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
@@ -72,43 +69,61 @@ 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 {
openContentUri(appContext, path, openmode)
FileUtil.openContentUri(path, openmode)
}
}
@Keep
@JvmStatic
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
} else {
getFileSize(appContext, path)
FileUtil.getFileSize(path)
}
}
@Keep
@JvmStatic
fun exists(path: String?): Boolean {
return if (isNativePath(path!!)) {
return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path)
} else {
exists(appContext, 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 {
isDirectory(appContext, path)
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
*/
@@ -219,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)
/**
@@ -263,7 +252,7 @@ object NativeLibrary {
external fun reloadKeys(): Boolean
external fun initializeEmulation()
external fun initializeSystem()
external fun defaultCPUCore(): Int
@@ -297,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).
*/
@@ -478,12 +462,12 @@ object NativeLibrary {
}
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
Log.verbose("[NativeLibrary] Registering EmulationActivity.")
Log.debug("[NativeLibrary] Registering EmulationActivity.")
sEmulationActivity = WeakReference(emulationActivity)
}
fun clearEmulationActivity() {
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
sEmulationActivity.clear()
}
@@ -521,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
*/
@@ -47,7 +47,7 @@ class YuzuApplication : Application() {
application = this
documentsTree = DocumentsTree()
DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters(applicationContext)
GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo()
createNotificationChannels()
@@ -45,9 +45,9 @@ 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.Log
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
@@ -57,17 +57,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"
@@ -82,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
override fun onCreate(savedInstanceState: Bundle?) {
Log.gameLaunched = true
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
@@ -95,8 +95,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 +103,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 +159,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
super.onResume()
nfcReader.startScanning()
startMotionSensorListener()
InputHandler.updateControllerIds()
buildPictureInPictureParams()
}
@@ -195,7 +193,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 +208,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return true
}
return inputHandler.dispatchGenericMotionEvent(event)
return InputHandler.dispatchGenericMotionEvent(event)
}
override fun onSensorChanged(event: SensorEvent) {
@@ -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
)
)
}
}
}
@@ -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)
}
}
}
@@ -0,0 +1,117 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
class DriverAdapter(private val driverViewModel: DriverViewModel) :
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
AsyncDifferConfig.Builder(DiffCallback()).build()
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
val binding =
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DriverViewHolder(binding)
}
override fun getItemCount(): Int = currentList.size
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
holder.bind(currentList[position])
private fun onSelectDriver(position: Int) {
driverViewModel.setSelectedDriverIndex(position)
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
}
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
if (driverViewModel.selectedDriver > position) {
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
}
if (GpuDriverHelper.customDriverData == driverData.second) {
driverViewModel.setSelectedDriverIndex(0)
}
driverViewModel.driversToDelete.add(driverData.first)
driverViewModel.removeDriver(driverData)
notifyItemRemoved(position)
notifyItemChanged(driverViewModel.selectedDriver)
}
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
RecyclerView.ViewHolder(binding.root) {
private lateinit var driverData: Pair<String, GpuDriverMetadata>
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
this.driverData = driverData
val driver = driverData.second
binding.apply {
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
root.setOnClickListener {
onSelectDriver(bindingAdapterPosition)
}
buttonDelete.setOnClickListener {
onDeleteDriver(driverData, bindingAdapterPosition)
}
// Delay marquee by 3s
title.postDelayed(
{
title.isSelected = true
title.ellipsize = TextUtils.TruncateAt.MARQUEE
version.isSelected = true
version.ellipsize = TextUtils.TruncateAt.MARQUEE
description.isSelected = true
description.ellipsize = TextUtils.TruncateAt.MARQUEE
},
3000
)
if (driver.name == null) {
title.setText(R.string.system_gpu_driver)
description.text = ""
version.text = ""
version.visibility = View.GONE
description.visibility = View.GONE
buttonDelete.visibility = View.GONE
} else {
title.text = driver.name
version.text = driver.version
description.text = driver.description
version.visibility = View.VISIBLE
description.visibility = View.VISIBLE
buttonDelete.visibility = View.VISIBLE
}
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
override fun areItemsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.first == newItem.first
}
override fun areContentsTheSame(
oldItem: Pair<String, GpuDriverMetadata>,
newItem: Pair<String, GpuDriverMetadata>
): Boolean {
return oldItem.second == newItem.second
}
}
}
@@ -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 {
@@ -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
}
}
@@ -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
}
}
@@ -0,0 +1,186 @@
// 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.activity.result.contract.ActivityResultContracts
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.File
import java.io.IOException
class DriverManagerFragment : Fragment() {
private var _binding: FragmentDriverManagerBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel 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 = FragmentDriverManagerBinding.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)
if (!driverViewModel.isInteractionAllowed) {
DriversLoadingDialogFragment().show(
childFragmentManager,
DriversLoadingDialogFragment.TAG
)
}
binding.toolbarDrivers.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
binding.buttonInstall.setOnClickListener {
getDriver.launch(arrayOf("application/zip"))
}
binding.listDrivers.apply {
layoutManager = GridLayoutManager(
requireContext(),
resources.getInteger(R.integer.grid_columns)
)
adapter = DriverAdapter(driverViewModel)
}
viewLifecycleOwner.lifecycleScope.apply {
launch {
driverViewModel.driverList.collectLatest {
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
}
}
launch {
driverViewModel.newDriverInstalled.collect {
if (_binding != null && it) {
(binding.listDrivers.adapter as DriverAdapter).apply {
notifyItemChanged(driverViewModel.previouslySelectedDriver)
notifyItemChanged(driverViewModel.selectedDriver)
driverViewModel.setNewDriverInstalled(false)
}
}
}
}
}
setInsets()
}
// Start installing requested driver
override fun onStop() {
super.onStop()
driverViewModel.onCloseDriverManager()
}
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.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.toolbarDrivers.layoutParams = mlpAppBar
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
mlplistDrivers.leftMargin = leftInsets
mlplistDrivers.rightMargin = rightInsets
binding.listDrivers.layoutParams = mlplistDrivers
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
val mlpFab =
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
mlpFab.leftMargin = leftInsets + fabSpacing
mlpFab.rightMargin = rightInsets + fabSpacing
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
binding.buttonInstall.layoutParams = mlpFab
binding.listDrivers.updatePadding(
bottom = barInsets.bottom +
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
)
windowInsets
}
private val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
requireActivity(),
R.string.installing_driver,
false
) {
val driverPath =
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip
try {
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
throw IOException("Driver failed validation!")
}
} catch (_: IOException) {
if (driverFile.exists()) {
driverFile.delete()
}
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.addDriver(Pair(driverPath, driverData))
driverViewModel.setNewDriverInstalled(true)
}
return@newInstance Any()
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}
@@ -0,0 +1,75 @@
// 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.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.DriverViewModel
class DriversLoadingDialogFragment : DialogFragment() {
private val driverViewModel: DriverViewModel by activityViewModels()
private lateinit var binding: DialogProgressBarBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogProgressBarBinding.inflate(layoutInflater)
binding.progressBar.isIndeterminate = true
isCancelable = false
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.loading)
.setView(binding.root)
.create()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = binding.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.areDriversLoading.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect { checkForDismiss() }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
}
}
}
}
private fun checkForDismiss() {
if (driverViewModel.isInteractionAllowed) {
dismiss()
}
}
companion object {
const val TAG = "DriversLoadingDialogFragment"
}
}
@@ -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
@@ -39,6 +41,7 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
@@ -50,6 +53,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay
@@ -70,6 +74,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false
@@ -153,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
@@ -281,6 +312,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.surfaceInputOverlay)
ViewUtils.hideView(binding.loadingIndicator)
emulationState.updateSurface()
// Setup overlay
updateShowFpsOverlay()
}
@@ -299,6 +332,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
driverViewModel.isDriverReady.collect {
if (it && !emulationState.isRunning) {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
}
}
}
}
}
@@ -332,17 +380,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
override fun onResume() {
super.onResume()
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause()
@@ -769,6 +806,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
@Synchronized
fun updateSurface() {
if (surface != null) {
NativeLibrary.surfaceChanged(surface)
}
}
@Synchronized
fun clearSurface() {
if (surface == null) {
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@@ -27,21 +26,23 @@ 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 com.google.android.material.dialog.MaterialAlertDialogBuilder
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
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null
@@ -50,6 +51,7 @@ class HomeSettingsFragment : Fragment() {
private lateinit var mainActivity: MainActivity
private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -85,35 +87,31 @@ class HomeSettingsFragment : Fragment() {
)
add(
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open,
{ openFileManager() }
)
)
add(
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette,
{
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
Settings.MenuTag.SECTION_THEME
)
binding.root.findNavController().navigate(action)
}
)
)
add(
HomeSetting(
R.string.install_gpu_driver,
R.string.gpu_driver_manager,
R.string.install_gpu_driver_description,
R.drawable.ic_exit,
{ driverInstaller() },
R.drawable.ic_build,
{
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
},
{ GpuDriverHelper.supportsCustomDriverLoading() },
R.string.custom_driver_not_supported,
R.string.custom_driver_not_supported_description
R.string.custom_driver_not_supported_description,
driverViewModel.selectedDriverMetadata
)
)
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(
@@ -151,6 +149,28 @@ class HomeSettingsFragment : Fragment() {
{ shareLog() }
)
)
add(
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open,
{ openFileManager() }
)
)
add(
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette,
{
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
Settings.MenuTag.SECTION_THEME
)
binding.root.findNavController().navigate(action)
}
)
)
add(
HomeSetting(
R.string.about,
@@ -182,7 +202,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,
@@ -292,44 +313,32 @@ class HomeSettingsFragment : Fragment() {
}
}
private fun driverInstaller() {
// Get the driver name for the dialog message.
var driverName = GpuDriverHelper.customDriverName
if (driverName == null) {
driverName = getString(R.string.system_gpu_driver)
}
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.select_gpu_driver_title))
.setMessage(driverName)
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
GpuDriverHelper.installDefaultDriver(requireContext())
Toast.makeText(
requireContext(),
R.string.select_gpu_driver_use_default,
Toast.LENGTH_SHORT
).show()
}
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
mainActivity.getDriver.launch(arrayOf("application/zip"))
}
.show()
}
// Share the current log if we just returned from a game but share the old log
// if we just started the app and the old log exists.
private fun shareLog() {
val file = DocumentFile.fromSingleUri(
val currentLog = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt"
)
)!!
if (file.exists()) {
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, FileUtil.TEXT_PLAIN)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
val oldLog = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt"
)
)!!
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (!Log.gameLaunched && oldLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
} else if (currentLog.exists()) {
intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
} else {
Toast.makeText(
@@ -10,8 +10,8 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
@@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
requireActivity().supportFragmentManager,
MessageDialogFragment.TAG
)
else -> {
// Do nothing
}
}
taskViewModel.clear()
}
@@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
private const val CANCELLABLE = "Cancellable"
fun newInstance(
activity: AppCompatActivity,
activity: FragmentActivity,
titleId: Int,
cancellable: Boolean = false,
task: () -> Any
@@ -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) {
@@ -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)
}
@@ -0,0 +1,158 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
import java.io.BufferedOutputStream
import java.io.File
class DriverViewModel : ViewModel() {
private val _areDriversLoading = MutableStateFlow(false)
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
private val _isDriverReady = MutableStateFlow(true)
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
private val _isDeletingDrivers = MutableStateFlow(false)
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
var previouslySelectedDriver = 0
var selectedDriver = -1
private val _selectedDriverMetadata =
MutableStateFlow(
GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
)
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
private val _newDriverInstalled = MutableStateFlow(false)
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
val driversToDelete = mutableListOf<String>()
val isInteractionAllowed
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
init {
_areDriversLoading.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
val drivers = GpuDriverHelper.getDrivers()
val currentDriverMetadata = GpuDriverHelper.customDriverData
for (i in drivers.indices) {
if (drivers[i].second == currentDriverMetadata) {
setSelectedDriverIndex(i)
break
}
}
// If a user had installed a driver before the manager was implemented, this zips
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
// be indexed and exported as expected.
if (selectedDriver == -1) {
val driverToSave =
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
driverToSave.createNewFile()
FileUtil.zipFromInternalStorage(
File(GpuDriverHelper.driverInstallationPath!!),
GpuDriverHelper.driverInstallationPath!!,
BufferedOutputStream(driverToSave.outputStream())
)
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
setSelectedDriverIndex(drivers.size - 1)
}
_driverList.value = drivers
_areDriversLoading.value = false
}
}
}
fun setSelectedDriverIndex(value: Int) {
if (selectedDriver != -1) {
previouslySelectedDriver = selectedDriver
}
selectedDriver = value
}
fun setNewDriverInstalled(value: Boolean) {
_newDriverInstalled.value = value
}
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
if (driverIndex == -1) {
setSelectedDriverIndex(_driverList.value.size)
_driverList.value.add(driverData)
_selectedDriverMetadata.value = driverData.second.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
} else {
setSelectedDriverIndex(driverIndex)
}
}
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
_driverList.value.remove(driverData)
}
fun onCloseDriverManager() {
_isDeletingDrivers.value = true
viewModelScope.launch {
withContext(Dispatchers.IO) {
driversToDelete.forEach {
val driver = File(it)
if (driver.exists()) {
driver.delete()
}
}
driversToDelete.clear()
_isDeletingDrivers.value = false
}
}
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
return
}
_isDriverReady.value = false
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (selectedDriver == 0) {
GpuDriverHelper.installDefaultDriver()
setDriverReady()
return@withContext
}
val driverToInstall = File(driverList.value[selectedDriver].first)
if (driverToInstall.exists()) {
GpuDriverHelper.installCustomDriver(driverToInstall)
} else {
GpuDriverHelper.installDefaultDriver()
}
setDriverReady()
}
}
}
private fun setDriverReady() {
_isDriverReady.value = true
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
}
}
@@ -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
}
@@ -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
@@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.CoroutineScope
import java.io.File
import java.io.FilenameFilter
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"prod.keys"
)
) != null
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
@@ -407,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) {
@@ -446,11 +443,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage(
applicationContext,
result,
dstPath,
"key_retail.bin"
)
) != null
) {
if (NativeLibrary.reloadKeys()) {
Toast.makeText(
@@ -469,59 +465,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
contentResolver.takePersistableUriPermission(
result,
takeFlags
)
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
progressBinding.progressBar.isIndeterminate = true
val installationDialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.installing_driver)
.setView(progressBinding.root)
.show()
lifecycleScope.launch {
withContext(Dispatchers.IO) {
// Ignore file exceptions when a user selects an invalid zip
try {
GpuDriverHelper.installCustomDriver(applicationContext, result)
} catch (_: IOException) {
}
withContext(Dispatchers.Main) {
installationDialog.dismiss()
val driverName = GpuDriverHelper.customDriverName
if (driverName != null) {
Toast.makeText(
applicationContext,
getString(
R.string.select_gpu_driver_install_success,
driverName
),
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
applicationContext,
R.string.select_gpu_driver_error,
Toast.LENGTH_LONG
).show()
}
}
}
}
}
val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> ->
@@ -706,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)
@@ -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
}
}
@@ -15,7 +15,7 @@ object DirectoryInitialization {
fun start() {
if (!areDirectoriesReady) {
initializeInternalStorage()
NativeLibrary.initializeEmulation()
NativeLibrary.initializeSystem()
areDirectoriesReady = true
}
}
@@ -7,7 +7,6 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import java.io.File
import java.util.*
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree {
@@ -22,7 +21,7 @@ class DocumentsTree {
fun openContentUri(filepath: String, openMode: String?): Int {
val node = resolvePath(filepath) ?: return -1
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
return FileUtil.openContentUri(node.uri.toString(), openMode)
}
fun getFileSize(filepath: String): Long {
@@ -30,7 +29,7 @@ class DocumentsTree {
return if (node == null || node.isDirectory) {
0
} else {
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
FileUtil.getFileSize(node.uri.toString())
}
}
@@ -43,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
@@ -67,7 +83,7 @@ class DocumentsTree {
* @param parent parent node of this level
*/
private fun structTree(parent: DocumentsNode) {
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
val documents = FileUtil.listFiles(parent.uri!!)
for (document in documents) {
val node = DocumentsNode(document)
node.parent = parent
@@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.provider.DocumentsContract
@@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.StateFlow
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLDecoder
@@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
import java.util.zip.ZipOutputStream
object FileUtil {
@@ -29,6 +29,8 @@ object FileUtil {
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val TEXT_PLAIN = "text/plain"
private val context get() = YuzuApplication.appContext
/**
* Create a file from directory with filename.
* @param context Application context
@@ -36,11 +38,11 @@ object FileUtil {
* @param filename file display name.
* @return boolean
*/
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
fun createFile(directory: String?, filename: String): DocumentFile? {
var decodedFilename = filename
try {
val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
var mimeType = APPLICATION_OCTET_STREAM
if (decodedFilename.endsWith(".txt")) {
@@ -56,16 +58,15 @@ object FileUtil {
/**
* Create a directory from directory with filename.
* @param context Application context
* @param directory parent path for directory.
* @param directoryName directory display name.
* @return boolean
*/
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
var decodedDirectoryName = directoryName
try {
val directoryUri = Uri.parse(directory)
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
val isExist = parent.findFile(decodedDirectoryName)
return isExist ?: parent.createDirectory(decodedDirectoryName)
@@ -77,13 +78,12 @@ object FileUtil {
/**
* Open content uri and return file descriptor to JNI.
* @param context Application context
* @param path Native content uri path
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
* @return file descriptor
*/
@JvmStatic
fun openContentUri(context: Context, path: String, openMode: String?): Int {
fun openContentUri(path: String, openMode: String?): Int {
try {
val uri = Uri.parse(path)
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
@@ -103,11 +103,10 @@ object FileUtil {
/**
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
* This function will be faster than DoucmentFile.listFiles
* @param context Application context
* @param uri Directory uri.
* @return CheapDocument lists.
*/
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@@ -145,7 +144,7 @@ object FileUtil {
* @param path Native content uri path
* @return bool
*/
fun exists(context: Context, path: String?): Boolean {
fun exists(path: String?, suppressLog: Boolean = false): Boolean {
var c: Cursor? = null
try {
val mUri = Uri.parse(path)
@@ -153,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)
}
@@ -165,7 +166,7 @@ object FileUtil {
* @param path content uri path
* @return bool
*/
fun isDirectory(context: Context, path: String): Boolean {
fun isDirectory(path: String): Boolean {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_MIME_TYPE
@@ -210,10 +211,10 @@ object FileUtil {
return filename
}
fun getFilesName(context: Context, path: String): Array<String> {
fun getFilesName(path: String): Array<String> {
val uri = Uri.parse(path)
val files: MutableList<String> = ArrayList()
for (file in listFiles(context, uri)) {
for (file in listFiles(uri)) {
files.add(file.filename)
}
return files.toTypedArray()
@@ -225,7 +226,7 @@ object FileUtil {
* @return long file size
*/
@JvmStatic
fun getFileSize(context: Context, path: String): Long {
fun getFileSize(path: String): Long {
val resolver = context.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_SIZE
@@ -245,44 +246,38 @@ object FileUtil {
return size
}
/**
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
* overwrite any pre-existing files.
*
* @param sourceUri The [Uri] to copy data from
* @param destinationParentPath Destination directory
* @param destinationFilename Optionally renames the file once copied
*/
fun copyUriToInternalStorage(
context: Context,
sourceUri: Uri?,
sourceUri: Uri,
destinationParentPath: String,
destinationFilename: String
): Boolean {
var input: InputStream? = null
var output: FileOutputStream? = null
destinationFilename: String = ""
): File? =
try {
input = context.contentResolver.openInputStream(sourceUri!!)
output = FileOutputStream("$destinationParentPath/$destinationFilename")
val buffer = ByteArray(1024)
var len: Int
while (input!!.read(buffer).also { len = it } != -1) {
output.write(buffer, 0, len)
val fileName =
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
val destinationFile = File("$destinationParentPath$fileName")
if (destinationFile.exists()) {
destinationFile.delete()
}
output.flush()
return true
} catch (e: Exception) {
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
} finally {
if (input != null) {
try {
input.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
}
}
if (output != null) {
try {
output.close()
} catch (e: IOException) {
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
}
destinationFile.outputStream().use { fos ->
inputStream.use { it.copyTo(fos) }
}
destinationFile
} catch (e: IOException) {
null
} catch (e: NullPointerException) {
null
}
return false
}
/**
* Extracts the given zip file into the given directory.
@@ -368,4 +363,12 @@ object FileUtil {
return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase()
}
@Throws(IOException::class)
fun getStringFromFile(file: File): String =
String(file.readBytes(), StandardCharsets.UTF_8)
@Throws(IOException::class)
fun getStringFromInputStream(stream: InputStream): String =
String(stream.readBytes(), StandardCharsets.UTF_8)
}
@@ -30,7 +30,7 @@ object GameHelper {
// Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys()
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
// Cache list of games found on disk
val serializedGames = mutableSetOf<String>()
@@ -58,7 +58,7 @@ object GameHelper {
if (it.isDirectory) {
addGamesRecursive(
games,
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
FileUtil.listFiles(it.uri),
depth - 1
)
} else {
@@ -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) {
@@ -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,
@@ -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()
}
@@ -3,64 +3,33 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri
import android.os.Build
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
import org.yuzu.yuzu_emu.YuzuApplication
import java.util.zip.ZipException
import java.util.zip.ZipFile
object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
private var fileRedirectionPath: String? = null
private var driverInstallationPath: String? = null
var driverInstallationPath: String? = null
private var hookLibPath: String? = null
@Throws(IOException::class)
private fun unzip(zipFilePath: String, destDir: String) {
val dir = File(destDir)
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
// Create output directory if it doesn't exist
if (!dir.exists()) dir.mkdirs()
// Unpack the files.
val inputStream = FileInputStream(zipFilePath)
val zis = ZipInputStream(BufferedInputStream(inputStream))
val buffer = ByteArray(1024)
var ze = zis.nextEntry
while (ze != null) {
val newFile = File(destDir, ze.name)
val canonicalPath = newFile.canonicalPath
if (!canonicalPath.startsWith(destDir + ze.name)) {
throw SecurityException("Zip file attempted path traversal! " + ze.name)
}
newFile.parentFile!!.mkdirs()
val fos = FileOutputStream(newFile)
var len: Int
while (zis.read(buffer).also { len = it } > 0) {
fos.write(buffer, 0, len)
}
fos.close()
zis.closeEntry()
ze = zis.nextEntry
}
zis.closeEntry()
}
fun initializeDriverParameters(context: Context) {
fun initializeDriverParameters() {
try {
// Initialize the file redirection directory.
fileRedirectionPath =
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
fileRedirectionPath = YuzuApplication.appContext
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
// Initialize the driver installation directory.
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
driverInstallationPath = YuzuApplication.appContext
.filesDir.canonicalPath + "/gpu_driver/"
} catch (e: IOException) {
throw RuntimeException(e)
}
@@ -69,68 +38,169 @@ object GpuDriverHelper {
initializeDirectories()
// Initialize hook libraries directory.
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
// Initialize GPU driver.
NativeLibrary.initializeGpuDriver(
hookLibPath,
driverInstallationPath,
customDriverLibraryName,
customDriverData.libraryName,
fileRedirectionPath
)
}
fun installDefaultDriver(context: Context) {
// Removing the installed driver will result in the backend using the default system driver.
val driverInstallationDir = File(driverInstallationPath!!)
deleteRecursive(driverInstallationDir)
initializeDriverParameters(context)
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
val driverZips = File(driverStoragePath).listFiles()
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
driverZips
?.mapNotNull {
val metadata = getMetadataFromZip(it)
metadata.name?.let { _ -> Pair(it.path, metadata) }
}
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
?.distinct()
?.toMutableList() ?: mutableListOf()
// TODO: Get system driver information
drivers.add(0, Pair("", GpuDriverMetadata()))
return drivers
}
fun installCustomDriver(context: Context, driverPathUri: Uri?) {
fun installDefaultDriver() {
// Removing the installed driver will result in the backend using the default system driver.
File(driverInstallationPath!!).deleteRecursively()
initializeDriverParameters()
}
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
// Ensure we have directories.
initializeDirectories()
// Copy the zip file URI to user data
val copiedFile =
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
// Validate driver
val metadata = getMetadataFromZip(copiedFile)
if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
return true
}
/**
* Copies driver zip into user data directory so that it can be exported along with
* other user data and also unzipped into the installation directory
*/
fun installCustomDriver(driverUri: Uri): Boolean {
// Revert to system default in the event the specified driver is bad.
installDefaultDriver(context)
installDefaultDriver()
// Ensure we have directories.
initializeDirectories()
// Copy the zip file URI into our private storage.
copyUriToInternalStorage(
context,
driverPathUri,
driverInstallationPath!!,
DRIVER_INTERNAL_FILENAME
)
// Copy the zip file URI to user data
val copiedFile =
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
// Validate driver
val metadata = getMetadataFromZip(copiedFile)
if (metadata.name == null) {
copiedFile.delete()
return false
}
if (metadata.minApi > Build.VERSION.SDK_INT) {
copiedFile.delete()
return false
}
// Unzip the driver.
try {
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
FileUtil.unzipToInternalStorage(
BufferedInputStream(copiedFile.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) {
return
return false
}
// Initialize the driver parameters.
initializeDriverParameters(context)
initializeDriverParameters()
return true
}
/**
* Unzips driver into installation directory
*/
fun installCustomDriver(driver: File): Boolean {
// Revert to system default in the event the specified driver is bad.
installDefaultDriver()
// Ensure we have directories.
initializeDirectories()
// Validate driver
val metadata = getMetadataFromZip(driver)
if (metadata.name == null) {
driver.delete()
return false
}
// Unzip the driver to the private installation directory
try {
FileUtil.unzipToInternalStorage(
BufferedInputStream(driver.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) {
return false
}
// Initialize the driver parameters.
initializeDriverParameters()
return true
}
/**
* Takes in a zip file and reads the meta.json file for presentation to the UI
*
* @param driver Zip containing driver and meta.json file
* @return A non-null [GpuDriverMetadata] instance that may have null members
*/
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
try {
ZipFile(driver).use { zf ->
val entries = zf.entries()
while (entries.hasMoreElements()) {
val entry = entries.nextElement()
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
zf.getInputStream(entry).use {
return GpuDriverMetadata(it, entry.size)
}
}
}
}
} catch (_: ZipException) {
}
return GpuDriverMetadata()
}
external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name.
val customDriverName: String?
get() {
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.name
}
val customDriverData: GpuDriverMetadata
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
// Parse the custom driver metadata to retrieve the library name.
private val customDriverLibraryName: String?
get() {
// Parse the custom driver metadata to retrieve the library name.
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.libraryName
}
private fun initializeDirectories() {
fun initializeDirectories() {
// Ensure the file redirection directory exists.
val fileRedirectionDir = File(fileRedirectionPath!!)
if (!fileRedirectionDir.exists()) {
@@ -141,14 +211,10 @@ object GpuDriverHelper {
if (!driverInstallationDir.exists()) {
driverInstallationDir.mkdirs()
}
}
private fun deleteRecursive(fileOrDirectory: File) {
if (fileOrDirectory.isDirectory) {
for (child in fileOrDirectory.listFiles()!!) {
deleteRecursive(child)
}
// Ensure the driver storage directory exists
val driverStorageDirectory = File(driverStoragePath)
if (!driverStorageDirectory.exists()) {
driverStorageDirectory.mkdirs()
}
fileOrDirectory.delete()
}
}
@@ -4,29 +4,29 @@
package org.yuzu.yuzu_emu.utils
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.InputStream
class GpuDriverMetadata(metadataFilePath: String) {
var name: String? = null
var description: String? = null
var author: String? = null
var vendor: String? = null
var driverVersion: String? = null
var minApi = 0
var libraryName: String? = null
class GpuDriverMetadata {
/**
* Tries to get driver metadata information from a meta.json [File]
*
* @param metadataFile meta.json file provided with a GPU driver
*/
constructor(metadataFile: File) {
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
return
}
init {
try {
val json = JSONObject(getStringFromFile(metadataFilePath))
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
name = json.getString("name")
description = json.getString("description")
author = json.getString("author")
vendor = json.getString("vendor")
driverVersion = json.getString("driverVersion")
version = json.getString("driverVersion")
minApi = json.getInt("minApi")
libraryName = json.getString("libraryName")
} catch (e: JSONException) {
@@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
}
}
companion object {
@Throws(IOException::class)
private fun getStringFromFile(filePath: String): String {
val path = Paths.get(filePath)
val bytes = Files.readAllBytes(path)
return String(bytes, StandardCharsets.UTF_8)
/**
* Tries to get driver metadata information from an input stream that's intended to be
* from a zip file
*
* @param metadataStream ZipEntry input stream
* @param size Size of the file in bytes
*/
constructor(metadataStream: InputStream, size: Long) {
if (size > MAX_META_SIZE_BYTES) {
return
}
try {
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
name = json.getString("name")
description = json.getString("description")
author = json.getString("author")
vendor = json.getString("vendor")
version = json.getString("driverVersion")
minApi = json.getInt("minApi")
libraryName = json.getString("libraryName")
} catch (e: JSONException) {
// JSON is malformed, ignore and treat as unsupported metadata.
} catch (e: IOException) {
// File is inaccessible, ignore and treat as unsupported metadata.
}
}
/**
* Creates an empty metadata instance
*/
constructor()
override fun equals(other: Any?): Boolean {
if (other !is GpuDriverMetadata) {
return false
}
return other.name == name &&
other.description == description &&
other.author == author &&
other.vendor == vendor &&
other.version == version &&
other.minApi == minApi &&
other.libraryName == libraryName
}
override fun hashCode(): Int {
var result = name?.hashCode() ?: 0
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + (author?.hashCode() ?: 0)
result = 31 * result + (vendor?.hashCode() ?: 0)
result = 31 * result + (version?.hashCode() ?: 0)
result = 31 * result + minApi
result = 31 * result + (libraryName?.hashCode() ?: 0)
return result
}
override fun toString(): String =
"""
Name - $name
Description - $description
Author - $author
Vendor - $vendor
Version - $version
Min API - $minApi
Library Name - $libraryName
""".trimMargin().trimIndent()
var name: String? = null
var description: String? = null
var author: String? = null
var vendor: String? = null
var version: String? = null
var minApi = 0
var libraryName: String? = null
companion object {
private const val MAX_META_SIZE_BYTES = 500000
}
}
@@ -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] ?: 0
}
// 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
}
}
@@ -3,38 +3,17 @@
package org.yuzu.yuzu_emu.utils
import android.util.Log
import org.yuzu.yuzu_emu.BuildConfig
/**
* Contains methods that call through to [android.util.Log], but
* with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log
* levels in release builds.
*/
object Log {
private const val TAG = "Yuzu Frontend"
// Tracks whether we should share the old log or the current log
var gameLaunched = false
fun verbose(message: String) {
if (BuildConfig.DEBUG) {
Log.v(TAG, message)
}
}
external fun debug(message: String)
fun debug(message: String) {
if (BuildConfig.DEBUG) {
Log.d(TAG, message)
}
}
external fun warning(message: String)
fun info(message: String) {
Log.i(TAG, message)
}
external fun info(message: String)
fun warning(message: String) {
Log.w(TAG, message)
}
external fun error(message: String)
fun error(message: String) {
Log.e(TAG, message)
}
external fun critical(message: String)
}
@@ -14,8 +14,11 @@ add_library(yuzu-android SHARED
id_cache.cpp
id_cache.h
native.cpp
native.h
native_config.cpp
uisettings.cpp
game_metadata.cpp
native_log.cpp
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
@@ -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"
File diff suppressed because it is too large Load Diff

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