Compare commits

...

186 Commits

Author SHA1 Message Date
Kelebek1
9c2b211f12 Audren wait as suggested by ByLaws 2023-05-27 17:38:07 +01:00
Matías Locatti
d6db422098 Merge pull request #10414 from liamwhite/anv-push-descriptor
vulkan_device: Enable VK_KHR_push_descriptor on newer ANV
2023-05-26 17:36:37 -03:00
Matías Locatti
919b54848b Merge pull request #10418 from liamwhite/blink-and-youll-miss-it
texture_cache: process aliases and overlaps in the correct order
2023-05-26 17:36:09 -03:00
Matías Locatti
eff0cc3591 Merge pull request #10459 from liamwhite/shf
shader_recompiler: fix copy-paste error
2023-05-26 17:35:13 -03:00
Liam
13d25063a1 shader_recompiler: fix copy-paste error 2023-05-26 00:36:12 -04:00
bunnei
83b502c08c Merge pull request #10221 from Kelebek1/partial_dsp_revert
Add a 5ms tiemout to the DSP processing wait
2023-05-25 21:34:50 -07:00
bunnei
ffa1fba7d6 Merge pull request #10396 from german77/amiibo_write
input_common: Implement amiibo writing
2023-05-25 14:07:16 -07:00
liamwhite
a596c6e438 Merge pull request #10454 from 521337/fix-u-option
Don't exit when using "-u" option in yuzu-cmd
2023-05-25 14:12:51 -04:00
Fernando S
3c3830953a Merge pull request #10452 from liamwhite/ibgc
video_core: don't garbage collect during configuration
2023-05-25 20:09:08 +02:00
Ariel Cabello
7d5df4f0ba Don't exit when using "-u" option in yuzu-cmd 2023-05-25 20:07:52 +02:00
liamwhite
385636dbb1 Merge pull request #10450 from 521337/add-u-option
Add short "-u" option for yuzu_cmd.
2023-05-25 12:24:36 -04:00
Liam
904dc1a567 video_core: don't garbage collect during configuration 2023-05-25 12:03:12 -04:00
Ariel Cabello
d33bdc97d0 Add short "-u" option for yuzu_cmd.
The -u short option was documented but not implemented in yuzu_cmd.
The same long option --user worked before.
2023-05-25 16:05:22 +02:00
bunnei
e264ab4ad0 Merge pull request #10415 from german77/amiibo-no-key
service: nfc: Remove encryption key requirement
2023-05-24 22:14:55 -07:00
bunnei
73a0ea0738 Merge pull request #10435 from FernandoS27/gotta-clean-mess-ups
Texture cache: revert wrong acceleration assumption
2023-05-24 21:00:53 -07:00
bunnei
593236f211 Merge pull request #10433 from FernandoS27/theres-a-lime-coming-around
Texture Cache Util: Fix block depth adjustment on slices.
2023-05-24 20:59:21 -07:00
Fernando Sahmkow
be3a7f4096 Texture cache: revert wrong acceleration assumption 2023-05-24 10:52:02 +02:00
Fernando Sahmkow
01c4568786 Texture Cache Util: Fix block depth adjustment on slices. 2023-05-24 10:06:58 +02:00
Fernando Sahmkow
72c1ee1bf9 texture_cache: process aliases and overlaps in the correct order 2023-05-24 09:53:42 +02:00
Fernando S
72a14a6817 Merge pull request #10422 from liamwhite/gc
video_core: tune garbage collection aggressiveness
2023-05-24 03:58:49 +02:00
Fernando S
4ee4fdf78c Merge pull request #10417 from liamwhite/check-state
k_memory_block_manager: remove auditing calls
2023-05-24 03:56:58 +02:00
Fernando S
76f6388969 Merge pull request #10398 from liamwhite/bcn
video_core: add ASTC recompression
2023-05-24 03:55:45 +02:00
Liam
3b1172c10f video_core: tune garbage collection aggressiveness 2023-05-23 12:55:14 -04:00
Liam
415c78b87c textures: add BC1 and BC3 compressors and recompression setting 2023-05-23 12:54:40 -04:00
liamwhite
a5d4c3e5ad Merge pull request #10388 from GPUCode/fence-wait
vk_master_semaphore: Move fence wait on separate thread
2023-05-23 09:42:56 -04:00
liamwhite
58be9b12f4 Merge pull request #10402 from liamwhite/uh
renderer_vulkan: barrier attachment feedback loops
2023-05-23 09:42:49 -04:00
liamwhite
09ed5ec665 Merge pull request #10411 from scorpion81/gc-steamdeck-fix-attempt
Limit device access memory for integrated devices to 4 GB
2023-05-23 09:42:39 -04:00
Liam
5e3b3c6643 k_memory_block_manager: remove auditing calls 2023-05-23 09:15:16 -04:00
Narr the Reg
f63586c5f5 service: nfc: Remove encryption key requirement 2023-05-22 18:42:09 -06:00
Liam
8bba9f7dea vulkan_device: Enable VK_KHR_push_descriptor on newer ANV 2023-05-22 19:53:20 -04:00
Liam
8758932031 renderer_vulkan: barrier attachment feedback loops 2023-05-22 18:10:16 -04:00
Narr the Reg
e8a025b4f8 Merge pull request #10386 from german77/high
input_common: Map motion with relative values not absolute ones
2023-05-22 13:13:50 -06:00
scorpion81
f8e7b44d28 Limit the device access memory to 4 GB
Hardly limiting the device access memory to 4 GB for integrated vulkan devices here. This works for the Steam Deck in order not to go above 4 GB VRAM usage any more (above this value the likelihood to crash when the RAM exceeds 12 GB as well raises).

