Compare commits

...

245 Commits

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

Signed-off-by: flodavid <fl.david.53@gmail.com>
2023-10-17 23:19:11 +02:00
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
fa56518f20 Merge pull request #11747 from Kelebek1/image_alias_sample_names
Small things
2023-10-17 11:48:57 -04:00
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
d9dde7e6f3 renderer_vulkan: add locks to avoid scheduler flushes from CPU 2023-10-17 10:00:25 -04:00
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
c73bb33ff1 service: hle: Allow to access read buffer A and X directly 2023-10-16 23:36:46 -06:00
bcce184e60 service: acc: Implement functions needed for profile select (#11653) 2023-10-17 05:12:55 +02:00
326ebbb2fa Changes based on hardware tests
Removes unnecessary d32f to bgra shader and blit functions,
update vk_texture_cache to use abgr shader for d32f to BGRA formats
updates  abgr to d32f shader to comply with hardware tests
2023-10-17 02:42:40 +11:00
07143ce15c Make Clang happy. 2023-10-17 00:26:19 +11:00
dbc73c6c6c Added missing BuildShader line
Adds `convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),`
2023-10-17 00:15:31 +11:00
8becf13e8b Merge pull request #11786 from v1993/cuda-on-linux
host1x/codecs: enable CUDA on Linux
2023-10-15 22:23:00 -04:00
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
90c56f5dc1 added missing trailing line. 2023-10-16 06:07:26 +11:00
4b0291172e meant to add the unorms as well 2023-10-16 04:29:24 +11:00
12e4757cf3 use texelfetch instead of texturelod 2023-10-16 04:20:45 +11:00
144c0734f5 appease the format gods 2023-10-16 03:24:44 +11:00
f40f65f5d2 Another missing copy connected to Bravely Default II
adds blit_image_helper.ConvertABGR8ToD32F and fragment shader for performing ABGR and BGRA to D32F copies
2023-10-16 03:17:53 +11:00
03c3f936cf missed this line when editing the copypasta 2023-10-15 20:58:50 +11:00
66f41da365 moved line to appease the format gods 2023-10-15 20:54:25 +11:00
7a986d731b Implement missing formats for Bravely Default 2 2023-10-15 20:43:48 +11:00
eae0570a1c input_common: udp: Avoid crash when trying to map motion before client is ready 2023-10-15 02:13:51 -06:00
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
762ac5aa9f host1x/codecs: enable CUDA on Linux 2023-10-14 17:35:45 +03:00
1a4abd184f Merge pull request #11780 from Darkness4/master
qt: add network components when using discord
2023-10-14 09:58:33 -04:00
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
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
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
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
32ad99701d Implement vertex array first and subsequent draws 2023-10-14 12:09:35 +01:00
63c5340cc4 Revert "cmake: only add network component if qt used"
This reverts commit a94371f67b.
2023-10-14 08:46:05 +02:00
a94371f67b cmake: only add network component if qt used 2023-10-14 01:46:20 +02:00
22e4add562 qt: add missing target_link_libraries for discordrpc 2023-10-14 01:15:28 +02:00
b1a7bbd458 qt: add network components when using discord 2023-10-14 01:01:02 +02:00
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
68ea0a2b72 audio: fix shutdown deadlock in audio renderer 2023-10-13 16:34:31 -04:00
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
3e4edbe007 Merge pull request #11767 from t895/gradle-stuff
android: Update dependencies
2023-10-13 15:08:47 -04:00
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
053a16799e fsmitm_romfsbuild: avoid unnecessary copies of vfs pointers 2023-10-13 14:22:52 -04:00
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
3aa6d4d8ce android: Allow ANDROID_STL 2023-10-13 12:55:41 -04:00
9b961dddb4 android: Remove unnecessary flag to extract native libs in AndroidManifest.xml 2023-10-13 12:55:41 -04:00
224b6036a4 android: Update dependencies
Updates to androidx navigation, lifecycle, preference, fragment, recyclerview, and core
2023-10-13 12:55:41 -04:00
2c3281c66b externals: Update LLVM to 17.0.2
Matches android ndk
2023-10-13 12:55:41 -04:00
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
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
ca75c9125d common/polyfill_thread: use std::forward where appropriate, qualify std::move calls 2023-10-13 18:51:11 +03:00
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
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
c8602e1b1f Merge pull request #11649 from t895/driver-manager
android: Driver manager
2023-10-13 09:28:53 -04:00
faa6c35e78 qt: ensure worker cancellation is complete before clearing 2023-10-12 21:07:49 -04:00
8b64878258 android: Update AGP to 8.1.2 2023-10-12 18:59:57 -04:00
519c12da15 Merge pull request #11746 from liamwhite/relr
jit: add support for relr-type relocations
2023-10-12 18:23:31 -04:00
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
d011d3ff0e Merge pull request #11765 from german77/cap_mac
service: caps: Remove ambiguous call
2023-10-12 18:23:18 -04:00
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
c4ec76edba k_page_table: add missing page group open when locking memory 2023-10-12 15:00:26 -04:00
3f05b8facd service: caps: Remove ambiguous call 2023-10-12 12:52:55 -06:00
65d3300875 Merge pull request #11753 from german77/timex2
service: caps: Fix GetAlbumFileList3AaeAruid and GetAlbumFileList0AafeAruidDeprecated
2023-10-12 11:17:35 -04:00
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
d9456f0a11 fix style 2023-10-12 16:06:44 +02:00
2212c9653d kernel: mark TLS accessors as noinline for non-MSVC LTO 2023-10-12 09:16:22 -04:00
fe04a7523a service: caps: Fix GetAlbumFileList3AaeAruid and GetAlbumFileList0AafeAruidDeprecated 2023-10-11 20:01:33 -06:00
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
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
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
6c246f2ac5 yuzu: Use new setting method for stop emulation 2023-10-12 01:51:53 +02:00
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
6f4a080b98 Transition MSAA images to general layout without uploading data 2023-10-11 23:27:23 +01:00
da6824d9fd Merge pull request #11720 from lat9nq/dbg-syms
ci/linux: Upload separated debug symbols
2023-10-11 17:55:17 -04:00
b36f45b239 Merge pull request #11740 from german77/shorcuts
yuzu: Save multiple resolutions per icon
2023-10-11 17:55:08 -04:00
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
880b004321 Merge pull request #11750 from lat9nq/2022g
externals/tzdb_to_nx: Update to 221202
2023-10-11 17:54:50 -04:00
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
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
7b5d234558 jit: add support for relr-type relocations 2023-10-11 11:13:19 -04:00
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
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
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
b50ce645ac Merge pull request #11144 from flodavid/master
Enable controller interaction in Controller Applet
2023-10-11 09:23:52 -04:00
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
8c769b71a1 Do not set rescaled flag when rescaling is disabled 2023-10-11 10:29:19 +01:00
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
6b10f04322 yuzu: Save multiple resolutions per icon 2023-10-10 17:24:49 -06:00
c206a04747 ci/linux: Fix find parameter order
Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
2023-10-10 13:50:50 -04:00
ec6ddaf766 Clear DSP buffer after each execution 2023-10-10 18:22:08 +01:00
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
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
ed58445111 Not not allocate DeviceLocal buffers as mapped 2023-10-10 12:49:07 +01:00
b28b05e2aa Merge pull request #11718 from liamwhite/arm64-native-clock
common: add arm64 native clock
2023-10-10 11:48:06 +02:00
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
8ac8d703b9 Merge pull request #11686 from liamwhite/trmem
kernel: implement transfer memory
2023-10-10 11:45:56 +02:00
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
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
85d99f873f Merge pull request #10519 from mdmrk/master
yuzu-qt: Track play time
2023-10-08 17:11:34 -04:00
21bc2c14bc common: add arm64 native clock 2023-10-08 12:54:23 -04:00
d3997bad9b qt: implement automatic crash dump support 2023-10-08 11:35:53 -04:00
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
8347e5cdb9 service: caps: Implement album manager and reorganize service 2023-10-07 20:57:20 -06:00
dac53b4ba0 externals: stb: Split library into cpp file 2023-10-07 20:57:19 -06:00
0bb7990c49 service: Stub multiple functions to increase stability of album applet 2023-10-07 20:57:10 -06:00
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
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
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
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
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
0e9b839b6f Merge pull request #11648 from liamwhite/unicode-nonsense
gdbserver: use numeric character references for unicode
2023-10-07 12:49:27 -04:00
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
fc4cde7513 Merge pull request #11669 from german77/settings2
yuzu: Fix custom rtc and mute audio settings
2023-10-07 10:55:21 -04:00
ff3859d482 Merge pull request #11688 from Kelebek1/x8d42
Implement X8_D24 pixel format
2023-10-07 10:55:14 -04:00
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
51b89fddd0 update shader to confirmed format copy 2023-10-07 18:28:09 +11:00
f585dec48d Allow GPUs without rebar to open multiple RenderDoc captures 2023-10-06 07:52:06 +01:00
ad1a9f3d3a Implement X8_D24 format 2023-10-06 00:58:30 +01:00
e797a917a9 kernel: implement transfer memory 2023-10-04 22:32:27 -04:00
71044f6def Rework nvidia architecture detection, disable push descriptor for Pascal and older 2023-10-05 03:13:42 +01:00
a764f49910 Mark a buffer GPU modified after the buffers are confirmed, do not double synch them 2023-10-05 00:19:11 +01:00
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
a84c928827 Fix CI Formatting check 2023-10-04 19:12:08 +11:00
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
0fe935a5de core: Update clocks when settings are saved 2023-10-03 20:20:26 -06:00
c84c35ac74 yuzu: Fix mute when in background setting 2023-10-03 20:12:06 -06:00
b32940d3ea vk_present_manager: recreate surface on any surface loss 2023-10-02 19:07:18 -04:00
c334959440 service: caps: Partially implement IAlbumAccessorService 2023-10-02 12:38:03 -06:00
2fa53ec1d9 yuzu: Allow to launch album applet from firmware 2023-10-02 11:29:12 -06:00
e37ad99f22 externals: Add stb_image and stb_image_resize 2023-10-02 11:29:12 -06:00
38394f36d7 gdbserver: use numeric character references for unicode 2023-10-01 19:22:08 -04:00
26f9d1f122 android: Use application context for all FileUtil functions 2023-10-01 15:56:02 -04:00
2f0db2708c loader: don't reassign program ID on npdm reparse 2023-09-30 11:35:42 -04:00
1a246bf135 Enable depth test on stencil clear path 2023-09-28 21:19:51 +01:00
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
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
e69eebb14a video_core: Fix d24r8/s8d24 convert shader build error in moltenvk 2023-09-07 18:01:36 +08:00
0145c89879 video_core: Add missing scissor update when viewport scale offset disable 2023-09-07 18:01:30 +08:00
667ec28697 Address review comments 2023-08-27 19:45:25 -04:00
5464423667 yuzu-qt: Track play time 2023-08-26 22:20:19 -04:00
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
b881949b6d yuzu: Enable controller interaction in Controller Applet 2023-07-25 19:03:12 +02:00
312 changed files with 20899 additions and 3870 deletions

View File

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

View File

@ -5,6 +5,6 @@
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`" GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
GITREV="`git show -s --format='%h'`" GITREV="`git show -s --format='%h'`"
ARTIFACTS_DIR="artifacts" ARTIFACTS_DIR="$PWD/artifacts"
mkdir -p "${ARTIFACTS_DIR}/" mkdir -p "${ARTIFACTS_DIR}/"

View File

@ -11,7 +11,7 @@ ccache -s
mkdir build || true && cd build mkdir build || true && cd build
cmake .. \ cmake .. \
-DBoost_USE_STATIC_LIBS=ON \ -DBoost_USE_STATIC_LIBS=ON \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \ -DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \ -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \ -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
@ -23,6 +23,7 @@ cmake .. \
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
-DYUZU_USE_BUNDLED_FFMPEG=ON \ -DYUZU_USE_BUNDLED_FFMPEG=ON \
-DYUZU_ENABLE_LTO=ON \ -DYUZU_ENABLE_LTO=ON \
-DYUZU_CRASH_DUMPS=ON \
-GNinja -GNinja
ninja ninja
@ -31,6 +32,19 @@ ccache -s
ctest -VV -C Release 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 DESTDIR="$PWD/AppDir" ninja install
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester

View File

@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage" cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
fi 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 . .ci/scripts/common/post-upload.sh

View File

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

View File

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

5
.gitmodules vendored
View File

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

View File

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

View File

@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
include(DownloadExternals) include(DownloadExternals)
include(CMakeDependentOption) include(CMakeDependentOption)
include(CTest) include(CTest)
include(FetchContent)
# Set bundled sdl2/qt as dependent options. # Set bundled sdl2/qt as dependent options.
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON # 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_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}") 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}") DESTINATION "${vvl_lib_path}")
endif() endif()
# On Android, fetch and compile libcxx before doing anything else
if (ANDROID) if (ANDROID)
set(CMAKE_SKIP_INSTALL_RULES ON) 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() endif()
if (YUZU_USE_BUNDLED_VCPKG) if (YUZU_USE_BUNDLED_VCPKG)
@ -179,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
if (YUZU_TESTS) if (YUZU_TESTS)
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests") list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
endif() endif()
if (YUZU_CRASH_DUMPS)
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
endif()
if (ENABLE_WEB_SERVICE) if (ENABLE_WEB_SERVICE)
list(APPEND VCPKG_MANIFEST_FEATURES "web-service") list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
endif() endif()
@ -329,11 +286,12 @@ find_package(Boost 1.79.0 REQUIRED context)
find_package(enet 1.3 MODULE) find_package(enet 1.3 MODULE)
find_package(fmt 9 REQUIRED) find_package(fmt 9 REQUIRED)
find_package(inih 52 MODULE COMPONENTS INIReader) 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(lz4 REQUIRED)
find_package(nlohmann_json 3.8 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED)
find_package(Opus 1.3 MODULE) find_package(Opus 1.3 MODULE)
find_package(RenderDoc MODULE) find_package(RenderDoc MODULE)
find_package(stb MODULE)
find_package(VulkanMemoryAllocator CONFIG) find_package(VulkanMemoryAllocator CONFIG)
find_package(ZLIB 1.2 REQUIRED) find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED) find_package(zstd 1.5 REQUIRED)
@ -400,6 +358,9 @@ function(set_yuzu_qt_components)
if (ENABLE_QT_TRANSLATION) if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif() endif()
if (USE_DISCORD_PRESENCE)
list(APPEND YUZU_QT_COMPONENTS2 Network)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components) 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}) find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
endif() 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. # Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
@ -606,13 +579,6 @@ elseif (WIN32)
# PSAPI is the Process Status API # PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version) set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif() 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)$") elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt) set(PLATFORM_LIBRARIES rt)
endif() endif()

View File

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

View File

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

View File

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

View File

@ -120,6 +120,10 @@ QWidget#connectedControllers {
background: transparent; background: transparent;
} }
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported, QWidget#playersSupported,
QWidget#controllersSupported, QWidget#controllersSupported,
QWidget#controllerSupported1, QWidget#controllerSupported1,

View File

@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
background: transparent; background: transparent;
} }
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported, QWidget#playersSupported,
QWidget#controllersSupported, QWidget#controllersSupported,
QWidget#controllerSupported1, QWidget#controllerSupported1,

View File

@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
background: transparent; background: transparent;
} }
QWidget#closeButtons {
background: transparent;
}
QWidget#playersSupported, QWidget#playersSupported,
QWidget#controllersSupported, QWidget#controllersSupported,
QWidget#controllerSupported1, QWidget#controllerSupported1,

View File

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

2
externals/SDL vendored

1
externals/breakpad vendored Submodule

Submodule externals/breakpad added at c89f9dddc7

View File

@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE) 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}") make_directory("${LIBUSB_PREFIX}")
add_custom_command( add_custom_command(
@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
target_include_directories(usb BEFORE PRIVATE libusb/msvc) target_include_directories(usb BEFORE PRIVATE libusb/msvc)
endif() 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() else()
target_include_directories(usb target_include_directories(usb
# turns out other projects also have "config.h", so make sure the # turns out other projects also have "config.h", so make sure the

View File

@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
set(CAN_BUILD_NX_TZDB false) set(CAN_BUILD_NX_TZDB false)
endif() 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_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb") set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")

1
externals/opus vendored Submodule

Submodule externals/opus added at 101a71e03b

View File

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

1
externals/opus/opus vendored

Submodule externals/opus/opus deleted from ad8fe90db7

7987
externals/stb/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff

2634
externals/stb/stb_image_resize.h vendored Normal file

File diff suppressed because it is too large Load Diff

1724
externals/stb/stb_image_write.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ android {
namespace = "org.yuzu.yuzu_emu" namespace = "org.yuzu.yuzu_emu"
compileSdkVersion = "android-34" compileSdkVersion = "android-34"
ndkVersion = "25.2.9519653" ndkVersion = "26.1.10909125"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
@ -203,23 +203,23 @@ ktlint {
} }
dependencies { 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.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.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("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0") implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference:1.2.0") implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("io.coil-kt:coil:2.2.2") implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1") implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.2.0-beta03") implementation("androidx.window:window:1.2.0-beta03")
implementation("org.ini4j:ini4j:0.5.4") implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0") implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.6.0") implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
implementation("info.debatty:java-string-similarity:2.0.0") implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
} }

View File

@ -28,7 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:appCategory="game" android:appCategory="game"
android:localeConfig="@xml/locales_config" android:localeConfig="@xml/locales_config"
android:banner="@drawable/tv_banner" android:banner="@drawable/tv_banner"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/data_extraction_rules" android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31" android:dataExtractionRules="@xml/data_extraction_rules_api_31"
android:enableOnBackInvokedCallback="true"> android:enableOnBackInvokedCallback="true">

View File

@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface import android.content.DialogInterface
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Html import android.text.Html
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
@ -15,13 +16,9 @@ import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference 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.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.FileUtil.exists import org.yuzu.yuzu_emu.utils.FileUtil
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.Log import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
@ -72,43 +69,61 @@ object NativeLibrary {
@Keep @Keep
@JvmStatic @JvmStatic
fun openContentUri(path: String?, openmode: String?): Int { fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) { return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode) YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else { } else {
openContentUri(appContext, path, openmode) FileUtil.openContentUri(path, openmode)
} }
} }
@Keep @Keep
@JvmStatic @JvmStatic
fun getSize(path: String?): Long { fun getSize(path: String?): Long {
return if (isNativePath(path!!)) { return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path) YuzuApplication.documentsTree!!.getFileSize(path)
} else { } else {
getFileSize(appContext, path) FileUtil.getFileSize(path)
} }
} }
@Keep @Keep
@JvmStatic @JvmStatic
fun exists(path: String?): Boolean { fun exists(path: String?): Boolean {
return if (isNativePath(path!!)) { return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path) YuzuApplication.documentsTree!!.exists(path)
} else { } else {
exists(appContext, path) FileUtil.exists(path, suppressLog = true)
} }
} }
@Keep @Keep
@JvmStatic @JvmStatic
fun isDirectory(path: String?): Boolean { fun isDirectory(path: String?): Boolean {
return if (isNativePath(path!!)) { return if (DocumentsTree.isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path) YuzuApplication.documentsTree!!.isDirectory(path)
} else { } 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 * Returns true if pro controller isn't available and handheld is
*/ */
@ -219,32 +234,6 @@ object NativeLibrary {
external fun initGameIni(gameID: String?) 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) external fun setAppDirectory(directory: String)
/** /**
@ -297,11 +286,6 @@ object NativeLibrary {
*/ */
external fun stopEmulation() external fun stopEmulation()
/**
* Resets the in-memory ROM metadata cache.
*/
external fun resetRomMetadata()
/** /**
* Returns true if emulation is running (or is paused). * Returns true if emulation is running (or is paused).
*/ */

View File

@ -47,7 +47,7 @@ class YuzuApplication : Application() {
application = this application = this
documentsTree = DocumentsTree() documentsTree = DocumentsTree()
DirectoryInitialization.start() DirectoryInitialization.start()
GpuDriverHelper.initializeDriverParameters(applicationContext) GpuDriverHelper.initializeDriverParameters()
NativeLibrary.logDeviceInfo() NativeLibrary.logDeviceInfo()
createNotificationChannels() createNotificationChannels()

View File

@ -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
}
}
}

View File

@ -147,7 +147,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
private class DiffCallback : DiffUtil.ItemCallback<Game>() { private class DiffCallback : DiffUtil.ItemCallback<Game>() {
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean { 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 { override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {

View File

@ -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)
}
}

View File

@ -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"
}
}

View File

@ -15,6 +15,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.SystemClock
import android.view.* import android.view.*
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -25,6 +26,7 @@ import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle 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.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections 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.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings 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.Game
import org.yuzu.yuzu_emu.model.EmulationViewModel import org.yuzu.yuzu_emu.model.EmulationViewModel
import org.yuzu.yuzu_emu.overlay.InputOverlay import org.yuzu.yuzu_emu.overlay.InputOverlay
@ -70,6 +74,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game private lateinit var game: Game
private val emulationViewModel: EmulationViewModel by activityViewModels() private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false private var isInFoldableLayout = false
@ -153,6 +158,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.showFpsText.setTextColor(Color.YELLOW) binding.showFpsText.setTextColor(Color.YELLOW)
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() } 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.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text = binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
game.title game.title
@ -299,6 +330,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 +378,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
override fun onResume() {
super.onResume()
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start()
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()

View File

@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
import android.Manifest import android.Manifest
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
@ -27,8 +26,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections import org.yuzu.yuzu_emu.HomeNavigationDirections
@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings 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.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity import org.yuzu.yuzu_emu.ui.main.MainActivity
@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
private lateinit var mainActivity: MainActivity private lateinit var mainActivity: MainActivity
private val homeViewModel: HomeViewModel by activityViewModels() private val homeViewModel: HomeViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
) )
add( add(
HomeSetting( HomeSetting(
R.string.install_gpu_driver, R.string.gpu_driver_manager,
R.string.install_gpu_driver_description, R.string.install_gpu_driver_description,
R.drawable.ic_exit, R.drawable.ic_build,
{ driverInstaller() }, {
binding.root.findNavController()
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
},
{ GpuDriverHelper.supportsCustomDriverLoading() }, { GpuDriverHelper.supportsCustomDriverLoading() },
R.string.custom_driver_not_supported, R.string.custom_driver_not_supported,
R.string.custom_driver_not_supported_description R.string.custom_driver_not_supported_description,
driverViewModel.selectedDriverMetadata
) )
) )
add( add(
@ -182,7 +186,8 @@ class HomeSettingsFragment : Fragment() {
} }
binding.homeSettingsList.apply { binding.homeSettingsList.apply {
layoutManager = LinearLayoutManager(requireContext()) layoutManager =
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
adapter = HomeSettingAdapter( adapter = HomeSettingAdapter(
requireActivity() as AppCompatActivity, requireActivity() as AppCompatActivity,
viewLifecycleOwner, viewLifecycleOwner,
@ -292,31 +297,6 @@ 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()
}
private fun shareLog() { private fun shareLog() {
val file = DocumentFile.fromSingleUri( val file = DocumentFile.fromSingleUri(
mainActivity, mainActivity,

View File

@ -10,8 +10,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
requireActivity().supportFragmentManager, requireActivity().supportFragmentManager,
MessageDialogFragment.TAG MessageDialogFragment.TAG
) )
else -> {
// Do nothing
}
} }
taskViewModel.clear() taskViewModel.clear()
} }
@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
private const val CANCELLABLE = "Cancellable" private const val CANCELLABLE = "Cancellable"
fun newInstance( fun newInstance(
activity: AppCompatActivity, activity: FragmentActivity,
titleId: Int, titleId: Int,
cancellable: Boolean = false, cancellable: Boolean = false,
task: () -> Any task: () -> Any

View File

@ -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)
}
}

View File

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

View File

@ -14,15 +14,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper import org.yuzu.yuzu_emu.utils.GameHelper
import org.yuzu.yuzu_emu.utils.GameMetadata
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() { class GamesViewModel : ViewModel() {
val games: StateFlow<List<Game>> get() = _games val games: StateFlow<List<Game>> get() = _games
private val _games = MutableStateFlow(emptyList<Game>()) private val _games = MutableStateFlow(emptyList<Game>())
@ -49,26 +47,34 @@ class GamesViewModel : ViewModel() {
// Retrieve list of cached games // Retrieve list of cached games
val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) val storedGames = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
.getStringSet(GameHelper.KEY_GAMES, emptySet()) .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 = viewModelScope.launch {
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path)) withContext(Dispatchers.IO) {
?.exists() if (storedGames!!.isNotEmpty()) {
if (gameExists == true) { val deserializedGames = mutableSetOf<Game>()
deserializedGames.add(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>) { fun setGames(games: List<Game>) {
@ -106,7 +112,7 @@ class GamesViewModel : ViewModel() {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
NativeLibrary.resetRomMetadata() GameMetadata.resetMetadata()
setGames(GameHelper.getGames()) setGames(GameHelper.getGames())
_isReloading.value = false _isReloading.value = false

View File

@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView import com.google.android.material.navigation.NavigationBarView
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import java.io.File import java.io.File
import java.io.FilenameFilter import java.io.FilenameFilter
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext 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.R
import org.yuzu.yuzu_emu.activities.EmulationActivity import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding 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.DocumentProvider
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/" val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage( if (FileUtil.copyUriToInternalStorage(
applicationContext,
result, result,
dstPath, dstPath,
"prod.keys" "prod.keys"
) ) != null
) { ) {
if (NativeLibrary.reloadKeys()) { if (NativeLibrary.reloadKeys()) {
Toast.makeText( Toast.makeText(
@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val dstPath = DirectoryInitialization.userDirectory + "/keys/" val dstPath = DirectoryInitialization.userDirectory + "/keys/"
if (FileUtil.copyUriToInternalStorage( if (FileUtil.copyUriToInternalStorage(
applicationContext,
result, result,
dstPath, dstPath,
"key_retail.bin" "key_retail.bin"
) ) != null
) { ) {
if (NativeLibrary.reloadKeys()) { if (NativeLibrary.reloadKeys()) {
Toast.makeText( Toast.makeText(
@ -469,59 +464,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( val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments() ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> -> ) { documents: List<Uri> ->

View File

@ -7,7 +7,6 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import java.io.File import java.io.File
import java.util.* import java.util.*
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree { class DocumentsTree {
@ -22,7 +21,7 @@ class DocumentsTree {
fun openContentUri(filepath: String, openMode: String?): Int { fun openContentUri(filepath: String, openMode: String?): Int {
val node = resolvePath(filepath) ?: return -1 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 { fun getFileSize(filepath: String): Long {
@ -30,7 +29,7 @@ class DocumentsTree {
return if (node == null || node.isDirectory) { return if (node == null || node.isDirectory) {
0 0
} else { } else {
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) FileUtil.getFileSize(node.uri.toString())
} }
} }
@ -43,6 +42,23 @@ class DocumentsTree {
return node != null && node.isDirectory 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? { private fun resolvePath(filepath: String): DocumentsNode? {
val tokens = StringTokenizer(filepath, File.separator, false) val tokens = StringTokenizer(filepath, File.separator, false)
var iterator = root var iterator = root
@ -67,7 +83,7 @@ class DocumentsTree {
* @param parent parent node of this level * @param parent parent node of this level
*/ */
private fun structTree(parent: DocumentsNode) { private fun structTree(parent: DocumentsNode) {
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!) val documents = FileUtil.listFiles(parent.uri!!)
for (document in documents) { for (document in documents) {
val node = DocumentsNode(document) val node = DocumentsNode(document)
node.parent = parent node.parent = parent

View File

@ -3,7 +3,6 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.URLDecoder 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.MinimalDocumentFile
import org.yuzu.yuzu_emu.model.TaskState import org.yuzu.yuzu_emu.model.TaskState
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.lang.NullPointerException
import java.nio.charset.StandardCharsets
import java.util.zip.ZipOutputStream import java.util.zip.ZipOutputStream
object FileUtil { object FileUtil {
@ -29,6 +29,8 @@ object FileUtil {
const val APPLICATION_OCTET_STREAM = "application/octet-stream" const val APPLICATION_OCTET_STREAM = "application/octet-stream"
const val TEXT_PLAIN = "text/plain" const val TEXT_PLAIN = "text/plain"
private val context get() = YuzuApplication.appContext
/** /**
* Create a file from directory with filename. * Create a file from directory with filename.
* @param context Application context * @param context Application context
@ -36,11 +38,11 @@ object FileUtil {
* @param filename file display name. * @param filename file display name.
* @return boolean * @return boolean
*/ */
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? { fun createFile(directory: String?, filename: String): DocumentFile? {
var decodedFilename = filename var decodedFilename = filename
try { try {
val directoryUri = Uri.parse(directory) 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) decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
var mimeType = APPLICATION_OCTET_STREAM var mimeType = APPLICATION_OCTET_STREAM
if (decodedFilename.endsWith(".txt")) { if (decodedFilename.endsWith(".txt")) {
@ -56,16 +58,15 @@ object FileUtil {
/** /**
* Create a directory from directory with filename. * Create a directory from directory with filename.
* @param context Application context
* @param directory parent path for directory. * @param directory parent path for directory.
* @param directoryName directory display name. * @param directoryName directory display name.
* @return boolean * @return boolean
*/ */
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? { fun createDir(directory: String?, directoryName: String?): DocumentFile? {
var decodedDirectoryName = directoryName var decodedDirectoryName = directoryName
try { try {
val directoryUri = Uri.parse(directory) 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) decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
val isExist = parent.findFile(decodedDirectoryName) val isExist = parent.findFile(decodedDirectoryName)
return isExist ?: parent.createDirectory(decodedDirectoryName) return isExist ?: parent.createDirectory(decodedDirectoryName)
@ -77,13 +78,12 @@ object FileUtil {
/** /**
* Open content uri and return file descriptor to JNI. * Open content uri and return file descriptor to JNI.
* @param context Application context
* @param path Native content uri path * @param path Native content uri path
* @param openMode will be one of "r", "r", "rw", "wa", "rwa" * @param openMode will be one of "r", "r", "rw", "wa", "rwa"
* @return file descriptor * @return file descriptor
*/ */
@JvmStatic @JvmStatic
fun openContentUri(context: Context, path: String, openMode: String?): Int { fun openContentUri(path: String, openMode: String?): Int {
try { try {
val uri = Uri.parse(path) val uri = Uri.parse(path)
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!) val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
@ -103,11 +103,10 @@ object FileUtil {
/** /**
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
* This function will be faster than DoucmentFile.listFiles * This function will be faster than DoucmentFile.listFiles
* @param context Application context
* @param uri Directory uri. * @param uri Directory uri.
* @return CheapDocument lists. * @return CheapDocument lists.
*/ */
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> { fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@ -145,7 +144,7 @@ object FileUtil {
* @param path Native content uri path * @param path Native content uri path
* @return bool * @return bool
*/ */
fun exists(context: Context, path: String?): Boolean { fun exists(path: String?, suppressLog: Boolean = false): Boolean {
var c: Cursor? = null var c: Cursor? = null
try { try {
val mUri = Uri.parse(path) val mUri = Uri.parse(path)
@ -153,7 +152,9 @@ object FileUtil {
c = context.contentResolver.query(mUri, columns, null, null, null) c = context.contentResolver.query(mUri, columns, null, null, null)
return c!!.count > 0 return c!!.count > 0
} catch (e: Exception) { } 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 { } finally {
closeQuietly(c) closeQuietly(c)
} }
@ -165,7 +166,7 @@ object FileUtil {
* @param path content uri path * @param path content uri path
* @return bool * @return bool
*/ */
fun isDirectory(context: Context, path: String): Boolean { fun isDirectory(path: String): Boolean {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_MIME_TYPE DocumentsContract.Document.COLUMN_MIME_TYPE
@ -210,10 +211,10 @@ object FileUtil {
return filename return filename
} }
fun getFilesName(context: Context, path: String): Array<String> { fun getFilesName(path: String): Array<String> {
val uri = Uri.parse(path) val uri = Uri.parse(path)
val files: MutableList<String> = ArrayList() val files: MutableList<String> = ArrayList()
for (file in listFiles(context, uri)) { for (file in listFiles(uri)) {
files.add(file.filename) files.add(file.filename)
} }
return files.toTypedArray() return files.toTypedArray()
@ -225,7 +226,7 @@ object FileUtil {
* @return long file size * @return long file size
*/ */
@JvmStatic @JvmStatic
fun getFileSize(context: Context, path: String): Long { fun getFileSize(path: String): Long {
val resolver = context.contentResolver val resolver = context.contentResolver
val columns = arrayOf( val columns = arrayOf(
DocumentsContract.Document.COLUMN_SIZE DocumentsContract.Document.COLUMN_SIZE
@ -245,44 +246,38 @@ object FileUtil {
return size 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( fun copyUriToInternalStorage(
context: Context, sourceUri: Uri,
sourceUri: Uri?,
destinationParentPath: String, destinationParentPath: String,
destinationFilename: String destinationFilename: String = ""
): Boolean { ): File? =
var input: InputStream? = null
var output: FileOutputStream? = null
try { try {
input = context.contentResolver.openInputStream(sourceUri!!) val fileName =
output = FileOutputStream("$destinationParentPath/$destinationFilename") if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
val buffer = ByteArray(1024) val inputStream = context.contentResolver.openInputStream(sourceUri)!!
var len: Int
while (input!!.read(buffer).also { len = it } != -1) { val destinationFile = File("$destinationParentPath$fileName")
output.write(buffer, 0, len) if (destinationFile.exists()) {
destinationFile.delete()
} }
output.flush()
return true destinationFile.outputStream().use { fos ->
} catch (e: Exception) { inputStream.use { it.copyTo(fos) }
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
} catch (e: IOException) {
null
} catch (e: NullPointerException) {
null
} }
return false
}
/** /**
* Extracts the given zip file into the given directory. * Extracts the given zip file into the given directory.
@ -368,4 +363,12 @@ object FileUtil {
return fileName.substring(fileName.lastIndexOf(".") + 1) return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase() .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)
} }

View File

@ -30,7 +30,7 @@ object GameHelper {
// Ensure keys are loaded so that ROM metadata can be decrypted. // Ensure keys are loaded so that ROM metadata can be decrypted.
NativeLibrary.reloadKeys() NativeLibrary.reloadKeys()
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3) addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
// Cache list of games found on disk // Cache list of games found on disk
val serializedGames = mutableSetOf<String>() val serializedGames = mutableSetOf<String>()
@ -58,7 +58,7 @@ object GameHelper {
if (it.isDirectory) { if (it.isDirectory) {
addGamesRecursive( addGamesRecursive(
games, games,
FileUtil.listFiles(YuzuApplication.appContext, it.uri), FileUtil.listFiles(it.uri),
depth - 1 depth - 1
) )
} else { } else {
@ -71,27 +71,26 @@ object GameHelper {
fun getGame(uri: Uri, addedToLibrary: Boolean): Game { fun getGame(uri: Uri, addedToLibrary: Boolean): Game {
val filePath = uri.toString() 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 the game's title field is empty, use the filename.
if (name.isEmpty()) { if (name.isEmpty()) {
name = FileUtil.getFilename(uri) 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 the game's ID field is empty, use the filename without extension.
if (gameId.isEmpty()) { if (programId.isEmpty()) {
gameId = name.substring(0, name.lastIndexOf(".")) programId = name.substring(0, name.lastIndexOf("."))
} }
val newGame = Game( val newGame = Game(
name, name,
NativeLibrary.getDescription(filePath).replace("\n", " "),
NativeLibrary.getRegions(filePath),
filePath, filePath,
gameId, programId,
NativeLibrary.getCompany(filePath), GameMetadata.getDeveloper(filePath),
NativeLibrary.isHomebrew(filePath) GameMetadata.getVersion(filePath),
GameMetadata.getIsHomebrew(filePath)
) )
if (addedToLibrary) { if (addedToLibrary) {

View File

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

View File

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

View File

@ -3,64 +3,33 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.NativeLibrary 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 { object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json" private const val META_JSON_FILENAME = "meta.json"
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
private var fileRedirectionPath: String? = null private var fileRedirectionPath: String? = null
private var driverInstallationPath: String? = null var driverInstallationPath: String? = null
private var hookLibPath: String? = null private var hookLibPath: String? = null
@Throws(IOException::class) val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
private fun unzip(zipFilePath: String, destDir: String) {
val dir = File(destDir)
// Create output directory if it doesn't exist fun initializeDriverParameters() {
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) {
try { try {
// Initialize the file redirection directory. // Initialize the file redirection directory.
fileRedirectionPath = fileRedirectionPath = YuzuApplication.appContext
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/" .getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
// Initialize the driver installation directory. // Initialize the driver installation directory.
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/" driverInstallationPath = YuzuApplication.appContext
.filesDir.canonicalPath + "/gpu_driver/"
} catch (e: IOException) { } catch (e: IOException) {
throw RuntimeException(e) throw RuntimeException(e)
} }
@ -69,68 +38,169 @@ object GpuDriverHelper {
initializeDirectories() initializeDirectories()
// Initialize hook libraries directory. // Initialize hook libraries directory.
hookLibPath = context.applicationInfo.nativeLibraryDir + "/" hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
// Initialize GPU driver. // Initialize GPU driver.
NativeLibrary.initializeGpuDriver( NativeLibrary.initializeGpuDriver(
hookLibPath, hookLibPath,
driverInstallationPath, driverInstallationPath,
customDriverLibraryName, customDriverData.libraryName,
fileRedirectionPath fileRedirectionPath
) )
} }
fun installDefaultDriver(context: Context) { fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
// Removing the installed driver will result in the backend using the default system driver. val driverZips = File(driverStoragePath).listFiles()
val driverInstallationDir = File(driverInstallationPath!!) val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
deleteRecursive(driverInstallationDir) driverZips
initializeDriverParameters(context) ?.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. // Revert to system default in the event the specified driver is bad.
installDefaultDriver(context) installDefaultDriver()
// Ensure we have directories. // Ensure we have directories.
initializeDirectories() initializeDirectories()
// Copy the zip file URI into our private storage. // Copy the zip file URI to user data
copyUriToInternalStorage( val copiedFile =
context, FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
driverPathUri,
driverInstallationPath!!, // Validate driver
DRIVER_INTERNAL_FILENAME 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. // Unzip the driver.
try { try {
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!) FileUtil.unzipToInternalStorage(
BufferedInputStream(copiedFile.inputStream()),
File(driverInstallationPath!!)
)
} catch (e: SecurityException) { } catch (e: SecurityException) {
return return false
} }
// Initialize the driver parameters. // 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 external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name. // Parse the custom driver metadata to retrieve the name.
val customDriverName: String? val customDriverData: GpuDriverMetadata
get() { get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
return metadata.name
}
// Parse the custom driver metadata to retrieve the library name. fun initializeDirectories() {
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() {
// Ensure the file redirection directory exists. // Ensure the file redirection directory exists.
val fileRedirectionDir = File(fileRedirectionPath!!) val fileRedirectionDir = File(fileRedirectionPath!!)
if (!fileRedirectionDir.exists()) { if (!fileRedirectionDir.exists()) {
@ -141,14 +211,10 @@ object GpuDriverHelper {
if (!driverInstallationDir.exists()) { if (!driverInstallationDir.exists()) {
driverInstallationDir.mkdirs() driverInstallationDir.mkdirs()
} }
} // Ensure the driver storage directory exists
val driverStorageDirectory = File(driverStoragePath)
private fun deleteRecursive(fileOrDirectory: File) { if (!driverStorageDirectory.exists()) {
if (fileOrDirectory.isDirectory) { driverStorageDirectory.mkdirs()
for (child in fileOrDirectory.listFiles()!!) {
deleteRecursive(child)
}
} }
fileOrDirectory.delete()
} }
} }

View File

@ -4,29 +4,29 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import java.io.IOException 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.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.File
import java.io.InputStream
class GpuDriverMetadata(metadataFilePath: String) { class GpuDriverMetadata {
var name: String? = null /**
var description: String? = null * Tries to get driver metadata information from a meta.json [File]
var author: String? = null *
var vendor: String? = null * @param metadataFile meta.json file provided with a GPU driver
var driverVersion: String? = null */
var minApi = 0 constructor(metadataFile: File) {
var libraryName: String? = null if (metadataFile.length() > MAX_META_SIZE_BYTES) {
return
}
init {
try { try {
val json = JSONObject(getStringFromFile(metadataFilePath)) val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
name = json.getString("name") name = json.getString("name")
description = json.getString("description") description = json.getString("description")
author = json.getString("author") author = json.getString("author")
vendor = json.getString("vendor") vendor = json.getString("vendor")
driverVersion = json.getString("driverVersion") version = json.getString("driverVersion")
minApi = json.getInt("minApi") minApi = json.getInt("minApi")
libraryName = json.getString("libraryName") libraryName = json.getString("libraryName")
} catch (e: JSONException) { } catch (e: JSONException) {
@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
} }
} }
companion object { /**
@Throws(IOException::class) * Tries to get driver metadata information from an input stream that's intended to be
private fun getStringFromFile(filePath: String): String { * from a zip file
val path = Paths.get(filePath) *
val bytes = Files.readAllBytes(path) * @param metadataStream ZipEntry input stream
return String(bytes, StandardCharsets.UTF_8) * @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
}
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
</vector>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/materialCardViewOutlinedStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center"
android:padding="16dp">
<RadioButton
android:id="@+id/radio_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="false"
android:checked="false" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/select_gpu_driver_default" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/version"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/description"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:ellipsize="none"
android:marqueeRepeatLimit="marquee_forever"
android:requiresFadingEdge="horizontal"
android:singleLine="true"
android:textAlignment="viewStart"
tools:text="@string/install_gpu_driver_description" />
</LinearLayout>
<Button
android:id="@+id/button_delete"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/delete"
android:tooltipText="@string/delete"
app:icon="@drawable/ic_delete"
app:iconTint="?attr/colorControlNormal" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>

View File

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

View File

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

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_licenses"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_drivers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:liftOnScrollTargetViewId="@id/list_drivers">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_drivers"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_back"
app:title="@string/gpu_driver_manager" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_drivers"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/button_install"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:text="@string/install"
app:icon="@drawable/ic_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -22,6 +22,9 @@
<action <action
android:id="@+id/action_homeSettingsFragment_to_installableFragment" android:id="@+id/action_homeSettingsFragment_to_installableFragment"
app:destination="@id/installableFragment" /> app:destination="@id/installableFragment" />
<action
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
app:destination="@id/driverManagerFragment" />
</fragment> </fragment>
<fragment <fragment
@ -95,5 +98,9 @@
android:id="@+id/installableFragment" android:id="@+id/installableFragment"
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment" android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
android:label="InstallableFragment" /> android:label="InstallableFragment" />
<fragment
android:id="@+id/driverManagerFragment"
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
android:label="DriverManagerFragment" />
</navigation> </navigation>

View File

@ -168,9 +168,7 @@
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string> <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
<string name="select_gpu_driver_install">Installieren</string> <string name="select_gpu_driver_install">Installieren</string>
<string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
<string name="system_gpu_driver">System GPU-Treiber</string> <string name="system_gpu_driver">System GPU-Treiber</string>
<string name="installing_driver">Treiber wird installiert...</string> <string name="installing_driver">Treiber wird installiert...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string> <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Predeterminado</string> <string name="select_gpu_driver_default">Predeterminado</string>
<string name="select_gpu_driver_install_success">Instalado %s</string>
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string> <string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Instalando driver...</string> <string name="installing_driver">Instalando driver...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
<string name="select_gpu_driver_install">Installer</string> <string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Défaut</string> <string name="select_gpu_driver_default">Défaut</string>
<string name="select_gpu_driver_install_success">%s Installé</string>
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
<string name="system_gpu_driver">Pilote du GPU du système</string> <string name="system_gpu_driver">Pilote du GPU du système</string>
<string name="installing_driver">Installation du pilote...</string> <string name="installing_driver">Installation du pilote...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string> <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
<string name="select_gpu_driver_install">Installa</string> <string name="select_gpu_driver_install">Installa</string>
<string name="select_gpu_driver_default">Predefinito</string> <string name="select_gpu_driver_default">Predefinito</string>
<string name="select_gpu_driver_install_success">Installato%s</string>
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
<string name="system_gpu_driver">Driver GPU del sistema</string> <string name="system_gpu_driver">Driver GPU del sistema</string>
<string name="installing_driver">Installando i driver...</string> <string name="installing_driver">Installando i driver...</string>

View File

@ -170,9 +170,7 @@
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか</string> <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか</string>
<string name="select_gpu_driver_install">インストール</string> <string name="select_gpu_driver_install">インストール</string>
<string name="select_gpu_driver_default">デフォルト</string> <string name="select_gpu_driver_default">デフォルト</string>
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
<string name="system_gpu_driver">システムのGPUドライバ</string> <string name="system_gpu_driver">システムのGPUドライバ</string>
<string name="installing_driver">インストール中…</string> <string name="installing_driver">インストール中…</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
<string name="select_gpu_driver_install">설치</string> <string name="select_gpu_driver_install">설치</string>
<string name="select_gpu_driver_default">기본값</string> <string name="select_gpu_driver_default">기본값</string>
<string name="select_gpu_driver_install_success">설치된 %s</string>
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
<string name="system_gpu_driver">시스템 GPU 드라이버</string> <string name="system_gpu_driver">시스템 GPU 드라이버</string>
<string name="installing_driver">드라이버 설치 중...</string> <string name="installing_driver">드라이버 설치 중...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
<string name="select_gpu_driver_install">Installer</string> <string name="select_gpu_driver_install">Installer</string>
<string name="select_gpu_driver_default">Standard</string> <string name="select_gpu_driver_default">Standard</string>
<string name="select_gpu_driver_install_success">Installert %s</string>
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
<string name="system_gpu_driver">Systemets GPU-driver</string> <string name="system_gpu_driver">Systemets GPU-driver</string>
<string name="installing_driver">Installerer driver...</string> <string name="installing_driver">Installerer driver...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
<string name="select_gpu_driver_install">Zainstaluj</string> <string name="select_gpu_driver_install">Zainstaluj</string>
<string name="select_gpu_driver_default">Domyślne</string> <string name="select_gpu_driver_default">Domyślne</string>
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
<string name="system_gpu_driver">Systemowy sterownik GPU</string> <string name="system_gpu_driver">Systemowy sterownik GPU</string>
<string name="installing_driver">Instalowanie sterownika...</string> <string name="installing_driver">Instalowanie sterownika...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string> <string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string> <string name="installing_driver">A instalar o Driver...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
<string name="select_gpu_driver_install">Instalar</string> <string name="select_gpu_driver_install">Instalar</string>
<string name="select_gpu_driver_default">Padrão</string> <string name="select_gpu_driver_default">Padrão</string>
<string name="select_gpu_driver_install_success">Instalado%s</string>
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
<string name="system_gpu_driver">Driver do GPU padrão</string> <string name="system_gpu_driver">Driver do GPU padrão</string>
<string name="installing_driver">A instalar o Driver...</string> <string name="installing_driver">A instalar o Driver...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string> <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
<string name="select_gpu_driver_install">Установить</string> <string name="select_gpu_driver_install">Установить</string>
<string name="select_gpu_driver_default">По умолчанию</string> <string name="select_gpu_driver_default">По умолчанию</string>
<string name="select_gpu_driver_install_success">Установлено %s</string>
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
<string name="system_gpu_driver">Системный драйвер ГП</string> <string name="system_gpu_driver">Системный драйвер ГП</string>
<string name="installing_driver">Установка драйвера...</string> <string name="installing_driver">Установка драйвера...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
<string name="select_gpu_driver_install">Встановити</string> <string name="select_gpu_driver_install">Встановити</string>
<string name="select_gpu_driver_default">За замовчуванням</string> <string name="select_gpu_driver_default">За замовчуванням</string>
<string name="select_gpu_driver_install_success">Встановлено %s</string>
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
<string name="system_gpu_driver">Системний драйвер ГП</string> <string name="system_gpu_driver">Системний драйвер ГП</string>
<string name="installing_driver">Встановлення драйвера...</string> <string name="installing_driver">Встановлення драйвера...</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string> <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
<string name="select_gpu_driver_install">安装</string> <string name="select_gpu_driver_install">安装</string>
<string name="select_gpu_driver_default">系统默认</string> <string name="select_gpu_driver_default">系统默认</string>
<string name="select_gpu_driver_install_success">已安装 %s</string>
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
<string name="system_gpu_driver">系统 GPU 驱动程序</string> <string name="system_gpu_driver">系统 GPU 驱动程序</string>
<string name="installing_driver">正在安装驱动程序…</string> <string name="installing_driver">正在安装驱动程序…</string>

View File

@ -171,9 +171,7 @@
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string> <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
<string name="select_gpu_driver_install">安裝</string> <string name="select_gpu_driver_install">安裝</string>
<string name="select_gpu_driver_default">預設</string> <string name="select_gpu_driver_default">預設</string>
<string name="select_gpu_driver_install_success">已安裝 %s</string>
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
<string name="system_gpu_driver">系統 GPU 驅動程式</string> <string name="system_gpu_driver">系統 GPU 驅動程式</string>
<string name="installing_driver">正在安裝驅動程式…</string> <string name="installing_driver">正在安裝驅動程式…</string>

View File

@ -13,6 +13,8 @@
<dimen name="menu_width">256dp</dimen> <dimen name="menu_width">256dp</dimen>
<dimen name="card_width">165dp</dimen> <dimen name="card_width">165dp</dimen>
<dimen name="icon_inset">24dp</dimen> <dimen name="icon_inset">24dp</dimen>
<dimen name="spacing_bottom_list_fab">72dp</dimen>
<dimen name="spacing_fab">24dp</dimen>
<dimen name="dialog_margin">20dp</dimen> <dimen name="dialog_margin">20dp</dimen>
<dimen name="elevated_app_bar">3dp</dimen> <dimen name="elevated_app_bar">3dp</dimen>

View File

@ -72,6 +72,7 @@
<string name="invalid_keys_error">Invalid encryption keys</string> <string name="invalid_keys_error">Invalid encryption keys</string>
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
<string name="gpu_driver_manager">GPU Driver Manager</string>
<string name="install_gpu_driver">Install GPU driver</string> <string name="install_gpu_driver">Install GPU driver</string>
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
<string name="advanced_settings">Advanced settings</string> <string name="advanced_settings">Advanced settings</string>
@ -234,15 +235,17 @@
<string name="export_failed">Export failed</string> <string name="export_failed">Export failed</string>
<string name="import_failed">Import failed</string> <string name="import_failed">Import failed</string>
<string name="cancelling">Cancelling</string> <string name="cancelling">Cancelling</string>
<string name="install">Install</string>
<string name="delete">Delete</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string> <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
<string name="select_gpu_driver_install">Install</string> <string name="select_gpu_driver_install">Install</string>
<string name="select_gpu_driver_default">Default</string> <string name="select_gpu_driver_default">Default</string>
<string name="select_gpu_driver_install_success">Installed %s</string>
<string name="select_gpu_driver_use_default">Using default GPU driver</string> <string name="select_gpu_driver_use_default">Using default GPU driver</string>
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string> <string name="select_gpu_driver_error">Invalid driver selected</string>
<string name="driver_already_installed">Driver already installed</string>
<string name="system_gpu_driver">System GPU driver</string> <string name="system_gpu_driver">System GPU driver</string>
<string name="installing_driver">Installing driver…</string> <string name="installing_driver">Installing driver…</string>

View File

@ -3,8 +3,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id("com.android.application") version "8.0.2" apply false id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.0.2" apply false id("com.android.library") version "8.1.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.21" apply false id("org.jetbrains.kotlin.android") version "1.8.21" apply false
} }

View File

@ -77,6 +77,7 @@ void AudioRenderer::Wait() {
"{}, got {}", "{}, got {}",
Message::RenderResponse, msg); Message::RenderResponse, msg);
} }
PostDSPClearCommandBuffer();
} }
void AudioRenderer::Send(Direction dir, u32 message) { void AudioRenderer::Send(Direction dir, u32 message) {
@ -96,6 +97,14 @@ void AudioRenderer::SetCommandBuffer(s32 session_id, CpuAddr buffer, u64 size, u
command_buffers[session_id].reset_buffer = reset; command_buffers[session_id].reset_buffer = reset;
} }
void AudioRenderer::PostDSPClearCommandBuffer() noexcept {
for (auto& buffer : command_buffers) {
buffer.buffer = 0;
buffer.size = 0;
buffer.reset_buffer = false;
}
}
u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept { u32 AudioRenderer::GetRemainCommandCount(s32 session_id) const noexcept {
return command_buffers[session_id].remaining_command_count; return command_buffers[session_id].remaining_command_count;
} }

View File

@ -85,6 +85,8 @@ private:
*/ */
void CreateSinkStreams(); void CreateSinkStreams();
void PostDSPClearCommandBuffer() noexcept;
/// Core system /// Core system
Core::System& system; Core::System& system;
/// The output sink the AudioRenderer will send samples to /// The output sink the AudioRenderer will send samples to

View File

@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
// paused and we'll desync, so just play silence. // paused and we'll desync, so just play silence.
if (system.IsPaused() || system.IsShuttingDown()) { if (system.IsPaused() || system.IsShuttingDown()) {
if (system.IsShuttingDown()) { if (system.IsShuttingDown()) {
{
std::scoped_lock lk{release_mutex};
queued_buffers.store(0);
}
release_cv.notify_one(); release_cv.notify_one();
} }

View File

@ -120,6 +120,8 @@ add_library(common STATIC
socket_types.h socket_types.h
spin_lock.cpp spin_lock.cpp
spin_lock.h spin_lock.h
stb.cpp
stb.h
steady_clock.cpp steady_clock.cpp
steady_clock.h steady_clock.h
stream.cpp stream.cpp
@ -189,6 +191,14 @@ if(ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak::xbyak) target_link_libraries(common PRIVATE xbyak::xbyak)
endif() endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
target_sources(common
PRIVATE
arm64/native_clock.cpp
arm64/native_clock.h
)
endif()
if (MSVC) if (MSVC)
target_compile_definitions(common PRIVATE target_compile_definitions(common PRIVATE
# The standard library doesn't provide any replacement for codecvt yet # The standard library doesn't provide any replacement for codecvt yet
@ -200,6 +210,8 @@ if (MSVC)
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
/we4800 # Implicit conversion from 'type' to bool. Possible information loss /we4800 # Implicit conversion from 'type' to bool. Possible information loss
) )
else()
set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers")
endif() endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@ -215,7 +227,7 @@ endif()
create_target_directory_groups(common) create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads) target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle) target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
if (ANDROID) if (ANDROID)

View File

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/arm64/native_clock.h"
namespace Common::Arm64 {
namespace {
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
}
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
return static_cast<u64>((m * factor) >> 64);
}
} // namespace
NativeClock::NativeClock() {
const u64 host_cntfrq = GetHostCNTFRQ();
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
}
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
return std::chrono::nanoseconds{MultiplyHigh(GetHostTicksElapsed(), ns_cntfrq_factor)};
}
std::chrono::microseconds NativeClock::GetTimeUS() const {
return std::chrono::microseconds{MultiplyHigh(GetHostTicksElapsed(), us_cntfrq_factor)};
}
std::chrono::milliseconds NativeClock::GetTimeMS() const {
return std::chrono::milliseconds{MultiplyHigh(GetHostTicksElapsed(), ms_cntfrq_factor)};
}
u64 NativeClock::GetCNTPCT() const {
return MultiplyHigh(GetHostTicksElapsed(), guest_cntfrq_factor);
}
u64 NativeClock::GetGPUTick() const {
return MultiplyHigh(GetHostTicksElapsed(), gputick_cntfrq_factor);
}
u64 NativeClock::GetHostTicksNow() const {
u64 cntvct_el0 = 0;
asm volatile("dsb ish\n\t"
"mrs %[cntvct_el0], cntvct_el0\n\t"
"dsb ish\n\t"
: [cntvct_el0] "=r"(cntvct_el0));
return cntvct_el0;
}
u64 NativeClock::GetHostTicksElapsed() const {
return GetHostTicksNow();
}
bool NativeClock::IsNative() const {
return true;
}
u64 NativeClock::GetHostCNTFRQ() {
u64 cntfrq_el0 = 0;
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
return cntfrq_el0;
}
} // namespace Common::Arm64

View File

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/wall_clock.h"
namespace Common::Arm64 {
class NativeClock final : public WallClock {
public:
explicit NativeClock();
std::chrono::nanoseconds GetTimeNS() const override;
std::chrono::microseconds GetTimeUS() const override;
std::chrono::milliseconds GetTimeMS() const override;
u64 GetCNTPCT() const override;
u64 GetGPUTick() const override;
u64 GetHostTicksNow() const override;
u64 GetHostTicksElapsed() const override;
bool IsNative() const override;
static u64 GetHostCNTFRQ();
public:
using FactorType = unsigned __int128;
FactorType GetGuestCNTFRQFactor() const {
return guest_cntfrq_factor;
}
private:
FactorType ns_cntfrq_factor;
FactorType us_cntfrq_factor;
FactorType ms_cntfrq_factor;
FactorType guest_cntfrq_factor;
FactorType gputick_cntfrq_factor;
};
} // namespace Common::Arm64

View File

@ -39,8 +39,12 @@
#define Crash() exit(1) #define Crash() exit(1)
#endif #endif
#define LTO_NOINLINE __attribute__((noinline))
#else // _MSC_VER #else // _MSC_VER
#define LTO_NOINLINE
// Locale Cross-Compatibility // Locale Cross-Compatibility
#define locale_t _locale_t #define locale_t _locale_t

View File

@ -211,6 +211,11 @@ struct Elf64_Rela {
Elf64_Sxword r_addend; /* Addend */ Elf64_Sxword r_addend; /* Addend */
}; };
/* RELR relocation table entry */
using Elf32_Relr = Elf32_Word;
using Elf64_Relr = Elf64_Xword;
/* How to extract and insert information held in the r_info field. */ /* How to extract and insert information held in the r_info field. */
static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
@ -328,6 +333,9 @@ constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
constexpr u32 ElfDtRelrsz = 35; /* Size of RELR relative relocations */
constexpr u32 ElfDtRelr = 36; /* Address of RELR relative relocations */
constexpr u32 ElfDtRelrent = 37; /* Size of one RELR relative relocation */
} // namespace ELF } // namespace ELF
} // namespace Common } // namespace Common

View File

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

View File

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

View File

@ -13,15 +13,18 @@
#define AMIIBO_DIR "amiibo" #define AMIIBO_DIR "amiibo"
#define CACHE_DIR "cache" #define CACHE_DIR "cache"
#define CONFIG_DIR "config" #define CONFIG_DIR "config"
#define CRASH_DUMPS_DIR "crash_dumps"
#define DUMP_DIR "dump" #define DUMP_DIR "dump"
#define KEYS_DIR "keys" #define KEYS_DIR "keys"
#define LOAD_DIR "load" #define LOAD_DIR "load"
#define LOG_DIR "log" #define LOG_DIR "log"
#define NAND_DIR "nand" #define NAND_DIR "nand"
#define PLAY_TIME_DIR "play_time"
#define SCREENSHOTS_DIR "screenshots" #define SCREENSHOTS_DIR "screenshots"
#define SDMC_DIR "sdmc" #define SDMC_DIR "sdmc"
#define SHADER_DIR "shader" #define SHADER_DIR "shader"
#define TAS_DIR "tas" #define TAS_DIR "tas"
#define ICONS_DIR "icons"
// yuzu-specific files // yuzu-specific files

View File

@ -119,15 +119,18 @@ public:
GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR);
GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache);
GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config);
GenerateYuzuPath(YuzuPath::CrashDumpsDir, yuzu_path / CRASH_DUMPS_DIR);
GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR);
GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR); GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR);
GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR);
GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR); GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR);
GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR); GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR);
GenerateYuzuPath(YuzuPath::PlayTimeDir, yuzu_path / PLAY_TIME_DIR);
GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR); GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR);
GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR);
GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR);
GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR);
GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR);
} }
private: private:
@ -398,6 +401,16 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
} }
std::string_view GetParentPath(std::string_view path) { std::string_view GetParentPath(std::string_view path) {
if (path.empty()) {
return path;
}
#ifdef ANDROID
if (path[0] != '/') {
std::string path_string{path};
return FS::Android::GetParentDirectory(path_string);
}
#endif
const auto name_bck_index = path.rfind('\\'); const auto name_bck_index = path.rfind('\\');
const auto name_fwd_index = path.rfind('/'); const auto name_fwd_index = path.rfind('/');
std::size_t name_index; std::size_t name_index;

View File

@ -15,15 +15,18 @@ enum class YuzuPath {
AmiiboDir, // Where Amiibo backups are stored. AmiiboDir, // Where Amiibo backups are stored.
CacheDir, // Where cached filesystem data is stored. CacheDir, // Where cached filesystem data is stored.
ConfigDir, // Where config files are stored. ConfigDir, // Where config files are stored.
CrashDumpsDir, // Where crash dumps are stored.
DumpDir, // Where dumped data is stored. DumpDir, // Where dumped data is stored.
KeysDir, // Where key files are stored. KeysDir, // Where key files are stored.
LoadDir, // Where cheat/mod files are stored. LoadDir, // Where cheat/mod files are stored.
LogDir, // Where log files are stored. LogDir, // Where log files are stored.
NANDDir, // Where the emulated NAND is stored. NANDDir, // Where the emulated NAND is stored.
PlayTimeDir, // Where play time data is stored.
ScreenshotsDir, // Where yuzu screenshots are stored. ScreenshotsDir, // Where yuzu screenshots are stored.
SDMCDir, // Where the emulated SDMC is stored. SDMCDir, // Where the emulated SDMC is stored.
ShaderDir, // Where shaders are stored. ShaderDir, // Where shaders are stored.
TASDir, // Where TAS scripts are stored. TASDir, // Where TAS scripts are stored.
IconsDir, // Where Icons for Windows shortcuts are stored.
}; };
/** /**

View File

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

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