But there will be perhaps a detection mechanism necessary for detecting the real memory limit for integrated vulkan devices. Those likely might have small limits anyway, but what about integrated GPUs on machines with > 16 GB RAM, aka larger amounts ?
2023-05-22 16:48:55 +02:00
Narr the Reg
fdb2002f77 input_common: Implement amiibo writting 2023-05-21 21:09:20 -06:00
liamwhite
f82efe9f65 Merge pull request #10392 from danilaml/update-cubeb-again
externals: update cubeb (again)
2023-05-21 00:24:12 -04:00
Danila Malyutin
e5c2ec223a externals: update cubeb 2023-05-21 03:02:26 +04:00
GPUCode
4e491ab59b vk_master_semaphore: Move fence wait on separate thread 2023-05-20 19:23:53 +03:00
german77
dbcdb3523b input_common: Map motion with relative values not absolute ones 2023-05-19 21:19:29 -06:00
bunnei
1e398e6c36 Merge pull request #10344 from german77/pro-amiibo
input_common: Fix pro controller amiibo support
2023-05-18 18:10:38 -07:00
Danila Malyutin
55d740fffa externals: update cubeb (#10362) 2023-05-19 00:24:45 +02:00
Mai
f6c5507873 Merge pull request #10377 from liamwhite/constexpr
renderer_vulkan: remove wrong constexpr
2023-05-18 18:04:07 -04:00
Liam
de7c92d7c4 renderer_vulkan: remove wrong constexpr 2023-05-18 18:01:01 -04:00
Kelebek1
d75bcdd077 Smooth out the DSP callback by adding a 5ms wait time limit 2023-05-18 13:03:32 +01:00
Matías Locatti
f35c14fb73 Merge pull request #10365 from lat9nq/anv_push_descriptor
vulkan_device: Disable VK_KHR_push_descriptor on ANV
2023-05-18 01:38:24 -03:00
lat9nq
c1a8a508bc vulkan_device: Disable VK_KHR_push_descriptor on ANV
Mesa commit ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc breaks
VK_KHR_push_descriptor usage on ANV drivers 22.3.0, so disable it
and allow games to boot.
2023-05-17 22:19:57 -04:00
bunnei
9d4a2de72b Merge pull request #10262 from liamwhite/depth-clamp
vulkan_common: disable depth clamp dynamic state for older radv
2023-05-17 12:19:03 -07:00
Narr the Reg
5693434b8a input_common: Fix pro controller amiibo support 2023-05-16 18:37:22 -06:00
liamwhite
47c5c37bed Merge pull request #10217 from Kelebek1/clear_value
Use the rendertarget format of the correct RT rather than the first valid
2023-05-16 10:06:30 -04:00
liamwhite
a540d248f3 Merge pull request #10107 from grimkor/allow-fully-customised-hotkeys
Allow fully customised controller hotkeys
2023-05-16 10:06:15 -04:00
liamwhite
bbb6b58aa4 Merge pull request #10181 from lat9nq/intel-compute-toggle
configure_graphics: Add option to enable compute pipelines for Intel proprietary
2023-05-15 12:05:24 -04:00
liamwhite
736a31e4ff Merge pull request #10234 from Kelebek1/clouds_depth
Fix Tears of the Kingdom flickering clouds and depths geometry.
2023-05-15 12:03:31 -04:00
liamwhite
6060685609 Merge pull request #10249 from FernandoS27/sorry-i-am-late
Buffer Cache: Clear sync code.
2023-05-15 12:03:25 -04:00
liamwhite
5b85925469 Merge pull request #10254 from danilaml/fix-h264-decode
Fix missing pic_order_present_flag in h264 header
2023-05-15 12:03:14 -04:00
liamwhite
033aa264cf Merge pull request #10265 from german77/amiibo-lag
input_common: Make amiibo scanning less demanding
2023-05-15 12:03:08 -04:00
liamwhite
9087fe10e9 Merge pull request #10294 from german77/vibration_span
service: hid: Use span instead of vector reference
2023-05-15 12:03:00 -04:00
german77
6e54615b16 service: hid: Use span instead of vector reference 2023-05-14 18:07:04 -06:00
Fernando Sahmkow
c4bfbc6d25 Buffer Cache: Clear sync code. 2023-05-15 01:50:21 +02:00
liamwhite
e9069dfe76 Merge pull request #10288 from liamwhite/vram-limits
vulkan_device: reserve extra memory to prevent swaps
2023-05-14 17:02:15 -04:00
Liam
2be751100b vulkan_device: reserve extra memory to prevent swaps 2023-05-14 16:49:59 -04:00
german77
ac531aa15f input_common: Make amiibo scanning less demanding 2023-05-14 09:04:37 -06:00
bunnei
29c7176f55 Merge pull request #10286 from liamwhite/compatible-bits
vulkan_common: fix incompatible property flags
2023-05-14 01:23:29 -07:00
Liam
122435e080 vulkan_common: fix incompatible property flags 2023-05-14 01:13:11 -04:00
Liam
856838f7ce vulkan_common: disable depth clamp dynamic state for older radv 2023-05-13 00:37:17 -04:00
Fernando S
9c739f1506 Merge pull request #10244 from liamwhite/lower-upper
time: implement ContinuousAdjustmentTimePoint
2023-05-13 03:51:05 +02:00
Fernando S
075d73f076 Merge pull request #10243 from Kelebek1/red_dot
Correctly track render target index in the framebuffer for image aspects
2023-05-13 03:50:31 +02:00
bunnei
021e503cc8 Merge pull request #10237 from liamwhite/cache-storage
fs: stub cache storage
2023-05-12 16:42:17 -07:00
bunnei
1805de0301 Merge pull request #10236 from liamwhite/thats-not-an-ibinder
nvnflinger: fix Parcel serialization
2023-05-12 16:07:35 -07:00
Danila Malyutin
9367769fe7 Fix missing pic_order_present_flag in h264 header
Fixes #9635
2023-05-12 22:30:59 +04:00
Liam
a22c5a3880 time: implement ContinuousAdjustmentTimePoint 2023-05-11 21:05:27 -04:00
Kelebek1
cd0ded7771 Correctly track RT indexes for image aspect lookup during clears 2023-05-12 01:40:21 +01:00
Liam
351079a4ba fs: adjust future save path 2023-05-11 17:30:30 -04:00
Liam
62bcb99ba8 am: stub CreateCacheStorage 2023-05-11 17:26:02 -04:00
Liam
13e4ceb990 fs: stub cache storage and fix params alignment 2023-05-11 17:23:28 -04:00
Liam
bb94beed15 nvnflinger: fix Parcel serialization 2023-05-11 17:09:19 -04:00
Liam
6e10a0c130 nvnflinger: fix producer slot fence init 2023-05-11 17:08:14 -04:00
Kelebek1
e42b4a16b6 Fix Tears of the Kingdom flickering clouds and depths. 2023-05-11 19:25:24 +01:00
liamwhite
182221b9ff Merge pull request #10132 from Kelebek1/fermi_blit2
Allow Fermi blit accelerate to work without images in cache
2023-05-11 10:45:59 -04:00
liamwhite
2643ea80df Merge pull request #10216 from Kelebek1/buffer_cache_region_checks
Swap order of checking/setting region modifications in the buffer_cache
2023-05-11 10:45:47 -04:00
liamwhite
f94186d3c3 Merge pull request #10222 from liamwhite/q
renderer_vulkan: separate guest and host compute descriptor queues
2023-05-11 10:45:36 -04:00
Kelebek1
bf08bc3c0f Allow Fermi blit accelerate to add src/dst to the cache if they don't exist already. Use ScratchBuffers in the software blit path. 2023-05-11 06:42:38 +01:00
Fernando S
871e7cacf6 Merge pull request #10224 from yuzu-emu/readme-update
Update README.md to remove Skyline license exception.
2023-05-11 05:51:32 +02:00
bunnei
2fe922aae5 Update README.md to remove Skyline license exception. 2023-05-10 15:52:30 -07:00
grimkor
ceb65c259a Allow fully customisable controller hotkeys 2023-05-10 19:28:11 +01:00
Liam
67fd1df762 renderer_vulkan: separate guest and host compute descriptor queues 2023-05-10 13:46:48 -04:00
liamwhite
b7f60e9123 Merge pull request #10207 from german77/amiibo_cheater
service: nfp: Allow to load with a different amiibo id
2023-05-10 10:25:48 -04:00
Narr the Reg
3ec027400e Merge pull request #10119 from marius851000/improved_non_hd_feeback
Attempt at improving HD Rumble emulation
2023-05-09 22:59:32 -06:00
Narr the Reg
42e1db4b0e service: nfc: Seed all random values 2023-05-09 17:54:07 -06:00
german77
1968cc7b10 service: nfp: Allow to load with a different amiibo id 2023-05-09 17:51:59 -06:00
Kelebek1
a386003b64 Use the rendertarget format of the correct RT rather than the first valid 2023-05-09 22:13:15 +01:00
Kelebek1
b3691fc33c Swap order of checking/setting region modifications in the buffer_cache 2023-05-09 20:21:08 +01:00
liamwhite
7944f271dc Merge pull request #10183 from liamwhite/mods
vfs_vector: avoid n^2 lookup in layeredfs building
2023-05-09 09:47:36 -04:00
liamwhite
5890b96ce5 Merge pull request #10203 from german77/calibration
core: hid: Allow to calibrate gyro sensor
2023-05-09 09:47:29 -04:00
liamwhite
1f14b58315 Merge pull request #10206 from FernandoS27/astc-3d
Texture Cache: Fix 3D ASTC textures
2023-05-09 09:47:22 -04:00
liamwhite
3f048770d3 Merge pull request #10208 from german77/amiibo_joycon
input_common: Fix nfc detection for joycons
2023-05-09 09:47:14 -04:00
german77
cb1487d774 input_common: Fix nfc detection for joycons 2023-05-09 00:32:53 -06:00
Fernando Sahmkow
8a214e5530 Texture Cache: Fix ASTC textures 2023-05-09 02:42:10 +02:00
liamwhite
15ec8d3e44 Merge pull request #10205 from jbeich/freebsd
qt_common: unbreak build on BSDs
2023-05-08 17:29:33 -04:00
Jan Beich
a4362765a6 qt_common: consistently ifdef QPlatform after cbd79df233
src/yuzu/qt_common.cpp:45:33: error: member access into incomplete type 'QPlatformNativeInterface'
    wsi.display_connection = pni->nativeResourceForWindow("display", window);
                                ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
src/yuzu/qt_common.cpp:47:42: error: member access into incomplete type 'QPlatformNativeInterface'
        wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
                                         ^
/usr/include/qt6/QtGui/qguiapplication.h:20:7: note: forward declaration of 'QPlatformNativeInterface'
class QPlatformNativeInterface;
      ^
2023-05-08 20:47:16 +00:00
Narr the Reg
e1838f51a3 yuzu: Make 3d cube with joycon shape 2023-05-08 12:06:39 -06:00
Narr the Reg
97bd6d6418 core: hid: Allow to calibrate gyro sensor 2023-05-08 12:06:38 -06:00
lat9nq
40f3e2fbf1 configure_graphics_advanced: Hide input compute toggle a little later
SetColoredTristate causes the setting to become visible as it calls
`show()` on it.
2023-05-08 12:32:56 -04:00
bunnei
b70a205a96 Merge pull request #10075 from Kelebek1/silence_nifm_spam
Silence network spam
2023-05-07 17:45:32 -07:00
bunnei
3d8eca92f9 Merge pull request #10197 from liamwhite/resume-token
bootmanager: remove stop_token header
2023-05-07 17:43:15 -07:00
bunnei
d6d60f7104 Merge pull request #10194 from bunnei/update-dynarmic-3
externals: Update dynarmic to include latest patch.
2023-05-07 17:13:32 -07:00
Liam
bdb7c11d8e bootmanager: remove stop_token header 2023-05-07 19:20:09 -04:00
liamwhite
8f605b542c Merge pull request #10195 from german77/mutex
core: hid: Update motion on a better place
2023-05-07 19:06:10 -04:00
liamwhite
2688fb1aa2 Merge pull request #10155 from FernandoS27/reactive-flushing-new
Y.F.C. bring back Reactive Flushing
2023-05-07 19:05:56 -04:00
Liam
d100de27ee vfs_layered: avoid n^2 lookup in layeredfs building 2023-05-07 19:03:41 -04:00
german77
cf023aa8ec core: hid: Update motion on a better place 2023-05-07 17:01:57 -06:00
bunnei
7eb17f3aa5 externals: Update dynarmic to include latest patch. 2023-05-07 15:06:58 -07:00
Fernando Sahmkow
8014dd8259 Texture cache: Only force flush the dma downloads 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
2df19ef0fd Buffer Cache: disable reactive flushing in it. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
016c6feb49 Texture cache: reverse inmediate flush changes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
36c302fa32 Buffer cache: always use async buffer downloads and fix regression. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
6f90dff293 Address feedback, add CR notice, etc 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
ab0c0a469c Query cache: stop updating pages as it's not affected by cpu writes 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
92da86290c Settings: add option to enable / disable reactive flushing 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
0f4f18265f Texture cache: sync the first flush. 2023-05-07 23:46:12 +02:00
Fernando Sahmkow
c6cac2ffaa GPU: Add Reactive flushing 2023-05-07 23:46:12 +02:00
Liam
5792a72c29 vfs_vector: avoid n^2 lookup in layeredfs building 2023-05-07 16:50:35 -04:00
bunnei
e58090c9c7 Merge pull request #10097 from german77/nfp_full
service: nfc: Merge device interfaces and create the device manager
2023-05-07 12:18:09 -07:00
bunnei
ed98afdebc Merge pull request #10192 from bunnei/update-dynarmic-2
externals: Update dynarmic to include latest patch.
2023-05-07 12:17:40 -07:00
bunnei
e46074a2e3 externals: Update dynarmic to include latest patch. 2023-05-07 12:04:42 -07:00
liamwhite
0c5bdc7241 Merge pull request #10081 from Kelebek1/copy_overlap_tick
Sort overlap_ids by modification tick before copy
2023-05-07 14:09:10 -04:00
liamwhite
cb49c3b64c Merge pull request #10172 from Kelebek1/debug_validation_names
Log object names with debug renderer, add a GPU address to ImageViews
2023-05-07 14:09:03 -04:00
liamwhite
35862b21d6 Merge pull request #10189 from Qigo42/master
yuzu/applets/qt_profile_select: connect double-click to accept()
2023-05-07 14:08:57 -04:00
QGJ
4366a21eae yuzu/applets/qt_profile_select: connect double-click to accept()
In the profile selection window:

Allow the user to start the game by double-clicking a profile to avoid having to additionally click the OK button. This avoids an unnecessary "step" to the start of the game...
2023-05-07 18:05:42 +02:00
Fernando S
1166ca3a57 Merge pull request #10187 from Kelebek1/address_space
Fix address space allocator slow path to avoid OOB
2023-05-07 17:06:36 +02:00
Kelebek1
6fed48b3a4 Fix address space allocator slow path to avoid OOB 2023-05-07 15:11:16 +01:00
bunnei
f36b5dfd18 Merge pull request #10180 from german77/debug
input_common: Revert debugging changes
2023-05-06 22:26:53 -07:00
german77
91695a453b input_common: Revert debugging changes 2023-05-06 23:06:44 -06:00
lat9nq
55c77dd25b yuzu-qt/config: Add option to disable compute on Intel
This option is only visible if an Intel GPU using the proprietary
driver is found during Vulkan device enumeration.

configure_graphics: More directly get driver id

Vulkan::Device does quite a bit more than we need just to see the
driver ID here.
2023-05-07 01:06:22 -04:00
lat9nq
6ed6e6e18e vk_pipeline_cache: Use setting to disable intel compute 2023-05-07 01:06:22 -04:00
lat9nq
725aacb4bc settings: Add enable compute pipelines
For the Intel proprietary driver's deficiencies.

settings: Restore compute option global state
2023-05-07 01:06:20 -04:00
bunnei
3547248ec2 Merge pull request #10125 from lat9nq/vsync-select
configuration: Expose separate swap present modes
2023-05-06 21:55:39 -07:00
bunnei
82188b2f79 Merge pull request #10174 from german77/motriod
input_common: Add experimental motion to button
2023-05-06 21:40:20 -07:00
bunnei
5467d39bc3 Merge pull request #10171 from german77/powera
input_common: Add property to invert an axis button
2023-05-06 21:39:44 -07:00
liamwhite
27ff00acb1 Merge pull request #10162 from lat9nq/sdl-remove-old
yuzu-sdl,audio_core: Remove antiquated warning ignore
2023-05-06 23:10:09 -04:00
liamwhite
f1b770a8f0 Merge pull request #10165 from merryhime/update-dynarmic
externals: Update dynarmic to 6.4.7
2023-05-06 23:10:02 -04:00
liamwhite
9c9b4616c3 Merge pull request #10167 from german77/motion_preview
yuzu: Add motion preview to controller input
2023-05-06 23:09:55 -04:00
liamwhite
e7b6389e44 Merge pull request #10178 from ronikirla/2-hour-crash
Fix read access violation after 2 hours of gameplay in PMD: RTDX
2023-05-06 23:09:36 -04:00
Roni Kirla
a13fd5f7cc Fix read access violation 2023-05-07 00:13:33 +03:00
german77
9b771bcb8f input_common: Add experimental motion to button 2023-05-05 22:49:56 -06:00
Narr the Reg
94151097b9 service: nfc: Merge device interfaces and create the device manager 2023-05-05 22:02:59 -06:00
Kelebek1
ca6bf06ef7 Log object names with debug renderer, add a GPU address to ImageViews 2023-05-06 04:48:32 +01:00
Narr the Reg
f017335fef input_common: Add property to invert an axis button 2023-05-05 17:18:35 -06:00
Narr the Reg
f764223f93 input_common: Add property to invert an axis button 2023-05-05 17:14:20 -06:00
Narr the Reg
46e835f2d6 yuzu: Add motion preview to controller input 2023-05-05 13:53:38 -06:00
bunnei
bb2e407772 Merge pull request #10159 from german77/home_screenshot
core: hid: Fix state of capture and home buttons
2023-05-05 12:02:15 -07:00
marius david
0a6bd8b236 Improve emulation of HD Rumble 2023-05-05 19:30:40 +02:00
Merry
432d754d7d externals: Update dynarmic to 6.4.7 2023-05-05 16:30:18 +01:00
lat9nq
740c349820 yuzu-sdl,audio_core: Remove antiquated warning ignore
Issue was fixed a long time ago, both by SDL2 and in yuzu by including
SDL2 as a system library.
2023-05-05 03:48:28 -04:00
german77
8df3aed2f1 core: hid: Fix state of capture and home buttons 2023-05-04 22:36:59 -06:00
liamwhite
16939b1a6e Merge pull request #10128 from Kelebek1/audren_terminate
Wait for the terminate event before destroying a system instance
2023-05-04 14:44:09 -04:00
liamwhite
60d54d911e Merge pull request #10145 from Kelebek1/code_size
Fix shader code resize to use word size rather than byte size
2023-05-04 14:44:02 -04:00
liamwhite
e2b81ae5fe Merge pull request #10156 from v1993/looks-decent-to-me
Remove LGTM config
2023-05-04 14:43:55 -04:00
Valeri
b095a0242d Remove LGTM config
LGTM.com is no longer available since it was superseded by CodeQL.
2023-05-04 15:36:47 +03:00
Fernando S
2506594c50 Merge pull request #10153 from FernandoS27/a-quickie-fixie
Memory manager: Fix possible softlock
2023-05-04 03:56:53 +02:00
Fernando S
7d5683c63c Merge pull request #10154 from liamwhite/optimistic
settings: remove pessimistic flushing
2023-05-04 01:55:51 +02:00
bunnei
055ee84024 Merge pull request #10142 from FernandoS27/missing-astc
GPU: implement missing ASTC
2023-05-03 16:49:27 -07:00
Liam
ae59ffc56c settings: remove pessimistic flushing 2023-05-03 18:52:42 -04:00
lat9nq
a71498d163 qt_common: Remove yuzu prefix 2023-05-03 18:11:53 -04:00
Fernando Sahmkow
87a9be8dec GPU: implement missing ASTC 2023-05-03 11:33:28 -04:00
lat9nq
35e7f36a39 configure_graphics: No there isn't a hyphen in VSync
Most sources seem to suggest VSync and not V-Sync
2023-05-02 21:52:43 -04:00
lat9nq
d82cad3fb3 configure_input_player: Add missing include
Cleaning up includes in bootmanager and configure_graphics has exposed a
missing include here.
2023-05-02 21:52:43 -04:00
lat9nq
cd2981ee12 configure_graphics: Clean up includes [IWYU] 2023-05-02 21:52:43 -04:00
lat9nq
0c0f5b7ccc bootmanager: Clean up includes [IWYU]
bootmanager: Remove system-specific headers

IWYU can be too complete I suppose.
2023-05-02 21:52:43 -04:00
lat9nq
a546ecbb12 configure_graphics: Actively find present modes
When Vulkan devices are enumerated, this also determines the available
present modes for each device, maps them to a vector, and gives
those options to the user.
OpenGL options are limited to On/Off.

Required creating a VkSurfaceKHR during device enumeration, which
may or may not be desireable. For the sake of a less confusing UI.

Also fixes a bug where if a graphics device disappears on the host, we
don't try and select the non-existant devices.

configure_graphics: Remove vsync runtime lock for Vulkan

configure_graphics: Recommend Mailbox present mode

configure_graphics: Fix type-limits warning

configure_graphics: Clean up includes

configure_graphics: Add tooltip
2023-05-02 21:52:43 -04:00
lat9nq
952b271092 vk_swapchain: Use certain modes for unlocked
Uses mailbox, then immediate for unlocked framerate depending on
support for either. Also adds support for FIFO_RELAXED.

This function now assumes vsync_mode was originially configured to a value
that the driver supports.

vk_swapchain: ChooseSwapPresentMode determines updates

Simplifies swapchain a bit and allows us to change the present mode
during guest runtime.

vk_swapchain: Fix MSVC error

vk_swapchain: Enforce available present modes

Some frontends don't check the value of vsync_mode before comitting it.
Just as well, since a driver update or misconfiguration could problems
in the swap chain.

vk_swapchain: Silence warnings

Silences GCC warnings implicit-fallthrough and shadow, which apparently
are not enabled on clang.
2023-05-02 21:52:43 -04:00
lat9nq
a090a380be bootmanager: Remove inaccurate switch
Those vulkan settings do not correspond 1:1 to the swap intervals that
they set for OpenGL, so remove it.

bootmanager: Add missing include

I didn't add this log why did it break
2023-05-02 21:51:30 -04:00
lat9nq
cbd79df233 qt_common: Move window info function out of bootmanager
Function is useful outside of bootmanager, so put it in a common place.

qt_common: Add missing include

qt_common: Add some newlines

qt_common: Add trailing newline

qt_common: Add trainline newline
2023-05-02 21:51:30 -04:00
lat9nq
c6c11c1553 vulkan_surface: Pass only window info for surface creation
We don't need the whole EmuWindow when creating a surface,
and it creates onerous requirements outside of typical usage for
creating a surface elsewhere.
2023-05-02 21:51:30 -04:00
lat9nq
2528cf7c54 settings: Enable FIFO relaxed
Not entirely sure if we need this, but there's also no reason not to
support it.

settings: Give VSyncMode values
2023-05-02 21:51:30 -04:00
lat9nq
6b973c5986 configure_graphics: Fix another typo 2023-05-02 21:51:29 -04:00
lat9nq
40f062f749 telemetry_session: Make translate function static
Addresses review feedback

Co-authored-by: Lioncash <mathew1800@gmail.com>
2023-05-02 21:51:29 -04:00
lat9nq
29a56496bf bootmanager: Return value in impossible case
The setting is ranged, so this return statement is unreachable.
But GCC can't tell I guess.
2023-05-02 21:51:29 -04:00
lat9nq
41a103c0fc configure_graphics: Fix typo 2023-05-02 21:51:29 -04:00
lat9nq
66ed1c1872 default_ini: Update V-Sync description
default_ini: Update vsync text

default_ini: Add tooltip from configure_graphics
2023-05-02 21:51:29 -04:00
lat9nq
6f0929df82 configuration: Expose separate swap present modes
Previously, yuzu would try and guess which vsync mode to use given
different scenarios, but apparently we didn't always get it right. This
exposes the separate modes in a drop-down the user can select.

If a mode isn't available in Vulkan, it defaults to FIFO.
2023-05-02 21:51:29 -04:00
Kelebek1
f902cc2a2b Fix code resize to use word size rather than byte size 2023-05-02 23:52:21 +01:00
Kelebek1
2feb40f14d Wait for the terminate event before destroying a system instance 2023-05-01 00:27:12 +01:00
german77
5e16fe4579 core: service: Add FunctionInfoTyped to allow expanding existing interfaces 2023-04-25 23:36:24 -06:00
Narr the Reg
00d76fc5f5 service: nfc: Create mifare interface 2023-04-23 23:28:09 -06:00
Narr the Reg
a3fa64fcc4 service: nfc: Create interface 2023-04-23 23:28:09 -06:00
Kelebek1
4da4ecb1ff Silence nifm spam 2023-04-22 14:29:58 +01:00
Kelebek1
3346de365a Sort overlap_ids by modification tick before copy 2023-04-22 14:02:10 +01:00
201 changed files with 5897 additions and 3628 deletions

View File

@@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
path_classifiers:
library: "externals"
extraction:
cpp:
prepare:
packages:
- "libsdl2-dev"
- "qtmultimedia5-dev"
- "libtbb-dev"
- "libjack-jackd2-dev"

View File

@@ -83,5 +83,3 @@ If you wish to support us a different way, please join our [Discord](https://dis
## License
yuzu is licensed under the GPLv3 (or any later version). Refer to the [LICENSE.txt](https://github.com/yuzu-emu/yuzu/blob/master/LICENSE.txt) file.
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) may choose to use the code from these contributors under the GPL-3.0-or-later OR MPL-2.0: [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq)

View File

@@ -139,3 +139,7 @@ if (NOT TARGET LLVM::Demangle)
target_sources(demangle PRIVATE demangle/ItaniumDemangle.cpp)
add_library(LLVM::Demangle ALIAS demangle)
endif()
add_library(stb STATIC)
target_include_directories(stb PUBLIC ./stb)
target_sources(stb PRIVATE stb/stb_dxt.cpp)

765
externals/stb/stb_dxt.cpp vendored Normal file
View File

@@ -0,0 +1,765 @@
// SPDX-FileCopyrightText: fabian "ryg" giesen
// SPDX-License-Identifier: MIT
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor
#include <stb_dxt.h>
#include <stdlib.h>
#include <string.h>
#if !defined(STBD_FABS)
#include <math.h>
#endif
#ifndef STBD_FABS
#define STBD_FABS(x) fabs(x)
#endif
static const unsigned char stb__OMatch5[256][2] = {
{0, 0}, {0, 0}, {0, 1}, {0, 1}, {1, 0}, {1, 0}, {1, 0}, {1, 1}, {1, 1},
{1, 1}, {1, 2}, {0, 4}, {2, 1}, {2, 1}, {2, 1}, {2, 2}, {2, 2}, {2, 2},
{2, 3}, {1, 5}, {3, 2}, {3, 2}, {4, 0}, {3, 3}, {3, 3}, {3, 3}, {3, 4},
{3, 4}, {3, 4}, {3, 5}, {4, 3}, {4, 3}, {5, 2}, {4, 4}, {4, 4}, {4, 5},
{4, 5}, {5, 4}, {5, 4}, {5, 4}, {6, 3}, {5, 5}, {5, 5}, {5, 6}, {4, 8},
{6, 5}, {6, 5}, {6, 5}, {6, 6}, {6, 6}, {6, 6}, {6, 7}, {5, 9}, {7, 6},
{7, 6}, {8, 4}, {7, 7}, {7, 7}, {7, 7}, {7, 8}, {7, 8}, {7, 8}, {7, 9},
{8, 7}, {8, 7}, {9, 6}, {8, 8}, {8, 8}, {8, 9}, {8, 9}, {9, 8}, {9, 8},
{9, 8}, {10, 7}, {9, 9}, {9, 9}, {9, 10}, {8, 12}, {10, 9}, {10, 9}, {10, 9},
{10, 10}, {10, 10}, {10, 10}, {10, 11}, {9, 13}, {11, 10}, {11, 10}, {12, 8}, {11, 11},
{11, 11}, {11, 11}, {11, 12}, {11, 12}, {11, 12}, {11, 13}, {12, 11}, {12, 11}, {13, 10},
{12, 12}, {12, 12}, {12, 13}, {12, 13}, {13, 12}, {13, 12}, {13, 12}, {14, 11}, {13, 13},
{13, 13}, {13, 14}, {12, 16}, {14, 13}, {14, 13}, {14, 13}, {14, 14}, {14, 14}, {14, 14},
{14, 15}, {13, 17}, {15, 14}, {15, 14}, {16, 12}, {15, 15}, {15, 15}, {15, 15}, {15, 16},
{15, 16}, {15, 16}, {15, 17}, {16, 15}, {16, 15}, {17, 14}, {16, 16}, {16, 16}, {16, 17},
{16, 17}, {17, 16}, {17, 16}, {17, 16}, {18, 15}, {17, 17}, {17, 17}, {17, 18}, {16, 20},
{18, 17}, {18, 17}, {18, 17}, {18, 18}, {18, 18}, {18, 18}, {18, 19}, {17, 21}, {19, 18},
{19, 18}, {20, 16}, {19, 19}, {19, 19}, {19, 19}, {19, 20}, {19, 20}, {19, 20}, {19, 21},
{20, 19}, {20, 19}, {21, 18}, {20, 20}, {20, 20}, {20, 21}, {20, 21}, {21, 20}, {21, 20},
{21, 20}, {22, 19}, {21, 21}, {21, 21}, {21, 22}, {20, 24}, {22, 21}, {22, 21}, {22, 21},
{22, 22}, {22, 22}, {22, 22}, {22, 23}, {21, 25}, {23, 22}, {23, 22}, {24, 20}, {23, 23},
{23, 23}, {23, 23}, {23, 24}, {23, 24}, {23, 24}, {23, 25}, {24, 23}, {24, 23}, {25, 22},
{24, 24}, {24, 24}, {24, 25}, {24, 25}, {25, 24}, {25, 24}, {25, 24}, {26, 23}, {25, 25},
{25, 25}, {25, 26}, {24, 28}, {26, 25}, {26, 25}, {26, 25}, {26, 26}, {26, 26}, {26, 26},
{26, 27}, {25, 29}, {27, 26}, {27, 26}, {28, 24}, {27, 27}, {27, 27}, {27, 27}, {27, 28},
{27, 28}, {27, 28}, {27, 29}, {28, 27}, {28, 27}, {29, 26}, {28, 28}, {28, 28}, {28, 29},
{28, 29}, {29, 28}, {29, 28}, {29, 28}, {30, 27}, {29, 29}, {29, 29}, {29, 30}, {29, 30},
{30, 29}, {30, 29}, {30, 29}, {30, 30}, {30, 30}, {30, 30}, {30, 31}, {30, 31}, {31, 30},
{31, 30}, {31, 30}, {31, 31}, {31, 31},
};
static const unsigned char stb__OMatch6[256][2] = {
{0, 0}, {0, 1}, {1, 0}, {1, 1}, {1, 1}, {1, 2}, {2, 1}, {2, 2}, {2, 2},
{2, 3}, {3, 2}, {3, 3}, {3, 3}, {3, 4}, {4, 3}, {4, 4}, {4, 4}, {4, 5},
{5, 4}, {5, 5}, {5, 5}, {5, 6}, {6, 5}, {6, 6}, {6, 6}, {6, 7}, {7, 6},
{7, 7}, {7, 7}, {7, 8}, {8, 7}, {8, 8}, {8, 8}, {8, 9}, {9, 8}, {9, 9},
{9, 9}, {9, 10}, {10, 9}, {10, 10}, {10, 10}, {10, 11}, {11, 10}, {8, 16}, {11, 11},
{11, 12}, {12, 11}, {9, 17}, {12, 12}, {12, 13}, {13, 12}, {11, 16}, {13, 13}, {13, 14},
{14, 13}, {12, 17}, {14, 14}, {14, 15}, {15, 14}, {14, 16}, {15, 15}, {15, 16}, {16, 14},
{16, 15}, {17, 14}, {16, 16}, {16, 17}, {17, 16}, {18, 15}, {17, 17}, {17, 18}, {18, 17},
{20, 14}, {18, 18}, {18, 19}, {19, 18}, {21, 15}, {19, 19}, {19, 20}, {20, 19}, {20, 20},
{20, 20}, {20, 21}, {21, 20}, {21, 21}, {21, 21}, {21, 22}, {22, 21}, {22, 22}, {22, 22},
{22, 23}, {23, 22}, {23, 23}, {23, 23}, {23, 24}, {24, 23}, {24, 24}, {24, 24}, {24, 25},
{25, 24}, {25, 25}, {25, 25}, {25, 26}, {26, 25}, {26, 26}, {26, 26}, {26, 27}, {27, 26},
{24, 32}, {27, 27}, {27, 28}, {28, 27}, {25, 33}, {28, 28}, {28, 29}, {29, 28}, {27, 32},
{29, 29}, {29, 30}, {30, 29}, {28, 33}, {30, 30}, {30, 31}, {31, 30}, {30, 32}, {31, 31},
{31, 32}, {32, 30}, {32, 31}, {33, 30}, {32, 32}, {32, 33}, {33, 32}, {34, 31}, {33, 33},
{33, 34}, {34, 33}, {36, 30}, {34, 34}, {34, 35}, {35, 34}, {37, 31}, {35, 35}, {35, 36},
{36, 35}, {36, 36}, {36, 36}, {36, 37}, {37, 36}, {37, 37}, {37, 37}, {37, 38}, {38, 37},
{38, 38}, {38, 38}, {38, 39}, {39, 38}, {39, 39}, {39, 39}, {39, 40}, {40, 39}, {40, 40},
{40, 40}, {40, 41}, {41, 40}, {41, 41}, {41, 41}, {41, 42}, {42, 41}, {42, 42}, {42, 42},
{42, 43}, {43, 42}, {40, 48}, {43, 43}, {43, 44}, {44, 43}, {41, 49}, {44, 44}, {44, 45},
{45, 44}, {43, 48}, {45, 45}, {45, 46}, {46, 45}, {44, 49}, {46, 46}, {46, 47}, {47, 46},
{46, 48}, {47, 47}, {47, 48}, {48, 46}, {48, 47}, {49, 46}, {48, 48}, {48, 49}, {49, 48},
{50, 47}, {49, 49}, {49, 50}, {50, 49}, {52, 46}, {50, 50}, {50, 51}, {51, 50}, {53, 47},
{51, 51}, {51, 52}, {52, 51}, {52, 52}, {52, 52}, {52, 53}, {53, 52}, {53, 53}, {53, 53},
{53, 54}, {54, 53}, {54, 54}, {54, 54}, {54, 55}, {55, 54}, {55, 55}, {55, 55}, {55, 56},
{56, 55}, {56, 56}, {56, 56}, {56, 57}, {57, 56}, {57, 57}, {57, 57}, {57, 58}, {58, 57},
{58, 58}, {58, 58}, {58, 59}, {59, 58}, {59, 59}, {59, 59}, {59, 60}, {60, 59}, {60, 60},
{60, 60}, {60, 61}, {61, 60}, {61, 61}, {61, 61}, {61, 62}, {62, 61}, {62, 62}, {62, 62},
{62, 63}, {63, 62}, {63, 63}, {63, 63},
};
static int stb__Mul8Bit(int a, int b) {
int t = a * b + 128;
return (t + (t >> 8)) >> 8;
}
static void stb__From16Bit(unsigned char* out, unsigned short v) {
int rv = (v & 0xf800) >> 11;
int gv = (v & 0x07e0) >> 5;
int bv = (v & 0x001f) >> 0;
// expand to 8 bits via bit replication
out[0] = static_cast<unsigned char>((rv * 33) >> 2);
out[1] = static_cast<unsigned char>((gv * 65) >> 4);
out[2] = static_cast<unsigned char>((bv * 33) >> 2);
out[3] = 0;
}
static unsigned short stb__As16Bit(int r, int g, int b) {
return (unsigned short)((stb__Mul8Bit(r, 31) << 11) + (stb__Mul8Bit(g, 63) << 5) +
stb__Mul8Bit(b, 31));
}
// linear interpolation at 1/3 point between a and b, using desired rounding
// type
static int stb__Lerp13(int a, int b) {
#ifdef STB_DXT_USE_ROUNDING_BIAS
// with rounding bias
return a + stb__Mul8Bit(b - a, 0x55);
#else
// without rounding bias
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really
// need every ounce of speed.
return (2 * a + b) / 3;
#endif
}
// linear interpolation at 1/2 point between a and b
static int stb__Lerp12(int a, int b) {
return (a + b) / 2;
}
// lerp RGB color
static void stb__Lerp13RGB(unsigned char* out, unsigned char* p1, unsigned char* p2) {
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
}
static void stb__Lerp12RGB(unsigned char* out, unsigned char* p1, unsigned char* p2) {
out[0] = (unsigned char)stb__Lerp12(p1[0], p2[0]);
out[1] = (unsigned char)stb__Lerp12(p1[1], p2[1]);
out[2] = (unsigned char)stb__Lerp12(p1[2], p2[2]);
}
/****************************************************************************/
static void stb__Eval4Colors(unsigned char* color, unsigned short c0, unsigned short c1) {
stb__From16Bit(color + 0, c0);
stb__From16Bit(color + 4, c1);
stb__Lerp13RGB(color + 8, color + 0, color + 4);
stb__Lerp13RGB(color + 12, color + 4, color + 0);
}
static void stb__Eval3Colors(unsigned char* color, unsigned short c0, unsigned short c1) {
stb__From16Bit(color + 0, c0);
stb__From16Bit(color + 4, c1);
stb__Lerp12RGB(color + 8, color + 0, color + 4);
}
// The color matching function
static unsigned int stb__MatchColorsBlock(unsigned char* block, unsigned char* color) {
unsigned int mask = 0;
int dirr = color[0 * 4 + 0] - color[1 * 4 + 0];
int dirg = color[0 * 4 + 1] - color[1 * 4 + 1];
int dirb = color[0 * 4 + 2] - color[1 * 4 + 2];
int dots[16];
int stops[4];
int i;
int c0Point, halfPoint, c3Point;
for (i = 0; i < 16; i++)
dots[i] = block[i * 4 + 0] * dirr + block[i * 4 + 1] * dirg + block[i * 4 + 2] * dirb;
for (i = 0; i < 4; i++)
stops[i] = color[i * 4 + 0] * dirr + color[i * 4 + 1] * dirg + color[i * 4 + 2] * dirb;
// think of the colors as arranged on a line; project point onto that line,
// then choose next color out of available ones. we compute the crossover
// points for "best color in top half"/"best in bottom half" and then the same
// inside that subinterval.
//
// relying on this 1d approximation isn't always optimal in terms of euclidean
// distance, but it's very close and a lot faster.
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
c0Point = (stops[1] + stops[3]);
halfPoint = (stops[3] + stops[2]);
c3Point = (stops[2] + stops[0]);
for (i = 15; i >= 0; i--) {
int dot = dots[i] * 2;
mask <<= 2;
if (dot < halfPoint)
mask |= (dot < c0Point) ? 1 : 3;
else
mask |= (dot < c3Point) ? 2 : 0;
}
return mask;
}
static unsigned int stb__MatchColorsAlphaBlock(unsigned char* block, unsigned char* color) {
unsigned int mask = 0;
int dirr = color[0 * 4 + 0] - color[1 * 4 + 0];
int dirg = color[0 * 4 + 1] - color[1 * 4 + 1];
int dirb = color[0 * 4 + 2] - color[1 * 4 + 2];
int dots[16];
int stops[3];
int i;
int c0Point, c2Point;
for (i = 0; i < 16; i++)
dots[i] = block[i * 4 + 0] * dirr + block[i * 4 + 1] * dirg + block[i * 4 + 2] * dirb;
for (i = 0; i < 3; i++)
stops[i] = color[i * 4 + 0] * dirr + color[i * 4 + 1] * dirg + color[i * 4 + 2] * dirb;
c0Point = (stops[1] + stops[2]);
c2Point = (stops[2] + stops[0]);
for (i = 15; i >= 0; i--) {
int dot = dots[i] * 2;
mask <<= 2;
if (block[i * 4 + 3] == 0)
mask |= 3;
else if (dot < c2Point)
mask |= (dot < c0Point) ? 0 : 2;
else
mask |= (dot < c0Point) ? 1 : 0;
}
return mask;
}
static void stb__ReorderColors(unsigned short* pmax16, unsigned short* pmin16) {
if (*pmin16 < *pmax16) {
unsigned short t = *pmin16;
*pmin16 = *pmax16;
*pmax16 = t;
}
}
static void stb__FinalizeColors(unsigned short* pmax16, unsigned short* pmin16,
unsigned int* pmask) {
if (*pmax16 < *pmin16) {
unsigned short t = *pmin16;
*pmin16 = *pmax16;
*pmax16 = t;
*pmask ^= 0x55555555;
}
}
// The color optimization function. (Clever code, part 1)
static void stb__OptimizeColorsBlock(unsigned char* block, unsigned short* pmax16,
unsigned short* pmin16) {
int mind, maxd;
unsigned char *minp, *maxp;
double magn;
int v_r, v_g, v_b;
static const int nIterPower = 4;
float covf[6], vfr, vfg, vfb;
// determine color distribution
int cov[6];
int mu[3], min[3], max[3];
int ch, i, iter;
for (ch = 0; ch < 3; ch++) {
const unsigned char* bp = ((const unsigned char*)block) + ch;
int muv, minv, maxv;
muv = minv = maxv = bp[0];
for (i = 4; i < 64; i += 4) {
muv += bp[i];
if (bp[i] < minv)
minv = bp[i];
else if (bp[i] > maxv)
maxv = bp[i];
}
mu[ch] = (muv + 8) >> 4;
min[ch] = minv;
max[ch] = maxv;
}
// determine covariance matrix
for (i = 0; i < 6; i++)
cov[i] = 0;
for (i = 0; i < 16; i++) {
int r = block[i * 4 + 0] - mu[0];
int g = block[i * 4 + 1] - mu[1];
int b = block[i * 4 + 2] - mu[2];
cov[0] += r * r;
cov[1] += r * g;
cov[2] += r * b;
cov[3] += g * g;
cov[4] += g * b;
cov[5] += b * b;
}
// convert covariance matrix to float, find principal axis via power iter
for (i = 0; i < 6; i++)
covf[i] = static_cast<float>(cov[i]) / 255.0f;
vfr = (float)(max[0] - min[0]);
vfg = (float)(max[1] - min[1]);
vfb = (float)(max[2] - min[2]);
for (iter = 0; iter < nIterPower; iter++) {
float r = vfr * covf[0] + vfg * covf[1] + vfb * covf[2];
float g = vfr * covf[1] + vfg * covf[3] + vfb * covf[4];
float b = vfr * covf[2] + vfg * covf[4] + vfb * covf[5];
vfr = r;
vfg = g;
vfb = b;
}
magn = STBD_FABS(vfr);
if (STBD_FABS(vfg) > magn)
magn = STBD_FABS(vfg);
if (STBD_FABS(vfb) > magn)
magn = STBD_FABS(vfb);
if (magn < 4.0f) { // too small, default to luminance
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
v_g = 587;
v_b = 114;
} else {
magn = 512.0 / magn;
v_r = (int)(vfr * magn);
v_g = (int)(vfg * magn);
v_b = (int)(vfb * magn);
}
minp = maxp = block;
mind = maxd = block[0] * v_r + block[1] * v_g + block[2] * v_b;
// Pick colors at extreme points
for (i = 1; i < 16; i++) {
int dot = block[i * 4 + 0] * v_r + block[i * 4 + 1] * v_g + block[i * 4 + 2] * v_b;
if (dot < mind) {
mind = dot;
minp = block + i * 4;
}
if (dot > maxd) {
maxd = dot;
maxp = block + i * 4;
}
}
*pmax16 = stb__As16Bit(maxp[0], maxp[1], maxp[2]);
*pmin16 = stb__As16Bit(minp[0], minp[1], minp[2]);
stb__ReorderColors(pmax16, pmin16);
}
static void stb__OptimizeColorsAlphaBlock(unsigned char* block, unsigned short* pmax16,
unsigned short* pmin16) {
int mind, maxd;
unsigned char *minp, *maxp;
double magn;
int v_r, v_g, v_b;
static const int nIterPower = 4;
float covf[6], vfr, vfg, vfb;
// determine color distribution
int cov[6];
int mu[3], min[3], max[3];
int ch, i, iter;
for (ch = 0; ch < 3; ch++) {
const unsigned char* bp = ((const unsigned char*)block) + ch;
int muv = 0, minv = 256, maxv = -1;
int num = 0;
for (i = 0; i < 64; i += 4) {
if (bp[3 - ch] == 0) {
continue;
}
muv += bp[i];
if (bp[i] < minv)
minv = bp[i];
else if (bp[i] > maxv)
maxv = bp[i];
num++;
}
mu[ch] = num > 0 ? (muv + 8) / num : 0;
min[ch] = minv;
max[ch] = maxv;
}
// determine covariance matrix
for (i = 0; i < 6; i++)
cov[i] = 0;
for (i = 0; i < 16; i++) {
if (block[i * 4 + 3] == 0) {
continue;
}
int r = block[i * 4 + 0] - mu[0];
int g = block[i * 4 + 1] - mu[1];
int b = block[i * 4 + 2] - mu[2];
cov[0] += r * r;
cov[1] += r * g;
cov[2] += r * b;
cov[3] += g * g;
cov[4] += g * b;
cov[5] += b * b;
}
// convert covariance matrix to float, find principal axis via power iter
for (i = 0; i < 6; i++)
covf[i] = static_cast<float>(cov[i]) / 255.0f;
vfr = (float)(max[0] - min[0]);
vfg = (float)(max[1] - min[1]);
vfb = (float)(max[2] - min[2]);
for (iter = 0; iter < nIterPower; iter++) {
float r = vfr * covf[0] + vfg * covf[1] + vfb * covf[2];
float g = vfr * covf[1] + vfg * covf[3] + vfb * covf[4];
float b = vfr * covf[2] + vfg * covf[4] + vfb * covf[5];
vfr = r;
vfg = g;
vfb = b;
}
magn = STBD_FABS(vfr);
if (STBD_FABS(vfg) > magn)
magn = STBD_FABS(vfg);
if (STBD_FABS(vfb) > magn)
magn = STBD_FABS(vfb);
if (magn < 4.0f) { // too small, default to luminance
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
v_g = 587;
v_b = 114;
} else {
magn = 512.0 / magn;
v_r = (int)(vfr * magn);
v_g = (int)(vfg * magn);
v_b = (int)(vfb * magn);
}
minp = maxp = NULL;
mind = 0x7fffffff;
maxd = -0x80000000;
// Pick colors at extreme points
for (i = 0; i < 16; i++) {
if (block[i * 4 + 3] == 0) {
continue;
}
int dot = block[i * 4 + 0] * v_r + block[i * 4 + 1] * v_g + block[i * 4 + 2] * v_b;
if (dot < mind) {
mind = dot;
minp = block + i * 4;
}
if (dot > maxd) {
maxd = dot;
maxp = block + i * 4;
}
}
if (!maxp) {
// all alpha, no color
*pmin16 = 0xffff;
*pmax16 = 0;
} else {
// endpoint colors found
*pmax16 = stb__As16Bit(maxp[0], maxp[1], maxp[2]);
*pmin16 = stb__As16Bit(minp[0], minp[1], minp[2]);
if (*pmax16 == *pmin16) {
// modify the endpoints to indicate presence of an alpha block
if (*pmax16 > 0) {
(*pmax16)--;
} else {
(*pmin16)++;
}
}
stb__ReorderColors(pmax16, pmin16);
}
}
static const float stb__midpoints5[32] = {
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f,
0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f,
0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f};
static const float stb__midpoints6[64] = {
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f,
0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f,
0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f,
0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f,
0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f};
static unsigned short stb__Quantize5(float x) {
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 31);
q += (x > stb__midpoints5[q]);
return q;
}
static unsigned short stb__Quantize6(float x) {
unsigned short q;
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
q = (unsigned short)(x * 63);
q += (x > stb__midpoints6[q]);
return q;
}
// The refinement function. (Clever code, part 2)
// Tries to optimize colors to suit block contents better.
// (By solving a least squares system via normal equations+Cramer's rule)
static int stb__RefineBlock(unsigned char* block, unsigned short* pmax16, unsigned short* pmin16,
unsigned int mask) {
static const int w1Tab[4] = {3, 0, 2, 1};
static const int prods[4] = {0x090000, 0x000900, 0x040102, 0x010402};
// ^some magic to save a lot of multiplies in the accumulating loop...
// (precomputed products of weights for least squares system, accumulated
// inside one 32-bit register)
float f;
unsigned short oldMin, oldMax, min16, max16;
int i, akku = 0, xx, xy, yy;
int At1_r, At1_g, At1_b;
int At2_r, At2_g, At2_b;
unsigned int cm = mask;
oldMin = *pmin16;
oldMax = *pmax16;
if ((mask ^ (mask << 2)) < 4) // all pixels have the same index?
{
// yes, linear system would be singular; solve using optimal
// single-color match on average color
int r = 8, g = 8, b = 8;
for (i = 0; i < 16; ++i) {
r += block[i * 4 + 0];
g += block[i * 4 + 1];
b += block[i * 4 + 2];
}
r >>= 4;
g >>= 4;
b >>= 4;
max16 = static_cast<unsigned short>((stb__OMatch5[r][0] << 11) | (stb__OMatch6[g][0] << 5) |
stb__OMatch5[b][0]);
min16 = static_cast<unsigned short>((stb__OMatch5[r][1] << 11) | (stb__OMatch6[g][1] << 5) |
stb__OMatch5[b][1]);
} else {
At1_r = At1_g = At1_b = 0;
At2_r = At2_g = At2_b = 0;
for (i = 0; i < 16; ++i, cm >>= 2) {
int step = cm & 3;
int w1 = w1Tab[step];
int r = block[i * 4 + 0];
int g = block[i * 4 + 1];
int b = block[i * 4 + 2];
akku += prods[step];
At1_r += w1 * r;
At1_g += w1 * g;
At1_b += w1 * b;
At2_r += r;
At2_g += g;
At2_b += b;
}
At2_r = 3 * At2_r - At1_r;
At2_g = 3 * At2_g - At1_g;
At2_b = 3 * At2_b - At1_b;
// extract solutions and decide solvability
xx = akku >> 16;
yy = (akku >> 8) & 0xff;
xy = (akku >> 0) & 0xff;
f = 3.0f / 255.0f / static_cast<float>(xx * yy - xy * xy);
max16 = static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At1_r * yy - At2_r * xy) * f) << 11);
max16 |= static_cast<unsigned short>(
stb__Quantize6(static_cast<float>(At1_g * yy - At2_g * xy) * f) << 5);
max16 |= static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At1_b * yy - At2_b * xy) * f) << 0);
min16 = static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At2_r * xx - At1_r * xy) * f) << 11);
min16 |= static_cast<unsigned short>(
stb__Quantize6(static_cast<float>(At2_g * xx - At1_g * xy) * f) << 5);
min16 |= static_cast<unsigned short>(
stb__Quantize5(static_cast<float>(At2_b * xx - At1_b * xy) * f) << 0);
}
*pmin16 = min16;
*pmax16 = max16;
stb__ReorderColors(pmax16, pmin16);
return oldMin != min16 || oldMax != max16;
}
// Color block compression
static void stb__CompressColorBlock(unsigned char* dest, unsigned char* block, int alpha,
int mode) {
unsigned int mask;
int i;
int refinecount;
unsigned short max16, min16;
unsigned char color[4 * 4];
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
// check if block is constant
for (i = 1; i < 16; i++)
if (((unsigned int*)block)[i] != ((unsigned int*)block)[0])
break;
if (i == 16 && block[3] == 0 && alpha) { // constant alpha
mask = 0xffffffff;
max16 = 0;
min16 = 0xffff;
} else if (i == 16) { // constant color
int r = block[0], g = block[1], b = block[2];
mask = 0xaaaaaaaa;
max16 = static_cast<unsigned short>((stb__OMatch5[r][0] << 11) | (stb__OMatch6[g][0] << 5) |
stb__OMatch5[b][0]);
min16 = static_cast<unsigned short>((stb__OMatch5[r][1] << 11) | (stb__OMatch6[g][1] << 5) |
stb__OMatch5[b][1]);
} else if (alpha) {
stb__OptimizeColorsAlphaBlock(block, &max16, &min16);
stb__Eval3Colors(color, max16, min16);
mask = stb__MatchColorsAlphaBlock(block, color);
} else {
// first step: PCA+map along principal axis
stb__OptimizeColorsBlock(block, &max16, &min16);
if (max16 != min16) {
stb__Eval4Colors(color, max16, min16);
mask = stb__MatchColorsBlock(block, color);
} else
mask = 0;
// third step: refine (multiple times if requested)
for (i = 0; i < refinecount; i++) {
unsigned int lastmask = mask;
if (stb__RefineBlock(block, &max16, &min16, mask)) {
if (max16 != min16) {
stb__Eval4Colors(color, max16, min16);
mask = stb__MatchColorsBlock(block, color);
} else {
mask = 0;
break;
}
}
if (mask == lastmask)
break;
}
}
// write the color block
if (!alpha)
stb__FinalizeColors(&max16, &min16, &mask);
dest[0] = (unsigned char)(max16);
dest[1] = (unsigned char)(max16 >> 8);
dest[2] = (unsigned char)(min16);
dest[3] = (unsigned char)(min16 >> 8);
dest[4] = (unsigned char)(mask);
dest[5] = (unsigned char)(mask >> 8);
dest[6] = (unsigned char)(mask >> 16);
dest[7] = (unsigned char)(mask >> 24);
}
// Alpha block compression (this is easy for a change)
static void stb__CompressAlphaBlock(unsigned char* dest, unsigned char* src, int stride) {
int i, dist, bias, dist4, dist2, bits, mask;
// find min/max color
int mn, mx;
mn = mx = src[0];
for (i = 1; i < 16; i++) {
if (src[i * stride] < mn)
mn = src[i * stride];
else if (src[i * stride] > mx)
mx = src[i * stride];
}
// encode them
dest[0] = (unsigned char)mx;
dest[1] = (unsigned char)mn;
dest += 2;
// determine bias and emit color indices
// given the choice of mx/mn, these indices are optimal:
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
dist = mx - mn;
dist4 = dist * 4;
dist2 = dist * 2;
bias = (dist < 8) ? (dist - 1) : (dist / 2 + 2);
bias -= mn * 7;
bits = 0, mask = 0;
for (i = 0; i < 16; i++) {
int a = src[i * stride] * 7 + bias;
int ind, t;
// select index. this is a "linear scale" lerp factor between 0 (val=min)
// and 7 (val=max).
t = (a >= dist4) ? -1 : 0;
ind = t & 4;
a -= dist4 & t;
t = (a >= dist2) ? -1 : 0;
ind += t & 2;
a -= dist2 & t;
ind += (a >= dist);
// turn linear scale into DXT index (0/1 are extremal pts)
ind = -ind & 7;
ind ^= (2 > ind);
// write index
mask |= ind << bits;
if ((bits += 3) >= 8) {
*dest++ = (unsigned char)mask;
mask >>= 8;
bits -= 8;
}
}
}
void stb_compress_bc1_block(unsigned char* dest, const unsigned char* src, int alpha, int mode) {
stb__CompressColorBlock(dest, (unsigned char*)src, alpha, mode);
}
void stb_compress_bc3_block(unsigned char* dest, const unsigned char* src, int mode) {
unsigned char data[16][4];
int i;
stb__CompressAlphaBlock(dest, (unsigned char*)src + 3, 4);
dest += 8;
// make a new copy of the data in which alpha is opaque,
// because code uses a fast test for color constancy
memcpy(data, src, 4 * 16);
for (i = 0; i < 16; ++i)
data[i][3] = 255;
src = &data[0][0];
stb__CompressColorBlock(dest, (unsigned char*)src, 0, mode);
}

36
externals/stb/stb_dxt.h vendored Normal file
View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: fabian "ryg" giesen
// SPDX-License-Identifier: MIT
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor
#ifndef STB_INCLUDE_STB_DXT_H
#define STB_INCLUDE_STB_DXT_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef STB_DXT_STATIC
#define STBDDEF static
#else
#define STBDDEF extern
#endif
// compression mode (bitflags)
#define STB_DXT_NORMAL 0
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
#define STB_DXT_HIGHQUAL \
2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
STBDDEF void stb_compress_bc1_block(unsigned char* dest,
const unsigned char* src_rgba_four_bytes_per_pixel, int alpha,
int mode);
STBDDEF void stb_compress_bc3_block(unsigned char* dest, const unsigned char* src, int mode);
#define STB_COMPRESS_DXT_BLOCK
#ifdef __cplusplus
}
#endif
#endif // STB_INCLUDE_STB_DXT_H

View File

@@ -154,6 +154,11 @@ void AudioRenderer::ThreadFunc() {
return;
case RenderMessage::AudioRenderer_Render: {
if (system.IsShuttingDown()) [[unlikely]] {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
mailbox->ADSPSendMessage(RenderMessage::AudioRenderer_RenderResponse);
continue;
}
std::array<bool, MaxRendererSessions> buffers_reset{};
std::array<u64, MaxRendererSessions> render_times_taken{};
const auto start_time{system.CoreTiming().GetClockTicks()};

View File

@@ -436,10 +436,7 @@ void System::Stop() {
}
if (execution_mode == ExecutionMode::Auto) {
// Should wait for the system to terminate here, but core timing (should have) already
// stopped, so this isn't needed. Find a way to make this definite.
// terminate_event.Wait();
terminate_event.Wait();
}
}

View File

@@ -27,7 +27,7 @@ bool SystemManager::InitializeUnsafe() {
if (!active) {
if (adsp.Start()) {
active = true;
thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(); });
thread = std::jthread([this](std::stop_token stop_token) { ThreadFunc(stop_token); });
}
}
@@ -39,8 +39,7 @@ void SystemManager::Stop() {
return;
}
active = false;
update.store(true);
update.notify_all();
thread.request_stop();
thread.join();
adsp.Stop();
}
@@ -85,12 +84,12 @@ bool SystemManager::Remove(System& system_) {
return true;
}
void SystemManager::ThreadFunc() {
void SystemManager::ThreadFunc(std::stop_token stop_token) {
static constexpr char name[]{"AudioRenderSystemManager"};
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
while (active) {
while (active && !stop_token.stop_requested()) {
{
std::scoped_lock l{mutex1};

View File

@@ -66,13 +66,7 @@ private:
/**
* Main thread responsible for command generation.
*/
void ThreadFunc();
enum class StreamState {
Filling,
Steady,
Draining,
};
void ThreadFunc(std::stop_token stop_token);
/// Core system
Core::System& core;
@@ -90,8 +84,6 @@ private:
ADSP::ADSP& adsp;
/// AudioRenderer mailbox for communication
ADSP::AudioRenderer_Mailbox* mailbox{};
/// Atomic for main thread to wait on
std::atomic<bool> update{};
};
} // namespace AudioCore::AudioRenderer

View File

@@ -3,6 +3,7 @@
#include <span>
#include <vector>
#include <SDL.h>
#include "audio_core/common/common.h"
#include "audio_core/sink/sdl2_sink.h"
@@ -10,16 +11,6 @@
#include "common/logging/log.h"
#include "core/core.h"
// Ignore -Wimplicit-fallthrough due to https://github.com/libsdl-org/SDL/issues/4307
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#endif
#include <SDL.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace AudioCore::Sink {
/**
* SDL sink stream, responsible for sinking samples to hardware.

View File

@@ -271,8 +271,11 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
void SinkStream::WaitFreeSpace() {
std::unique_lock lk{release_mutex};
release_cv.wait(
lk, [this]() { return queued_buffers < max_queue_size || system.IsShuttingDown(); });
release_cv.wait_for(lk, std::chrono::milliseconds(5),
[this]() { return queued_buffers < max_queue_size; });
if (queued_buffers > max_queue_size + 3) {
release_cv.wait(lk, [this]() { return queued_buffers < max_queue_size; });
}
}
} // namespace AudioCore::Sink

View File

@@ -72,7 +72,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
}
}()};
if (block_end_predecessor->virt >= virt) {
if (block_end_predecessor != blocks.begin() && block_end_predecessor->virt >= virt) {
// If this block's start would be overlapped by the map then reuse it as a tail
// block
block_end_predecessor->virt = virt_end;
@@ -336,7 +336,7 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
ASSERT_MSG(false, "Unexpected allocator state!");
}
auto search_predecessor{this->blocks.begin()};
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&

View File

@@ -111,6 +111,8 @@ struct AnalogProperties {
float offset{};
// Invert direction of the sensor data
bool inverted{};
// Invert the state if it's converted to a button
bool inverted_button{};
// Press once to activate, press again to release
bool toggle{};
};

View File

@@ -23,7 +23,10 @@ public:
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
~ScratchBuffer() = default;
ScratchBuffer(const ScratchBuffer&) = delete;
ScratchBuffer& operator=(const ScratchBuffer&) = delete;
ScratchBuffer(ScratchBuffer&&) = default;
ScratchBuffer& operator=(ScratchBuffer&&) = default;
/// This will only grow the buffer's capacity if size is greater than the current capacity.
/// The previously held data will remain intact.
@@ -87,6 +90,12 @@ public:
return buffer_capacity;
}
void swap(ScratchBuffer& other) noexcept {
std::swap(last_requested_size, other.last_requested_size);
std::swap(buffer_capacity, other.buffer_capacity);
std::swap(buffer, other.buffer);
}
private:
size_t last_requested_size{};
size_t buffer_capacity{};

View File

@@ -61,7 +61,9 @@ void LogSettings() {
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_AstcRecompression", values.astc_recompression.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
log_setting("Renderer_UseReactiveFlushing", values.use_reactive_flushing.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
@@ -223,15 +225,16 @@ void RestoreGlobalState(bool is_powered_on) {
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.astc_recompression.SetGlobal(true);
values.use_reactive_flushing.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
values.use_pessimistic_flushes.SetGlobal(true);
values.use_vulkan_driver_pipeline_cache.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
values.enable_compute_pipelines.SetGlobal(true);
// System
values.language_index.SetGlobal(true);

View File

@@ -16,6 +16,13 @@
namespace Settings {
enum class VSyncMode : u32 {
Immediate = 0,
Mailbox = 1,
FIFO = 2,
FIFORelaxed = 3,
};
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
@@ -83,6 +90,12 @@ enum class AntiAliasing : u32 {
LastAA = Smaa,
};
enum class AstcRecompression : u32 {
Uncompressed = 0,
Bc1 = 1,
Bc3 = 2,
};
struct ResolutionScalingInfo {
u32 up_scale{1};
u32 down_shift{0};
@@ -456,14 +469,19 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> async_astc{false, "async_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
VSyncMode::FIFORelaxed, "use_vsync"};
SwitchableSetting<bool> use_reactive_flushing{true, "use_reactive_flushing"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
SwitchableSetting<bool> use_pessimistic_flushes{false, "use_pessimistic_flushes"};
SwitchableSetting<bool> use_vulkan_driver_pipeline_cache{true,
"use_vulkan_driver_pipeline_cache"};
SwitchableSetting<bool> enable_compute_pipelines{false, "enable_compute_pipelines"};
SwitchableSetting<AstcRecompression, true> astc_recompression{
AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3,
"astc_recompression"};
SwitchableSetting<u8> bg_red{0, "bg_red"};
SwitchableSetting<u8> bg_green{0, "bg_green"};
@@ -527,6 +545,8 @@ struct Values {
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
Setting<bool> random_amiibo_id{false, "random_amiibo_id"};
// Data Storage
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};

View File

@@ -259,6 +259,20 @@ public:
return *this;
}
void RotateFromOrigin(float roll, float pitch, float yaw) {
float temp = y;
y = std::cos(roll) * y - std::sin(roll) * z;
z = std::sin(roll) * temp + std::cos(roll) * z;
temp = x;
x = std::cos(pitch) * x + std::sin(pitch) * z;
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
temp = x;
x = std::cos(yaw) * x - std::sin(yaw) * y;
y = std::sin(yaw) * temp + std::cos(yaw) * y;
}
[[nodiscard]] constexpr T Length2() const {
return x * x + y * y + z * z;
}

View File

@@ -555,21 +555,22 @@ add_library(core STATIC
hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/mifare_user.cpp
hle/service/nfc/mifare_user.h
hle/service/nfc/common/amiibo_crypto.cpp
hle/service/nfc/common/amiibo_crypto.h
hle/service/nfc/common/device.cpp
hle/service/nfc/common/device.h
hle/service/nfc/common/device_manager.cpp
hle/service/nfc/common/device_manager.h
hle/service/nfc/mifare_result.h
hle/service/nfc/mifare_types.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
hle/service/nfc/nfc_device.cpp
hle/service/nfc/nfc_device.h
hle/service/nfc/nfc_interface.cpp
hle/service/nfc/nfc_interface.h
hle/service/nfc/nfc_result.h
hle/service/nfc/nfc_user.cpp
hle/service/nfc/nfc_user.h
hle/service/nfp/amiibo_crypto.cpp
hle/service/nfp/amiibo_crypto.h
hle/service/nfc/nfc_types.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
hle/service/nfp/nfp_device.cpp
hle/service/nfp/nfp_device.h
hle/service/nfp/nfp_interface.cpp
hle/service/nfp/nfp_interface.h
hle/service/nfp/nfp_result.h

View File

@@ -612,6 +612,10 @@ void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index);
}
size_t System::GetCurrentHostThreadID() const {
return impl->kernel.GetCurrentHostThreadID();
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}

View File

@@ -222,6 +222,8 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
[[nodiscard]] size_t GetCurrentHostThreadID() const;
/// Gets and resets core performance statistics
[[nodiscard]] PerfStatsResults GetAndResetPerfStats();

View File

@@ -82,9 +82,9 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
// Only detect account/device saves from the future location.
switch (type) {
case SaveDataType::SaveData:
return fmt::format("{}/account/{}/{:016X}/1", space_id_path, uuid.RawString(), title_id);
return fmt::format("{}/account/{}/{:016X}/0", space_id_path, uuid.RawString(), title_id);
case SaveDataType::DeviceSaveData:
return fmt::format("{}/device/{:016X}/1", space_id_path, title_id);
return fmt::format("{}/device/{:016X}/0", space_id_path, title_id);
default:
return "";
}

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <set>
#include <utility>
#include "core/file_sys/vfs_layered.h"
@@ -58,11 +59,13 @@ std::string LayeredVfsDirectory::GetFullPath() const {
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
std::vector<VirtualFile> out;
std::set<std::string, std::less<>> out_names;
for (const auto& layer : dirs) {
for (const auto& file : layer->GetFiles()) {
if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
return comp->GetName() == file->GetName();
}) == out.end()) {
auto file_name = file->GetName();
if (!out_names.contains(file_name)) {
out_names.emplace(std::move(file_name));
out.push_back(file);
}
}

View File

@@ -67,6 +67,23 @@ VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
VectorVfsDirectory::~VectorVfsDirectory() = default;
VirtualFile VectorVfsDirectory::GetFile(std::string_view file_name) const {
if (!optimized_file_index_built) {
optimized_file_index.clear();
for (size_t i = 0; i < files.size(); i++) {
optimized_file_index.emplace(files[i]->GetName(), i);
}
optimized_file_index_built = true;
}
const auto it = optimized_file_index.find(file_name);
if (it != optimized_file_index.end()) {
return files[it->second];
}
return nullptr;
}
std::vector<VirtualFile> VectorVfsDirectory::GetFiles() const {
return files;
}
@@ -107,6 +124,7 @@ bool VectorVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) {
}
bool VectorVfsDirectory::DeleteFile(std::string_view file_name) {
optimized_file_index_built = false;
return FindAndRemoveVectorElement(files, file_name);
}
@@ -124,6 +142,7 @@ VirtualFile VectorVfsDirectory::CreateFile(std::string_view file_name) {
}
void VectorVfsDirectory::AddFile(VirtualFile file) {
optimized_file_index_built = false;
files.push_back(std::move(file));
}

View File

@@ -105,6 +105,7 @@ public:
VirtualDir parent = nullptr);
~VectorVfsDirectory() override;
VirtualFile GetFile(std::string_view file_name) const override;
std::vector<VirtualFile> GetFiles() const override;
std::vector<VirtualDir> GetSubdirectories() const override;
bool IsWritable() const override;
@@ -126,6 +127,9 @@ private:
VirtualDir parent;
std::string name;
mutable std::map<std::string, size_t, std::less<>> optimized_file_index;
mutable bool optimized_file_index_built{};
};
} // namespace FileSys

View File

@@ -14,7 +14,7 @@ void DefaultCabinetApplet::Close() const {}
void DefaultCabinetApplet::ShowCabinetApplet(
const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const {
LOG_WARNING(Service_AM, "(STUBBED) called");
callback(false, {});
}

View File

@@ -7,9 +7,9 @@
#include "core/frontend/applets/applet.h"
#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
class NfpDevice;
} // namespace Service::NFP
namespace Service::NFC {
class NfcDevice;
} // namespace Service::NFC
namespace Core::Frontend {
@@ -26,14 +26,14 @@ public:
virtual ~CabinetApplet();
virtual void ShowCabinetApplet(const CabinetCallback& callback,
const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const = 0;
};
class DefaultCabinetApplet final : public CabinetApplet {
public:
void Close() const override;
void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device) const override;
};
} // namespace Core::Frontend

View File

@@ -376,6 +376,7 @@ void EmulatedController::ReloadInput() {
motion.accel = emulated_motion.GetAcceleration();
motion.gyro = emulated_motion.GetGyroscope();
motion.rotation = emulated_motion.GetRotations();
motion.euler = emulated_motion.GetEulerAngles();
motion.orientation = emulated_motion.GetOrientation();
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
}
@@ -551,6 +552,8 @@ void EmulatedController::EnableSystemButtons() {
void EmulatedController::DisableSystemButtons() {
std::scoped_lock lock{mutex};
system_buttons_enabled = false;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
}
void EmulatedController::ResetSystemButtons() {
@@ -685,6 +688,12 @@ void EmulatedController::SetMotionParam(std::size_t index, Common::ParamPackage
ReloadInput();
}
void EmulatedController::StartMotionCalibration() {
for (ControllerMotionInfo& motion : controller.motion_values) {
motion.emulated.Calibrate();
}
}
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
Common::UUID uuid) {
if (index >= controller.button_values.size()) {
@@ -734,6 +743,8 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (is_configuring) {
controller.npad_button_state.raw = NpadButton::None;
controller.debug_pad_button_state.raw = 0;
controller.home_button_state.raw = 0;
controller.capture_button_state.raw = 0;
lock.unlock();
TriggerOnChange(ControllerTriggerType::Button, false);
return;
@@ -974,16 +985,12 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
emulated.SetUserGyroThreshold(raw_status.gyro.x.properties.threshold);
emulated.UpdateRotation(raw_status.delta_timestamp);
emulated.UpdateOrientation(raw_status.delta_timestamp);
force_update_motion = raw_status.force_update;
if (is_configuring) {
return;
}
auto& motion = controller.motion_state[index];
motion.accel = emulated.GetAcceleration();
motion.gyro = emulated.GetGyroscope();
motion.rotation = emulated.GetRotations();
motion.euler = emulated.GetEulerAngles();
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
}
@@ -1276,9 +1283,14 @@ bool EmulatedController::HasNfc() const {
}
bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
auto& nfc_output_device = output_devices[3];
auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
auto& nfc_virtual_output_device = output_devices[3];
return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) {
return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
}
return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
}
void EmulatedController::SetLedPattern() {
@@ -1616,19 +1628,6 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
MotionState EmulatedController::GetMotions() const {
std::unique_lock lock{mutex};
// Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
lock.unlock();
device->ForceUpdate();
lock.lock();
}
}
return controller.motion_state;
}
@@ -1694,8 +1693,21 @@ void EmulatedController::DeleteCallback(int key) {
callback_list.erase(iterator);
}
void EmulatedController::TurboButtonUpdate() {
void EmulatedController::StatusUpdate() {
turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
// Some drivers like key motion need constant refreshing
for (std::size_t index = 0; index < motion_devices.size(); ++index) {
const auto& raw_status = controller.motion_values[index].raw_status;
auto& device = motion_devices[index];
if (!raw_status.force_update) {
continue;
}
if (!device) {
continue;
}
device->ForceUpdate();
}
}
NpadButton EmulatedController::GetTurboButtonMask() const {

View File

@@ -106,6 +106,7 @@ struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
Common::Vec3f rotation{};
Common::Vec3f euler{};
std::array<Common::Vec3f, 3> orientation{};
bool is_at_rest{};
};
@@ -289,6 +290,9 @@ public:
*/
void SetMotionParam(std::size_t index, Common::ParamPackage param);
/// Auto calibrates the current motion devices
void StartMotionCalibration();
/// Returns the latest button status from the controller with parameters
ButtonValues GetButtonsValues() const;
@@ -414,8 +418,8 @@ public:
*/
void DeleteCallback(int key);
/// Swaps the state of the turbo buttons
void TurboButtonUpdate();
/// Swaps the state of the turbo buttons and updates motion input
void StatusUpdate();
private:
/// creates input devices from params
@@ -527,7 +531,6 @@ private:
bool is_configuring{false};
bool system_buttons_enabled{true};
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
bool force_update_motion{false};
u32 turbo_button_state{0};
// Temporary values to avoid doing changes while the controller is in configuring mode

View File

@@ -54,6 +54,7 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Analog:
status.value = TransformToTrigger(callback).pressed.value;
status.toggle = callback.analog_status.properties.toggle;
status.inverted = callback.analog_status.properties.inverted_button;
break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
@@ -61,6 +62,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
case Common::Input::InputType::Button:
status = callback.button_status;
break;
case Common::Input::InputType::Motion:
status.value = std::abs(callback.motion_status.gyro.x.raw_value) > 1.0f;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to button not implemented", callback.type);
break;
@@ -82,7 +86,7 @@ Common::Input::MotionStatus TransformToMotion(const Common::Input::CallbackStatu
.range = 1.0f,
.offset = 0.0f,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
status.accel.x = {
.value = 0.0f,
@@ -226,6 +230,10 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
status = callback.trigger_status;
calculate_button_value = false;
break;
case Common::Input::InputType::Motion:
status.analog.properties.range = 1.0f;
raw_value = callback.motion_status.accel.x.raw_value;
break;
default:
LOG_ERROR(Input, "Conversion from type {} to trigger not implemented", callback.type);
break;

View File

@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
#include "common/math_util.h"
#include "core/hid/motion_input.h"
@@ -35,11 +37,17 @@ void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
gyro.y = std::clamp(gyro.y, -GyroMaxValue, GyroMaxValue);
gyro.z = std::clamp(gyro.z, -GyroMaxValue, GyroMaxValue);
// Auto adjust drift to minimize drift
// Auto adjust gyro_bias to minimize drift
if (!IsMoving(IsAtRestRelaxed)) {
gyro_bias = (gyro_bias * 0.9999f) + (gyroscope * 0.0001f);
}
// Adjust drift when calibration mode is enabled
if (calibration_mode) {
gyro_bias = (gyro_bias * 0.99f) + (gyroscope * 0.01f);
StopCalibration();
}
if (gyro.Length() < gyro_threshold * user_gyro_threshold) {
gyro = {};
} else {
@@ -51,6 +59,20 @@ void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
quat = quaternion;
}
void MotionInput::SetEulerAngles(const Common::Vec3f& euler_angles) {
const float cr = std::cos(euler_angles.x * 0.5f);
const float sr = std::sin(euler_angles.x * 0.5f);
const float cp = std::cos(euler_angles.y * 0.5f);
const float sp = std::sin(euler_angles.y * 0.5f);
const float cy = std::cos(euler_angles.z * 0.5f);
const float sy = std::sin(euler_angles.z * 0.5f);
quat.w = cr * cp * cy + sr * sp * sy;
quat.xyz.x = sr * cp * cy - cr * sp * sy;
quat.xyz.y = cr * sp * cy + sr * cp * sy;
quat.xyz.z = cr * cp * sy - sr * sp * cy;
}
void MotionInput::SetGyroBias(const Common::Vec3f& bias) {
gyro_bias = bias;
}
@@ -91,6 +113,19 @@ void MotionInput::UpdateRotation(u64 elapsed_time) {
rotations += gyro * sample_period;
}
void MotionInput::Calibrate() {
calibration_mode = true;
calibration_counter = 0;
}
void MotionInput::StopCalibration() {
if (calibration_counter++ > CalibrationSamples) {
calibration_mode = false;
ResetQuaternion();
ResetRotations();
}
}
// Based on Madgwick's implementation of Mayhony's AHRS algorithm.
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
void MotionInput::UpdateOrientation(u64 elapsed_time) {
@@ -222,6 +257,26 @@ Common::Vec3f MotionInput::GetRotations() const {
return rotations;
}
Common::Vec3f MotionInput::GetEulerAngles() const {
// roll (x-axis rotation)
const float sinr_cosp = 2 * (quat.w * quat.xyz.x + quat.xyz.y * quat.xyz.z);
const float cosr_cosp = 1 - 2 * (quat.xyz.x * quat.xyz.x + quat.xyz.y * quat.xyz.y);
// pitch (y-axis rotation)
const float sinp = std::sqrt(1 + 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
const float cosp = std::sqrt(1 - 2 * (quat.w * quat.xyz.y - quat.xyz.x * quat.xyz.z));
// yaw (z-axis rotation)
const float siny_cosp = 2 * (quat.w * quat.xyz.z + quat.xyz.x * quat.xyz.y);
const float cosy_cosp = 1 - 2 * (quat.xyz.y * quat.xyz.y + quat.xyz.z * quat.xyz.z);
return {
std::atan2(sinr_cosp, cosr_cosp),
2 * std::atan2(sinp, cosp) - Common::PI / 2,
std::atan2(siny_cosp, cosy_cosp),
};
}
void MotionInput::ResetOrientation() {
if (!reset_enabled || only_accelerometer) {
return;

View File

@@ -23,6 +23,8 @@ public:
static constexpr float GyroMaxValue = 5.0f;
static constexpr float AccelMaxValue = 7.0f;
static constexpr std::size_t CalibrationSamples = 300;
explicit MotionInput();
MotionInput(const MotionInput&) = default;
@@ -35,6 +37,7 @@ public:
void SetAcceleration(const Common::Vec3f& acceleration);
void SetGyroscope(const Common::Vec3f& gyroscope);
void SetQuaternion(const Common::Quaternion<f32>& quaternion);
void SetEulerAngles(const Common::Vec3f& euler_angles);
void SetGyroBias(const Common::Vec3f& bias);
void SetGyroThreshold(f32 threshold);
@@ -48,17 +51,21 @@ public:
void UpdateRotation(u64 elapsed_time);
void UpdateOrientation(u64 elapsed_time);
void Calibrate();
[[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
[[nodiscard]] Common::Vec3f GetAcceleration() const;
[[nodiscard]] Common::Vec3f GetGyroscope() const;
[[nodiscard]] Common::Vec3f GetGyroBias() const;
[[nodiscard]] Common::Vec3f GetRotations() const;
[[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
[[nodiscard]] Common::Vec3f GetEulerAngles() const;
[[nodiscard]] bool IsMoving(f32 sensitivity) const;
[[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
private:
void StopCalibration();
void ResetOrientation();
void SetOrientationFromAccelerometer();
@@ -101,6 +108,12 @@ private:
// Use accelerometer values to calculate position
bool only_accelerometer = true;
// When enabled it will aggressively adjust for gyro drift
bool calibration_mode = false;
// Used to auto disable calibration mode
std::size_t calibration_counter = 0;
};
} // namespace Core::HID

View File

@@ -144,14 +144,10 @@ private:
class KScopedMemoryBlockManagerAuditor {
public:
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) {
ASSERT(m_manager->CheckState());
}
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager* m) : m_manager(m) {}
explicit KScopedMemoryBlockManagerAuditor(KMemoryBlockManager& m)
: KScopedMemoryBlockManagerAuditor(std::addressof(m)) {}
~KScopedMemoryBlockManagerAuditor() {
ASSERT(m_manager->CheckState());
}
~KScopedMemoryBlockManagerAuditor() = default;
private:
KMemoryBlockManager* m_manager;

View File

@@ -13,6 +13,7 @@
#include "core/file_sys/savedata_factory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
@@ -1335,7 +1336,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
{27, nullptr, "CreateCacheStorage"},
{27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
{28, nullptr, "GetSaveDataSizeMax"},
{29, nullptr, "GetCacheStorageMax"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
@@ -1738,6 +1739,36 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
rb.Push(size.journal);
}
void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
struct InputParameters {
u16 index;
s64 size;
s64 journal_size;
};
static_assert(sizeof(InputParameters) == 24);
struct OutputParameters {
u32 storage_target;
u64 required_size;
};
static_assert(sizeof(OutputParameters) == 16);
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<InputParameters>();
LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
params.index, params.size, params.journal_size);
const OutputParameters resp{
.storage_target = 1,
.required_size = 0,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(resp);
}
void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");

View File

@@ -333,6 +333,7 @@ private:
void GetPseudoDeviceId(HLERequestContext& ctx);
void ExtendSaveData(HLERequestContext& ctx);
void GetSaveDataSize(HLERequestContext& ctx);
void CreateCacheStorage(HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void BeginBlockingHomeButton(HLERequestContext& ctx);

View File

@@ -11,7 +11,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfp/nfp_device.h"
#include "core/hle/service/nfc/common/device.h"
namespace Service::AM::Applets {
@@ -72,10 +72,10 @@ void Cabinet::Execute() {
// TODO: listen on all controllers
if (nfp_device == nullptr) {
nfp_device = std::make_shared<Service::NFP::NfpDevice>(
nfp_device = std::make_shared<Service::NFC::NfcDevice>(
system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
nfp_device->Initialize();
nfp_device->StartDetection(Service::NFP::TagProtocol::All);
nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
}
const Core::Frontend::CabinetParameters parameters{
@@ -106,20 +106,22 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
Cancel();
}
if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
Cancel();
}
if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::MountTarget::All);
if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) {
nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
}
switch (applet_input_common.applet_mode) {
case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
Service::NFP::AmiiboName name{};
std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
nfp_device->SetRegisterInfoPrivate(name);
Service::NFP::RegisterInfoPrivate register_info{};
std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
nfp_device->SetRegisterInfoPrivate(register_info);
break;
}
case Service::NFP::CabinetMode::StartGameDataEraser:
@@ -139,7 +141,7 @@ void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name)
applet_output.device_handle = applet_input_common.device_handle;
applet_output.result = CabinetResult::Cancel;
const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info, false);
nfp_device->Finalize();
if (reg_result.IsSuccess()) {

View File

@@ -19,8 +19,8 @@ namespace Core {
class System;
} // namespace Core
namespace Service::NFP {
class NfpDevice;
namespace Service::NFC {
class NfcDevice;
}
namespace Service::AM::Applets {
@@ -96,7 +96,7 @@ private:
Core::System& system;
bool is_complete{false};
std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
Kernel::KEvent* availability_change_event;
KernelHelpers::ServiceContext service_context;
StartParamForAmiiboSettings applet_input_common{};

View File

@@ -24,8 +24,10 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_srv.h"
#include "core/hle/service/hle_ipc.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/reporter.h"
@@ -552,9 +554,9 @@ public:
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(actual_entries));
rb.Push<u64>(actual_entries);
}
private:
@@ -712,7 +714,7 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
{60, nullptr, "OpenSaveDataInfoReader"},
{61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{62, nullptr, "OpenCacheStorageList"},
{62, &FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage, "OpenSaveDataInfoReaderOnlyCacheStorage"},
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
{66, nullptr, "WriteSaveDataFileSystemExtraData2"},
@@ -921,6 +923,15 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx) {
std::make_shared<ISaveDataInfoReader>(system, space, fsc));
}
void FSP_SRV::OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISaveDataInfoReader>(system, FileSys::SaveDataSpaceId::TemporaryStorage,
fsc);
}
void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called.");

View File

@@ -42,6 +42,7 @@ private:
void OpenSaveDataFileSystem(HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(HLERequestContext& ctx);
void OpenSaveDataInfoReaderBySaveDataSpaceId(HLERequestContext& ctx);
void OpenSaveDataInfoReaderOnlyCacheStorage(HLERequestContext& ctx);
void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(HLERequestContext& ctx);
void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(HLERequestContext& ctx);

View File

@@ -423,8 +423,8 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
return;
}
// This function is unique to yuzu for the turbo buttons to work properly
controller.device->TurboButtonUpdate();
// This function is unique to yuzu for the turbo buttons and motion to work properly
controller.device->StatusUpdate();
auto& pad_entry = controller.npad_pad_state;
auto& trigger_entry = controller.npad_trigger_state;
@@ -979,8 +979,8 @@ void Controller_NPad::VibrateController(
}
void Controller_NPad::VibrateControllers(
const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
const std::vector<Core::HID::VibrationValue>& vibration_values) {
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values) {
if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
return;
}

View File

@@ -112,8 +112,8 @@ public:
const Core::HID::VibrationValue& vibration_value);
void VibrateControllers(
const std::vector<Core::HID::VibrationDeviceHandle>& vibration_device_handles,
const std::vector<Core::HID::VibrationValue>& vibration_values);
std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
std::span<const Core::HID::VibrationValue> vibration_values);
Core::HID::VibrationValue GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;

View File

@@ -1601,16 +1601,16 @@ void Hid::SendVibrationValues(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto handles = ctx.ReadBuffer(0);
const auto vibrations = ctx.ReadBuffer(1);
const auto handle_data = ctx.ReadBuffer(0);
const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
const auto vibration_data = ctx.ReadBuffer(1);
const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
std::vector<Core::HID::VibrationDeviceHandle> vibration_device_handles(
handles.size() / sizeof(Core::HID::VibrationDeviceHandle));
std::vector<Core::HID::VibrationValue> vibration_values(vibrations.size() /
sizeof(Core::HID::VibrationValue));
std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
auto vibration_device_handles =
std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
handle_count);
auto vibration_values = std::span(
reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateControllers(vibration_device_handles, vibration_values);

View File

@@ -12,7 +12,7 @@
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "core/hle/service/nfp/amiibo_crypto.h"
#include "core/hle/service/nfc/common/amiibo_crypto.h"
namespace Service::NFP::AmiiboCrypto {
@@ -52,10 +52,7 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
if (ntag_file.compability_container != 0xEEFF10F1U) {
return false;
}
if (amiibo_data.constant_value != 0xA5) {
return false;
}
if (amiibo_data.model_info.tag_type != PackedTagType::Type2) {
if (amiibo_data.model_info.tag_type != NFC::PackedTagType::Type2) {
return false;
}
if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {

View File

@@ -24,9 +24,9 @@ using DrgbOutput = std::array<u8, 0x20>;
struct HashSeed {
u16_be magic;
std::array<u8, 0xE> padding;
UniqueSerialNumber uid_1;
NFC::UniqueSerialNumber uid_1;
u8 nintendo_id_1;
UniqueSerialNumber uid_2;
NFC::UniqueSerialNumber uid_2;
u8 nintendo_id_2;
std::array<u8, 0x20> keygen_salt;
};

View File

@@ -0,0 +1,141 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NfcProtocol allowed_protocol);
Result StopDetection();
Result GetTagInfo(TagInfo& tag_info, bool is_mifare) const;
Result ReadMifare(std::span<const MifareReadBlockParameter> parameters,
std::span<MifareReadBlockData> read_block_data) const;
Result ReadMifare(const MifareReadBlockParameter& parameter,
MifareReadBlockData& read_block_data) const;
Result WriteMifare(std::span<const MifareWriteBlockParameter> parameters);
Result WriteMifare(const MifareWriteBlockParameter& parameter);
Result SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
Result Mount(NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(NFP::BreakType break_type);
Result Restore();
Result GetCommonInfo(NFP::CommonInfo& common_info) const;
Result GetModelInfo(NFP::ModelInfo& model_info) const;
Result GetRegisterInfo(NFP::RegisterInfo& register_info) const;
Result GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(NFP::AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::span<u8> data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistsApplicationArea(bool& has_application_area) const;
Result GetAll(NFP::NfpData& data) const;
Result SetAll(const NFP::NfpData& data);
Result BreakTag(NFP::BreakType break_type);
Result ReadBackupData(std::span<u8> data) const;
Result WriteBackupData(std::span<const u8> data);
Result WriteNtf(std::span<const u8> data);
u64 GetHandle() const;
DeviceState GetCurrentState() const;
Result GetNpadId(Core::HID::NpadIdType& out_npad_id) const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const;
void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name);
NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 GetCurrentPosixTime() const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
void BuildAmiiboWithoutKeys();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NfcProtocol allowed_protocols{};
DeviceState device_state{DeviceState::Unavailable};
// NFP data
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
bool is_write_protected{};
NFP::MountTarget mount_target{NFP::MountTarget::None};
NFP::NTAG215File tag_data{};
std::vector<u8> mifare_data{};
NFP::EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,695 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
DeviceManager::DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: system{system_}, service_context{service_context_} {
availability_change_event =
service_context.CreateEvent("Nfc:DeviceManager:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < devices.size(); device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
is_initialized = false;
}
DeviceManager ::~DeviceManager() {
service_context.CloseEvent(availability_change_event);
}
Result DeviceManager::Initialize() {
for (auto& device : devices) {
device->Initialize();
}
is_initialized = true;
return ResultSuccess;
}
Result DeviceManager::Finalize() {
for (auto& device : devices) {
device->Finalize();
}
is_initialized = false;
return ResultSuccess;
}
Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices,
std::size_t max_allowed_devices) const {
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
return ResultDeviceNotFound;
}
return ResultSuccess;
}
DeviceState DeviceManager::GetDeviceState(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
const auto result = GetDeviceFromHandle(device_handle, device, false);
if (result.IsSuccess()) {
return device->GetCurrentState();
}
return DeviceState::Unavailable;
}
Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetNpadId(npad_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachAvailabilityChangeEvent() const {
return availability_change_event->GetReadableEvent();
}
Result DeviceManager::StartDetection(u64 device_handle, NfcProtocol tag_protocol) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StartDetection(tag_protocol);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::StopDetection(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->StopDetection();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info, bool is_mifare) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetTagInfo(tag_info, is_mifare);
result = VerifyDeviceResult(device, result);
}
return result;
}
Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetActivateEvent();
}
Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
GetDeviceFromHandle(device_handle, device, false);
// TODO: Return proper error code on failure
return device->GetDeactivateEvent();
}
Result DeviceManager::ReadMifare(u64 device_handle,
std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadMifare(read_parameters, read_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteMifare(write_parameters);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SendCommandByPassThrough(u64 device_handle,
const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data,
std::span<u8> out_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SendCommandByPassThrough(timeout, command_data, out_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Mount(u64 device_handle, NFP::ModelType model_type,
NFP::MountTarget mount_target) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Mount(model_type, mount_target);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Unmount(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Unmount();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->OpenApplicationArea(access_id);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetApplicationArea(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetApplicationArea(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Flush(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Flush();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Restore(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Restore();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->CreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfo(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetCommonInfo(common_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetModelInfo(model_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
u32 DeviceManager::GetApplicationAreaSize() const {
return sizeof(NFP::ApplicationArea);
}
Result DeviceManager::RecreateApplicationArea(u64 device_handle, u32 access_id,
std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->RecreateApplicationArea(access_id, data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::Format(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->Format();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAdminInfo(admin_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle,
NFP::RegisterInfoPrivate& register_info) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetRegisterInfoPrivate(u64 device_handle,
const NFP::RegisterInfoPrivate& register_info) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetRegisterInfoPrivate(register_info);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteRegisterInfo(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteRegisterInfo();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::DeleteApplicationArea(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->DeleteApplicationArea();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ExistsApplicationArea(has_application_area);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->GetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::SetAll(u64 device_handle, const NFP::NfpData& nfp_data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->SetAll(nfp_data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::FlushDebug(u64 device_handle) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->FlushDebug();
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->BreakTag(break_type);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->ReadBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteBackupData(u64 device_handle, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteBackupData(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data) {
std::scoped_lock lock{mutex};
std::shared_ptr<NfcDevice> device = nullptr;
auto result = GetDeviceHandle(device_handle, device);
if (result.IsSuccess()) {
result = device->WriteNtf(data);
result = VerifyDeviceResult(device, result);
}
return result;
}
Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device,
bool check_state) const {
if (check_state) {
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
}
for (auto& device : devices) {
if (device->GetHandle() == handle) {
nfc_device = device;
return ResultSuccess;
}
}
return ResultDeviceNotFound;
}
std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
const std::optional<std::shared_ptr<NfcDevice>> DeviceManager::GetNfcDevice(u64 handle) const {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const {
const auto result = GetDeviceFromHandle(handle, device, true);
if (result.IsError()) {
return result;
}
return CheckDeviceState(device);
}
Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device,
Result operation_result) const {
if (operation_result.IsSuccess()) {
return operation_result;
}
const Result is_parameter_set = IsNfcParameterSet();
if (is_parameter_set.IsError()) {
return is_parameter_set;
}
const Result is_enabled = IsNfcEnabled();
if (is_enabled.IsError()) {
return is_enabled;
}
const Result is_nfc_initialized = IsNfcInitialized();
if (is_nfc_initialized.IsError()) {
return is_nfc_initialized;
}
const Result device_state = CheckDeviceState(device);
if (device_state.IsError()) {
return device_state;
}
return operation_result;
}
Result DeviceManager::CheckDeviceState(std::shared_ptr<NfcDevice> device) const {
if (device == nullptr) {
return ResultInvalidArgument;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcEnabled() const {
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
if (!is_enabled) {
return ResultNfcDisabled;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcParameterSet() const {
// TODO: This calls checks against a bool on offset 0x450
const bool is_set = true;
if (!is_set) {
return ResultUnknown76;
}
return ResultSuccess;
}
Result DeviceManager::IsNfcInitialized() const {
if (!is_initialized) {
return ResultNfcNotInitialized;
}
return ResultSuccess;
}
} // namespace Service::NFC

View File

@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include <span>
#include "core/hid/hid_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager {
public:
explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
~DeviceManager();
// Nfc device manager
Result Initialize();
Result Finalize();
Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const;
DeviceState GetDeviceState(u64 device_handle) const;
Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const;
Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
Result StopDetection(u64 device_handle);
Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info, bool is_mifare) const;
Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const;
Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const;
Result ReadMifare(u64 device_handle,
const std::span<const MifareReadBlockParameter> read_parameters,
std::span<MifareReadBlockData> read_data);
Result WriteMifare(u64 device_handle,
std::span<const MifareWriteBlockParameter> write_parameters);
Result SendCommandByPassThrough(u64 device_handle, const Time::Clock::TimeSpanType& timeout,
std::span<const u8> command_data, std::span<u8> out_data);
// Nfp device manager
Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
Result Unmount(u64 device_handle);
Result OpenApplicationArea(u64 device_handle, u32 access_id);
Result GetApplicationArea(u64 device_handle, std::span<u8> data) const;
Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
Result Flush(u64 device_handle);
Result Restore(u64 device_handle);
Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const;
Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const;
Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const;
u32 GetApplicationAreaSize() const;
Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
Result Format(u64 device_handle);
Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const;
Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const;
Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
Result DeleteRegisterInfo(u64 device_handle);
Result DeleteApplicationArea(u64 device_handle);
Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const;
Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const;
Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
Result FlushDebug(u64 device_handle);
Result BreakTag(u64 device_handle, NFP::BreakType break_type);
Result ReadBackupData(u64 device_handle, std::span<u8> data) const;
Result WriteBackupData(u64 device_handle, std::span<const u8> data);
Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);
private:
Result IsNfcEnabled() const;
Result IsNfcParameterSet() const;
Result IsNfcInitialized() const;
Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
bool check_state) const;
Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const;
Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;
bool is_initialized = false;
mutable std::mutex mutex;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
Core::System& system;
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::NFC::Mifare {
constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result ResultReadError(ErrorModule::NFCMifare, 288);
} // namespace Service::NFC::Mifare

View File

@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
// This is nn::nfc::MifareReadBlockParameter
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
// This is nn::nfc::MifareReadBlockData
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
// This is nn::nfc::MifareWriteBlockParameter
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFC

View File

@@ -1,400 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
namespace Service::NFC {
MFIUser::MFIUser(Core::System& system_)
: ServiceFramework{system_, "NFC::MFIUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::Read, "Read"},
{6, &MFIUser::Write, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::GetActivateEventHandle, "GetActivateEventHandle"},
{9, &MFIUser::GetDeactivateEventHandle, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::GetAvailabilityChangeEventHandle, "GetAvailabilityChangeEventHandle"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("MFIUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
MFIUser ::~MFIUser() {
availability_change_event->Close();
}
void MFIUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void MFIUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void MFIUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareInvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (const auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void MFIUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(NFP::TagProtocol::All);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareReadBlockParameter>()};
std::vector<NFP::MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareRead(read_commands[i], out_data[i]);
if (result.IsError()) {
break;
}
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<NFP::MifareWriteBlockParameter>()};
std::vector<NFP::MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(NFP::MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
Result result = ResultSuccess;
std::vector<NFP::MifareReadBlockData> out_data(number_of_commands);
for (std::size_t i = 0; i < number_of_commands; i++) {
result = device.value()->MifareWrite(write_commands[i]);
if (result.IsError()) {
break;
}
}
if (result.IsSuccess()) {
result = device.value()->Flush();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, true);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void MFIUser::GetActivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void MFIUser::GetDeactivateEventHandle(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void MFIUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void MFIUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void MFIUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareDeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void MFIUser::GetAvailabilityChangeEventHandle(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(MifareNfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
std::optional<std::shared_ptr<NfcDevice>> MFIUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class MFIUser final : public ServiceFramework<MFIUser> {
public:
explicit MFIUser(Core::System& system_);
~MFIUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Read(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetActivateEventHandle(HLERequestContext& ctx);
void GetDeactivateEventHandle(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetAvailabilityChangeEventHandle(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFC

View File

@@ -6,14 +6,115 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/mifare_user.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class IUser final : public NfcInterface {
public:
explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare ,"WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class ISystem final : public NfcInterface {
public:
explicit ISystem(Core::System& system_)
: NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NfcInterface::Initialize, "InitializeOld"},
{1, &NfcInterface::Finalize, "FinalizeOld"},
{2, &NfcInterface::GetState, "GetStateOld"},
{3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, &NfcInterface::Initialize, "Initialize"},
{401, &NfcInterface::Finalize, "Finalize"},
{402, &NfcInterface::GetState, "GetState"},
{403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
{404, &NfcInterface::ListDevices, "ListDevices"},
{405, &NfcInterface::GetDeviceState, "GetDeviceState"},
{406, &NfcInterface::GetNpadId, "GetNpadId"},
{407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &NfcInterface::StartDetection, "StartDetection"},
{409, &NfcInterface::StopDetection, "StopDetection"},
{410, &NfcInterface::GetTagInfo, "GetTagInfo"},
{411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
{412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, &NfcInterface::ReadMifare, "ReadMifare"},
{1001, &NfcInterface::WriteMifare, "WriteMifare"},
{1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code
// simpler
using MFInterface = NfcInterface;
class MFIUser final : public MFInterface {
public:
explicit MFIUser(Core::System& system_)
: MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} {
// clang-format off
static const FunctionInfoTyped<MFIUser> functions[] = {
{0, &MFIUser::Initialize, "Initialize"},
{1, &MFIUser::Finalize, "Finalize"},
{2, &MFIUser::ListDevices, "ListDevices"},
{3, &MFIUser::StartDetection, "StartDetection"},
{4, &MFIUser::StopDetection, "StopDetection"},
{5, &MFIUser::ReadMifare, "Read"},
{6, &MFIUser::WriteMifare, "Write"},
{7, &MFIUser::GetTagInfo, "GetTagInfo"},
{8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"},
{9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"},
{10, &MFIUser::GetState, "GetState"},
{11, &MFIUser::GetDeviceState, "GetDeviceState"},
{12, &MFIUser::GetNpadId, "GetNpadId"},
{13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class IAm final : public ServiceFramework<IAm> {
public:
explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} {
@@ -34,7 +135,7 @@ public:
explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_AM::CreateAmInterface, "CreateAmInterface"},
{0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"},
};
// clang-format on
@@ -42,7 +143,7 @@ public:
}
private:
void CreateAmInterface(HLERequestContext& ctx) {
void CreateAmNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -56,7 +157,7 @@ public:
explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -64,7 +165,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -78,7 +179,7 @@ public:
explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
{0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
};
// clang-format on
@@ -86,7 +187,7 @@ public:
}
private:
void CreateUserInterface(HLERequestContext& ctx) {
void CreateUserNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -95,49 +196,12 @@ private:
}
};
class ISystem final : public ServiceFramework<ISystem> {
public:
explicit ISystem(Core::System& system_) : ServiceFramework{system_, "ISystem"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Initialize"},
{1, nullptr, "Finalize"},
{2, nullptr, "GetStateOld"},
{3, nullptr, "IsNfcEnabledOld"},
{100, nullptr, "SetNfcEnabledOld"},
{400, nullptr, "InitializeSystem"},
{401, nullptr, "FinalizeSystem"},
{402, nullptr, "GetState"},
{403, nullptr, "IsNfcEnabled"},
{404, nullptr, "ListDevices"},
{405, nullptr, "GetDeviceState"},
{406, nullptr, "GetNpadId"},
{407, nullptr, "AttachAvailabilityChangeEvent"},
{408, nullptr, "StartDetection"},
{409, nullptr, "StopDetection"},
{410, nullptr, "GetTagInfo"},
{411, nullptr, "AttachActivateEvent"},
{412, nullptr, "AttachDeactivateEvent"},
{500, nullptr, "SetNfcEnabled"},
{510, nullptr, "OutputTestWave"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, nullptr, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class NFC_SYS final : public ServiceFramework<NFC_SYS> {
public:
explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"},
{0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"},
};
// clang-format on
@@ -145,7 +209,7 @@ public:
}
private:
void CreateSystemInterface(HLERequestContext& ctx) {
void CreateSystemNfcInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -161,6 +225,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system));
server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system));
server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system));
ServerManager::RunServer(std::move(server_manager));
}

View File

@@ -1,288 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/input.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/hid_core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
namespace Service::NFC {
NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_)
: npad_id{npad_id_}, system{system_}, service_context{service_context_},
availability_change_event{availability_change_event_} {
activate_event = service_context.CreateEvent("IUser:NFCActivateEvent");
deactivate_event = service_context.CreateEvent("IUser:NFCDeactivateEvent");
npad_device = system.HIDCore().GetEmulatedController(npad_id);
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
.is_npad_service = false,
};
is_controller_set = true;
callback_key = npad_device->SetCallback(engine_callback);
}
NfcDevice::~NfcDevice() {
activate_event->Close();
deactivate_event->Close();
if (!is_controller_set) {
return;
}
npad_device->DeleteCallback(callback_key);
is_controller_set = false;
};
void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
if (!is_initalized) {
return;
}
if (type == Core::HID::ControllerTriggerType::Connected) {
Initialize();
availability_change_event->Signal();
return;
}
if (type == Core::HID::ControllerTriggerType::Disconnected) {
device_state = NFP::DeviceState::Unavailable;
availability_change_event->Signal();
return;
}
if (type != Core::HID::ControllerTriggerType::Nfc) {
return;
}
if (!npad_device->IsConnected()) {
return;
}
const auto nfc_status = npad_device->GetNfc();
switch (nfc_status.state) {
case Common::Input::NfcState::NewAmiibo:
LoadNfcTag(nfc_status.data);
break;
case Common::Input::NfcState::AmiiboRemoved:
if (device_state != NFP::DeviceState::SearchingForTag) {
CloseNfcTag();
}
break;
default:
break;
}
}
bool NfcDevice::LoadNfcTag(std::span<const u8> data) {
if (device_state != NFP::DeviceState::SearchingForTag) {
LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
return false;
}
if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
return false;
}
tag_data.resize(data.size());
memcpy(tag_data.data(), data.data(), data.size());
memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
device_state = NFP::DeviceState::TagFound;
deactivate_event->GetReadableEvent().Clear();
activate_event->Signal();
return true;
}
void NfcDevice::CloseNfcTag() {
LOG_INFO(Service_NFC, "Remove nfc tag");
device_state = NFP::DeviceState::TagRemoved;
encrypted_tag_data = {};
activate_event->GetReadableEvent().Clear();
deactivate_event->Signal();
}
Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
return activate_event->GetReadableEvent();
}
Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
return deactivate_event->GetReadableEvent();
}
void NfcDevice::Initialize() {
device_state =
npad_device->HasNfc() ? NFP::DeviceState::Initialized : NFP::DeviceState::Unavailable;
encrypted_tag_data = {};
is_initalized = true;
}
void NfcDevice::Finalize() {
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
StopDetection();
}
device_state = NFP::DeviceState::Unavailable;
is_initalized = false;
}
Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
if (device_state != NFP::DeviceState::Initialized &&
device_state != NFP::DeviceState::TagRemoved) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::NFC) !=
Common::Input::DriverResult::Success) {
LOG_ERROR(Service_NFC, "Nfc not supported");
return NfcDisabled;
}
device_state = NFP::DeviceState::SearchingForTag;
allowed_protocols = allowed_protocol;
return ResultSuccess;
}
Result NfcDevice::StopDetection() {
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
Common::Input::PollingMode::Active);
if (device_state == NFP::DeviceState::Initialized) {
return ResultSuccess;
}
if (device_state == NFP::DeviceState::TagFound ||
device_state == NFP::DeviceState::TagMounted) {
CloseNfcTag();
return ResultSuccess;
}
if (device_state == NFP::DeviceState::SearchingForTag ||
device_state == NFP::DeviceState::TagRemoved) {
device_state = NFP::DeviceState::Initialized;
return ResultSuccess;
}
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
return WrongDeviceState;
}
Result NfcDevice::Flush() {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (!npad_device->WriteNfc(tag_data)) {
LOG_ERROR(Service_NFP, "Error writing to file");
return MifareReadError;
}
return ResultSuccess;
}
Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const {
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (is_mifare) {
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type4,
};
return ResultSuccess;
}
// Protocol and tag type may change here
tag_info = {
.uuid = encrypted_tag_data.uuid.uid,
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
.protocol = NFP::TagProtocol::TypeA,
.tag_type = NFP::TagType::Type2,
};
return ResultSuccess;
}
Result NfcDevice::MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
read_block_data.sector_number = parameter.sector_number;
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to read encrypted data
memcpy(read_block_data.data.data(), tag_data.data() + sector_index, sizeof(NFP::DataBlock));
return ResultSuccess;
}
Result NfcDevice::MifareWrite(const NFP::MifareWriteBlockParameter& parameter) {
const std::size_t sector_index = parameter.sector_number * sizeof(NFP::DataBlock);
if (device_state != NFP::DeviceState::TagFound &&
device_state != NFP::DeviceState::TagMounted) {
LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
if (device_state == NFP::DeviceState::TagRemoved) {
return TagRemoved;
}
return WrongDeviceState;
}
if (tag_data.size() < sector_index + sizeof(NFP::DataBlock)) {
return MifareReadError;
}
// TODO: Use parameter.sector_key to encrypt the data
memcpy(tag_data.data() + sector_index, parameter.data.data(), sizeof(NFP::DataBlock));
return ResultSuccess;
}
u64 NfcDevice::GetHandle() const {
// Generate a handle based of the npad id
return static_cast<u64>(npad_id);
}
NFP::DeviceState NfcDevice::GetCurrentState() const {
return device_state;
}
Core::HID::NpadIdType NfcDevice::GetNpadId() const {
return npad_id;
}
} // namespace Service::NFC

View File

@@ -1,78 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFC {
class NfcDevice {
public:
NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfcDevice();
void Initialize();
void Finalize();
Result StartDetection(NFP::TagProtocol allowed_protocol);
Result StopDetection();
Result Flush();
Result GetTagInfo(NFP::TagInfo& tag_info, bool is_mifare) const;
Result MifareRead(const NFP::MifareReadBlockParameter& parameter,
NFP::MifareReadBlockData& read_block_data);
Result MifareWrite(const NFP::MifareWriteBlockParameter& parameter);
u64 GetHandle() const;
NFP::DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadNfcTag(std::span<const u8> data);
void CloseNfcTag();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
NFP::TagProtocol allowed_protocols{};
NFP::DeviceState device_state{NFP::DeviceState::Unavailable};
NFP::EncryptedNTAG215File encrypted_tag_data{};
std::vector<u8> tag_data{};
};
} // namespace Service::NFC

View File

@@ -0,0 +1,382 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/mifare_result.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend)
: ServiceFramework{system_, name}, service_context{system_, service_name},
backend_type{service_backend} {}
NfcInterface ::~NfcInterface() = default;
void NfcInterface::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
auto manager = GetManager();
auto result = manager->Initialize();
if (result.IsSuccess()) {
state = State::Initialized;
} else {
manager->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(result);
}
void NfcInterface::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state != State::NonInitialized) {
if (GetBackendType() != BackendType::None) {
GetManager()->Finalize();
}
device_manager = nullptr;
state = State::NonInitialized;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void NfcInterface::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
// TODO: This calls nn::settings::detail::GetNfcEnableFlag
const bool is_enabled = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(is_enabled);
}
void NfcInterface::ListDevices(HLERequestContext& ctx) {
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
LOG_DEBUG(Service_NFC, "called");
auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void NfcInterface::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
const auto device_state = GetManager()->GetDeviceState(device_handle);
if (device_state > DeviceState::Finalized) {
ASSERT_MSG(false, "Invalid device state");
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device_state);
}
void NfcInterface::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
Core::HID::NpadIdType npad_id{};
auto result = GetManager()->GetNpadId(device_handle, npad_id);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(npad_id);
}
void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
}
void NfcInterface::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto tag_protocol{rp.PopEnum<NfcProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
auto result = GetManager()->StartDetection(device_handle, tag_protocol);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
auto result = GetManager()->StopDetection(device_handle);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
TagInfo tag_info{};
auto result =
GetManager()->GetTagInfo(device_handle, tag_info, backend_type == BackendType::Mifare);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(tag_info);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle));
}
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle));
}
void NfcInterface::ReadMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareReadBlockParameter>()};
std::vector<MifareReadBlockParameter> read_commands(number_of_commands);
memcpy(read_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareReadBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, read_commands_size={}",
device_handle, number_of_commands);
std::vector<MifareReadBlockData> out_data(number_of_commands);
auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
result = TranslateResultToServiceError(result);
if (result.IsSuccess()) {
ctx.WriteBuffer(out_data);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::WriteMifare(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto buffer{ctx.ReadBuffer()};
const auto number_of_commands{ctx.GetReadBufferNumElements<MifareWriteBlockParameter>()};
std::vector<MifareWriteBlockParameter> write_commands(number_of_commands);
memcpy(write_commands.data(), buffer.data(),
number_of_commands * sizeof(MifareWriteBlockParameter));
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
device_handle, number_of_commands);
auto result = GetManager()->WriteMifare(device_handle, write_commands);
result = TranslateResultToServiceError(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
std::vector<u8> out_data(1);
auto result =
GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data);
result = TranslateResultToServiceError(result);
if (result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::shared_ptr<DeviceManager> NfcInterface::GetManager() {
if (device_manager == nullptr) {
device_manager = std::make_shared<DeviceManager>(system, service_context);
}
return device_manager;
}
BackendType NfcInterface::GetBackendType() const {
return backend_type;
}
Result NfcInterface::TranslateResultToServiceError(Result result) const {
const auto backend = GetBackendType();
if (result.IsSuccess()) {
return result;
}
if (result.module != ErrorModule::NFC) {
return result;
}
switch (backend) {
case BackendType::Mifare:
return TranslateResultToNfp(result);
case BackendType::Nfp: {
return TranslateResultToNfp(result);
}
default:
if (result != ResultUnknown216) {
return result;
}
return ResultUnknown74;
}
}
Result NfcInterface::TranslateResultToNfp(Result result) const {
if (result == ResultDeviceNotFound) {
return NFP::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return NFP::ResultInvalidArgument;
}
if (result == ResultWrongApplicationAreaSize) {
return NFP::ResultWrongApplicationAreaSize;
}
if (result == ResultWrongDeviceState) {
return NFP::ResultWrongDeviceState;
}
if (result == ResultUnknown74) {
return NFP::ResultUnknown74;
}
if (result == ResultNfcDisabled) {
return NFP::ResultNfcDisabled;
}
if (result == ResultNfcNotInitialized) {
return NFP::ResultNfcDisabled;
}
if (result == ResultWriteAmiiboFailed) {
return NFP::ResultWriteAmiiboFailed;
}
if (result == ResultTagRemoved) {
return NFP::ResultTagRemoved;
}
if (result == ResultRegistrationIsNotInitialized) {
return NFP::ResultRegistrationIsNotInitialized;
}
if (result == ResultApplicationAreaIsNotInitialized) {
return NFP::ResultApplicationAreaIsNotInitialized;
}
if (result == ResultCorruptedData) {
return NFP::ResultCorruptedData;
}
if (result == ResultWrongApplicationAreaId) {
return NFP::ResultWrongApplicationAreaId;
}
if (result == ResultApplicationAreaExist) {
return NFP::ResultApplicationAreaExist;
}
if (result == ResultNotAnAmiibo) {
return NFP::ResultNotAnAmiibo;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
Result NfcInterface::TranslateResultToMifare(Result result) const {
if (result == ResultDeviceNotFound) {
return Mifare::ResultDeviceNotFound;
}
if (result == ResultInvalidArgument) {
return Mifare::ResultInvalidArgument;
}
if (result == ResultWrongDeviceState) {
return Mifare::ResultWrongDeviceState;
}
if (result == ResultNfcDisabled) {
return Mifare::ResultNfcDisabled;
}
if (result == ResultTagRemoved) {
return Mifare::ResultTagRemoved;
}
LOG_WARNING(Service_NFC, "Result conversion not handled");
return result;
}
} // namespace Service::NFC

View File

@@ -3,26 +3,17 @@
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/service.h"
namespace Service::NFC {
class NfcDevice;
class DeviceManager;
class IUser final : public ServiceFramework<IUser> {
class NfcInterface : public ServiceFramework<NfcInterface> {
public:
explicit IUser(Core::System& system_);
~IUser();
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend);
~NfcInterface();
void Initialize(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
@@ -37,16 +28,22 @@ private:
void GetTagInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void ReadMifare(HLERequestContext& ctx);
void WriteMifare(HLERequestContext& ctx);
void SendCommandByPassThrough(HLERequestContext& ctx);
std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
protected:
std::shared_ptr<DeviceManager> GetManager();
BackendType GetBackendType() const;
Result TranslateResultToServiceError(Result result) const;
Result TranslateResultToNfp(Result result) const;
Result TranslateResultToMifare(Result result) const;
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfcDevice>, 10> devices{};
BackendType backend_type;
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
std::shared_ptr<DeviceManager> device_manager = nullptr;
};
} // namespace Service::NFC

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -7,17 +7,22 @@
namespace Service::NFC {
constexpr Result DeviceNotFound(ErrorModule::NFC, 64);
constexpr Result InvalidArgument(ErrorModule::NFC, 65);
constexpr Result WrongDeviceState(ErrorModule::NFC, 73);
constexpr Result NfcDisabled(ErrorModule::NFC, 80);
constexpr Result TagRemoved(ErrorModule::NFC, 97);
constexpr Result MifareDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result MifareInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result MifareWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result MifareNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result MifareTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result MifareReadError(ErrorModule::NFCMifare, 288);
constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnknown216(ErrorModule::NFC, 216);
} // namespace Service::NFC

View File

@@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::NFC {
enum class BackendType : u32 {
None,
Nfc,
Nfp,
Mifare,
};
// This is nn::nfc::DeviceState
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfc::State
enum class State : u32 {
NonInitialized,
Initialized,
};
// This is nn::nfc::TagType
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony FeliCa RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// This is nn::nfc::NfcProtocol
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class NfcProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony FeliCa
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
// this is nn::nfc::TestWaveType
enum class TestWaveType : u32 {
Unknown,
};
using UniqueSerialNumber = std::array<u8, 7>;
using UniqueSerialNumberExtension = std::array<u8, 3>;
// This is nn::nfc::DeviceHandle
using DeviceHandle = u64;
// This is nn::nfc::TagInfo
struct TagInfo {
UniqueSerialNumber uuid;
UniqueSerialNumberExtension uuid_extension;
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
NfcProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
} // namespace Service::NFC

View File

@@ -1,365 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hid/hid_types.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc_device.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_user.h"
#include "core/hle/service/time/clock_types.h"
namespace Service::NFC {
IUser::IUser(Core::System& system_)
: ServiceFramework{system_, "NFC::IUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "InitializeOld"},
{1, &IUser::Finalize, "FinalizeOld"},
{2, &IUser::GetState, "GetStateOld"},
{3, &IUser::IsNfcEnabled, "IsNfcEnabledOld"},
{400, &IUser::Initialize, "Initialize"},
{401, &IUser::Finalize, "Finalize"},
{402, &IUser::GetState, "GetState"},
{403, &IUser::IsNfcEnabled, "IsNfcEnabled"},
{404, &IUser::ListDevices, "ListDevices"},
{405, &IUser::GetDeviceState, "GetDeviceState"},
{406, &IUser::GetNpadId, "GetNpadId"},
{407, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
{408, &IUser::StartDetection, "StartDetection"},
{409, &IUser::StopDetection, "StopDetection"},
{410, &IUser::GetTagInfo, "GetTagInfo"},
{411, &IUser::AttachActivateEvent, "AttachActivateEvent"},
{412, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
{1000, nullptr, "ReadMifare"},
{1001, nullptr, "WriteMifare"},
{1300, &IUser::SendCommandByPassThrough, "SendCommandByPassThrough"},
{1301, nullptr, "KeepPassThroughSession"},
{1302, nullptr, "ReleasePassThroughSession"},
};
RegisterHandlers(functions);
availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
for (u32 device_index = 0; device_index < 10; device_index++) {
devices[device_index] =
std::make_shared<NfcDevice>(Core::HID::IndexToNpadIdType(device_index), system,
service_context, availability_change_event);
}
}
IUser ::~IUser() {
availability_change_event->Close();
}
void IUser::Initialize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::Initialized;
for (auto& device : devices) {
device->Initialize();
}
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(ResultSuccess);
}
void IUser::Finalize(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
state = State::NonInitialized;
for (auto& device : devices) {
device->Finalize();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IUser::GetState(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(state);
}
void IUser::IsNfcEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(state != State::NonInitialized);
}
void IUser::ListDevices(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
if (!ctx.CanWriteBuffer()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
if (ctx.GetWriteBufferSize() == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(InvalidArgument);
return;
}
std::vector<u64> nfp_devices;
const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
for (auto& device : devices) {
if (nfp_devices.size() >= max_allowed_devices) {
continue;
}
if (device->GetCurrentState() != NFP::DeviceState::Unavailable) {
nfp_devices.push_back(device->GetHandle());
}
}
if (nfp_devices.empty()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
ctx.WriteBuffer(nfp_devices);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<s32>(nfp_devices.size()));
}
void IUser::GetDeviceState(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetCurrentState());
}
void IUser::GetNpadId(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(device.value()->GetNpadId());
}
void IUser::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
LOG_INFO(Service_NFC, "called");
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(availability_change_event->GetReadableEvent());
}
void IUser::StartDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto nfp_protocol{rp.PopEnum<NFP::TagProtocol>()};
LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StartDetection(nfp_protocol);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::StopDetection(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
const auto result = device.value()->StopDetection();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::GetTagInfo(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
NFP::TagInfo tag_info{};
const auto result = device.value()->GetTagInfo(tag_info, false);
ctx.WriteBuffer(tag_info);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void IUser::AttachActivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetActivateEvent());
}
void IUser::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(device.value()->GetDeactivateEvent());
}
void IUser::SendCommandByPassThrough(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto device_handle{rp.Pop<u64>()};
const auto timeout{rp.PopRaw<Time::Clock::TimeSpanType>()};
const auto command_data{ctx.ReadBuffer()};
LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
device_handle, timeout.ToSeconds(), command_data.size());
if (state == State::NonInitialized) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(NfcDisabled);
return;
}
auto device = GetNfcDevice(device_handle);
if (!device.has_value()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(DeviceNotFound);
return;
}
std::vector<u8> out_data(1);
// TODO: Request data from nfc device
ctx.WriteBuffer(out_data);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(out_data.size()));
}
std::optional<std::shared_ptr<NfcDevice>> IUser::GetNfcDevice(u64 handle) {
for (auto& device : devices) {
if (device->GetHandle() == handle) {
return device;
}
}
return std::nullopt;
}
} // namespace Service::NFC

View File

@@ -13,7 +13,7 @@ class IUser final : public Interface {
public:
explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IUser> functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
{2, &IUser::ListDevices, "ListDevices"},
@@ -50,7 +50,7 @@ class ISystem final : public Interface {
public:
explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<ISystem> functions[] = {
{0, &ISystem::InitializeSystem, "InitializeSystem"},
{1, &ISystem::FinalizeSystem, "FinalizeSystem"},
{2, &ISystem::ListDevices, "ListDevices"},
@@ -89,7 +89,7 @@ class IDebug final : public Interface {
public:
explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
// clang-format off
static const FunctionInfo functions[] = {
static const FunctionInfoTyped<IDebug> functions[] = {
{0, &IDebug::InitializeDebug, "InitializeDebug"},
{1, &IDebug::FinalizeDebug, "FinalizeDebug"},
{2, &IDebug::ListDevices, "ListDevices"},
@@ -126,9 +126,9 @@ public:
{201, &IDebug::SetAll, "SetAll"},
{202, &IDebug::FlushDebug, "FlushDebug"},
{203, &IDebug::BreakTag, "BreakTag"},
{204, nullptr, "ReadBackupData"},
{205, nullptr, "WriteBackupData"},
{206, nullptr, "WriteNtf"},
{204, &IDebug::ReadBackupData, "ReadBackupData"},
{205, &IDebug::WriteBackupData, "WriteBackupData"},
{206, &IDebug::WriteNtf, "WriteNtf"},
};
// clang-format on
@@ -152,16 +152,10 @@ private:
void CreateUserInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (user_interface == nullptr) {
user_interface = std::make_shared<IUser>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(user_interface);
rb.PushIpcInterface<IUser>(system);
}
std::shared_ptr<IUser> user_interface;
};
class ISystemManager final : public ServiceFramework<ISystemManager> {
@@ -180,16 +174,10 @@ private:
void CreateSystemInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<ISystem>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(system_interface);
rb.PushIpcInterface<ISystem>(system);
}
std::shared_ptr<ISystem> system_interface;
};
class IDebugManager final : public ServiceFramework<IDebugManager> {
@@ -208,16 +196,10 @@ private:
void CreateDebugInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
if (system_interface == nullptr) {
system_interface = std::make_shared<IDebug>(system);
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebug>(system_interface);
rb.PushIpcInterface<IDebug>(system);
}
std::shared_ptr<IDebug> system_interface;
};
void LoopProcess(Core::System& system) {

View File

@@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Core {
class System;
} // namespace Core
namespace Core::HID {
class EmulatedController;
enum class ControllerTriggerType;
enum class NpadIdType : u32;
} // namespace Core::HID
namespace Service::NFP {
class NfpDevice {
public:
NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
KernelHelpers::ServiceContext& service_context_,
Kernel::KEvent* availability_change_event_);
~NfpDevice();
void Initialize();
void Finalize();
Result StartDetection(TagProtocol allowed_protocol);
Result StopDetection();
Result Mount(MountTarget mount_target);
Result Unmount();
Result Flush();
Result FlushDebug();
Result FlushWithBreak(BreakType break_type);
Result GetTagInfo(TagInfo& tag_info) const;
Result GetCommonInfo(CommonInfo& common_info) const;
Result GetModelInfo(ModelInfo& model_info) const;
Result GetRegisterInfo(RegisterInfo& register_info) const;
Result GetRegisterInfoPrivate(RegisterInfoPrivate& register_info) const;
Result GetAdminInfo(AdminInfo& admin_info) const;
Result DeleteRegisterInfo();
Result SetRegisterInfoPrivate(const AmiiboName& amiibo_name);
Result RestoreAmiibo();
Result Format();
Result OpenApplicationArea(u32 access_id);
Result GetApplicationAreaId(u32& application_area_id) const;
Result GetApplicationArea(std::vector<u8>& data) const;
Result SetApplicationArea(std::span<const u8> data);
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
Result DeleteApplicationArea();
Result ExistApplicationArea(bool& has_application_area);
Result GetAll(NfpData& data) const;
Result SetAll(const NfpData& data);
Result BreakTag(BreakType break_type);
Result ReadBackupData();
Result WriteBackupData();
Result WriteNtf();
u64 GetHandle() const;
u32 GetApplicationAreaSize() const;
DeviceState GetCurrentState() const;
Core::HID::NpadIdType GetNpadId() const;
Kernel::KReadableEvent& GetActivateEvent() const;
Kernel::KReadableEvent& GetDeactivateEvent() const;
private:
void NpadUpdate(Core::HID::ControllerTriggerType type);
bool LoadAmiibo(std::span<const u8> data);
void CloseAmiibo();
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
AmiiboDate GetAmiiboDate(s64 posix_time) const;
u64 RemoveVersionByte(u64 application_id) const;
void UpdateSettingsCrc();
void UpdateRegisterInfoCrc();
bool is_controller_set{};
int callback_key;
const Core::HID::NpadIdType npad_id;
Core::System& system;
Core::HID::EmulatedController* npad_device = nullptr;
KernelHelpers::ServiceContext& service_context;
Kernel::KEvent* activate_event = nullptr;
Kernel::KEvent* deactivate_event = nullptr;
Kernel::KEvent* availability_change_event = nullptr;
bool is_initalized{};
bool is_data_moddified{};
bool is_app_area_open{};
bool is_plain_amiibo{};
TagProtocol allowed_protocols{};
s64 current_posix_time{};
MountTarget mount_target{MountTarget::None};
DeviceState device_state{DeviceState::Unavailable};
NTAG215File tag_data{};
EncryptedNTAG215File encrypted_tag_data{};
};
} // namespace Service::NFP

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <memory>
#include <optional>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
class NfpDevice;
class Interface : public ServiceFramework<Interface> {
class Interface : public NFC::NfcInterface {
public:
explicit Interface(Core::System& system_, const char* name);
~Interface() override;
void Initialize(HLERequestContext& ctx);
void InitializeSystem(HLERequestContext& ctx);
void InitializeDebug(HLERequestContext& ctx);
void Finalize(HLERequestContext& ctx);
void FinalizeSystem(HLERequestContext& ctx);
void FinalizeDebug(HLERequestContext& ctx);
void ListDevices(HLERequestContext& ctx);
void StartDetection(HLERequestContext& ctx);
void StopDetection(HLERequestContext& ctx);
void Mount(HLERequestContext& ctx);
void Unmount(HLERequestContext& ctx);
void OpenApplicationArea(HLERequestContext& ctx);
@@ -35,17 +26,10 @@ public:
void Flush(HLERequestContext& ctx);
void Restore(HLERequestContext& ctx);
void CreateApplicationArea(HLERequestContext& ctx);
void GetTagInfo(HLERequestContext& ctx);
void GetRegisterInfo(HLERequestContext& ctx);
void GetCommonInfo(HLERequestContext& ctx);
void GetModelInfo(HLERequestContext& ctx);
void AttachActivateEvent(HLERequestContext& ctx);
void AttachDeactivateEvent(HLERequestContext& ctx);
void GetState(HLERequestContext& ctx);
void GetDeviceState(HLERequestContext& ctx);
void GetNpadId(HLERequestContext& ctx);
void GetApplicationAreaSize(HLERequestContext& ctx);
void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
void RecreateApplicationArea(HLERequestContext& ctx);
void Format(HLERequestContext& ctx);
void GetAdminInfo(HLERequestContext& ctx);
@@ -61,21 +45,6 @@ public:
void ReadBackupData(HLERequestContext& ctx);
void WriteBackupData(HLERequestContext& ctx);
void WriteNtf(HLERequestContext& ctx);
private:
enum class State : u32 {
NonInitialized,
Initialized,
};
std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
KernelHelpers::ServiceContext service_context;
std::array<std::shared_ptr<NfpDevice>, 10> devices{};
State state{State::NonInitialized};
Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFP

View File

@@ -1,5 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -7,18 +7,19 @@
namespace Service::NFP {
constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
constexpr Result InvalidArgument(ErrorModule::NFP, 65);
constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
constexpr Result NfcDisabled(ErrorModule::NFP, 80);
constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result TagRemoved(ErrorModule::NFP, 97);
constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result CorruptedData(ErrorModule::NFP, 144);
constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
} // namespace Service::NFP

View File

@@ -7,32 +7,19 @@
#include "common/swap.h"
#include "core/hle/service/mii/types.h"
#include "core/hle/service/nfc/nfc_types.h"
namespace Service::NFP {
static constexpr std::size_t amiibo_name_length = 0xA;
static constexpr std::size_t application_id_version_offset = 0x1c;
static constexpr std::size_t counter_limit = 0xffff;
enum class ServiceType : u32 {
User,
Debug,
System,
};
enum class DeviceState : u32 {
Initialized,
SearchingForTag,
TagFound,
TagRemoved,
TagMounted,
Unavailable,
Finalized,
};
// This is nn::nfp::ModelType
enum class ModelType : u32 {
Amiibo,
};
// This is nn::nfp::MountTarget
enum class MountTarget : u32 {
None,
Rom,
@@ -72,35 +59,6 @@ enum class AmiiboSeries : u8 {
Diablo,
};
enum class TagType : u32 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
enum class PackedTagType : u8 {
None,
Type1, // ISO14443A RW 96-2k bytes 106kbit/s
Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
};
// Verify this enum. It might be completely wrong default protocol is 0x48
enum class TagProtocol : u32 {
None,
TypeA = 1U << 0, // ISO14443A
TypeB = 1U << 1, // ISO14443B
TypeF = 1U << 2, // Sony Felica
Unknown1 = 1U << 3,
Unknown2 = 1U << 5,
All = 0xFFFFFFFFU,
};
enum class AppAreaVersion : u8 {
Nintendo3DS = 0,
NintendoWiiU = 1,
@@ -115,6 +73,11 @@ enum class BreakType : u32 {
Unknown2,
};
enum class WriteType : u32 {
Unknown0,
Unknown1,
};
enum class CabinetMode : u8 {
StartNicknameAndOwnerSettings,
StartGameDataEraser,
@@ -122,27 +85,16 @@ enum class CabinetMode : u8 {
StartFormatter,
};
enum class MifareCmd : u8 {
AuthA = 0x60,
AuthB = 0x61,
Read = 0x30,
Write = 0xA0,
Transfer = 0xB0,
Decrement = 0xC0,
Increment = 0xC1,
Store = 0xC2
};
using UniqueSerialNumber = std::array<u8, 7>;
using LockBytes = std::array<u8, 2>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;
// This is nn::nfp::TagInfo
using TagInfo = NFC::TagInfo;
struct TagUuid {
UniqueSerialNumber uid;
NFC::UniqueSerialNumber uid;
u8 nintendo_id;
LockBytes lock_bytes;
};
@@ -243,7 +195,7 @@ struct AmiiboModelInfo {
AmiiboType amiibo_type;
u16_be model_number;
AmiiboSeries series;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
INSERT_PADDING_BYTES(0x4); // Unknown
};
static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
@@ -298,7 +250,7 @@ struct NTAG215File {
u32_be register_info_crc;
ApplicationArea application_area; // Encrypted Game data
HashData hmac_tag; // Hash
UniqueSerialNumber uid; // Unique serial number
NFC::UniqueSerialNumber uid; // Unique serial number
u8 nintendo_id; // Tag UUID
AmiiboModelInfo model_info;
HashData keygen_salt; // Salt
@@ -326,17 +278,7 @@ static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File),
static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
"EncryptedNTAG215File must be trivially copyable.");
struct TagInfo {
UniqueSerialNumber uuid;
INSERT_PADDING_BYTES(0x3);
u8 uuid_length;
INSERT_PADDING_BYTES(0x15);
TagProtocol protocol;
TagType tag_type;
INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
// This is nn::nfp::CommonInfo
struct CommonInfo {
WriteDate last_write_date;
u16 write_counter;
@@ -347,6 +289,7 @@ struct CommonInfo {
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
// This is nn::nfp::ModelInfo
struct ModelInfo {
u16 character_id;
u8 character_variant;
@@ -357,6 +300,7 @@ struct ModelInfo {
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
// This is nn::nfp::RegisterInfo
struct RegisterInfo {
Service::Mii::CharInfo mii_char_info;
WriteDate creation_date;
@@ -366,6 +310,7 @@ struct RegisterInfo {
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
Service::Mii::MiiStoreData mii_store_data;
WriteDate creation_date;
@@ -375,12 +320,13 @@ struct RegisterInfoPrivate {
};
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");
// This is nn::nfp::AdminInfo
struct AdminInfo {
u64 application_id;
u32 application_area_id;
u16 crc_change_counter;
u8 flags;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion app_area_version;
INSERT_PADDING_BYTES(0x7);
INSERT_PADDING_BYTES(0x28);
@@ -411,7 +357,7 @@ struct NfpData {
u32 access_id;
u16 settings_crc_counter;
u8 font_region;
PackedTagType tag_type;
NFC::PackedTagType tag_type;
AppAreaVersion console_type;
u8 application_id_byte;
INSERT_PADDING_BYTES(0x2E);
@@ -420,37 +366,4 @@ struct NfpData {
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
#pragma pack()
struct SectorKey {
MifareCmd command;
u8 unknown; // Usually 1
INSERT_PADDING_BYTES(0x6);
KeyData sector_key;
INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");
struct MifareReadBlockParameter {
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
"MifareReadBlockParameter is an invalid size");
struct MifareReadBlockData {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");
struct MifareWriteBlockParameter {
DataBlock data;
u8 sector_number;
INSERT_PADDING_BYTES(0x7);
SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
"MifareWriteBlockParameter is an invalid size");
} // namespace Service::NFP

View File

@@ -218,7 +218,7 @@ public:
private:
void Submit(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
if (state == RequestState::NotSubmitted) {
UpdateState(RequestState::OnHold);
@@ -229,7 +229,7 @@ private:
}
void GetRequestState(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -237,7 +237,7 @@ private:
}
void GetResult(HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
LOG_DEBUG(Service_NIFM, "(STUBBED) called");
const auto result = [this] {
const auto has_connection = Network::GetHostIPv4Address().has_value();

View File

@@ -793,6 +793,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
std::scoped_lock lock{core->mutex};
slots[slot] = {};
slots[slot].fence = Fence::NoFence();
slots[slot].graphic_buffer = buffer;
slots[slot].frame_number = 0;
@@ -854,7 +855,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
parcel_out.Write(slot);
parcel_out.WriteObject(&fence);
parcel_out.WriteFlattenedObject(&fence);
break;
}
case TransactionId::RequestBuffer: {
@@ -864,7 +865,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
status = RequestBuffer(slot, &buf);
parcel_out.WriteObject(buf);
parcel_out.WriteFlattenedObject(buf);
break;
}
case TransactionId::QueueBuffer: {

View File

@@ -117,61 +117,67 @@ private:
class OutputParcel final {
public:
static constexpr std::size_t DefaultBufferSize = 0x40;
OutputParcel() : buffer(DefaultBufferSize) {}
template <typename T>
explicit OutputParcel(const T& out_data) : buffer(DefaultBufferSize) {
Write(out_data);
}
OutputParcel() = default;
template <typename T>
void Write(const T& val) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
if (buffer.size() < write_index + sizeof(T)) {
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
}
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
write_index += sizeof(T);
write_index = Common::AlignUp(write_index, 4);
this->WriteImpl(val, m_data_buffer);
}
template <typename T>
void WriteObject(const T* ptr) {
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
void WriteFlattenedObject(const T* ptr) {
if (!ptr) {
Write<u32>(0);
this->Write<u32>(0);
return;
}
Write<u32>(1);
Write<s64>(sizeof(T));
Write(*ptr);
this->Write<u32>(1);
this->Write<s64>(sizeof(T));
this->Write(*ptr);
}
template <typename T>
void WriteObject(const std::shared_ptr<T> ptr) {
WriteObject(ptr.get());
void WriteFlattenedObject(const std::shared_ptr<T> ptr) {
this->WriteFlattenedObject(ptr.get());
}
template <typename T>
void WriteInterface(const T& val) {
this->WriteImpl(val, m_data_buffer);
this->WriteImpl(0U, m_object_buffer);
}
std::vector<u8> Serialize() const {
ParcelHeader header{};
header.data_size = static_cast<u32>(write_index - sizeof(ParcelHeader));
header.data_offset = sizeof(ParcelHeader);
header.objects_size = 4;
header.objects_offset = static_cast<u32>(sizeof(ParcelHeader) + header.data_size);
std::memcpy(buffer.data(), &header, sizeof(ParcelHeader));
std::vector<u8> output_buffer(sizeof(ParcelHeader) + m_data_buffer.size() +
m_object_buffer.size());
return buffer;
ParcelHeader header{};
header.data_size = static_cast<u32>(m_data_buffer.size());
header.data_offset = sizeof(ParcelHeader);
header.objects_size = static_cast<u32>(m_object_buffer.size());
header.objects_offset = header.data_offset + header.data_size;
std::memcpy(output_buffer.data(), &header, sizeof(header));
std::ranges::copy(m_data_buffer, output_buffer.data() + header.data_offset);
std::ranges::copy(m_object_buffer, output_buffer.data() + header.objects_offset);
return output_buffer;
}
private:
mutable std::vector<u8> buffer;
std::size_t write_index = sizeof(ParcelHeader);
template <typename T>
requires(std::is_trivially_copyable_v<T>)
void WriteImpl(const T& val, std::vector<u8>& buffer) {
const size_t aligned_size = Common::AlignUp(sizeof(T), 4);
const size_t old_size = buffer.size();
buffer.resize(old_size + aligned_size);
std::memcpy(buffer.data() + old_size, &val, sizeof(T));
}
private:
std::vector<u8> m_data_buffer;
std::vector<u8> m_object_buffer;
};
} // namespace Service::android

View File

@@ -142,7 +142,8 @@ template <typename Self>
class ServiceFramework : public ServiceFrameworkBase {
protected:
/// Contains information about a request type which is handled by the service.
struct FunctionInfo : FunctionInfoBase {
template <typename T>
struct FunctionInfoTyped : FunctionInfoBase {
// TODO(yuriks): This function could be constexpr, but clang is the only compiler that
// doesn't emit an ICE or a wrong diagnostic because of the static_cast.
@@ -155,12 +156,13 @@ protected:
* the request
* @param name_ human-friendly name for the request. Used mostly for logging purposes.
*/
FunctionInfo(u32 expected_header_, HandlerFnP<Self> handler_callback_, const char* name_)
FunctionInfoTyped(u32 expected_header_, HandlerFnP<T> handler_callback_, const char* name_)
: FunctionInfoBase{
expected_header_,
// Type-erase member function pointer by casting it down to the base class.
static_cast<HandlerFnP<ServiceFrameworkBase>>(handler_callback_), name_} {}
};
using FunctionInfo = FunctionInfoTyped<Self>;
/**
* Initializes the handler with no functions installed.
@@ -175,8 +177,8 @@ protected:
: ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlers(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlers(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlers(functions, N);
}
@@ -184,13 +186,14 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlers(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlers(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBase(functions, n);
}
/// Registers handlers in the service.
template <std::size_t N>
void RegisterHandlersTipc(const FunctionInfo (&functions)[N]) {
template <typename T = Self, std::size_t N>
void RegisterHandlersTipc(const FunctionInfoTyped<T> (&functions)[N]) {
RegisterHandlersTipc(functions, N);
}
@@ -198,7 +201,8 @@ protected:
* Registers handlers in the service. Usually prefer using the other RegisterHandlers
* overload in order to avoid needing to specify the array size.
*/
void RegisterHandlersTipc(const FunctionInfo* functions, std::size_t n) {
template <typename T = Self>
void RegisterHandlersTipc(const FunctionInfoTyped<T>* functions, std::size_t n) {
RegisterHandlersBaseTipc(functions, n);
}

View File

@@ -59,6 +59,18 @@ static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorre
static_assert(std::is_trivially_copyable_v<SystemClockContext>,
"SystemClockContext must be trivially copyable");
struct ContinuousAdjustmentTimePoint {
s64 measurement_offset;
s64 diff_scale;
u32 shift_amount;
s64 lower;
s64 upper;
Common::UUID clock_source_id;
};
static_assert(sizeof(ContinuousAdjustmentTimePoint) == 0x38);
static_assert(std::is_trivially_copyable_v<ContinuousAdjustmentTimePoint>,
"ContinuousAdjustmentTimePoint must be trivially copyable");
/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
struct TimeSpanType {
s64 nanoseconds{};

View File

@@ -30,6 +30,25 @@ void SharedMemory::SetupStandardSteadyClock(const Common::UUID& clock_source_id,
}
void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
// lower and upper are related to the measurement point for the steady time point,
// and compare equal on boot
const s64 time_point_ns = context.steady_time_point.time_point * 1'000'000'000LL;
// This adjusts for some sort of time skew
// Both 0 on boot
const s64 diff_scale = 0;
const u32 shift_amount = 0;
const Clock::ContinuousAdjustmentTimePoint adjustment{
.measurement_offset = system.CoreTiming().GetGlobalTimeNs().count(),
.diff_scale = diff_scale,
.shift_amount = shift_amount,
.lower = time_point_ns,
.upper = time_point_ns,
.clock_source_id = context.steady_time_point.clock_source_id,
};
StoreToLockFreeAtomicType(&GetFormat()->continuous_adjustment_timepoint, adjustment);
StoreToLockFreeAtomicType(&GetFormat()->standard_local_system_clock_context, context);
}

View File

@@ -65,14 +65,15 @@ public:
LockFreeAtomicType<Clock::SystemClockContext> standard_local_system_clock_context;
LockFreeAtomicType<Clock::SystemClockContext> standard_network_system_clock_context;
LockFreeAtomicType<bool> is_standard_user_system_clock_automatic_correction_enabled;
u32 format_version;
LockFreeAtomicType<Clock::ContinuousAdjustmentTimePoint> continuous_adjustment_timepoint;
};
static_assert(offsetof(Format, standard_steady_clock_timepoint) == 0x0);
static_assert(offsetof(Format, standard_local_system_clock_context) == 0x38);
static_assert(offsetof(Format, standard_network_system_clock_context) == 0x80);
static_assert(offsetof(Format, is_standard_user_system_clock_automatic_correction_enabled) ==
0xc8);
static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
static_assert(offsetof(Format, continuous_adjustment_timepoint) == 0xd0);
static_assert(sizeof(Format) == 0x148, "Format is an invalid size");
void SetupStandardSteadyClock(const Common::UUID& clock_source_id,
Clock::TimeSpanType current_time_point);

View File

@@ -64,8 +64,8 @@ public:
private:
const u32 magic = 2;
const u32 process_id = 1;
const u32 id;
INSERT_PADDING_WORDS(3);
const u64 id;
INSERT_PADDING_WORDS(2);
std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
INSERT_PADDING_WORDS(2);
};
@@ -608,7 +608,9 @@ private:
return;
}
const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 4};
@@ -654,7 +656,9 @@ private:
return;
}
const auto parcel = android::OutputParcel{NativeWindow{*buffer_queue_id}};
android::OutputParcel parcel;
parcel.WriteInterface(NativeWindow{*buffer_queue_id});
const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 6};

View File

@@ -356,7 +356,7 @@ NetworkInstance::~NetworkInstance() {
std::optional<IPv4Address> GetHostIPv4Address() {
const auto network_interface = Network::GetSelectedNetworkInterface();
if (!network_interface.has_value()) {
LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
LOG_DEBUG(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}

View File

@@ -200,7 +200,7 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
});
if (res == network_interfaces.end()) {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
LOG_DEBUG(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return std::nullopt;
}

View File

@@ -13,10 +13,12 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_download_area.h"
namespace Core::Memory {
@@ -243,7 +245,7 @@ struct Memory::Impl {
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
const u8* const host_ptr) {
if constexpr (!UNSAFE) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
}
std::memcpy(dest_buffer, host_ptr, copy_amount);
},
@@ -334,7 +336,7 @@ struct Memory::Impl {
},
[&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
u8* const host_ptr) {
system.GPU().FlushRegion(GetInteger(current_vaddr), copy_amount);
HandleRasterizerDownload(GetInteger(current_vaddr), copy_amount);
WriteBlockImpl<false>(process, dest_addr, host_ptr, copy_amount);
},
[&](const std::size_t copy_amount) {
@@ -373,7 +375,7 @@ struct Memory::Impl {
const std::size_t block_size) {
// dc ivac: Invalidate to point of coherency
// GPU flush -> CPU invalidate
system.GPU().FlushRegion(GetInteger(current_vaddr), block_size);
HandleRasterizerDownload(GetInteger(current_vaddr), block_size);
};
return PerformCacheOperation(process, dest_addr, size, on_rasterizer);
}
@@ -462,7 +464,8 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
const bool is_read_enable =
!Settings::values.use_reactive_flushing.GetValue() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}
@@ -651,7 +654,7 @@ struct Memory::Impl {
LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:016X}", sizeof(T) * 8,
GetInteger(vaddr));
},
[&]() { system.GPU().FlushRegion(GetInteger(vaddr), sizeof(T)); });
[&]() { HandleRasterizerDownload(GetInteger(vaddr), sizeof(T)); });
if (ptr) {
std::memcpy(&result, ptr, sizeof(T));
}
@@ -712,7 +715,19 @@ struct Memory::Impl {
return true;
}
void HandleRasterizerDownload(VAddr address, size_t size) {
const size_t core = system.GetCurrentHostThreadID();
auto& current_area = rasterizer_areas[core];
const VAddr end_address = address + size;
if (current_area.start_address <= address && end_address <= current_area.end_address)
[[likely]] {
return;
}
current_area = system.GPU().OnCPURead(address, size);
}
Common::PageTable* current_page_table = nullptr;
std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> rasterizer_areas{};
Core::System& system;
};

View File

@@ -85,6 +85,20 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
return "Unknown";
}
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return "Immediate";
case Settings::VSyncMode::Mailbox:
return "Mailbox";
case Settings::VSyncMode::FIFO:
return "FIFO";
case Settings::VSyncMode::FIFORelaxed:
return "FIFO Relaxed";
}
return "Unknown";
}
u64 GetTelemetryId() {
u64 telemetry_id{};
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -241,7 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend",
static_cast<u32>(Settings::values.shader_backend.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousShaders",

View File

@@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
OnMotionUpdate(port, type, id, value);
}},
.on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
.on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
OnAmiiboUpdate(port, amiibo_data);
.on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) {
OnAmiiboUpdate(port, type, amiibo_data);
}},
.on_camera_data = {[this, port](const std::vector<u8>& camera_data,
Joycon::IrsResolution format) {
@@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
return Common::Input::NfcState::Success;
};
Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
const std::vector<u8>& data) {
return Common::Input::NfcState::NotSupported;
auto handle = GetHandle(identifier);
if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) {
return Common::Input::NfcState::WriteFailed;
}
return Common::Input::NfcState::Success;
};
Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
@@ -398,8 +402,9 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
SetAxis(identifier, 100, ring_data);
}
void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
const std::vector<u8>& amiibo_data) {
const auto identifier = GetIdentifier(port, type);
const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
: Common::Input::NfcState::NewAmiibo;
SetNfc(identifier, {nfc_state, amiibo_data});

View File

@@ -81,7 +81,8 @@ private:
void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
const Joycon::MotionData& value);
void OnRingConUpdate(f32 ring_data);
void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
const std::vector<u8>& amiibo_data);
void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
Joycon::IrsResolution format);

View File

@@ -109,14 +109,37 @@ public:
}
bool RumblePlay(const Common::Input::VibrationStatus vibration) {
constexpr u32 rumble_max_duration_ms = 1000;
constexpr u32 rumble_max_duration_ms = 2000;
constexpr f32 low_start_sensitivity_limit = 140.0;
constexpr f32 low_width_sensitivity_limit = 400.0;
constexpr f32 high_start_sensitivity_limit = 200.0;
constexpr f32 high_width_sensitivity_limit = 700.0;
// Try to provide some feeling of the frequency by reducing the amplitude depending on it.
f32 low_frequency_scale = 1.0;
if (vibration.low_frequency > low_start_sensitivity_limit) {
low_frequency_scale =
std::max(1.0f - (vibration.low_frequency - low_start_sensitivity_limit) /
low_width_sensitivity_limit,
0.3f);
}
f32 low_amplitude = vibration.low_amplitude * low_frequency_scale;
f32 high_frequency_scale = 1.0;
if (vibration.high_frequency > high_start_sensitivity_limit) {
high_frequency_scale =
std::max(1.0f - (vibration.high_frequency - high_start_sensitivity_limit) /
high_width_sensitivity_limit,
0.3f);
}
f32 high_amplitude = vibration.high_amplitude * high_frequency_scale;
if (sdl_controller) {
return SDL_GameControllerRumble(
sdl_controller.get(), static_cast<u16>(vibration.low_amplitude),
static_cast<u16>(vibration.high_amplitude), rumble_max_duration_ms) != -1;
return SDL_GameControllerRumble(sdl_controller.get(), static_cast<u16>(low_amplitude),
static_cast<u16>(high_amplitude),
rumble_max_duration_ms) != -1;
} else if (sdl_joystick) {
return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(vibration.low_amplitude),
static_cast<u16>(vibration.high_amplitude),
return SDL_JoystickRumble(sdl_joystick.get(), static_cast<u16>(low_amplitude),
static_cast<u16>(high_amplitude),
rumble_max_duration_ms) != -1;
}

View File

@@ -492,6 +492,26 @@ DriverResult JoyconDriver::SetRingConMode() {
return result;
}
DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) {
std::scoped_lock lock{mutex};
disable_input_thread = true;
if (!supported_features.nfc) {
return DriverResult::NotSupported;
}
if (!nfc_protocol->IsEnabled()) {
return DriverResult::Disabled;
}
if (!amiibo_detected) {
return DriverResult::ErrorWritingData;
}
const auto result = nfc_protocol->WriteAmiibo(data);
disable_input_thread = false;
return result;
}
bool JoyconDriver::IsConnected() const {
std::scoped_lock lock{mutex};
return is_connected.load();

View File

@@ -49,6 +49,7 @@ public:
DriverResult SetIrMode();
DriverResult SetNfcMode();
DriverResult SetRingConMode();
DriverResult WriteNfcData(std::span<const u8> data);
void SetCallbacks(const JoyconCallbacks& callbacks);

View File

@@ -236,13 +236,13 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
return DriverResult::Success;
}
DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, MCUSubCommand sc,
std::span<const u8> buffer,
MCUCommandResponse& output) {
SubCommandPacket packet{
.output_report = OutputReport::MCU_DATA,
.packet_counter = GetCounter(),
.sub_command = sc,
.mcu_sub_command = sc,
.command_data = {},
};
@@ -265,12 +265,11 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubComman
DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
MCUCommandResponse output{};
constexpr std::size_t MaxTries{8};
constexpr std::size_t MaxTries{16};
std::size_t tries{};
do {
const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
const auto result = SendMCUData(report_mode, MCUSubCommand::SetDeviceMode, {}, output);
if (result != DriverResult::Success) {
return result;

View File

@@ -156,7 +156,7 @@ public:
* @param buffer data to be send
* @returns output buffer containing the response
*/
DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
DriverResult SendMCUData(ReportMode report_mode, MCUSubCommand sc, std::span<const u8> buffer,
MCUCommandResponse& output);
/**

View File

@@ -23,6 +23,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
using MacAddress = std::array<u8, 6>;
using SerialNumber = std::array<u8, 15>;
using TagUUID = std::array<u8, 7>;
enum class ControllerType : u8 {
None = 0x00,
@@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 {
LastCommandPacket = 0x08,
};
enum class NFCReadCommand : u8 {
enum class NFCCommand : u8 {
CancelAll = 0x00,
StartPolling = 0x01,
StopPolling = 0x02,
StartWaitingRecieve = 0x04,
Ntag = 0x06,
ReadNtag = 0x06,
WriteNtag = 0x08,
Mifare = 0x0F,
};
@@ -292,14 +294,19 @@ enum class NFCTagType : u8 {
enum class NFCPages {
Block0 = 0,
Block3 = 3,
Block45 = 45,
Block135 = 135,
Block231 = 231,
};
enum class NFCStatus : u8 {
Ready = 0x00,
Polling = 0x01,
LastPackage = 0x04,
WriteDone = 0x05,
TagLost = 0x07,
WriteReady = 0x09,
};
enum class IrsMode : u8 {
@@ -394,6 +401,7 @@ enum class DriverResult {
InvalidHandle,
NotSupported,
Disabled,
Delayed,
Unknown,
};
@@ -558,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv
struct NFCReadCommandData {
u8 unknown;
u8 uuid_length;
u8 unknown_2;
std::array<u8, 6> uid;
TagUUID uid;
NFCTagType tag_type;
NFCReadBlockCommand read_block;
};
static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
#pragma pack(push, 1)
struct NFCWriteCommandData {
u8 unknown;
u8 uuid_length;
TagUUID uid;
NFCTagType tag_type;
u8 unknown2;
u8 unknown3;
u8 unknown4;
u8 unknown5;
u8 unknown6;
u8 unknown7;
u8 unknown8;
u8 magic;
u16_be write_count;
u8 amiibo_version;
};
static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size");
#pragma pack(pop)
struct NFCPollingCommandData {
u8 enable_mifare;
u8 unknown_1;
@@ -575,10 +602,9 @@ struct NFCPollingCommandData {
static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
struct NFCRequestState {
MCUSubCommand sub_command;
NFCReadCommand command_argument;
NFCCommand command_argument;
u8 block_id;
u8 packet_id;
INSERT_PADDING_BYTES(0x1);
MCUPacketFlag packet_flag;
u8 data_length;
union {
@@ -587,9 +613,22 @@ struct NFCRequestState {
NFCPollingCommandData nfc_polling;
};
u8 crc;
INSERT_PADDING_BYTES(0x1);
};
static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
struct NFCDataChunk {
u8 nfc_page;
u8 data_size;
std::array<u8, 0xFF> data;
};
struct NFCWritePackage {
NFCWriteCommandData command_data;
u8 number_of_chunks;
std::array<NFCDataChunk, 4> data_chunks;
};
struct IrsConfigure {
MCUCommand command;
MCUSubCommand sub_command;
@@ -659,7 +698,10 @@ struct SubCommandPacket {
OutputReport output_report;
u8 packet_counter;
INSERT_PADDING_BYTES(0x8); // This contains vibration data
SubCommand sub_command;
union {
SubCommand sub_command;
MCUSubCommand mcu_sub_command;
};
std::array<u8, 0x26> command_data;
};
static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");

View File

@@ -34,6 +34,12 @@ DriverResult NfcProtocol::EnableNfc() {
result = ConfigureMCU(config);
}
if (result == DriverResult::Success) {
result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIs(NFCStatus::Ready);
}
return result;
}
@@ -56,13 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
LOG_DEBUG(Input, "Start NFC pooling Mode");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
MCUCommandResponse output{};
result = SendStopPollingRequest(output);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIsReady();
result = WaitUntilNfcIs(NFCStatus::Ready);
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStartPollingRequest(output);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIs(NFCStatus::Polling);
}
if (result == DriverResult::Success) {
is_enabled = true;
@@ -72,49 +85,99 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
}
DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
LOG_DEBUG(Input, "Start NFC pooling Mode");
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
return DriverResult::Delayed;
}
update_counter = 0;
LOG_DEBUG(Input, "Scan for amiibos");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
}
if (result == DriverResult::Success) {
result = ReadTag(tag_data);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIsReady();
}
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
result = IsTagInRange(tag_data);
}
if (result == DriverResult::Success) {
std::string uuid_string;
for (auto& content : tag_data.uuid) {
uuid_string += fmt::format(" {:02x}", content);
}
LOG_INFO(Input, "Tag detected, type={}, uuid={}", tag_data.type, uuid_string);
result = GetAmiiboData(data);
}
return result;
}
DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) {
LOG_DEBUG(Input, "Write amiibo");
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagUUID tag_uuid = GetTagUUID(data);
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = IsTagInRange(tag_data, 7);
}
if (result == DriverResult::Success) {
if (tag_data.uuid != tag_uuid) {
result = DriverResult::InvalidParameters;
}
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStopPollingRequest(output);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIs(NFCStatus::Ready);
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStartPollingRequest(output, true);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIs(NFCStatus::WriteReady);
}
if (result == DriverResult::Success) {
result = WriteAmiiboData(tag_uuid, data);
}
if (result == DriverResult::Success) {
result = WaitUntilNfcIs(NFCStatus::WriteDone);
}
if (result == DriverResult::Success) {
MCUCommandResponse output{};
result = SendStopPollingRequest(output);
}
return result;
}
bool NfcProtocol::HasAmiibo() {
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
return true;
}
update_counter = 0;
ScopedSetBlocking sb(this);
DriverResult result{DriverResult::Success};
TagFoundData tag_data{};
if (result == DriverResult::Success) {
result = StartPolling(tag_data);
result = IsTagInRange(tag_data, 7);
}
return result == DriverResult::Success;
}
DriverResult NfcProtocol::WaitUntilNfcIsReady() {
DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
constexpr std::size_t timeout_limit = 10;
MCUCommandResponse output{};
std::size_t tries = 0;
do {
auto result = SendStartWaitingRecieveRequest(output);
auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
@@ -124,19 +187,17 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
return DriverResult::Success;
}
DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
LOG_DEBUG(Input, "Start Polling for tag");
constexpr std::size_t timeout_limit = 7;
DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_limit) {
MCUCommandResponse output{};
std::size_t tries = 0;
do {
const auto result = SendStartPollingRequest(output);
const auto result = SendNextPackageRequest(output, {});
if (result != DriverResult::Success) {
return result;
}
@@ -145,94 +206,31 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
}
} while (output.mcu_report != MCUReport::NFCState ||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
output.mcu_data[6] != 0x09);
(output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
data.type = output.mcu_data[12];
data.uuid.resize(output.mcu_data[14]);
data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));
memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
return DriverResult::Success;
}
DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
constexpr std::size_t timeout_limit = 10;
MCUCommandResponse output{};
std::size_t tries = 0;
std::string uuid_string;
for (auto& content : data.uuid) {
uuid_string += fmt::format(" {:02x}", content);
}
LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
tries = 0;
NFCPages ntag_pages = NFCPages::Block0;
// Read Tag data
while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
return result;
}
if ((output.mcu_report == MCUReport::NFCReadData ||
output.mcu_report == MCUReport::NFCState) &&
nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData;
}
if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
output.mcu_data[2] == 0x01) {
if (data.type != 2) {
continue;
}
switch (output.mcu_data[24]) {
case 0:
ntag_pages = NFCPages::Block135;
break;
case 3:
ntag_pages = NFCPages::Block45;
break;
case 4:
ntag_pages = NFCPages::Block231;
break;
default:
return DriverResult::ErrorReadingData;
}
continue;
}
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
// finished
SendStopPollingRequest(output);
return DriverResult::Success;
}
// Ignore other state reports
if (output.mcu_report == MCUReport::NFCState) {
continue;
}
if (tries++ > timeout_limit) {
return DriverResult::Timeout;
}
}
return DriverResult::Success;
}
DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
constexpr std::size_t timeout_limit = 10;
constexpr std::size_t timeout_limit = 60;
MCUCommandResponse output{};
std::size_t tries = 0;
NFCPages ntag_pages = NFCPages::Block135;
u8 package_index = 0;
std::size_t ntag_buffer_pos = 0;
auto result = SendReadAmiiboRequest(output, NFCPages::Block135);
if (result != DriverResult::Success) {
return result;
}
// Read Tag data
while (true) {
auto result = SendReadAmiiboRequest(output, ntag_pages);
while (tries++ < timeout_limit) {
result = SendNextPackageRequest(output, package_index);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
@@ -255,6 +253,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
payload_size);
}
package_index++;
continue;
}
@@ -262,32 +261,99 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
LOG_INFO(Input, "Finished reading amiibo");
return DriverResult::Success;
}
}
// Ignore other state reports
if (output.mcu_report == MCUReport::NFCState) {
return DriverResult::Timeout;
}
DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) {
constexpr std::size_t timeout_limit = 60;
const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data);
const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data);
std::span<const u8> buffer(nfc_buffer_data);
MCUCommandResponse output{};
u8 block_id = 1;
u8 package_index = 0;
std::size_t tries = 0;
std::size_t current_position = 0;
LOG_INFO(Input, "Writing amiibo data");
auto result = SendWriteAmiiboRequest(output, tag_uuid);
if (result != DriverResult::Success) {
return result;
}
// Read Tag data but ignore the actual sent data
while (tries++ < timeout_limit) {
result = SendNextPackageRequest(output, package_index);
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if (result != DriverResult::Success) {
return result;
}
if ((output.mcu_report == MCUReport::NFCReadData ||
output.mcu_report == MCUReport::NFCState) &&
nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData;
}
if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
package_index++;
continue;
}
if (tries++ > timeout_limit) {
return DriverResult::Timeout;
if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
LOG_INFO(Input, "Finished reading amiibo");
break;
}
}
return DriverResult::Success;
// Send Data. Nfc buffer size is 31, Send the data in smaller packages
while (current_position < buffer.size() && tries++ < timeout_limit) {
const std::size_t next_position =
std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size());
const std::size_t block_size = next_position - current_position;
const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data);
SendWriteDataAmiiboRequest(output, block_id, is_last_packet,
buffer.subspan(current_position, block_size));
const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
if ((output.mcu_report == MCUReport::NFCReadData ||
output.mcu_report == MCUReport::NFCState) &&
nfc_status == NFCStatus::TagLost) {
return DriverResult::ErrorReadingData;
}
// Increase position when data is confirmed by the joycon
if (output.mcu_report == MCUReport::NFCState &&
(output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 &&
output.mcu_data[3] == block_id) {
block_id++;
current_position = next_position;
}
}
return result;
}
DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output,
bool is_second_attempt) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartPolling,
.packet_id = 0x0,
.command_argument = NFCCommand::StartPolling,
.block_id = {},
.packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = sizeof(NFCPollingCommandData),
.nfc_polling =
{
.enable_mifare = 0x01,
.unknown_1 = 0x00,
.unknown_2 = 0x00,
.enable_mifare = 0x00,
.unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00),
.unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),
.unknown_3 = 0x2c,
.unknown_4 = 0x01,
},
@@ -296,58 +362,60 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StopPolling,
.packet_id = 0x0,
.command_argument = NFCCommand::StopPolling,
.block_id = {},
.packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = 0,
.data_length = {},
.raw_data = {},
.crc = {},
};
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::StartWaitingRecieve,
.packet_id = 0x0,
.command_argument = NFCCommand::StartWaitingRecieve,
.block_id = {},
.packet_id = packet_id,
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = 0,
.data_length = {},
.raw_data = {},
.crc = {},
};
std::vector<u8> request_data(sizeof(NFCRequestState));
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
NFCRequestState request{
.sub_command = MCUSubCommand::ReadDeviceMode,
.command_argument = NFCReadCommand::Ntag,
.packet_id = 0x0,
.command_argument = NFCCommand::ReadNtag,
.block_id = {},
.packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = sizeof(NFCReadCommandData),
.nfc_read =
{
.unknown = 0xd0,
.uuid_length = 0x07,
.unknown_2 = 0x00,
.uuid_length = sizeof(NFCReadCommandData::uid),
.uid = {},
.tag_type = NFCTagType::AllTags,
.tag_type = NFCTagType::Ntag215,
.read_block = GetReadBlockCommand(ntag_pages),
},
.crc = {},
@@ -355,8 +423,124 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output,
const TagUUID& tag_uuid) {
NFCRequestState request{
.command_argument = NFCCommand::ReadNtag,
.block_id = {},
.packet_id = {},
.packet_flag = MCUPacketFlag::LastCommandPacket,
.data_length = sizeof(NFCReadCommandData),
.nfc_read =
{
.unknown = 0xd0,
.uuid_length = sizeof(NFCReadCommandData::uid),
.uid = tag_uuid,
.tag_type = NFCTagType::Ntag215,
.read_block = GetReadBlockCommand(NFCPages::Block3),
},
.crc = {},
};
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
bool is_last_packet,
std::span<const u8> data) {
const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data));
NFCRequestState request{
.command_argument = NFCCommand::WriteNtag,
.block_id = block_id,
.packet_id = {},
.packet_flag =
is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining,
.data_length = static_cast<u8>(data_size),
.raw_data = {},
.crc = {},
};
memcpy(request.raw_data.data(), data.data(), data_size);
std::array<u8, sizeof(NFCRequestState)> request_data{};
memcpy(request_data.data(), &request, sizeof(NFCRequestState));
request_data[36] = CalculateMCU_CRC8(request_data.data(), 36);
return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data,
output);
}
std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const {
const std::size_t header_size =
sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks);
std::vector<u8> serialized_data(header_size);
std::size_t start_index = 0;
memcpy(serialized_data.data(), &package, header_size);
start_index += header_size;
for (const auto& data_chunk : package.data_chunks) {
const std::size_t chunk_size =
sizeof(NFCDataChunk::nfc_page) + sizeof(NFCDataChunk::data_size) + data_chunk.data_size;
serialized_data.resize(start_index + chunk_size);
memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size);
start_index += chunk_size;
}
return serialized_data;
}
NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid,
std::span<const u8> data) const {
return {
.command_data{
.unknown = 0xd0,
.uuid_length = sizeof(NFCReadCommandData::uid),
.uid = tag_uuid,
.tag_type = NFCTagType::Ntag215,
.unknown2 = 0x00,
.unknown3 = 0x01,
.unknown4 = 0x04,
.unknown5 = 0xff,
.unknown6 = 0xff,
.unknown7 = 0xff,
.unknown8 = 0xff,
.magic = data[16],
.write_count = static_cast<u16>((data[17] << 8) + data[18]),
.amiibo_version = data[19],
},
.number_of_chunks = 3,
.data_chunks =
{
MakeAmiiboChunk(0x05, 0x20, data),
MakeAmiiboChunk(0x20, 0xf0, data),
MakeAmiiboChunk(0x5c, 0x98, data),
},
};
}
NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const {
constexpr u8 PAGE_SIZE = 4;
if (static_cast<std::size_t>(page * PAGE_SIZE) + size >= data.size()) {
return {};
}
NFCDataChunk chunk{
.nfc_page = page,
.data_size = size,
.data = {},
};
std::memcpy(chunk.data.data(), data.data() + (page * PAGE_SIZE), size);
return chunk;
}
NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
@@ -365,6 +549,14 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
return {
.block_count = 1,
};
case NFCPages::Block3:
return {
.block_count = 1,
.blocks =
{
NFCReadBlock{0x03, 0x03},
},
};
case NFCPages::Block45:
return {
.block_count = 1,
@@ -399,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
};
}
TagUUID NfcProtocol::GetTagUUID(std::span<const u8> data) const {
if (data.size() < 10) {
return {};
}
// crc byte 3 is omitted in this operation
return {
data[0], data[1], data[2], data[4], data[5], data[6], data[7],
};
}
bool NfcProtocol::IsEnabled() const {
return is_enabled;
}

View File

@@ -27,35 +27,56 @@ public:
DriverResult ScanAmiibo(std::vector<u8>& data);
DriverResult WriteAmiibo(std::span<const u8> data);
bool HasAmiibo();
bool IsEnabled() const;
private:
// Number of times the function will be delayed until it outputs valid data
static constexpr std::size_t AMIIBO_UPDATE_DELAY = 15;
struct TagFoundData {
u8 type;
std::vector<u8> uuid;
u8 uuid_size;
TagUUID uuid;
};
DriverResult WaitUntilNfcIsReady();
DriverResult WaitUntilNfcIs(NFCStatus status);
DriverResult StartPolling(TagFoundData& data);
DriverResult ReadTag(const TagFoundData& data);
DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
DriverResult GetAmiiboData(std::vector<u8>& data);
DriverResult SendStartPollingRequest(MCUCommandResponse& output);
DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data);
DriverResult SendStartPollingRequest(MCUCommandResponse& output,
bool is_second_attempt = false);
DriverResult SendStopPollingRequest(MCUCommandResponse& output);
DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
DriverResult SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id);
DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid);
DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id,
bool is_last_packet, std::span<const u8> data);
std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const;
NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const;
NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const;
NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
TagUUID GetTagUUID(std::span<const u8> data) const;
bool is_enabled{};
std::size_t update_counter{};
};
} // namespace InputCommon::Joycon

View File

@@ -380,13 +380,16 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
if (!configuring || !mapping_callback.on_data) {
return;
}
const auto old_value = GetMotion(identifier, motion);
bool is_active = false;
if (std::abs(value.accel_x) > 1.5f || std::abs(value.accel_y) > 1.5f ||
std::abs(value.accel_z) > 1.5f) {
if (std::abs(value.accel_x - old_value.accel_x) > 1.5f ||
std::abs(value.accel_y - old_value.accel_y) > 1.5f ||
std::abs(value.accel_z - old_value.accel_z) > 1.5f) {
is_active = true;
}
if (std::abs(value.gyro_x) > 0.6f || std::abs(value.gyro_y) > 0.6f ||
std::abs(value.gyro_z) > 0.6f) {
if (std::abs(value.gyro_x - old_value.gyro_x) > 0.6f ||
std::abs(value.gyro_y - old_value.gyro_y) > 0.6f ||
std::abs(value.gyro_z - old_value.gyro_z) > 0.6f) {
is_active = true;
}
if (!is_active) {

View File

@@ -82,6 +82,9 @@ void MappingFactory::RegisterButton(const MappingData& data) {
new_input.Set("axis", data.index);
new_input.Set("threshold", 0.5f);
break;
case EngineInputType::Motion:
new_input.Set("motion", data.index);
break;
default:
return;
}

View File

@@ -667,7 +667,7 @@ public:
.raw_value = input_engine->GetAxis(identifier, axis_z),
.properties = properties_z,
};
status.delta_timestamp = 5000;
status.delta_timestamp = 1000;
status.force_update = true;
return status;
}
@@ -939,6 +939,7 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateAnalogDevice(
.threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f),
.offset = std::clamp(params.Get("offset", 0.0f), -1.0f, 1.0f),
.inverted = params.Get("invert", "+") == "-",
.inverted_button = params.Get("inverted", false) != 0,
.toggle = params.Get("toggle", false) != 0,
};
input_engine->PreSetController(identifier);

View File

@@ -339,9 +339,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_id));
} else {
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_index));
}
case IR::Attribute::BaseInstance:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.base_instance));
@@ -386,9 +384,7 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, Id) {
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
} else {
const Id index{ctx.OpLoad(ctx.U32[1], ctx.vertex_index)};
const Id base{ctx.OpLoad(ctx.U32[1], ctx.base_vertex)};
return ctx.OpISub(ctx.U32[1], index, base);
return ctx.OpLoad(ctx.U32[1], ctx.vertex_index);
}
case IR::Attribute::BaseInstance:
return ctx.OpLoad(ctx.U32[1], ctx.base_instance);

View File

@@ -30,7 +30,7 @@ void SHF(TranslatorVisitor& v, u64 insn, const IR::U32& shift, const IR::U32& hi
union {
u64 insn;
BitField<0, 8, IR::Reg> dest_reg;
BitField<0, 8, IR::Reg> lo_bits_reg;
BitField<8, 8, IR::Reg> lo_bits_reg;
BitField<37, 2, MaxShift> max_shift;
BitField<47, 1, u64> cc;
BitField<48, 2, u64> x_mode;

View File

@@ -102,12 +102,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
}
IR::F32 value{v.ir.CompositeExtract(sample, element)};
if (element < 2) {
IR::U32 casted_value;
if (element == 0) {
casted_value = v.ir.ConvertFToU(32, value);
} else {
casted_value = v.ir.ConvertFToS(16, value);
}
IR::U32 casted_value = v.ir.ConvertFToU(32, value);
v.X(dest_reg, v.ir.ShiftLeftLogical(casted_value, v.ir.Imm32(8)));
} else {
v.F(dest_reg, value);

View File

@@ -535,12 +535,12 @@ TEST_CASE("MemoryTracker: Cached write downloads") {
memory_track->MarkRegionAsGpuModified(c + PAGE, PAGE);
int num = 0;
memory_track->ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 1);
REQUIRE(num == 0);
num = 0;
memory_track->ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
REQUIRE(num == 0);
REQUIRE(!memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));
REQUIRE(memory_track->IsRegionGpuModified(c + PAGE, PAGE));
memory_track->FlushCachedWrites();
REQUIRE(memory_track->IsRegionCpuModified(c + PAGE, PAGE));
REQUIRE(!memory_track->IsRegionGpuModified(c + PAGE, PAGE));

View File

@@ -246,10 +246,14 @@ add_library(video_core STATIC
texture_cache/util.h
textures/astc.h
textures/astc.cpp
textures/bcn.cpp
textures/bcn.h
textures/decoders.cpp
textures/decoders.h
textures/texture.cpp
textures/texture.h
textures/workers.cpp
textures/workers.h
transform_feedback.cpp
transform_feedback.h
video_core.cpp
@@ -275,7 +279,7 @@ add_library(video_core STATIC
create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PUBLIC glad shader_recompiler)
target_link_libraries(video_core PUBLIC glad shader_recompiler stb)
if (YUZU_USE_BUNDLED_FFMPEG AND NOT WIN32)
add_dependencies(video_core ffmpeg-build)

View File

@@ -18,6 +18,7 @@ namespace VideoCommon {
enum class BufferFlagBits {
Picked = 1 << 0,
CachedWrites = 1 << 1,
PreemtiveDownload = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(BufferFlagBits)
@@ -54,6 +55,10 @@ public:
flags |= BufferFlagBits::Picked;
}
void MarkPreemtiveDownload() noexcept {
flags |= BufferFlagBits::PreemtiveDownload;
}
/// Unmark buffer as picked
void Unpick() noexcept {
flags &= ~BufferFlagBits::Picked;
@@ -84,6 +89,10 @@ public:
return True(flags & BufferFlagBits::CachedWrites);
}
bool IsPreemtiveDownload() const noexcept {
return True(flags & BufferFlagBits::PreemtiveDownload);
}
/// Returns the base CPU address of the buffer
[[nodiscard]] VAddr CpuAddr() const noexcept {
return cpu_addr;

View File

@@ -23,8 +23,6 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
common_ranges.clear();
inline_buffer_id = NULL_BUFFER_ID;
active_async_buffers = !Settings::IsGPULevelHigh();
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
@@ -32,8 +30,8 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
}
const s64 device_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const s64 min_spacing_expected = device_memory - 1_GiB - 512_MiB;
const s64 min_spacing_critical = device_memory - 1_GiB;
const s64 min_spacing_expected = device_memory - 1_GiB;
const s64 min_spacing_critical = device_memory - 512_MiB;
const s64 mem_threshold = std::min(device_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (3 * mem_threshold) / 10;
@@ -75,8 +73,6 @@ void BufferCache<P>::TickFrame() {
uniform_cache_hits[0] = 0;
uniform_cache_shots[0] = 0;
active_async_buffers = !Settings::IsGPULevelHigh();
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
@@ -100,51 +96,50 @@ void BufferCache<P>::TickFrame() {
template <class P>
void BufferCache<P>::WriteMemory(VAddr cpu_addr, u64 size) {
memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
if (memory_tracker.IsRegionGpuModified(cpu_addr, size)) {
const IntervalType subtract_interval{cpu_addr, cpu_addr + size};
ClearDownload(subtract_interval);
common_ranges.subtract(subtract_interval);
}
memory_tracker.MarkRegionAsCpuModified(cpu_addr, size);
}
template <class P>
void BufferCache<P>::CachedWriteMemory(VAddr cpu_addr, u64 size) {
memory_tracker.CachedCpuWrite(cpu_addr, size);
const IntervalType add_interval{Common::AlignDown(cpu_addr, YUZU_PAGESIZE),
Common::AlignUp(cpu_addr + size, YUZU_PAGESIZE)};
cached_ranges.add(add_interval);
}
template <class P>
std::optional<VideoCore::RasterizerDownloadArea> BufferCache<P>::GetFlushArea(VAddr cpu_addr,
u64 size) {
std::optional<VideoCore::RasterizerDownloadArea> area{};
area.emplace();
VAddr cpu_addr_start_aligned = Common::AlignDown(cpu_addr, Core::Memory::YUZU_PAGESIZE);
VAddr cpu_addr_end_aligned = Common::AlignUp(cpu_addr + size, Core::Memory::YUZU_PAGESIZE);
area->start_address = cpu_addr_start_aligned;
area->end_address = cpu_addr_end_aligned;
if (memory_tracker.IsRegionPreflushable(cpu_addr, size)) {
area->preemtive = true;
return area;
};
area->preemtive =
!IsRegionGpuModified(cpu_addr_start_aligned, cpu_addr_end_aligned - cpu_addr_start_aligned);
memory_tracker.MarkRegionAsPreflushable(cpu_addr_start_aligned,
cpu_addr_end_aligned - cpu_addr_start_aligned);
return area;
}
template <class P>
void BufferCache<P>::DownloadMemory(VAddr cpu_addr, u64 size) {
WaitOnAsyncFlushes(cpu_addr, size);
ForEachBufferInRange(cpu_addr, size, [&](BufferId, Buffer& buffer) {
DownloadBufferMemory(buffer, cpu_addr, size);
});
}
template <class P>
void BufferCache<P>::WaitOnAsyncFlushes(VAddr cpu_addr, u64 size) {
bool must_wait = false;
ForEachInOverlapCounter(async_downloads, cpu_addr, size,
[&](VAddr, VAddr, int) { must_wait = true; });
bool must_release = false;
ForEachInRangeSet(pending_ranges, cpu_addr, size, [&](VAddr, VAddr) { must_release = true; });
if (must_release) {
std::function<void()> tmp([]() {});
rasterizer.SignalFence(std::move(tmp));
}
if (must_wait || must_release) {
rasterizer.ReleaseFences();
}
}
template <class P>
void BufferCache<P>::ClearDownload(IntervalType subtract_interval) {
RemoveEachInOverlapCounter(async_downloads, subtract_interval, -1024);
uncommitted_ranges.subtract(subtract_interval);
pending_ranges.subtract(subtract_interval);
for (auto& interval_set : committed_ranges) {
interval_set.subtract(subtract_interval);
}
@@ -164,7 +159,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
}
const IntervalType subtract_interval{*cpu_dest_address, *cpu_dest_address + amount};
WaitOnAsyncFlushes(*cpu_src_address, static_cast<u32>(amount));
ClearDownload(subtract_interval);
BufferId buffer_a;
@@ -192,7 +186,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
const IntervalType add_interval{new_base_address, new_base_address + size};
tmp_intervals.push_back(add_interval);
uncommitted_ranges.add(add_interval);
pending_ranges.add(add_interval);
};
ForEachInRangeSet(common_ranges, *cpu_src_address, amount, mirror);
// This subtraction in this order is important for overlapping copies.
@@ -205,7 +198,7 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
if (has_new_downloads) {
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
}
std::vector<u8> tmp_buffer(amount);
tmp_buffer.resize(amount);
cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount);
cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount);
return true;
@@ -441,9 +434,7 @@ void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_add
template <class P>
void BufferCache<P>::FlushCachedWrites() {
cached_write_buffer_ids.clear();
memory_tracker.FlushCachedWrites();
cached_ranges.clear();
}
template <class P>
@@ -474,15 +465,13 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
if (committed_ranges.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
MICROPROFILE_SCOPE(GPU_DownloadMemory);
pending_ranges.clear();
auto it = committed_ranges.begin();
while (it != committed_ranges.end()) {
auto& current_intervals = *it;
@@ -537,64 +526,65 @@ void BufferCache<P>::CommitAsyncFlushesHigh() {
committed_ranges.clear();
if (downloads.empty()) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
if (active_async_buffers) {
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
async_buffers.emplace_back(std::optional<Async_Buffer>{});
}
return;
}
if (active_async_buffers) {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if constexpr (IMPLEMENTS_ASYNC_DOWNLOADS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes, true);
boost::container::small_vector<BufferCopy, 4> normalized_copies;
IntervalSet new_async_range{};
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
BufferCopy second_copy{copy};
Buffer& buffer = slot_buffers[buffer_id];
second_copy.src_offset = static_cast<size_t>(buffer.CpuAddr()) + copy.src_offset;
VAddr orig_cpu_addr = static_cast<VAddr>(second_copy.src_offset);
const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size};
async_downloads += std::make_pair(base_interval, 1);
runtime.CopyBuffer(download_staging.buffer, buffer, copies, false);
normalized_copies.push_back(second_copy);
}
runtime.PostCopyBarrier();
pending_downloads.emplace_back(std::move(normalized_copies));
async_buffers.emplace_back(download_staging);
} else {
if (!Settings::IsGPULevelHigh()) {
committed_ranges.clear();
uncommitted_ranges.clear();
}
} else {
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset, immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
if constexpr (USE_MEMORY_MAPS) {
auto download_staging = runtime.DownloadStagingBuffer(total_size_bytes);
runtime.PreCopyBarrier();
for (auto& [copy, buffer_id] : downloads) {
// Have in mind the staging buffer offset for the copy
copy.dst_offset += download_staging.offset;
const std::array copies{copy};
runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies,
false);
}
runtime.PostCopyBarrier();
runtime.Finish();
for (const auto& [copy, buffer_id] : downloads) {
const Buffer& buffer = slot_buffers[buffer_id];
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
// Undo the modified offset
const u64 dst_offset = copy.dst_offset - download_staging.offset;
const u8* read_mapped_memory = download_staging.mapped_span.data() + dst_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, read_mapped_memory, copy.size);
}
} else {
const std::span<u8> immediate_buffer = ImmediateBuffer(largest_copy);
for (const auto& [copy, buffer_id] : downloads) {
Buffer& buffer = slot_buffers[buffer_id];
buffer.ImmediateDownload(copy.src_offset,
immediate_buffer.subspan(0, copy.size));
const VAddr cpu_addr = buffer.CpuAddr() + copy.src_offset;
cpu_memory.WriteBlockUnsafe(cpu_addr, immediate_buffer.data(), copy.size);
}
}
}
}
@@ -1213,16 +1203,14 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
template <class P>
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, VAddr cpu_addr, u32 size) {
memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
if (memory_tracker.IsRegionCpuModified(cpu_addr, size)) {
SynchronizeBuffer(slot_buffers[buffer_id], cpu_addr, size);
}
memory_tracker.MarkRegionAsGpuModified(cpu_addr, size);
const IntervalType base_interval{cpu_addr, cpu_addr + size};
common_ranges.add(base_interval);
uncommitted_ranges.add(base_interval);
pending_ranges.add(base_interval);
}
template <class P>
@@ -1629,7 +1617,6 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
replace(transform_feedback_buffers);
replace(compute_uniform_buffers);
replace(compute_storage_buffers);
std::erase(cached_write_buffer_ids, buffer_id);
// Mark the whole buffer as CPU written to stop tracking CPU writes
if (!do_not_mark) {
@@ -1668,15 +1655,16 @@ typename BufferCache<P>::Binding BufferCache<P>::StorageBufferBinding(GPUVAddr s
const bool is_nvn_cbuf = cbuf_index == 0;
// The NVN driver buffer (index 0) is known to pack the SSBO address followed by its size.
if (is_nvn_cbuf) {
return gpu_memory->Read<u32>(ssbo_addr + 8);
const u32 ssbo_size = gpu_memory->Read<u32>(ssbo_addr + 8);
if (ssbo_size != 0) {
return ssbo_size;
}
}
// Other titles (notably Doom Eternal) may use STG/LDG on buffer addresses in custom defined
// cbufs, which do not store the sizes adjacent to the addresses, so use the fully
// mapped buffer size for now.
const u32 memory_layout_size = static_cast<u32>(gpu_memory->GetMemoryLayoutSize(gpu_addr));
LOG_INFO(HW_GPU, "Binding storage buffer for cbuf index {}, MemoryLayoutSize 0x{:X}",
cbuf_index, memory_layout_size);
return memory_layout_size;
return std::min(memory_layout_size, static_cast<u32>(8_MiB));
}();
const std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!cpu_addr || size == 0) {

View File

@@ -188,6 +188,8 @@ public:
void DownloadMemory(VAddr cpu_addr, u64 size);
std::optional<VideoCore::RasterizerDownloadArea> GetFlushArea(VAddr cpu_addr, u64 size);
bool InlineMemory(VAddr dest_address, size_t copy_size, std::span<const u8> inlined_buffer);
void BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr, u32 size);
@@ -379,8 +381,6 @@ private:
void RunGarbageCollector();
void WaitOnAsyncFlushes(VAddr cpu_addr, u64 size);
void BindHostIndexBuffer();
void BindHostVertexBuffers();
@@ -541,13 +541,10 @@ private:
std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>, Empty>
uniform_buffer_binding_sizes{};
std::vector<BufferId> cached_write_buffer_ids;
MemoryTracker memory_tracker;
IntervalSet uncommitted_ranges;
IntervalSet common_ranges;
IntervalSet cached_ranges;
IntervalSet pending_ranges;
std::deque<IntervalSet> committed_ranges;
// Async Buffers
@@ -572,9 +569,8 @@ private:
u64 critical_memory = 0;
BufferId inline_buffer_id;
bool active_async_buffers = false;
std::array<BufferId, ((1ULL << 39) >> CACHING_PAGEBITS)> page_table;
std::vector<u8> tmp_buffer;
};
} // namespace VideoCommon

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