Compare commits

..

272 Commits

Author SHA1 Message Date
AniLeo 46a54f5474 rpcs3_version: Bump to 0.0.19 2021-11-01 03:05:52 +00:00
Eladash e31173e33e GUI: Always show thread name for fatal messages (#11090) 2021-10-31 13:24:41 +01:00
kd-11 5b0ef401f7 rsx: Fix sampling in X when 0 pitch is given
- A pitch of 0 still allows 1-dimensional addressing.
2021-10-31 14:32:42 +03:00
Megamouse 1650dd1c7d overlays: fix graph offset error after applying new config
I already had this figured out last time but forgot the dynamic config use case.
2021-10-31 10:14:08 +01:00
Megamouse 84f123041a overlays: fix offset of right edge oriented graphs when detail level is none 2021-10-31 10:14:08 +01:00
Megamouse f258ae795c Add more logging for Emulator Stop events
This should give us more insight into the conditions that cause emulation stops.
This may also help find false issue reports.
2021-10-31 04:12:47 +01:00
Megamouse 33e80a733d overlays: fix trophy notification sound in queue 2021-10-30 22:44:30 +02:00
Megamouse 20331a77ce Qt: multithreaded trophy icon refresh 2021-10-30 22:44:30 +02:00
Megamouse 4e6b37ca04 Qt: fix missing vfs mount 2021-10-30 22:44:30 +02:00
Eladash 646ad3eed4 SPU DisAsm: Force decimal point insertion for constant floats 2021-10-30 21:33:06 +02:00
Eladash edcc2a9e0d PS3 String Searcher: Implement instruction searching in embedded SPU images 2021-10-30 21:33:06 +02:00
Megamouse 0e20acdf55 overlays: add optional sound to trophy popup 2021-10-30 17:16:45 +02:00
Megamouse f262e77fbd overlays: add fade to trophy notification pop-ups 2021-10-30 17:16:45 +02:00
Megamouse 244aa6879a overlays: fix trophy notification pop-up locations 2021-10-30 17:16:45 +02:00
Megamouse e9fe90244f Emu: Move VFS mount to Init 2021-10-29 19:46:11 +02:00
Eladash a90376a7c4 SPU DisAsm: Print SPU floats 2021-10-29 19:15:21 +02:00
Megamouse aff7d8a994 Qt: fix QSvg deployment 2021-10-28 23:54:56 +02:00
Megamouse 7f4b6b1ae7 Qt: Use svg instead of png 2021-10-28 21:01:27 +02:00
kd-11 78bcb0fd53 rsx: Do not reuse/destroy sections that have references held
- Avoids a situation where blit-dst and blit-src have overlapping ranges. Uploading blit-dst destroys blit-src and vice-versa.
  This is not the end of the world, but blit-src should be kept around until the operation is completed to avoid stale references!
2021-10-27 12:30:43 +03:00
kd-11 c733e794de gl: Use real image dimensions when decoding compressed textures
- Image size is already correctly calculated using block dimensions
2021-10-27 12:30:43 +03:00
kd-11 99fc90648b gl: Disable shader interpreter if hardware does not support bindless textures 2021-10-27 12:30:43 +03:00
kd-11 2587545eed gl: Fix decoding of wide, swizzled textures
- Handle pre-byteswapped data (swizzled usually) in the compute-safe path
2021-10-27 12:30:43 +03:00
capriots ac300c7356 Correct channel order
Documentation says the order is Front L, Front R, Center, LFE, Surround L, Surround R, Rear L, Rear R
2021-10-27 10:24:02 +03:00
Megamouse 2af2db2cbf Lower some log spam
These functions probably aren't perfect, but they spam a lot of unnecessary log messages.
2021-10-27 00:25:27 +02:00
Megamouse d5ca9dacb6 Qt: add sound-effect to screenshots 2021-10-26 09:18:37 +02:00
Megamouse 5e436984a2 cellCamera: fix qt handler crash if object is destroyed from another thread 2021-10-24 15:01:43 +02:00
Megamouse be972f04ac cellGem: hook up camera 2021-10-24 15:01:43 +02:00
illusion0001 00c87a8fc9 Screenshot: add Title ID to filename 2021-10-24 13:12:01 +02:00
Megamouse e51656e070 Qt: Add camera settings dialog 2021-10-24 11:32:35 +02:00
Megamouse 9dc6d920ce cellCamera: don't flip qt image vertically by default 2021-10-24 11:32:35 +02:00
Megamouse 354db953e6 Qt: add QtMultimedia to ci 2021-10-24 11:32:35 +02:00
Megamouse e67cf68321 cellCamera: fix compilation on linux 2021-10-24 11:32:35 +02:00
Megamouse 60d35e17ac cellCamera: support all formats and set RGB32 if possible 2021-10-24 11:32:35 +02:00
Megamouse d161d8c545 cellCamera: add some more logging and fix some warnings 2021-10-24 11:32:35 +02:00
Megamouse f66b29f043 cellCamera: Implement dynamic camera selection 2021-10-24 11:32:35 +02:00
Megamouse 77f6db2543 cellCamera: fix some typos (fixes camera attachment in Kung Fu Live) 2021-10-24 11:32:35 +02:00
Megamouse 5eabe9aa33 cellCamera: silence some log errors 2021-10-24 11:32:35 +02:00
Megamouse 76c1681173 cellCamera: fix regular YUV422 conversion of qt camera handler 2021-10-24 11:32:35 +02:00
Megamouse 67ba381dfe cellCamera: fix qt camera handler format 2021-10-24 11:32:35 +02:00
Megamouse 9a6285942b cellCamera: fix buffer allocation 2021-10-24 11:32:35 +02:00
Megamouse d93f16fe87 cellCamera: log qt camera handler status strings 2021-10-24 11:32:35 +02:00
Megamouse 214c1ccc7e cellCamera: move camera handler interaction to seperate functions 2021-10-24 11:32:35 +02:00
Megamouse d1ac92fd99 cellCamera: improve image conversion speed 2021-10-24 11:32:35 +02:00
Megamouse 263b7854c1 cellCamera: sprinkle some mutexes and handle disconnected cameras during open phase 2021-10-24 11:32:35 +02:00
Megamouse 843aed228e cellCamera: fix camera interaction 2021-10-24 11:32:35 +02:00
Megamouse bf71b1fcf7 cellCamera: fix buffer size for YUV 2021-10-24 11:32:35 +02:00
Megamouse 81c216f330 cellCamera: add error handling to qt camera 2021-10-24 11:32:35 +02:00
Megamouse e4b242955c cellCamera: implement CELL_CAMERA_READ_DIRECT 2021-10-24 11:32:35 +02:00
Megamouse 3fac832567 cellCamera: fix qt camera width and height for non-default resolutions 2021-10-24 11:32:35 +02:00
Megamouse 08011e9b78 cellCamera: Add qt camera handler 2021-10-24 11:32:35 +02:00
Eladash ee7ed1fdc3 Allow booting any ELF inside disc directory, not just ones at 'PS3_GAME/USRDIR/' 2021-10-24 11:03:51 +02:00
Megamouse d63a712cc5 Qt: fix richtext_item_delegate palette
We have to use the styled palette instead of the default palette in order to fix the style of lists as seen the pkg installer.
2021-10-24 10:38:50 +02:00
illusion0001 c549d7154b Screenshot: sort folders by Title IDs 2021-10-24 05:24:18 +02:00
shockdude 590483b81f Add emulated Guitar Hero Live guitar controller in Settings I/O tab 2021-10-24 03:04:07 +02:00
Eladash 9ba9d9424e [Win32] Allow renaming uppercase RAP/EDAT to lowercase with drag-and-drop from /exdata 2021-10-24 02:22:45 +02:00
RipleyTom 962e140cea fix: cleanup basic handler in np term 2021-10-23 08:31:59 +02:00
Eladash 9afa960979 Fix possible fs::create_path recursion overflow 2021-10-22 15:37:47 +03:00
Ani 8b4f3fa775 glslang: Update from 11.5.0 to 11.6.0 2021-10-21 22:35:23 +02:00
Ani 5fb0b2c190 stb_truetype: Update from 1.25 to 1.26 2021-10-21 22:35:23 +02:00
Ani 266ab28145 cURL: Update from 7.77.0 to 7.79.1 2021-10-21 22:35:23 +02:00
Ani 2acac67570 FAudio: Update from 21.07 to 21.10 2021-10-21 22:35:23 +02:00
Ani 361ef33856 XAudio2: Update from 1.2.6 to 1.2.8 2021-10-21 22:35:23 +02:00
Eladash c0c52c33b9 SPU: Implement interrupts handling for remaining events 2021-10-20 15:46:50 +03:00
kd-11 4ed92f4155 vk: Fully allow CB change in emit_geometry
- upload_vertex_data can trigger a flush to CELL which will result in CB flush.
  Ensure CB state is correctly reloaded in such a situation.
2021-10-20 12:05:39 +03:00
Megamouse cb953a418f Linux: add org.mate.ScreenSaver as fallback 2021-10-20 02:09:28 +02:00
Megamouse 9c1f258be3 Qt: remove pad settings size constraint
This might fix some scrolling issues (or add more)
2021-10-19 22:09:02 +02:00
Eladash c6641748ce vm_native.cpp: Fix DeviceIoControl usage (#11029) 2021-10-19 12:53:08 +03:00
Eladash 12efd29121 SPU/PPU DisAsm: Implement more constant formation patterns 2021-10-17 12:15:58 +02:00
Eladash a4bd1755cf PPUDisAsm: Fix MFSPR/MTSPR disassembly 2021-10-16 15:03:52 +02:00
RipleyTom 683c061e60 sys_net: getsockname on unbound p2p socket fix 2021-10-16 11:41:20 +02:00
RipleyTom 0e625e193e sys_net: redirects 0.0.0.0 packets on windows 2021-10-16 11:41:20 +02:00
Eladash 43302f937c PPUDisAsm: Fixup SC printing, support LV1 syscalls 2021-10-16 10:56:31 +02:00
Megamouse 792ac5ed64 DS4: add version check 2021-10-15 01:20:15 +02:00
Megamouse 90548468ee mmjoy: add member prefix to blacklist 2021-10-15 01:20:15 +02:00
Megamouse 1584aba387 sceNpDrmIsAvailable: fix error check
No idea where I saw this check. Probably looked at the wrong function
2021-10-14 22:06:12 +02:00
RipleyTom caea340985 RPCN: Set error state on empty login/password 2021-10-14 19:28:20 +02:00
RipleyTom ab6c74b3c1 Enables resend token button 2021-10-13 21:18:29 +02:00
Malcolm Jestadt f06c8b22e8 PPU/SPU LLVM: Emulate VPERM2B with a 256 bit wide VPERMB
- Save 1 uop by using 256 wide VPERMB instead of VPERM2B. (Compiles down to a vinserti128 and vpermb)
2021-10-13 17:51:54 +03:00
RipleyTom 68fdc49528 Defaults to 127.0.0.1 when failing to discover IP 2021-10-13 08:17:29 +02:00
shockdude cd73d738e5 Add emulated DJ Hero turntable controller in Settings I/O tab 2021-10-12 23:29:19 +02:00
Eladash ab50e5483e GUI Utilities: Implement instruction search, PPU/SPU disasm improvements (#10968)
* GUI Utilities: Implement instruction search in PS3 memory
* String Searcher: Case insensitive search
* PPU DisAsm: Comment constants with ORI
* PPU DisAsm: Add 64-bit constant support
* SPU/PPU DisAsm: Print CELL errors in disasm
* PPU DisAsm: Constant comparison support
2021-10-12 23:12:30 +03:00
RipleyTom 8a72bdb422 rpcn: wait for signal before connect 2021-10-12 20:20:32 +02:00
RipleyTom df39bd39b0 Fix rpcn create/login not returning proper error 2021-10-12 17:30:46 +02:00
kd-11 d58df667b9 rsx: Fix some texture decode instructions
- Fix TEX1D_PROJ definition
- Make TEX3D_PROJ cubemap-compatible
2021-10-12 13:47:08 +03:00
Jan Beich 7a24642c56 vm_native: simplify vm.overcommit sysctl
dragonfly$ sysctl vm.overcommit
sysctl: unknown oid 'vm.overcommit'

freebsd$ rg VM_OVERCOMMIT /usr/include
/usr/include/vm/vm_param.h
89:#define VM_OVERCOMMIT                12      /* vm.overcommit */
2021-10-12 02:11:58 +03:00
Jan Beich 1a90adfb5e vm_native: unbreak runtime on FreeBSD after 63104af8e9
shm_open() returns a file descriptor on success, not zero. As SHM_ANON
only exists on FreeBSD which also has memfd_create use the same code
as on Linux.

$ rpcs3
[...]
Verification failed (in file rpcs3/util/vm_native.cpp:478[:4], in function shm) (errno=2)
Segmentation fault
2021-10-12 02:11:58 +03:00
Megamouse 3832d4fa1c sceNpFriendlist: some improvements 2021-10-12 00:40:27 +02:00
Megamouse 8f328b2624 sceNpCustomMenu: improvements 2021-10-12 00:40:27 +02:00
Megamouse 80ef348600 sceNp: more error checks 2021-10-12 00:40:27 +02:00
Megamouse e2a9c0a8f3 sceNpInit: fix error check order 2021-10-12 00:40:27 +02:00
Megamouse 780a234467 sceNpDrmIsAvailable: add param checks 2021-10-12 00:40:27 +02:00
RipleyTom ea9dc9317d rpcn v0.4.0 2021-10-11 22:20:20 +02:00
Eladash 15af1bca69 PPU DisAsm: Fix CMPLI disassembly 2021-10-11 19:34:01 +03:00
kd-11 479150b214 rsx: Fix decoding of linear cubemaps
- 128-byte boundary is not observed in linear tiling. Verified in hw.
2021-10-10 16:15:28 +03:00
kd-11 e1d1d16227 gl: Alias register binding points a bit
- While aliasing is easy to break, it allows outdated hw to run
2021-10-10 16:15:28 +03:00
Megamouse 877dd1f98d DS4: add ZEROPLUS Brook Mars controller 2021-10-10 10:34:03 +02:00
Megamouse 7df7ac57cc input: log hid_get_feature_report return values 2021-10-10 10:34:03 +02:00
Megamouse e5793acd78 Qt: write version changes to update.log 2021-10-10 10:08:31 +02:00
Eladash 24e76f2960 fs: Add fs::write_new, promotes safe file creation (no overwrite) 2021-10-10 09:34:50 +02:00
Eladash 75e66ab5e0 Make pad and patches config savinng atomic 2021-10-10 09:34:50 +02:00
kd-11 b3725baf5a rsx: Rewrite shader decompiler texture dispatch 2021-10-09 15:10:36 +03:00
kd-11 f1d9a014c0 vk: Silence compiler warning 2021-10-09 15:10:36 +03:00
RipleyTom 8e3112734e Add edat install support 2021-10-07 14:27:34 +02:00
Megamouse eb850681ee cellKb: implement key repeat
- Implements key repeat functionality
- Fixes an issue where games registered keys repeatedly even though we weren't actually setting multiple key presses.
2021-10-07 09:49:53 +02:00
Megamouse 70308d05f3 Update yaml-cpp 2021-10-05 20:16:11 +02:00
Megamouse c7e5a62909 Add missing cpu_flag formatting 2021-10-05 20:16:11 +02:00
Megamouse f72d148d37 Fix config mode reset 2021-10-05 20:16:11 +02:00
Megamouse d8435163bb input: increase mouse acceleration maximum 2021-10-04 21:21:32 +02:00
Megamouse af11546b1e Overlays: fix small performance overlay font sizes 2021-10-04 19:57:57 +02:00
Eladash 63f16d7a46 GUI Utilities: Implement PS3 SDAT/EDAT decryption 2021-10-02 11:29:01 +02:00
Megamouse c765de81d4 Fix clang-format for new ugly lambda indentations 2021-10-02 09:15:54 +02:00
Silent 02e1c49818 Set up VS environment before calling cmake in glslang and spirv (#10952)
This makes VS use its own cmake instead of the one in PATH,
if available.
2021-10-02 00:07:58 +01:00
Eladash 71b15c1fa6 Suggest tweaking the setting "Stub PPU Traps" after PPU trap (#10921) 2021-09-30 21:33:55 +03:00
Eladash 65e9388f00 Update unpkg.cpp 2021-09-30 21:33:28 +03:00
Eladash 056d43eca7 PKG: Simplify files management 2021-09-30 20:48:47 +03:00
Nekotekina 55ec4808f1 SPU LLVM Fixup for VDBPSADBW
Fixes #10947
2021-09-30 17:53:47 +03:00
Malcolm Jestadt 2cfa5406e7 LLVM DSL: reimplement vdbpsadbw 2021-09-30 13:22:35 +03:00
Malcolm Jestadt 86716dc37b SPU LLVM: Optimize branches following byteswaps
- The first element can be extracted via vmovd rather than vpextrd, which saves 1 uop.
2021-09-30 13:22:35 +03:00
Malcolm Jestadt f9ab077908 SPU LLVM: Use VDBPSADBW in SUMB
- This instruction can be used to sum bytes horrizontally if the second input vector is all zeroes.
2021-09-30 13:22:35 +03:00
Megamouse 8b3a3e4ac8 Qt/Linux: Properly hide taskbar progress when done 2021-09-29 22:38:50 +02:00
Megamouse 45f5819ef3 Qt/Linux: Implement SignalFailure with dbus 2021-09-29 22:38:50 +02:00
Megamouse 3f55067414 Qt: Add sound if download fails 2021-09-29 22:38:50 +02:00
Megamouse 797b69fe8f Qt: fix downloader taskbar progress 2021-09-29 22:38:50 +02:00
Megamouse 019fa390f2 settings_dialog: Add Restore Defaults button
Only works for config.yml for now (not gui settings)
2021-09-29 19:52:00 +02:00
Megamouse 42de2a3e0b settings: Fixup for ranged combobox fallback
# Conflicts:
#	3rdparty/SPIRV/SPIRV-Headers
#	3rdparty/SPIRV/SPIRV-Tools
#	3rdparty/yaml-cpp/yaml-cpp
#	3rdparty/zlib/zlib
2021-09-29 19:52:00 +02:00
kd-11 f90bf2dd40 vk: Use a dynamic number of descriptor allocations 2021-09-29 01:20:32 +03:00
kd-11 dc8fc9fc79 vk: Clean up around vkQueueSubmit handling
- Explicitly declare one version for CB flush and the other for Async flush
- Always flush descriptors on CB flush in case of page fault handling.
  Other threads other than offloader can also enter the method and require normal flow.
- Fix overlapping interrupt IDs.
- Minor formatting fixes
2021-09-28 23:18:26 +03:00
kd-11 3d49976b3c vk: Add deregister event for sets
- Unused in practice, but this is more for peace of mind.
2021-09-28 17:43:15 +03:00
kd-11 eed38e1bbc vk: Make the new descriptor system spec compliant 2021-09-28 17:43:15 +03:00
kd-11 9595297a3a Whitespace fix 2021-09-28 17:43:15 +03:00
kd-11 7c5b5d25e3 vk: Implement descriptor allocation batching 2021-09-28 17:43:15 +03:00
kd-11 2e22a0d9bb rsx: Optimize thread self-tests 2021-09-28 17:43:15 +03:00
kd-11 ba2a8ebf2e vk: Enable deferred descriptor updates via descriptor-indexing 2021-09-28 17:43:15 +03:00
kd-11 381c7544fa Optimize basic descriptor batching 2021-09-28 17:43:15 +03:00
kd-11 4752c4014b vk: Implement basic descriptor updates batching 2021-09-28 17:43:15 +03:00
kd-11 24642a4c18 vk: Refactor descriptors a bit 2021-09-28 17:43:15 +03:00
kd-11 62979c7bd9 vk: Enable descriptor indexing extension if supported 2021-09-28 17:43:15 +03:00
kd-11 7b9fb7ad9c rsx: refactor rsx_utils a bit
- Move obviously standalone things to their own utility files
2021-09-28 17:43:15 +03:00
kd-11 7f830d555d vk: Simplify texture cache OOM tracking a bit 2021-09-28 17:43:15 +03:00
kd-11 9aafd8c09f rsx: Avoid get_system_time for simple draw ordering 2021-09-28 17:43:15 +03:00
kd-11 6781eb7c76 rsx: Avoid calling get_system_time() every draw call 2021-09-28 17:43:15 +03:00
kd-11 3e09b97f58 rsx: Minor optimization; avoid preparing unused vertex streams
- Also discards unused program state variables
2021-09-28 17:43:15 +03:00
Eladash e7b9513d4a Bugfix after recent PPU patches pr 2021-09-28 12:12:12 +03:00
Eladash 34c36c73ee Debugger/Disasm: Name PPU Syscalls 2021-09-28 12:12:12 +03:00
Megamouse 2821eb7397 Remove some lwmutex log spam
I don't know if there's a reason for the warning message.
So I'll just change it and see how many developers throw insults at me :)
2021-09-26 21:28:17 +03:00
Eladash 65c9cd99cd Patches/PPU: Implement HLE/LLE/With-TOC function call patches
Example patches:
  [ jumpf, 0x12340, "cellGcmSys:cellGcmSetFlip"] // Places a call to cellGcmSetFlip at 0x12340
  [ jumpf, 0x12340, "cellGcmSys:0xDC09357E"] // Same, using FNID
  [ jumpf, 0x12340, 0x2345678 ] # Function OPD based call eading OPD at 0x2345678
2021-09-26 20:49:57 +03:00
Megamouse b217e8384c Qt: Fix SnapSlider in settings
The SnapSlider function is used to snap the current slider to certain values if modified with the mouse.
But it only triggered if the user was dragging the handle of the slider and not if the bar itself was clicked.

This is fixed by using an eventFilter that listens for mousepress and mouserelease events instead of the sliderPressed and sliderReleased signals.
Maybe this is a Qt bug, but I don't want to bother with reporting this right now.
2021-09-26 12:08:40 +02:00
Ani df96be199b config: Remove Lower SPU Priority setting 2021-09-26 00:29:21 +01:00
Ani ecc55048c5 ui: Remove SPU Cache from settings dialog 2021-09-26 00:29:21 +01:00
Ani e58911cc95 config: Disable SPU loop detection by default 2021-09-26 00:29:21 +01:00
Megamouse 269c4604aa VFS: move VFS settings to seperate file 2021-09-25 19:21:59 +03:00
Megamouse 8f1dc7a2d4 Fix VFS regression
Implements cfg_mode
2021-09-25 19:21:59 +03:00
Megamouse da91a841b7 VFS: add logging to mount 2021-09-25 19:21:59 +03:00
Megamouse 4323ca4a01 Remove unused lambda 2021-09-25 19:21:59 +03:00
Eladash fff0c96bf3 Debugger: Implement editing instruction for entire SPU group 2021-09-24 21:05:11 +03:00
Ani 7167fbf545 ci: Switch QT mirror (#10918) 2021-09-23 20:37:36 +01:00
kd-11 e4aff539b0 vk: Fix scanning for upload heap types.
- HOST_CACHED support must be prioritized, but is not a mandate.
- Scan for that flag explicitly and fall back to uncached if it is not supported.
- Uncached memory is too slow for our requirements to contend with cached memory.
2021-09-23 01:45:37 +03:00
Eladash d0e31f4ec3 sys_fs: Add write log messages for SDATA/EDATA 2021-09-22 14:57:16 +03:00
Eladash 3e06446297 sys_fs: Fix sys_fs_read/write when nbytes is 0 2021-09-22 14:57:16 +03:00
Eladash 4237039d53 sys_fs: Fix SDATA/EDATA file-stat 2021-09-22 14:57:16 +03:00
Eladash 5ae7dbf77c Fix cellFsSdataOpenByFd flags 2021-09-22 14:57:16 +03:00
Megamouse f1037f75d9 perf_overlay: fix initial graph positions with detail level none 2021-09-22 08:06:58 +02:00
Megamouse a7cb513a89 cellOsk: partially implement continuous mode 2021-09-21 23:22:26 +02:00
Megamouse e3ec71c683 cellOsk: partially implement cellOskDialogExtUpdateInputText 2021-09-21 23:22:26 +02:00
Megamouse f7870d4968 cellOsk: do not notify INPUT_ENTERED on each input
Apparently the event is only sent when a user confirms the dialog during any continuous mode with seperate windows.
2021-09-21 23:22:26 +02:00
Megamouse 80ef0ce9b5 cellOsk: reset options after unload 2021-09-21 23:22:26 +02:00
Megamouse 696a3c4a42 cellOsk: stub some settings 2021-09-21 23:22:26 +02:00
Megamouse 81a01134bb cellOsk: fix dialog abort w/o user interaction 2021-09-21 23:22:26 +02:00
Megamouse 70c291753c cellOsk: dont send INPUT_ENTERED unless continuous 2021-09-21 23:22:26 +02:00
kd-11 3c7ada8e83 rsx: Fix 3D texture decode
- 3D mipmaps are shrunk in all 3 axes, they are not 2D array textures.
- Fixes mip1-mipN for all situations
2021-09-21 19:53:46 +03:00
kd-11 46b3027981 rsx: Invariably clear the texture state if referenced. 2021-09-21 19:53:46 +03:00
kd-11 334999f639 vk: Enable sampler mirror-clamped-to-edge as an extension 2021-09-21 19:53:46 +03:00
kd-11 dabfce5c82 rsx: Rework how depth/stencil initialization+clear works 2021-09-21 19:53:46 +03:00
kd-11 0a8d9a12ab vk: Rewrite memory initialization 2021-09-21 19:53:46 +03:00
kd-11 19b2da2590 Enable stencil export extension when required 2021-09-21 19:53:46 +03:00
Eladash 17a074e1ac Fix memory leak in cellOskDialog (#10895) 2021-09-21 11:29:32 +03:00
Eladash 38d5f688b3 cellAudio fix 2021-09-20 07:59:34 +02:00
Megamouse e307a1b4bd input: fix keyboard_pad_handler pressure sensitivity 2021-09-19 23:46:51 +02:00
Megamouse a50e22a11f Overlays: Fix position of centered perf-overlay 2021-09-19 20:30:02 +02:00
AniLeo c995505023 edat: Check for the correct directory 2021-09-18 18:20:55 +01:00
AniLeo 1f8f428a59 unpkg: Don't append title ID to license packages 2021-09-18 18:20:55 +01:00
Eladash e10c6cbaf7 SPU: cpu_work() fixup, fix recursion in AV handler 2021-09-18 19:43:55 +03:00
Eladash 5870da0b55 SPU MFC: Add shuffling in steps setting 2021-09-18 19:43:55 +03:00
Eladash 5f729d4dbf GUI Settings: Add MFC Delay Command checkbox
Limited control over "MFC Commands Shuffling Limit" setting.
2021-09-18 19:43:55 +03:00
Megamouse ebde86b967 CLI: add "high-res-timer" argument (defaults to 1) 2021-09-18 00:58:05 +02:00
Megamouse 14a425e487 rsx: wait when emulation is paused
This decreases my cpu usage by to <1% during Emu.Pause()
2021-09-17 23:13:24 +02:00
Megamouse 1f1d1da87b silence some warnings 2021-09-17 23:13:24 +02:00
Megamouse 129ade2f73 System: only init fxo objects if a game is started
The progress_dialog and patch system aren't needed unless a game is started.
Before, they were both initialized everytime we added a single game to the game list or even when simply started RPCS3.
This both means that a thread was needlessly idling all the time and even worse: The patch.yml was read countless times when we didn't need it.
2021-09-17 22:45:14 +02:00
Eladash c37c6fb6de System.cpp: Remove outdated and bugged code-block 2021-09-17 22:15:48 +02:00
Eladash 5736fa3351 Fix Emulator::IsPathInsideDir 2021-09-17 22:15:48 +02:00
Eladash 3e84a2acc0 PKG: Implement SDATA decryption 2021-09-17 22:15:48 +02:00
Eladash 77c252a4c0 PKG: Debugging messages 2021-09-17 22:15:48 +02:00
Eladash e4f92ddf77 GUI: PARAM.SFO formatting 2021-09-17 22:15:48 +02:00
Eladash 1f3b1e1c99 Implement Emulator::GetFakeCat()
I still have nightmares about that cat.
2021-09-17 22:15:48 +02:00
Eladash 73bdf7481e Emu: Fix disc game move from hdd0/game 2021-09-17 22:15:48 +02:00
kd-11 c2ab3c664c rsx: Fix stupid overflow 2021-09-17 20:12:08 +03:00
Eladash ddec5d6908 CPUThread: Prevent recursive check_state calls 2021-09-17 14:02:22 +03:00
Eladash 975aae1d13 SPU MFC: Implement MFC commands execution shuffling 2021-09-17 11:38:10 +03:00
Nekotekina 9e62ca562b SPU LLVM: implement SQRT and DIV pattern detection (xf) 2021-09-17 10:23:43 +03:00
Nekotekina d28b0ba2fa SPU LLVM: implement spu_re, spu_rsqrte
Improve matching with peek_through_bitcasts() helper.
Implement erase_stores() helper.
2021-09-17 10:23:43 +03:00
Nekotekina aba332d4c4 SPU LLVM: make intrinsics for most xfloat instructions 2021-09-17 10:23:43 +03:00
Nekotekina 543fb7a9cb LLVM DSL / SPU LLVM: implement infinite precision shifts
Remove old make_*** helpers in favor of matcheable expressions.
2021-09-17 10:23:43 +03:00
Nekotekina 67b3fc70f8 LLVM DSL: implement absd and match helpers
Matcheable expression absd(a, b) (absolute difference).
2021-09-17 10:23:43 +03:00
Nekotekina 2268aa9093 LLVM DSL: reimplement fre, frsqe, fmax, fmin 2021-09-17 10:23:43 +03:00
Nekotekina 4b8ee85995 LLVM DSL: reimplement pshufb, add 'calli'
Implement postponed custom intrinsic replacement.
Make bitcast operator static like other ones.
2021-09-17 10:23:43 +03:00
Nekotekina 86ead1b93b SPU LLVM: implement FI instruction
Use approximate reciprocal in FRSQEST.
2021-09-17 10:23:43 +03:00
Nekotekina 7ec7f261b1 LLVM DSL: implement fpcast 2021-09-17 10:23:43 +03:00
Nekotekina 1685769bd9 LLVM DSL: reimplement fmuladd, force hw fma if present 2021-09-17 10:23:43 +03:00
Nekotekina 2acb6ed60d SPU LLVM: optimize SHUFB for permutation-only shuffles
Drop constant generation when unused.
2021-09-17 10:23:43 +03:00
Nekotekina 144244e902 SPU LLVM: implement missing constant mask handling in SHUFB 2021-09-17 10:23:43 +03:00
Nekotekina 7cf9d1380b LLVM DSL: add line number in get_const_vector automatically 2021-09-17 10:23:43 +03:00
Nekotekina f188019244 LLVM DSL: reimpelement fsqrt, fabs 2021-09-17 10:23:43 +03:00
Nekotekina 38dfc88e8d LLVM DSL: reimplement avg 2021-09-17 10:23:43 +03:00
Nekotekina 95c36221fa LLVM DSL: reimplement ctlz, ctpop 2021-09-17 10:23:43 +03:00
NicknineTheEagle b30ac48513 Refresh game list after installing RAP files via menus 2021-09-16 23:54:53 +02:00
NicknineTheEagle 4fcc1c0f31 Fully implement sceNpDrmVerifyUpgradeLicense 2021-09-16 23:54:53 +02:00
Megamouse 1a2fad4eac cellSearch: more stubs and implementations (#10578) 2021-09-16 22:05:29 +01:00
Megamouse da4f4dafbd overlays: Show feedback while stopping ppu compilation 2021-09-16 22:18:43 +02:00
Eladash e5876b3a1b Fix BDVD path setting 2021-09-16 21:28:52 +03:00
Eladash 9c2d94ca1a sys_prx: Improve sys_prx_get_module_info error checking 2021-09-16 19:09:18 +03:00
Eladash ae4f837f57 sys_prx: Implement sys_prx_get_module_id_by_address 2021-09-16 19:09:18 +03:00
polar c6c4aa4634 cmake corrections (#10782) 2021-09-16 13:59:08 +01:00
xddxd bcda172ae7 Switch from r16ui to r16 2021-09-16 14:09:21 +03:00
xddxd d511e76a63 Enable the precise occlusion query feature 2021-09-16 14:09:21 +03:00
Megamouse 56e982375e Qt: add changelog to updater (#10844) 2021-09-16 04:34:06 +01:00
Megamouse e3eaf5f29e Qt: Reset stick positions on pad handler change
Fixes weird stick positions when pads are disconnected or not sticks are not supported.
2021-09-15 21:43:29 +02:00
Megamouse e56609d836 Qt: Relocate pad handler parameter changes 2021-09-15 21:43:29 +02:00
Megamouse 460fb66949 Qt: reset config when null pad handler is selected 2021-09-15 21:43:29 +02:00
Megamouse b955469721 Qt: fix player selection checks 2021-09-15 21:43:29 +02:00
Megamouse 6378e16fdf Emu.Load: add more logging 2021-09-15 21:43:29 +02:00
Eladash 8b48fd95fe Fix sys_prx_start/stop_module for older firmware
The old structure is only 32 bytes long, not containing prologue/epilogue functions.
2021-09-15 18:08:58 +03:00
Eladash 5600430a05 Fix user_interface::alloc_thread_bit() usage 2021-09-13 22:36:53 +03:00
Eladash 50ad7ba1f6 vm: Fix vm::unmap
* Make vm::unmap atomic, squash the memory unmapping process inside this function while still using the same VM mutex ownership.
* Make vm::unmap not fail due to random vm::get calls, shared_ptr reference count is no longer a condition.
* Fix sys_mmapper_free_address spuriously failing with EBUSY due to random vm::get calls.
* Fix sys_vm_unmap race condition due to non-atomic vm::unmap.
* Add an optional verification block ptr arg to vm::unmap, used by patches.
2021-09-13 20:53:34 +03:00
Eladash fe6cb4774e Fixup default config-path (#10863)
Fixes #10862
2021-09-12 21:28:48 +03:00
Eladash 91fefd4671 Fix vm::try_alloc regression 2021-09-12 19:05:26 +03:00
Eladash 677d5f09ce Add debugging info for when sys_tty_write is executed 2021-09-12 19:05:26 +03:00
Eladash c7f5475b56 Improve sys_fs_close logging 2021-09-12 19:05:26 +03:00
陈俊嘉 42e3add6a1 BUILD: a couple of cmake msvc compile fixes (#10799)
* cmake: a couple of msvc compile fixes

Throw error when using multi-config generators.
Add USE_MSVC_STATIC_CRT option for statically linking with CRT.
Set curl's CURL_STATIC_CRT aligned with USE_MSVC_STATIC_CRT.
Fix cmake configs of 7z and OpenAL in msvc build.
Remove unecessary hard coded compiling and linking flags.
Use debug option in windeployqt when configuring for a debug build.
Search for windeployqt tool in configure time.
Add vcpkg's Qt support.

* Fix some typos
2021-09-11 23:55:53 +02:00
Megamouse 8dc98bbc1b Add support for sc vtrm crypto
For VSH for @Clienthax
2021-09-11 21:13:46 +02:00
Eladash f98595bee5 Patches/PPU: Add jump_link patch type 2021-09-10 11:46:39 +03:00
Eladash fafefb2cf5 Fixup No.3 after #10779 2021-09-10 11:46:39 +03:00
Eladash 1cbcf7e1ad Reimplement config selection for game startup
* Implement manual selection of config file.
* Implement default config option.
* Fix bug which led to 'force global config' to not work in some games. (any game using process relaunch such most game collections, RDR, MGS4 etc)
* Relax CLI config purpose - instead the emulator forever ignoring any other config except for the one provided in arg, use it only for the CLI-booted game.
2021-09-09 21:56:50 +02:00
Eladash df080fbc53 Ensure frozen emulation for special executables inspection modes 2021-09-09 19:30:54 +02:00
Eladash bd66dfedc9 Do not allow to unpause after fatal error occured in emulation
* Plus fix #10590
2021-09-09 19:30:54 +02:00
Megamouse 69faf14a79 System: remove deprecated custom config location 2021-09-08 21:56:50 +02:00
Megamouse 1a443cf6b5 Qt: remove deprecated playtime code 2021-09-08 21:56:50 +02:00
Megamouse 2fb68bfc03 Qt: release scaled pixmaps from memory if possible 2021-09-08 20:40:34 +02:00
Megamouse 4d70c483b2 Qt: release unused data when switching list modes 2021-09-08 20:40:34 +02:00
Megamouse 29256df5b1 input: make pad_thread a named_thread 2021-09-08 20:15:28 +02:00
Megamouse a74156fe5b input: reimplement lost optimization
We don't need to check all the buttons constantly
2021-09-08 20:15:28 +02:00
Megamouse 57473ff98d Qt: Add missing Boot confirmation 2021-09-08 20:15:28 +02:00
Talkashie 00618995d9 fix
Makes things more consistent.

There were cases where both "Alt-Enter" and "Alt+Enter" were being used, so I made them all use the same style.

Added more commas and periods to give a more natural flow of sentences.
2021-09-07 13:26:08 +02:00
Eladash 6a340afb8a Log emulation pause/unpause events 2021-09-07 08:51:59 +02:00
Eladash dfbc49513d Debugger/Disasm: Name SPU Syscalls 2021-09-07 01:13:57 +02:00
Megamouse 7bb830a228 Enable PPU java mode handling by default (#10825)
I don't remember if we had decided to do this, so I'll just make a PR and Ani or Neko can merge if needed.
2021-09-06 20:26:23 +01:00
kd-11 b3f002f68f Add enums for zcull precision control 2021-09-06 20:04:03 +03:00
kd-11 ea949a5f28 Add ZCULL control options to the GUI 2021-09-06 20:04:03 +03:00
kd-11 53457262d4 rsx: Implement ZPASS results scaling for precise stats 2021-09-06 20:04:03 +03:00
kd-11 472efc08eb rsx: Implement precise ZCULL stats 2021-09-06 20:04:03 +03:00
Megamouse 0525070898 remove deprecated avcodec_register_all
We already use newer avcodec code anyway.
2021-09-06 10:00:34 +02:00
Megamouse cbc1c472d5 VS: add missing generated file 2021-09-05 10:16:25 +02:00
Megamouse aca9884e76 input: fix custom pad config loading 2021-09-05 10:16:25 +02:00
Megamouse 17fb601c02 input: fix custom pad config saving 2021-09-05 10:16:25 +02:00
Megamouse 76ac8ffbd6 input: fix custom pad config removal 2021-09-05 10:16:25 +02:00
Eladash fe0c164de2 sys_net: Anti-log spam enhancement 2021-09-04 11:58:59 +02:00
Eladash ea2c5294df cellCamera: Anti-Log spam enhancement 2021-09-04 11:58:59 +02:00
Megamouse 3c0681ad6d Qt: patch creator 2021-09-04 10:39:32 +02:00
Megamouse 0debcfed0a Silence some warnings 2021-09-02 19:39:42 +02:00
Nekotekina 06f733a7f2 Fixup No.2 for #10779 2021-09-01 16:56:38 +03:00
Nekotekina ad6a72e369 Fixup for #10779 2021-09-01 14:38:20 +03:00
Eladash b40ed5bdb7 Patches/PPU: Extend and improve patching capabilities (code allocations, jumps to any address) (#10779)
* Patches/PPU: Implement dynamic code allocation + Any-Address jump patches

Also fix deallocation path of fixed allocation patches.
2021-09-01 13:38:17 +03:00
polar ee6e4c493d BUILD: Refactor wolfssl submodule (#10780) 2021-09-01 09:58:05 +02:00
330 changed files with 19440 additions and 6964 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ pkg info # debug
pkg install llvm12
# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets)
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent glew openal-soft ffmpeg
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent qt5-multimedia qt5-svg glew openal-soft ffmpeg
# Optional dependencies (libevdev is pulled by qt5-gui)
pkg install pkgconf alsa-lib pulseaudio sdl2 evdev-proto vulkan-headers vulkan-loader
+6 -2
View File
@@ -7,8 +7,8 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
# Resource/dependency URLs
# Qt mirrors can be volatile and slow, so we list 2
QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
#QT_HOST="http://qt.mirror.constant.com/"
#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
QT_HOST="http://qt.mirror.constant.com/"
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]')
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
@@ -17,6 +17,8 @@ QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}"
QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}"
QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}"
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}qttools${QT_SUFFIX}"
QT_MM_URL="${QT_HOST}${QT_PREFIX}qtmultimedia${QT_SUFFIX}"
QT_SVG_URL="${QT_HOST}${QT_PREFIX}qtsvg${QT_SUFFIX}"
LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win/llvmlibs_mt.7z'
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z'
VULKAN_SDK_URL="https://www.dropbox.com/s/fjlh97hrz0crqxv/VulkanSDK-1.2.182.0-Installer.exe"
@@ -26,6 +28,8 @@ DEP_URLS=" \
$QT_WINE_URL \
$QT_DECL_URL \
$QT_TOOL_URL \
$QT_MM_URL \
$QT_SVG_URL \
$LLVMLIBS_URL \
$GLSLANG_URL \
$VULKAN_SDK_URL"
+1 -1
View File
@@ -60,7 +60,7 @@ windows_task:
linux_task:
container:
image: rpcs3/rpcs3-ci-bionic:1.1
image: rpcs3/rpcs3-ci-bionic:1.3
cpu: 4
memory: 16G
env:
+2 -1
View File
@@ -1,5 +1,5 @@
Standard: c++20
UseTab: ForIndentation
UseTab: AlignWithSpaces
TabWidth: 4
IndentWidth: 4
AccessModifierOffset: -4
@@ -16,6 +16,7 @@ AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AllowShortLambdasOnASingleLine: Inline
Cpp11BracedListStyle: true
IndentCaseLabels: false
SortIncludes: false
+1 -1
View File
@@ -61,7 +61,7 @@
url = ../../curl/curl.git
ignore = dirty
[submodule "3rdparty/wolfssl"]
path = 3rdparty/wolfssl
path = 3rdparty/wolfssl/wolfssl
url = ../../wolfSSL/wolfssl.git
ignore = dirty
[submodule "3rdparty/flatbuffers"]
+7
View File
@@ -45,7 +45,14 @@ if(WIN32)
src/LzmaLib.c
src/7zCrc.c
src/MtDec.c)
target_include_directories(3rdparty_7z INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:/src>)
target_include_directories(3rdparty_7z INTERFACE 7z)
set_property(TARGET 3rdparty_7z PROPERTY FOLDER "3rdparty/")
else()
add_library(3rdparty_7z INTERFACE)
endif()
+4 -46
View File
@@ -239,6 +239,9 @@ if(USE_FAUDIO)
endif()
endif()
set_property(TARGET ${FAUDIO_TARGET} PROPERTY FOLDER "3rdparty/")
# FFMPEG
add_library(3rdparty_ffmpeg INTERFACE)
@@ -309,52 +312,7 @@ endif()
include(llvm.cmake)
# WOLFSSL
if(USE_SYSTEM_WOLFSSL)
message("-- RPCS3: using shared wolfssl")
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
add_library(wolfssl INTERFACE)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
# TODO(cjj19970505@live.cn)
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI)
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
# TODO(cjj19970505@live.cn)
# This only works in single-config generator
# For a multi-config generator, we need to provide different wolfssl binaries for different config
if (GENERATOR_IS_MULTI_CONFIG)
message( FATAL_ERROR "RPCS3 can only be configured using single-config generator." )
endif()
if(MSVC)
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/wolfssl.lib")
else()
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/libwolfssl.a")
endif()
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
# So that curl can be built on an environment where openssl headers are not provided
set(WolfSSL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/" "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/")
endif()
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
# CURL
add_subdirectory(curl EXCLUDE_FROM_ALL)
+2 -2
View File
@@ -1,7 +1,7 @@
# OpenAL
if (MSVC)
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
find_path(OPENAL_INCLUDE_DIR al.h PATHS include/)
find_library(OPENAL_LIBRARY OpenAL32 PATHS libs/Win64/)
else()
find_package(OpenAL REQUIRED)
endif()
+4 -2
View File
@@ -38,8 +38,10 @@
</ImportGroup>
<PropertyGroup Label="UserMacros">
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) SPIRV-Tools -B build</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DSPIRV_WERROR=OFF -DSPIRV-Headers_SOURCE_DIR=$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)/SPIRV-Headers')) -S SPIRV-Tools -B build</CmakeDebugCLI>
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
Binary file not shown.
+1
View File
@@ -14,4 +14,5 @@ target_link_libraries(asmjit PRIVATE ${ASMJIT_DEPS})
# ASMJIT should have a option for disabling installing and this wouldnt
# be required to avoid installing ASMJIT...
set_property(TARGET asmjit PROPERTY FOLDER "3rdparty/")
add_library(3rdparty::asmjit ALIAS asmjit)
+6
View File
@@ -12,5 +12,11 @@ else()
option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" ON)
option(CURL_ZLIB "Set to ON to enable building curl with zlib support." OFF)
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
if(USE_MSVC_STATIC_CRT)
set(CURL_STATIC_CRT ON CACHE BOOL "Use static crt to build curl" FORCE)
endif()
add_subdirectory(curl EXCLUDE_FROM_ALL)
set_property(TARGET libcurl PROPERTY FOLDER "3rdparty/")
endif()
+1 -1
View File
@@ -43,7 +43,7 @@
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
+4 -2
View File
@@ -38,8 +38,10 @@
</ImportGroup>
<PropertyGroup Label="UserMacros">
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
<CmakeReleaseCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
<CmakeDebugCLI>cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Release" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 -DCMAKE_BUILD_TYPE="Debug" -DLLVM_USE_CRT_DEBUG=MTd -DLLVM_USE_CRT_RELEASE=MT -S glslang -B build</CmakeDebugCLI>
<PropsAbsPath>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\buildfiles\msvc\common_default.props'))</PropsAbsPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+4 -4
View File
@@ -2,17 +2,17 @@ add_library(3rdparty_qt5 INTERFACE)
set(QT_MIN_VER 5.15.2)
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent)
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia MultimediaWidgets Svg)
if(WIN32)
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
else()
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
if(Qt5DBus_FOUND)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
else()
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent)
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia Qt5::MultimediaWidgets Qt5::Svg)
endif()
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
+81 -21
View File
@@ -1,4 +1,4 @@
// stb_truetype.h - v1.25 - public domain
// stb_truetype.h - v1.26 - public domain
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
//
// =======================================================================
@@ -58,6 +58,7 @@
//
// VERSION HISTORY
//
// 1.26 (2021-08-28) fix broken rasterizer
// 1.25 (2021-07-11) many fixes
// 1.24 (2020-02-05) fix warning
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
@@ -271,8 +272,8 @@
//// SAMPLE PROGRAMS
////
//
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
//
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
// See "tests/truetype_demo_win32.c" for a complete version.
#if 0
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h"
@@ -307,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
}
++text;
}
@@ -3061,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
}
}
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
{
STBTT_assert(top_width >= 0);
STBTT_assert(bottom_width >= 0);
return (top_width + bottom_width) / 2.0f * height;
}
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
{
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
}
static float stbtt__sized_triangle_area(float height, float width)
{
return height * width / 2;
}
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
{
float y_bottom = y_top+1;
@@ -3115,10 +3133,10 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height;
// simple case, only spans one pixel
int x = (int) x_top;
height = sy1 - sy0;
height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len);
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
scanline_fill[x] += height; // everything right of this pixel is filled
} else {
int x,x1,x2;
float y_crossing, y_final, step, sign, area;
@@ -3134,40 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
dy = -dy;
t = x0, x0 = xb, xb = t;
}
assert(dy >= 0);
assert(dx >= 0);
STBTT_assert(dy >= 0);
STBTT_assert(dx >= 0);
x1 = (int) x_top;
x2 = (int) x_bottom;
// compute intersection with y axis at x1+1
y_crossing = (x1+1 - x0) * dy + y_top;
y_crossing = y_top + dy * (x1+1 - x0);
// compute intersection with y axis at x2
y_final = y_top + dy * (x2 - x0);
// x1 x_top x2 x_bottom
// y_top +------|-----+------------+------------+--------|---+------------+
// | | | | | |
// | | | | | |
// sy0 | Txxxxx|............|............|............|............|
// y_crossing | *xxxxx.......|............|............|............|
// | | xxxxx..|............|............|............|
// | | /- xx*xxxx........|............|............|
// | | dy < | xxxxxx..|............|............|
// y_final | | \- | xx*xxx.........|............|
// sy1 | | | | xxxxxB...|............|
// | | | | | |
// | | | | | |
// y_bottom +------------+------------+------------+------------+------------+
//
// goal is to measure the area covered by '.' in each pixel
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
// @TODO: maybe test against sy1 rather than y_bottom?
if (y_crossing > y_bottom)
y_crossing = y_bottom;
sign = e->direction;
// area of the rectangle covered from y0..y_crossing
// area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing-sy0);
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
scanline[x1] += area * (x1+1 - x_top)/2;
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
// check if final y_crossing is blown up; no test case for this
y_final = y_crossing + dy * (x2 - (x1+1)); // advance y by number of steps taken below
if (y_final > y_bottom) {
y_final = y_bottom;
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
}
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, which is also how much pixel area changes for each step in x
// in second pixel, area covered by line segment found in first pixel
// is always a rectangle 1 wide * the height of that line segment; this
// is exactly what the variable 'area' stores. it also gets a contribution
// from the line segment within it. the THIRD pixel will get the first
// pixel's rectangle contribution, the second pixel's rectangle contribution,
// and its own contribution. the 'own contribution' is the same in every pixel except
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
// the second pixel's contribution to the third pixel will be the
// rectangle 1 wide times the height change in the second pixel, which is dy.
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
// so the area advances by 'step' every time
for (x = x1+1; x < x2; ++x) {
scanline[x] += area + step/2; // area of parallelogram is step/2
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
area += step;
}
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
STBTT_assert(sy1 > y_final-0.01f);
// area of the triangle (x2,y_crossing), (x_bottom,y1), (x2,y1)
scanline[x2] += area + sign * (x_bottom - x2)/2 * (sy1-y_crossing);
// area covered in the last pixel is the rectangle from all the pixels to the left,
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
// the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2] += sign * (sy1-sy0);
}
} else {
@@ -3175,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute
// force implementation
// note though that this does happen some of the time because
// x_top and x_bottom can be extrapolated at the top & bottom of
// the shape and actually lie outside the bounding box
int x;
for (x=0; x < len; ++x) {
// cases:
+50
View File
@@ -0,0 +1,50 @@
if(USE_SYSTEM_WOLFSSL)
message("-- RPCS3: using shared wolfssl")
pkg_check_modules(WolfSSL REQUIRED IMPORTED_TARGET wolfssl>=4.7.0)
add_library(wolfssl INTERFACE)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
# TODO(cjj19970505@live.cn)
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
set(WOLFSSL_SHA3 "yes" CACHE INTERNAL "")
set(WOLFSSL_SHAKE256 "yes" CACHE INTERNAL "")
set(WOLFSSL_BASE64_ENCODE "no" CACHE INTERNAL "")
set(WOLFSSL_DES3 "yes" CACHE INTERNAL "")
set(WOLFSSL_POLY1305 "yes" CACHE INTERNAL "")
set(WOLFSSL_CHACHA "yes" CACHE INTERNAL "")
set(WOLFSSL_FILESYSTEM "yes" CACHE INTERNAL "")
set(WOLFSSL_PWDBASED "yes" CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH "no" CACHE INTERNAL "")
set(WOLFSSL_EXAMPLES "no" CACHE INTERNAL "")
set(WOLFSSL_CRYPT_TESTS "no" CACHE INTERNAL "")
set(WOLFSSL_ASYNC_THREADS "no" CACHE INTERNAL "")
set(WOLFSSL_CONFIG_H "no" CACHE INTERNAL "")
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
# TODO(cjj19970505@live.cn)
# This only works in single-config generator
# For a multi-config generator, we need to provide different wolfssl binaries for different config
if (GENERATOR_IS_MULTI_CONFIG)
message( FATAL_ERROR "RPCS3 can only be configured using single-config generator." )
endif()
if(MSVC)
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/${CMAKE_BUILD_TYPE}/wolfssl.lib" CACHE INTERNAL "")
else()
set(WolfSSL_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/wolfssl/libwolfssl.a" CACHE INTERNAL "")
endif()
# "${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/" provides openssl headers
# So that curl can be built on an environment where openssl headers are not provided
set(WolfSSL_INCLUDE_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/"
"${CMAKE_CURRENT_SOURCE_DIR}/wolfssl/wolfssl/"
"${CMAKE_CURRENT_BINARY_DIR}/wolfssl/"
CACHE INTERNAL "")
endif()
View File
@@ -48,7 +48,7 @@
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;ECC_TIMING_RESISTANT;WC_RSA_BLINDING;HAVE_AESGCM;WOLFSSL_SHA512;WOLFSSL_SHA384;NO_DSA;HAVE_ECC;TFM_ECC256;ECC_SHAMIR;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_TLS_EXTENSIONS;HAVE_SNI;HAVE_SUPPORTED_CURVES;HAVE_EXTENDED_MASTER;NO_RC4;HAVE_ENCRYPT_THEN_MAC;NO_PSK;NO_MD4;WC_NO_ASYNC_THREADING;OPENSSL_EXTRA;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<PrecompiledHeader>
</PrecompiledHeader>
@@ -62,7 +62,7 @@
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>./wolfssl;./wolfssl/IDE/WIN;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;WOLFSSL_USER_SETTINGS;CYASSL_USER_SETTINGS;WC_NO_HARDEN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WOLFSSL_LIB;WOLFSSL_DES_ECB;HAVE_FFDHE_2048;TFM_TIMING_RESISTANT;NO_DSA;TFM_ECC256;NO_RC4;NO_HC128;NO_RABBIT;WOLFSSL_SHA224;WOLFSSL_SHA3;WOLFSSL_SHAKE256;HAVE_POLY1305;HAVE_ONE_TIME_AUTH;HAVE_CHACHA;HAVE_HASHDRBG;HAVE_SNI;HAVE_ENCRYPT_THEN_MAC;NO_MD4;WC_NO_ASYNC_THREADING;CYASSL_USER_SETTINGS;WC_NO_HARDEN;HAVE_WRITE_DUP;WC_RSA_BLINDING;NO_MULTIBYTE_PRINT;OPENSSL_EXTRA;WOLFSSL_RIPEMD;NO_PSK;HAVE_EXTENDED_MASTER;WOLFSSL_SNIFFER;HAVE_AESGCM;WOLFSSL_SHA384;WOLFSSL_SHA512;HAVE_SUPPORTED_CURVES;HAVE_TLS_EXTENSIONS;HAVE_ECC;ECC_SHAMIR;ECC_TIMING_RESISTANT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
+1
View File
@@ -52,6 +52,7 @@
</ClCompile>
<ClCompile Include="yaml-cpp\src\convert.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\depthguard.cpp" />
<ClCompile Include="yaml-cpp\src\directives.cpp">
</ClCompile>
<ClCompile Include="yaml-cpp\src\emit.cpp">
+3
View File
@@ -91,5 +91,8 @@
<ClCompile Include="yaml-cpp\src\exceptions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="yaml-cpp\src\depthguard.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+50 -3
View File
@@ -28,17 +28,64 @@ set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/buildfiles/cmake")
include(CheckCXXCompilerFlag)
# TODO(cjj19970505@live.cn)
# Currently cmake config scripts are single-config generator only.
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTI_CONFIG)
message(FATAL_ERROR "Multi-config generators are not supported.")
endif()
if(NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release")
endif()
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
add_definitions(-DNDEBUG)
elseif(NOT MSVC)
if(CMAKE_BUILD_TYPE MATCHES "Debug" AND NOT MSVC)
add_definitions(-D_DEBUG)
endif()
if(MSVC)
option(USE_MSVC_STATIC_CRT "Use static MSVC C runtime" OFF)
# TODO(cjj19970505@live.cno)
# DiscordRPC binary in 3rdparty is compiled /MT
# So theoretically we should enable DiscordRPC in Release and static CRT build
# since we might encounter some rumtime issues when more than one CRT version are presented.
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
# Add other DiscordRPC binaries(compiled with /MTd, /MD, /MDd) or compile it from source may address this issue.
if(NOT(CMAKE_BUILD_TYPE MATCHES "Release" AND USE_MSVC_STATIC_CRT))
set(USE_DISCORD_RPC OFF CACHE BOOL "Discord RPC is only available in Release and static CRT build." FORCE)
endif()
if(USE_MSVC_STATIC_CRT)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
else()
# though doc ( https://cmake.org/cmake/help/latest/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html )
# says if that property is not set then CMake uses the default value MultiThreaded$<$<CONFIG:Debug>:Debug>DLL
# to select a MSVC runtime library.
# But yaml-cpp set /MT(d) if CMAKE_MSVC_RUNTIME_LIBRARY is undefined
# So we have to define it explicitly
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
endif()
# TODO(cjj19970505@live.cn)
# offical QT uses dynamic CRT.
# When building our lib with static CRT and debug build type
# and linking with Qt with dynmaic CRT and debug build,
# error is encountered in runtime (which is expected).
# But building our lib with static CRT and release build type,
# and linking with Qt with dynamic CRT and release build seems to be working,
# which is the same config with VS solution.
# (though technically it might still have some hidden errors).
# So we allow static CRT in both relase and debug build, but prompt warning in debug build.
# For more info:
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
if(USE_MSVC_STATIC_CRT AND CMAKE_BUILD_TYPE MATCHES "Debug")
message(AUTHOR_WARNING "Debug build currently can not work with static CRT.")
endif()
endif()
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
endif()
+9 -1
View File
@@ -387,7 +387,7 @@ bool cfg::node::from_string(const std::string& value, bool dynamic)
return true;
}
cfg_log.fatal("Failed to load node: %s", error);
cfg_log.error("Failed to load node: %s", error);
return false;
}
@@ -429,6 +429,14 @@ void cfg::map_entry::set_map(std::map<std::string, std::string>&& map)
m_map = std::move(map);
}
void cfg::map_entry::erase(const std::string& key)
{
if (m_map.contains(key))
{
m_map.erase(key);
}
}
void cfg::map_entry::from_default()
{
set_map({});
+2
View File
@@ -494,6 +494,8 @@ namespace cfg
void set_value(const std::string& key, const std::string& value);
void set_map(std::map<std::string, std::string>&& map);
void erase(const std::string& key);
void from_default() override;
};
+4 -4
View File
@@ -383,12 +383,12 @@ shared_ptr<fs::device_base> fs::set_virtual_device(const std::string& name, shar
return get_device_manager().set_device(name, std::move(device));
}
std::string fs::get_parent_dir(const std::string& path)
std::string fs::get_parent_dir(const std::string& path, u32 levels)
{
std::string_view result = path;
// Number of path components to remove
usz to_remove = 1;
usz to_remove = levels;
while (to_remove--)
{
@@ -683,9 +683,9 @@ bool fs::create_path(const std::string& path)
#ifdef _WIN32
// Workaround: don't call is_dir with naked drive letter
if (!parent.empty() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
if (parent.size() < path.size() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
#else
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
if (parent.size() < path.size() && !is_dir(parent) && !create_path(parent))
#endif
{
return false;
+3 -2
View File
@@ -45,7 +45,8 @@ namespace fs
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
constexpr auto write_new = write + create + excl;
constexpr auto rewrite = write + create + trunc;
// File seek mode
enum class seek_mode : u32
@@ -156,7 +157,7 @@ namespace fs
shared_ptr<device_base> set_virtual_device(const std::string& name, shared_ptr<device_base> device);
// Try to get parent directory (returns empty string on failure)
std::string get_parent_dir(const std::string& path);
std::string get_parent_dir(const std::string& path, u32 levels = 1);
// Get file information
bool stat(const std::string& path, stat_t& info);
+6 -15
View File
@@ -1231,20 +1231,16 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
bool handled = rsx::g_access_violation_handler(addr, is_writing);
if (cpu && (cpu->state += cpu_flag::temp, cpu->test_stopped()))
{
//
}
if (handled)
{
g_tls_fault_rsx++;
if (cpu && cpu->test_stopped())
{
//
}
return true;
}
if (cpu && cpu->test_stopped())
{
}
}
const u8* const code = reinterpret_cast<u8*>(RIP(context));
@@ -1389,11 +1385,6 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
if (vm::check_addr(addr, is_writing ? vm::page_writable : vm::page_readable))
{
if (cpu && cpu->test_stopped())
{
//
}
return true;
}
@@ -1580,7 +1571,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context) no
}
}
Emu.Pause();
Emu.Pause(true);
if (!g_tls_access_violation_recovered)
{
+1 -1
View File
@@ -156,7 +156,7 @@ private:
static void set_name(std::string);
// Make entry point
static native_entry make_trampoline(u64(*)(thread_base*));
static native_entry make_trampoline(u64(*entry)(thread_base* _base));
friend class thread_ctrl;
+260 -61
View File
@@ -9,6 +9,8 @@
#include "util/endian.hpp"
#include "util/asm.hpp"
#include <charconv>
LOG_CHANNEL(patch_log, "PAT");
template <>
@@ -38,6 +40,10 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
{
case patch_type::invalid: return "invalid";
case patch_type::alloc: return "alloc";
case patch_type::code_alloc: return "calloc";
case patch_type::jump: return "jump";
case patch_type::jump_link: return "jumpl";
case patch_type::jump_func: return "jumpf";
case patch_type::load: return "load";
case patch_type::byte: return "byte";
case patch_type::le16: return "le16";
@@ -61,12 +67,6 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
patch_engine::patch_engine()
{
const std::string patches_path = get_patches_path();
if (!fs::create_path(patches_path))
{
patch_log.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error);
}
}
std::string patch_engine::get_patch_config_path()
@@ -174,6 +174,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
continue;
}
if (main_key.empty())
{
append_log_message(log_messages, "Error: Skipping empty key");
patch_log.error("Skipping empty key (file: %s)", path);
is_valid = false;
continue;
}
// Skip Anchors
if (main_key == patch_key::anchors)
{
@@ -182,8 +190,8 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
// Find or create an entry matching the key/hash in our map
auto& container = patches_map[main_key];
container.hash = main_key;
container.version = version;
container.hash = main_key;
container.version = version;
// Go through each patch
for (auto patches_entry : pair.second)
@@ -221,10 +229,18 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
{
const std::string& title = game_node.first.Scalar();
if (title.empty())
{
append_log_message(log_messages, fmt::format("Error: Empty game title (key: %s, file: %s)", main_key, path));
patch_log.error("Empty game title (key: %s, file: %s)", main_key, path);
is_valid = false;
continue;
}
if (const auto yml_type = game_node.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
patch_log.error("Skipping %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
append_log_message(log_messages, fmt::format("Error: Skipping game %s: expected Map, found %s (patch: %s, key: %s)", title, yml_type, description, main_key));
patch_log.error("Skipping game %s: expected Map, found %s (patch: %s, key: %s, file: %s)", title, yml_type, description, main_key, path);
is_valid = false;
continue;
}
@@ -235,7 +251,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
{
const std::string& serial = serial_node.first.Scalar();
if (serial == patch_key::all)
if (serial.empty())
{
append_log_message(log_messages, fmt::format("Error: Using empty serial (title: %s, patch: %s, key: %s)", title, description, main_key));
patch_log.error("Using empty serial (title: %s, patch: %s, key: %s, file: %s)", title, description, main_key, path);
is_valid = false;
continue;
}
else if (serial == patch_key::all)
{
if (!title_is_all_key)
{
@@ -361,11 +384,11 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
return is_valid;
}
patch_type patch_engine::get_patch_type(YAML::Node node)
patch_type patch_engine::get_patch_type(const std::string& text)
{
u64 type_val = 0;
if (!node || !node.IsScalar() || !cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, node.Scalar()))
if (!cfg::try_to_enum_value(&type_val, &fmt_class_string<patch_type>::format, text))
{
return patch_type::invalid;
}
@@ -373,6 +396,16 @@ patch_type patch_engine::get_patch_type(YAML::Node node)
return static_cast<patch_type>(type_val);
}
patch_type patch_engine::get_patch_type(YAML::Node node)
{
if (!node || !node.IsScalar())
{
return patch_type::invalid;
}
return get_patch_type(node.Scalar());
}
bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifier, const YAML::Node& root, std::stringstream* log_messages)
{
if (!node || !node.IsSequence())
@@ -434,6 +467,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
switch (p_data.type)
{
case patch_type::utf8:
case patch_type::jump_func:
{
break;
}
@@ -516,10 +550,21 @@ void patch_engine::append_title_patches(const std::string& title_id)
}
void ppu_register_range(u32 addr, u32 size);
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link = false, bool with_toc = false, std::string module_name = {});
u32 ppu_generate_id(std::string_view name);
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
{
std::basic_string<u32> applied;
if (ptr && ptr->flags & (1ull << 62))
{
vm::unmap(0, true, &ptr);
}
}
// Returns old 'applied' size
static usz apply_modification(std::basic_string<u32>& applied, const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
{
const usz old_applied_size = applied.size();
for (const auto& p : patch.data_list)
{
@@ -531,26 +576,24 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
// Allocate map if needed, if allocated flags will indicate that bit 62 is set (unique identifier)
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | vm::preallocated | vm::bf0_0x2 | (1ull << 62));
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | vm::preallocated | (1ull << 62));
u64 flags = 0;
u64 flags = vm::alloc_unwritable;
switch (p.offset % patch_engine::mem_protection::mask)
{
case patch_engine::mem_protection::wx: flags |= vm::page_writable + vm::page_readable + vm::page_executable; break;
case patch_engine::mem_protection::ro: flags |= vm::page_readable; break;
case patch_engine::mem_protection::rx: flags |= vm::page_writable + vm::page_executable; break;
case patch_engine::mem_protection::rw: flags |= vm::page_writable + vm::page_readable; break;
case patch_engine::mem_protection::wx: flags = vm::alloc_executable; break;
case patch_engine::mem_protection::ro: break;
case patch_engine::mem_protection::rx: flags |= vm::alloc_executable; break;
case patch_engine::mem_protection::rw: flags &= ~vm::alloc_unwritable; break;
default: ensure(false);
}
if (alloc_map)
{
if (alloc_map->falloc(alloc_at, alloc_size))
if ((p.alloc_addr = alloc_map->falloc(alloc_at, alloc_size, nullptr, flags)))
{
vm::page_protect(alloc_at, alloc_size, 0, flags, flags ^ (vm::page_writable + vm::page_readable + vm::page_executable));
if (flags & vm::page_executable)
if (flags & vm::alloc_executable)
{
ppu_register_range(alloc_at, alloc_size);
}
@@ -560,45 +603,41 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
}
// Revert if allocated map before failure
if (alloc_map->flags & (1ull << 62))
{
vm::unmap(vm::any, alloc_map->addr);
}
unmap_vm_area(alloc_map);
}
}
// Revert in case of failure
for (u32 index : applied)
std::for_each(applied.begin() + old_applied_size, applied.end(), [&](u32 index)
{
const u32 addr = patch.data_list[index].offset & -4096;
const u32 addr = std::exchange(patch.data_list[index].alloc_addr, 0);
// Try different alignments until works
if (!vm::dealloc(addr))
{
if (!vm::dealloc(addr & -0x10000))
{
vm::dealloc(addr & -0x100000);
}
}
vm::dealloc(addr);
if (auto alloc_map = vm::get(vm::any, addr); alloc_map->flags & (1ull << 62))
{
vm::unmap(vm::any, alloc_map->addr);
}
}
auto alloc_map = vm::get(vm::any, addr);
unmap_vm_area(alloc_map);
});
applied.clear();
return applied;
applied.resize(old_applied_size);
return old_applied_size;
}
// Fixup values from before
std::fill(applied.begin(), applied.end(), u32{umax});
std::fill(applied.begin() + old_applied_size, applied.end(), u32{umax});
u32 relocate_instructions_at = 0;
for (const auto& p : patch.data_list)
{
u32 offset = p.offset;
if (offset < min_addr || offset - min_addr >= filesz)
if (relocate_instructions_at && vm::read32(relocate_instructions_at) != 0x6000'0000u)
{
// No longer points a NOP to be filled, meaning we ran out of instructions
relocate_instructions_at = 0;
}
if (!relocate_instructions_at && (offset < min_addr || offset - min_addr >= filesz))
{
// This patch is out of range for this segment
continue;
@@ -608,6 +647,13 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
auto ptr = dst + offset;
if (relocate_instructions_at)
{
offset = relocate_instructions_at;
ptr = vm::get_super_ptr<u8>(relocate_instructions_at);
relocate_instructions_at += 4; // Advance to the next instruction on dynamic memory
}
u32 resval = umax;
switch (p.type)
@@ -623,6 +669,135 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
// Applied before
continue;
}
case patch_type::code_alloc:
{
relocate_instructions_at = 0;
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
// Allow only if points to a PPU executable instruction
if (out_branch < 0x10000 || out_branch >= 0x4000'0000 || !vm::check_addr<4>(out_branch, vm::page_executable))
{
continue;
}
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value + 1) * 4, 0x10000);
// Always executable
u64 flags = vm::alloc_executable | vm::alloc_unwritable;
switch (p.offset % patch_engine::mem_protection::mask)
{
case patch_engine::mem_protection::rw:
case patch_engine::mem_protection::wx:
{
flags &= ~vm::alloc_unwritable;
break;
}
case patch_engine::mem_protection::ro:
case patch_engine::mem_protection::rx:
{
break;
}
default: ensure(false);
}
const auto alloc_map = ensure(vm::get(vm::any, out_branch));
// Range allowed for absolute branches to operate at
// It takes into account that we need to put a branch for return at the end of memory space
const u32 addr = p.alloc_addr = alloc_map->alloc(alloc_size, nullptr, 0x10000, flags);
if (!addr)
{
patch_log.error("Failed to allocate 0x%x bytes for code (entry=0x%x)", alloc_size, addr, out_branch);
continue;
}
patch_log.success("Allocated 0x%x for code at 0x%x (entry=0x%x)", alloc_size, addr, out_branch);
// NOP filled
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
// Register code
ppu_register_range(addr, alloc_size);
// Write branch to code
ppu_form_branch_to_code(out_branch, addr);
resval = out_branch & -4;
// Write address of the allocated memory to the code entry
*vm::get_super_ptr<u32>(resval) = addr;
// Write branch to return to code
ppu_form_branch_to_code(addr + static_cast<u32>(p.value.long_value) * 4, resval + 4);
relocate_instructions_at = addr;
break;
}
case patch_type::jump:
case patch_type::jump_link:
{
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
const u32 dest = static_cast<u32>(p.value.long_value);
// Allow only if points to a PPU executable instruction
if (!ppu_form_branch_to_code(out_branch, dest, p.type == patch_type::jump_link))
{
continue;
}
resval = out_branch & -4;
break;
}
case patch_type::jump_func:
{
const std::string& str = p.original_value;
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
const usz sep_pos = str.find_first_of(':');
// Must contain only a single ':' or none
// If ':' is found: Left string is the module name, right string is the function name
// If ':' is not found: The entire string is a direct address of the function's descriptor in hexadecimal
if (str.size() <= 2 || !sep_pos || sep_pos == str.size() - 1 || sep_pos != str.find_last_of(":"))
{
continue;
}
const std::string_view func_name{std::string_view(str).substr(sep_pos + 1)};
u32 id = 0;
if (func_name.starts_with("0x"sv))
{
// Raw hexadeciaml-formatted FNID (real function name cannot contain a digit at the start, derived from C/CPP which were used in PS3 development)
const auto result = std::from_chars(func_name.data() + 2, func_name.data() + func_name.size() - 2, id, 16);
if (result.ec != std::errc() || str.data() + sep_pos != result.ptr)
{
continue;
}
}
else
{
if (sep_pos == umax)
{
continue;
}
// Generate FNID using function name
id = ppu_generate_id(func_name);
}
// Allow only if points to a PPU executable instruction
// FNID/OPD-address is placed at target
if (!ppu_form_branch_to_code(out_branch, id, true, true, std::string{str.data(), sep_pos != umax ? sep_pos : 0}))
{
continue;
}
resval = out_branch & -4;
break;
}
case patch_type::byte:
{
*ptr = static_cast<u8>(p.value.long_value);
@@ -721,7 +896,7 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
applied.push_back(resval);
}
return applied;
return old_applied_size;
}
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
@@ -812,11 +987,12 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
// Apply modifications sequentially
auto apply_func = [&](const patch_info& patch)
{
auto applied = apply_modification(patch, dst, filesz, min_addr);
const usz old_size = apply_modification(applied_total, patch, dst, filesz, min_addr);
applied_total += applied;
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied.size());
if (applied_total.size() != old_size)
{
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied_total.size() - old_size);
}
};
// Sort specific patches after global patches
@@ -858,18 +1034,36 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
return applied_total;
}
void patch_engine::unload(const std::string& name)
{
if (m_map.find(name) == m_map.cend())
{
return;
}
const auto& container = m_map.at(name);
for (const auto& [description, patch] : container.patch_info_map)
{
for (auto& entry : patch.data_list)
{
// Deallocate used memory
if (u32 addr = std::exchange(entry.alloc_addr, 0))
{
vm::dealloc(addr);
auto alloc_map = vm::get(vm::any, addr);
unmap_vm_area(alloc_map);
}
}
}
}
void patch_engine::save_config(const patch_map& patches_map)
{
const std::string path = get_patch_config_path();
patch_log.notice("Saving patch config file %s", path);
fs::file file(path, fs::rewrite);
if (!file)
{
patch_log.fatal("Failed to open patch config file %s (%s)", path, fs::g_tls_error);
return;
}
YAML::Emitter out;
out << YAML::BeginMap;
@@ -933,7 +1127,12 @@ void patch_engine::save_config(const patch_map& patches_map)
out << YAML::EndMap;
file.write(out.c_str(), out.size());
fs::pending_file file(path);
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
{
patch_log.error("Failed to create patch config file %s (%s)", path, fs::g_tls_error);
}
}
static void append_patches(patch_engine::patch_map& existing_patches, const patch_engine::patch_map& new_patches, usz& count, usz& total, std::stringstream* log_messages)
+11
View File
@@ -27,6 +27,10 @@ enum class patch_type
invalid,
load,
alloc, // Allocate memory at address (zeroized executable memory)
code_alloc,// Allocate memory somewhere, saves branch to memory at specfied address (filled with PPU NOP and branch for returning)
jump, // Install special 32-bit jump instruction (PPU only atm)
jump_link, // jump + set link (PPU only atm)
jump_func, // jump to exported function (PPU only, forever)
byte,
le16,
le32,
@@ -56,6 +60,7 @@ public:
u64 long_value;
f64 double_value;
} value{0};
mutable u32 alloc_addr = 0; // Used to save optional allocation address (if occured)
};
using patch_app_versions = std::unordered_map<std::string /*app_version*/, bool /*enabled*/>;
@@ -118,6 +123,9 @@ public:
// Read and add a patch node to the patch info
static bool read_patch_node(patch_info& info, YAML::Node node, const YAML::Node& root, std::stringstream* log_messages = nullptr);
// Get the patch type from a string
static patch_type get_patch_type(const std::string& text);
// Get the patch type of a patch node
static patch_type get_patch_type(YAML::Node node);
@@ -148,6 +156,9 @@ public:
// Apply patch (returns the number of entries applied)
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = -1, u32 min_addr = 0);
// Deallocate memory used by patches
void unload(const std::string& name);
private:
// Database
patch_map m_map{};
+2 -2
View File
@@ -35,13 +35,13 @@ jobs:
displayName: ccache
- bash: |
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.1
docker pull --quiet rpcs3/rpcs3-ci-bionic:1.3
docker run \
-v $(pwd):/rpcs3 \
--env-file .ci/docker.env \
-v $CCACHE_DIR:/root/.ccache \
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
rpcs3/rpcs3-ci-bionic:1.1 \
rpcs3/rpcs3-ci-bionic:1.3 \
/rpcs3/.ci/build-linux.sh
displayName: Docker setup and build
Binary file not shown.
+1 -2
View File
@@ -2,8 +2,7 @@
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:throwingNew- /constexpr:steps16777216 /D _CRT_SECURE_NO_DEPRECATE=1 /D _CRT_NON_CONFORMING_SWPRINTFS=1 /D _SCL_SECURE_NO_WARNINGS=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0 /MT")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D NOMINMAX /D _ENABLE_EXTENDED_ALIGNED_STORAGE=1 /D _HAS_EXCEPTIONS=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
#TODO: Some of these could be cleaned up
+1 -1
View File
@@ -66,7 +66,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "3rdp
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "7zlib", "3rdparty\7z\7zlib.vcxproj", "{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfssl", "3rdparty\wolfssl\wolfssl.vcxproj", "{73973223-5EE8-41CA-8E88-1D60E89A237B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rdparty\curl\libcurl.vcxproj", "{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}"
ProjectSection(ProjectDependencies) = postProject
+41 -2
View File
@@ -151,9 +151,48 @@ elseif(UNIX)
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git)
elseif(WIN32)
# TODO(cjj19970505@live.cn)
# this is a single-config generator workaround
# multi-config generator will fail.
# TODO(cjj19970505@live.cn)
# offical Qt binaries are built with -MD(d) only as stated in offical wiki
# https://wiki.qt.io/Technical_FAQ#Why_does_a_statically_built_Qt_use_the_dynamic_Visual_Studio_runtime_libraries_.3F_Do_I_need_to_deploy_those_with_my_application_.3F
# If we build our libs with /MT(d), we might encounter some issues.
# https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features?view=msvc-160#what-problems-exist-if-an-application-uses-more-than-one-crt-version
# Qt installed from Qt installer has following hierarchy:
# bin/ for release and debug dlls and windeployqt tools
# lib/cmake/Qt5/ for Qt5_Dir
# Qt installed from vcpkg has following hierarchy:
# bin/ for release dlls
# debug/bin/ for debug dlls
# tools/qt5/bin/ for tools including windeployqt
# tools/qt5/debug/bin/ for tools with debug build including windeployqt
# share/cmake/Qt5/ for Qt5_Dir
# If Qt5 is installed from official Qt installer
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../bin/")
# If Qt5 is installed from vcpkg
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/debug/bin/")
else()
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5/bin/")
endif()
find_program(_WINDEPLOYQT
NAMES windeployqt
PATHS ${_QT5_TOOLS_PATHS}
REQUIRED)
message("Use windeployqt in ${_WINDEPLOYQT}")
add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
COMMAND "${Qt5_DIR}/../../../bin/windeployqt" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-svg --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" --release "$<TARGET_FILE:rpcs3>")
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
# If Qt5 is installed from vcpkg, add binary path to PATH
# otherwise windeployqt tool won't be able to locate necessary dlls
COMMAND set PATH=${Qt5_DIR}/../../../$<$<CONFIG:Debug>:debug/>bin/$<SEMICOLON>%PATH%
COMMAND "${_WINDEPLOYQT}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" $<$<CONFIG:Debug>:--debug> $<$<CONFIG:Release>:--release> "$<TARGET_FILE:rpcs3>")
endif()
# Unix installation
+129
View File
@@ -33,6 +33,37 @@ struct SELF_KEY
SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct);
};
constexpr u32 PASSPHRASE_KEY_LEN = 16;
constexpr u32 PASSPHRASE_OUT_LEN = 4096;
constexpr u8 SC_ISO_SERIES_KEY_1[PASSPHRASE_KEY_LEN] = {
0xD4, 0x13, 0xB8, 0x96, 0x63, 0xE1, 0xFE, 0x9F, 0x75, 0x14, 0x3D, 0x3B, 0xB4, 0x56, 0x52, 0x74 // D413B89663E1FE9F75143D3BB4565274
};
constexpr u8 SC_ISO_SERIES_KEY_2[PASSPHRASE_KEY_LEN] = {
0xFA, 0x72, 0xCE, 0xEF, 0x59, 0xB4, 0xD2, 0x98, 0x9F, 0x11, 0x19, 0x13, 0x28, 0x7F, 0x51, 0xC7 // FA72CEEF59B4D2989F111913287F51C7
};
constexpr u8 SC_KEY_FOR_MASTER_1[PASSPHRASE_KEY_LEN] = {
0xDA, 0xA4, 0xB9, 0xF2, 0xBC, 0x70, 0xB2, 0x80, 0xA7, 0xB3, 0x40, 0xFA, 0x0D, 0x04, 0xBA, 0x14 // DAA4B9F2BC70B280A7B340FA0D04BA14
};
constexpr u8 SC_KEY_FOR_MASTER_2[PASSPHRASE_KEY_LEN] = {
0x29, 0xC1, 0x94, 0xFF, 0xEC, 0x1F, 0xD1, 0x4D, 0x4A, 0xAE, 0x00, 0x6C, 0x32, 0xB3, 0x59, 0x90 // 29C194FFEC1FD14D4AAE006C32B35990
};
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_1[PASSPHRASE_KEY_LEN] = {
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x00 // 73636B65795F7365726965736B657900
};
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_2[PASSPHRASE_KEY_LEN] = {
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x6B, 0x65, 0x79, 0x32 // 73636B65795F7365726965736B657932
};
constexpr u8 SC_ISO_SERIES_INTERNAL_KEY_3[PASSPHRASE_KEY_LEN] = {
0x73, 0x63, 0x6B, 0x65, 0x79, 0x5F, 0x66, 0x6F, 0x72, 0x5F, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72 // 73636B65795F666F725F6D6173746572
};
constexpr u8 PKG_AES_KEY_IDU[0x10] = {
0x5d, 0xb9, 0x11, 0xe6, 0xb7, 0xe5, 0x0a, 0x7d, 0x32, 0x15, 0x38, 0xfd, 0x7c, 0x66, 0xf1, 0x7b
};
@@ -180,6 +211,104 @@ constexpr u8 PUP_KEY[0x40] = {
0x30, 0xCE, 0x83, 0x66
};
// name; location; notes
constexpr s64 PAID_01 = 0x0003CD28CB47D3C1L; // spu_token_processor.self; CoreOS; Only for 2E - 083.007
constexpr s64 PAID_02 = 0x1010000001000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 2E - 080.006
constexpr s64 PAID_03 = 0x1010000001000003L; // retail games and their updates
constexpr s64 PAID_04 = 0x1010000002000003L;
constexpr s64 PAID_05 = 0x1020000401000001L; // ps2emu; /dev_flash/ps2emu; CEX DEX DECR ?
constexpr s64 PAID_06 = 0x1050000003000001L; // lv2_kernel.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_07 = 0x1070000001000002L; // onicore_child.self; /dev_flash/vsh/module; same for CEX DEX DECR
constexpr s64 PAID_08 = 0x1070000002000002L; // mcore.self; /dev_flash/vsh/module; same for CEX DEX DECR
constexpr s64 PAID_09 = 0x1070000003000002L; // mgvideo.self; /dev_flash/vsh/module; same for CEX DEX DECR
constexpr s64 PAID_10 = 0x1070000004000002L; // swagner / swreset; /dev_flash/vsh/module; DTCP-IP DRM modules
constexpr s64 PAID_11 = 0x107000000E000001L; // vtrm_server.fself; lv1
constexpr s64 PAID_12 = 0x107000000F000001L; // update_manager_server.fself; lv1
constexpr s64 PAID_13 = 0x1070000010000001L; // sc_manager_server.fself; lv1
constexpr s64 PAID_14 = 0x1070000011000001L; // secure_rtc_server.fself; lv1
constexpr s64 PAID_15 = 0x1070000012000001L; // spm_server.fself; lv1
constexpr s64 PAID_16 = 0x1070000013000001L; // sb_manager_server.fself; lv1
constexpr s64 PAID_17 = 0x1070000014000001L; // framework.fself; lv1
constexpr s64 PAID_18 = 0x1070000015000001L; // lv2_loader.fself; lv1
constexpr s64 PAID_19 = 0x1070000016000001L; // profile_loader.fself; lv1
constexpr s64 PAID_20 = 0x1070000017000001L; // ss_init.fself; lv1
constexpr s64 PAID_21 = 0x1070000018000001L; // individual_info_mgr_server.fself; lv1
constexpr s64 PAID_22 = 0x1070000019000001L; // app_info_manager_server.fself; lv1
constexpr s64 PAID_23 = 0x107000001A000001L; // ss_sc_init_pu.fself; JIG lv1 proc
constexpr s64 PAID_24 = 0x107000001C000001L; // updater_frontend.fself; lv1
constexpr s64 PAID_25 = 0x107000001D000001L; // sysmgr_ss.fself; lv1
constexpr s64 PAID_26 = 0x107000001F000001L; // sb_iso_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_27 = 0x1070000020000001L; // sc_iso.self / sc_iso_factory.self; CoreOS / [2.43 JIG PUP]; same for CEX DEX DECR
constexpr s64 PAID_28 = 0x1070000021000001L; // spp_verifier.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_29 = 0x1070000022000001L; // spu_pkg_rvk_verifier.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_30 = 0x1070000023000001L; // spu_token_processor.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_31 = 0x1070000024000001L; // sv_iso_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_32 = 0x1070000025000001L; // aim_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_33 = 0x1070000026000001L; // ss_sc_init.self; [2.43 JIG PUP]
constexpr s64 PAID_34 = 0x1070000027000001L; // dispatcher.fself; lv1;
constexpr s64 PAID_35 = 0x1070000028000001L; // factory_data_mngr_server.fself; JIG lv1 proc
constexpr s64 PAID_36 = 0x1070000029000001L; // fdm_spu_module.self; [2.43 JIG PUP]
constexpr s64 PAID_37 = 0x107000002A000001L;
constexpr s64 PAID_38 = 0x1070000031000001L;
constexpr s64 PAID_39 = 0x1070000032000001L; // ss_server1.fself; lv1
constexpr s64 PAID_40 = 0x1070000033000001L; // ss_server2.fself; lv1
constexpr s64 PAID_41 = 0x1070000034000001L; // ss_server3.fself; lv1
constexpr s64 PAID_42 = 0x1070000037000001L; // mc_iso_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_43 = 0x1070000039000001L; // bdp_bdmv.self; /dev_flash/bdplayer
constexpr s64 PAID_44 = 0x107000003A000001L; // bdj.self; /dev_flash/bdplayer
constexpr s64 PAID_45 = 0x1070000040000001L; // sys/external modules; /dev_flash/sys/external; same for CEX DEX DECR (incl. liblv2dbg_for_cex.sprx + liblv2dbg_for_dex.sprx)
constexpr s64 PAID_46 = 0x1070000041000001L; // ps1emu; /dev_flash/ps1emu; CEX DEX DECR ?
constexpr s64 PAID_47 = 0x1070000043000001L; // me_iso_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_48 = 0x1070000044000001L; // (related to usb dongle)
constexpr s64 PAID_49 = 0x1070000045000001L; // USB Dongle Authenticator; ss_server1.fself; same for CEX DEX DECR
constexpr s64 PAID_50 = 0x1070000046000001L; // spu_mode_auth.self; [2.43 JIG PUP]
constexpr s64 PAID_51 = 0x1070000047000001L; // otheros.self; otheros.self
constexpr s64 PAID_52 = 0x1070000048000001L; // ftpd; cell_root/target/images; DECR
constexpr s64 PAID_53 = 0x107000004C000001L; // spu_utoken_processor.self; CoreOS (since FW 2.40)
constexpr s64 PAID_54 = 0x107000004E000001L; // (syscall 878)
constexpr s64 PAID_55 = 0x107000004F000001L;
constexpr s64 PAID_56 = 0x1070000050000001L;
constexpr s64 PAID_57 = 0x1070000051000001L;
constexpr s64 PAID_58 = 0x1070000052000001L; // sys/internal CEX + vsh/module modules CEX; /dev_flash/sys/internal + /dev_flash/vsh/module; Differs between CEX (this authid) and DECR
constexpr s64 PAID_59 = 0x1070000054000001L; // (syscall 21)
constexpr s64 PAID_60 = 0x1070000055000001L; // manu_info_spu_module.self; CoreOS (since FW 3.50)
constexpr s64 PAID_61 = 0x1070000058000001L; // me_iso_for_ps2emu.self; CoreOS (since FW 3.70)
constexpr s64 PAID_62 = 0x1070000059000001L; // sv_iso_for_ps2emu.self; CoreOS (since FW 3.70)
constexpr s64 PAID_63 = 0x1070000300000001L; // Lv2diag.self; BD-remarry toolkit
constexpr s64 PAID_64 = 0x10700003FC000001L; // emer_init.self; CoreOS (since FW 2.00)
constexpr s64 PAID_65 = 0x10700003FD000001L; // ps3swu; PUP root; same for CEX DEX DECR
constexpr s64 PAID_66 = 0x10700003FD000001L; // PS3ToolUpdater; cell_root/target/images; Only DECR
constexpr s64 PAID_67 = 0x10700003FD000001L; // manufacturing_updater_for_reset.self; BD-remarry toolkit
constexpr s64 PAID_68 = 0x10700003FE000001L; // sys_agent.self DECR; /dev_flash/sys/internal; DECR
constexpr s64 PAID_69 = 0x10700003FF000001L; // db_backup, mkfs, mkfs_085, mount_hdd, registry_backup, set_monitor, most sys/internal modules DECR + most vsh/module modules DECR; /dev_flash/sys/internal + /dev_flash/vsh/module + cell_root/target/images; Differs between DECR (this authid) and CEX
constexpr s64 PAID_70 = 0x1070000400000001L; // vsh / games / utilities; /dev_flash/, cell_root/target/images; only for 081.003 - 083.007
constexpr s64 PAID_71 = 0x1070000409000001L; // psp_emulator.self; /dev_flash/pspemu/psp_emulator.self; CEX DEX DECR ?
constexpr s64 PAID_72 = 0x107000040A000001L; // psp_translator.self; /dev_flash/pspemu/psp_translator.self; CEX DEX DECR ?
constexpr s64 PAID_73 = 0x107000040B000001L; // emulator_api.sprx and other .sprx; /dev_flash/pspemu/release/; CEX DEX DECR ?
constexpr s64 PAID_74 = 0x107000040C000001L; // emulator_drm.sprx; /dev_flash/pspemu/release/emulator_drm.sprx; CEX DEX DECR ?
constexpr s64 PAID_75 = 0x107000040C000001L; // libchnnlsv.sprx; /dev_flash/sys/internal/; CEX DEX DECR ?
constexpr s64 PAID_76 = 0x107000040D000001L; // ?psp related?; ?/dev_flash/pspemu/release/?; CEX DEX DECR ?
constexpr s64 PAID_77 = 0x1070000500000001L; // cellftp; cell_root/target/images/; DECR
constexpr s64 PAID_78 = 0x1070000501000001L; // hdd_copy.self; CoreOS (since FW 3.10)
constexpr s64 PAID_79 = 0x10700005FC000001L; // sys_audio; /dev_flash/sys/internal; CEX
constexpr s64 PAID_80 = 0x10700005FD000001L; // sys_init_osd; /dev_flash/sys/internal; CEX
constexpr s64 PAID_81 = 0x10700005FF000001L; // vsh.self; /dev_flash/vsh/; CEX
constexpr s64 PAID_82 = 0x1070001002000001L; // PvrRecSvr.sprx; BCJB95006\USRDIR\v320; CEX
constexpr s64 PAID_83 = 0x1070200056000001L; // cachemgr.self; WebMAF apps/USRDIR
constexpr s64 PAID_84 = 0x1070200057000001L; // EBOOT.BIN.self + .sprx files; WebMAF apps/USRDIR/prx/ps3; Demen_prx.ppu.sprx + WebMAF sprx files
constexpr s64 PAID_85 = 0x1FF0000001000001L; // lv0; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_86 = 0x1FF0000002000001L; // lv1.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_87 = 0x1FF0000008000001L; // lv1ldr; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_88 = 0x1FF0000009000001L; // lv2ldr; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_89 = 0x1FF000000A000001L; // isoldr; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_90 = 0x1FF000000B000001L; // rvkldr; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_91 = 0x1FF000000C000001L; // appldr; CoreOS; same for CEX DEX DECR
constexpr s64 LAID_1 = 0x1070000001000001L; // (= HV processes / SCE_CELLOS_PME); flash and vflash
constexpr s64 LAID_2 = 0x1070000002000001L; // (= GameOS / PS3_LPAR); flash and vflash
constexpr s64 LAID_3 = 0x1020000003000001L; // (= PS2_LPAR / PS2_GX_LPAR / PS2_SW_LPAR / PS2_NE_LPAR); (used in ps3vflashc region inside vflash in NOR consoles, and ps3db region)... dev_flash and dev_hdd0 regions
constexpr s64 LAID_4 = 0x1080000004000001L; // (= LINUX_LPAR); (used in ps3vflashf region inside vflash in NOR consoles)... otheros bootloader region
class KeyVault
{
std::vector<SELF_KEY> sk_LV0_arr{};
+27 -33
View File
@@ -6,6 +6,7 @@
#include "ec.h"
#include "Utilities/mutex.h"
#include "Emu/system_utils.hpp"
#include <cmath>
#include "util/asm.hpp"
@@ -649,7 +650,7 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
EDAT.file_size = swap64(*reinterpret_cast<u64*>(&edat_header[8]));
}
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, unsigned char* rifkey, bool verbose)
bool extract_all_data(const fs::file* input, const fs::file* output, const char* input_file_name, unsigned char* devklic, bool verbose)
{
// Setup NPD and EDAT/SDAT structs.
NPD_HEADER NPD;
@@ -658,8 +659,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
// Read in the NPD and EDAT/SDAT headers.
read_npd_edat_header(input, NPD, EDAT);
unsigned char npd_magic[4] = {0x4E, 0x50, 0x44, 0x00}; //NPD0
if (memcmp(&NPD.magic, npd_magic, 4))
if (NPD.magic != "NPD\0"_u32)
{
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
return true;
@@ -671,6 +671,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
edat_log.notice("NPD version: %d", NPD.version);
edat_log.notice("NPD license: %d", NPD.license);
edat_log.notice("NPD type: %d", NPD.type);
edat_log.notice("NPD content_id: %s", NPD.content_id);
}
// Set decryption key.
@@ -715,27 +716,28 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
// Select EDAT key.
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
memcpy(&key, devklic, 0x10);
else if ((NPD.license & 0x2) == 0x2) // Type 2: Use key from RAP file (RIF key).
{
memcpy(&key, rifkey, 0x10);
std::memcpy(&key, devklic, 0x10);
}
else // Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
{
const std::string rap_path = rpcs3::utils::get_rap_file_path(NPD.content_id);
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(key))
{
key = GetEdatRifKeyFromRapFile(rap);
}
// Make sure we don't have an empty RIF key.
if (!key)
{
edat_log.error("A valid RAP file is needed for this EDAT file! (local activation)");
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", NPD.license);
return true;
}
}
else if ((NPD.license & 0x1) == 0x1) // Type 1: Use network activation.
{
memcpy(&key, rifkey, 0x10);
// Make sure we don't have an empty RIF key.
if (!key)
if (verbose)
{
edat_log.error("A valid RAP file is needed for this EDAT file! (network activation)");
return true;
edat_log.notice("RIFKEY: %s", std::bit_cast<be_t<u128>>(key));
}
}
@@ -745,8 +747,6 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
std::memcpy(&data, devklic, sizeof(data));
edat_log.notice("DEVKLIC: %s", data);
std::memcpy(&data, rifkey, sizeof(data));
edat_log.notice("RIF KEY: %s", data);
}
}
@@ -793,8 +793,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
// Read in the NPD and EDAT/SDAT headers.
read_npd_edat_header(&input, NPD, EDAT);
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
if (memcmp(&NPD.magic, npd_magic, 4))
if (NPD.magic != "NPD\0"_u32)
{
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
return false;
@@ -824,13 +823,17 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
}
// Decrypts full file
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose)
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose)
{
if (!input)
{
return {};
}
// Prepare the files.
input.seek(0);
// Set keys (RIF and DEVKLIC).
u128 rifKey{};
// Set DEVKLIC
u128 devklic{};
// Select the EDAT key mode.
@@ -875,17 +878,9 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
return fs::file{};
}
// Read the RAP file, if provided.
if (!rap_file_name.empty())
{
const fs::file rap(rap_file_name);
rifKey = GetEdatRifKeyFromRapFile(rap);
}
// Delete the bad output file if any errors arise.
fs::file output = fs::make_stream<std::vector<u8>>();
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), reinterpret_cast<uchar*>(&rifKey), verbose))
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), verbose))
{
output.release();
return fs::file{};
@@ -901,8 +896,7 @@ bool EDATADecrypter::ReadHeader()
// Read in the NPD and EDAT/SDAT headers.
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
unsigned char npd_magic[4] = { 0x4E, 0x50, 0x44, 0x00 }; //NPD0
if (memcmp(&npdHeader.magic, npd_magic, 4))
if (npdHeader.magic != "NPD\0"_u32)
{
return false;
}
+4 -8
View File
@@ -27,7 +27,7 @@ struct NPD_HEADER
s32 version;
s32 license;
s32 type;
u8 content_id[0x30];
char content_id[0x30];
u8 digest[0x10];
u8 title_hash[0x10];
u8 dev_hash[0x10];
@@ -43,7 +43,7 @@ struct EDAT_HEADER
};
// Decrypts full file, or null/empty file
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose);
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID);
@@ -86,13 +86,9 @@ public:
fs::stat_t stat() override
{
fs::stat_t stats;
stats.is_directory = false;
stats.is_writable = false;
fs::stat_t stats = edata_file.stat();
stats.is_writable = false; // TODO
stats.size = file_size;
stats.atime = -1;
stats.ctime = -1;
stats.mtime = -1;
return stats;
}
+60 -64
View File
@@ -15,14 +15,12 @@ LOG_CHANNEL(pkg_log, "PKG");
package_reader::package_reader(const std::string& path)
: m_path(path)
{
if (!fs::is_file(path))
if (!m_file.open(path))
{
pkg_log.error("PKG file not found!");
return;
}
m_filelist.emplace_back(fs::file{path});
m_is_valid = read_header();
if (!m_is_valid)
@@ -51,7 +49,7 @@ package_reader::~package_reader()
bool package_reader::read_header()
{
if (m_path.empty() || m_filelist.empty())
if (m_path.empty() || !m_file)
{
pkg_log.error("Reading PKG header: no file to read!");
return false;
@@ -75,7 +73,7 @@ bool package_reader::read_header()
pkg_log.notice("Header: data_size = 0x%x = %d", m_header.data_size, m_header.data_size);
pkg_log.notice("Header: title_id = %s", m_header.title_id);
pkg_log.notice("Header: qa_digest = 0x%x 0x%x", m_header.qa_digest[0], m_header.qa_digest[1]);
//pkg_log.notice("Header: klicensee = 0x%x = %d", header.klicensee, header.klicensee);
pkg_log.notice("Header: klicensee = %s", m_header.klicensee.value());
// Get extended PKG information for PSP or PSVita
if (m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA)
@@ -131,7 +129,7 @@ bool package_reader::read_header()
}
}
if (m_header.pkg_size > m_filelist[0].size())
if (m_header.pkg_size > m_file.size())
{
// Check if multi-files pkg
if (!m_path.ends_with("_00.pkg"))
@@ -140,11 +138,15 @@ bool package_reader::read_header()
return false;
}
std::vector<fs::file> filelist;
filelist.emplace_back(std::move(m_file));
const std::string name_wo_number = m_path.substr(0, m_path.size() - 7);
u64 cursize = m_filelist[0].size();
u64 cursize = filelist[0].size();
while (cursize < m_header.pkg_size)
{
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, m_filelist.size());
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
fs::file archive_file(archive_filename);
if (!archive_file)
@@ -153,9 +155,20 @@ bool package_reader::read_header()
return false;
}
cursize += archive_file.size();
m_filelist.emplace_back(std::move(archive_file));
const usz add_size = archive_file.size();
if (!add_size)
{
pkg_log.error("%s is empty, cannot read PKG", archive_filename);
return false;
}
cursize += add_size;
filelist.emplace_back(std::move(archive_file));
}
// Gather files
m_file = fs::make_gather(std::move(filelist));
}
if (m_header.data_size + m_header.data_offset > m_header.pkg_size)
@@ -510,10 +523,10 @@ bool package_reader::read_param_sfo()
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
const std::string_view name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
// We're looking for the PARAM.SFO file, if there is any
if (name != "PARAM.SFO")
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO")
{
continue;
}
@@ -542,6 +555,12 @@ bool package_reader::read_param_sfo()
m_psf = psf::load_object(tmp);
if (m_psf.empty())
{
// Invalid
continue;
}
return true;
}
else
@@ -667,6 +686,8 @@ package_error package_reader::check_target_app_version() const
return package_error::app_version;
}
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
bool package_reader::extract_data(atomic_t<double>& sync)
{
if (!m_is_valid)
@@ -704,7 +725,9 @@ bool package_reader::extract_data(atomic_t<double>& sync)
break;
}
dir += m_install_dir + '/';
// TODO: Verify whether other content types require appending title ID
if (m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
dir += m_install_dir + '/';
// If false, an existing directory is being overwritten: cannot cancel the operation
const bool was_null = !fs::is_dir(dir);
@@ -742,9 +765,10 @@ bool package_reader::extract_data(atomic_t<double>& sync)
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
const std::string path = dir + vfs::escape(name);
pkg_log.notice("Entry 0x%08x: %s", entry.type, name);
const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS);
(log_error ? pkg_log.error : pkg_log.notice)("Entry 0x%08x: %s (pad=0x%x)", entry.type, name, entry.pad);
switch (entry.type & 0xff)
switch (const u8 entry_type = entry.type & 0xff)
{
case PKG_FILE_ENTRY_NPDRM:
case PKG_FILE_ENTRY_NPDRMEDAT:
@@ -770,7 +794,14 @@ bool package_reader::extract_data(atomic_t<double>& sync)
break;
}
if (fs::file out{ path, fs::rewrite })
const bool is_buffered = entry_type == PKG_FILE_ENTRY_SDAT;
if (entry_type == PKG_FILE_ENTRY_NPDRMEDAT)
{
pkg_log.todo("NPDRM EDAT!");
}
if (fs::file out = is_buffered ? fs::make_stream<std::vector<u8>>() : fs::file{ path, fs::rewrite })
{
bool extract_success = true;
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
@@ -806,6 +837,17 @@ bool package_reader::extract_data(atomic_t<double>& sync)
}
}
if (is_buffered)
{
out = DecryptEDAT(out, name, 1, reinterpret_cast<u8*>(&m_header.klicensee), true);
if (!out || !fs::write_file(path, fs::rewrite, static_cast<fs::container_stream<std::vector<u8>>*>(out.release().get())->obj))
{
num_failures++;
pkg_log.error("Failed to create file %s", path);
break;
}
}
if (extract_success)
{
if (did_overwrite)
@@ -874,58 +916,12 @@ bool package_reader::extract_data(atomic_t<double>& sync)
void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode)
{
if (damode == fs::seek_set)
m_cur_offset = new_offset;
else if (damode == fs::seek_cur)
m_cur_offset += new_offset;
u64 _offset = 0;
for (usz i = 0; i < m_filelist.size(); i++)
{
if (m_cur_offset < (_offset + m_filelist[i].size()))
{
m_cur_file = i;
m_cur_file_offset = m_cur_offset - _offset;
m_filelist[i].seek(m_cur_file_offset);
break;
}
_offset += m_filelist[i].size();
}
if (m_file) m_file.seek(new_offset, damode);
};
u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
{
ensure(m_filelist.size() > m_cur_file && m_filelist[m_cur_file]);
const u64 num_bytes_left = m_filelist[m_cur_file].size() - m_cur_file_offset;
// check if it continues in another file
if (num_bytes > num_bytes_left)
{
m_filelist[m_cur_file].read(data_ptr, num_bytes_left);
if ((m_cur_file + 1) < m_filelist.size())
{
++m_cur_file;
}
else
{
m_cur_offset += num_bytes_left;
m_cur_file_offset = m_filelist[m_cur_file].size();
return num_bytes_left;
}
const u64 num_read = m_filelist[m_cur_file].read(static_cast<u8*>(data_ptr) + num_bytes_left, num_bytes - num_bytes_left);
m_cur_offset += (num_read + num_bytes_left);
m_cur_file_offset = num_read;
return (num_read + num_bytes_left);
}
const u64 num_read = m_filelist[m_cur_file].read(data_ptr, num_bytes);
m_cur_offset += num_read;
m_cur_file_offset += num_read;
return num_read;
return m_file ? m_file.read(data_ptr, num_bytes) : 0;
};
u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
+3 -4
View File
@@ -35,6 +35,8 @@ enum : u32
PKG_FILE_ENTRY_OVERWRITE = 0x80000000,
PKG_FILE_ENTRY_PSP = 0x10000000,
PKG_FILE_ENTRY_KNOWN_BITS = 0xff | PKG_FILE_ENTRY_PSP | PKG_FILE_ENTRY_OVERWRITE,
};
enum : u32
@@ -325,10 +327,7 @@ private:
std::string m_path{};
std::string m_install_dir{};
std::vector<fs::file> m_filelist{};
usz m_cur_file = 0;
u64 m_cur_offset = 0;
u64 m_cur_file_offset = 0;
fs::file m_file{};
std::unique_ptr<u128[]> m_buf{};
std::array<uchar, 16> m_dec_key{};
+78
View File
@@ -5,6 +5,7 @@
#include "utils.h"
#include "aes.h"
#include "sha1.h"
#include "key_vault.h"
#include <cstring>
#include <stdio.h>
#include <time.h>
@@ -142,3 +143,80 @@ void mbedtls_zeroize(void *v, size_t n)
static void *(*const volatile unop_memset)(void *, int, size_t) = &memset;
(void)unop_memset(v, 0, n);
}
// SC passphrase crypto
void sc_form_key(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* key)
{
for (u32 i = 0; i < PASSPHRASE_KEY_LEN; i++)
{
key[i] = static_cast<u8>(sc_key[i] ^ laid_paid[i]);
}
}
std::array<u8, PASSPHRASE_KEY_LEN> sc_combine_laid_paid(s64 laid, s64 paid)
{
const std::string paid_laid = fmt::format("%016llx%016llx", laid, paid);
std::array<u8, PASSPHRASE_KEY_LEN> out{};
hex_to_bytes(out.data(), paid_laid.c_str(), PASSPHRASE_KEY_LEN * 2);
return out;
}
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_get_laid_paid_from_type(int type)
{
// No idea what this type stands for
switch (type)
{
case 0: return sc_combine_laid_paid(0xFFFFFFFFFFFFFFFFL, 0xFFFFFFFFFFFFFFFFL);
case 1: return sc_combine_laid_paid(LAID_2, 0x1070000000000001L);
case 2: return sc_combine_laid_paid(LAID_2, 0x0000000000000000L);
case 3: return sc_combine_laid_paid(LAID_2, PAID_69);
default:
fmt::throw_exception("vtrm_get_laid_paid_from_type: Wrong type specified (type=%d)", type);
}
}
std::array<u8, PASSPHRASE_KEY_LEN> vtrm_portability_laid_paid()
{
// 107000002A000001
return sc_combine_laid_paid(0x0000000000000000L, 0x0000000000000000L);
}
int sc_decrypt(const u8* sc_key, const std::array<u8, PASSPHRASE_KEY_LEN>& laid_paid, u8* iv, u8* input, u8* output)
{
aes_context ctx;
u8 key[PASSPHRASE_KEY_LEN];
sc_form_key(sc_key, laid_paid, key);
aes_setkey_dec(&ctx, key, 128);
return aes_crypt_cbc(&ctx, AES_DECRYPT, PASSPHRASE_OUT_LEN, iv, input, output);
}
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output)
{
return sc_decrypt(SC_ISO_SERIES_KEY_2, vtrm_get_laid_paid_from_type(type), iv, input, output);
}
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output)
{
return sc_decrypt(SC_ISO_SERIES_INTERNAL_KEY_3, sc_combine_laid_paid(laid, paid), iv, input, output);
}
const u8* vtrm_portability_type_mapper(int type)
{
// No idea what this type stands for
switch (type)
{
//case 0: return key_for_type_1;
case 1: return SC_ISO_SERIES_KEY_2;
case 2: return SC_ISO_SERIES_KEY_1;
case 3: return SC_KEY_FOR_MASTER_2;
default:
fmt::throw_exception("vtrm_portability_type_mapper: Wrong type specified (type=%d)", type);
}
}
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output)
{
return sc_decrypt(vtrm_portability_type_mapper(type), vtrm_portability_laid_paid(), iv, input, output);
}
+6
View File
@@ -53,3 +53,9 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len);
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash);
void mbedtls_zeroize(void *v, size_t n);
// SC passphrase crypto
int vtrm_decrypt(int type, u8* iv, u8* input, u8* output);
int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output);
int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output);
+5 -5
View File
@@ -18,11 +18,11 @@ private:
IFACEMETHODIMP_(ULONG) AddRef() override { return 1; };
IFACEMETHODIMP_(ULONG) Release() override { return 1; };
IFACEMETHODIMP QueryInterface(REFIID iid, void** object) override { return S_OK; };
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) override { return S_OK; };
IFACEMETHODIMP OnDeviceAdded(LPCWSTR device_id) override { return S_OK; };
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR device_id) override { return S_OK; };
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) override { return S_OK; };
IFACEMETHODIMP QueryInterface(REFIID /*iid*/, void** /*object*/) override { return S_OK; };
IFACEMETHODIMP OnPropertyValueChanged(LPCWSTR /*device_id*/, const PROPERTYKEY /*key*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceAdded(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceRemoved(LPCWSTR /*device_id*/) override { return S_OK; };
IFACEMETHODIMP OnDeviceStateChanged(LPCWSTR /*device_id*/, DWORD /*new_state*/) override { return S_OK; };
IFACEMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR new_default_device_id) override;
} m_listener;
+3
View File
@@ -7,6 +7,7 @@
system_config_types.cpp
system_progress.cpp
system_utils.cpp
vfs_config.cpp
VFS.cpp
GDB.cpp
title.cpp
@@ -358,6 +359,7 @@ target_link_libraries(rpcs3_emu
# Io
target_sources(rpcs3_emu PRIVATE
Io/camera_config.cpp
Io/interception.cpp
Io/KeyboardHandler.cpp
Io/pad_config.cpp
@@ -457,6 +459,7 @@ if(TARGET 3rdparty_vulkan)
RSX/VK/vkutils/chip_class.cpp
RSX/VK/vkutils/commands.cpp
RSX/VK/vkutils/data_heap.cpp
RSX/VK/vkutils/descriptors.cpp
RSX/VK/vkutils/image.cpp
RSX/VK/vkutils/image_helpers.cpp
RSX/VK/vkutils/scratch.cpp
+26 -24
View File
@@ -17,12 +17,13 @@ class cpu_thread;
class CPUDisAsm
{
protected:
const cpu_disasm_mode m_mode{};
const std::add_pointer_t<const u8> m_offset{};
cpu_disasm_mode m_mode{};
const u8* m_offset{};
const u32 m_start_pc;
const std::add_pointer_t<const cpu_thread> m_cpu{};
u32 m_op = 0;
void Write(const std::string& value)
void format_by_mode()
{
switch (m_mode)
{
@@ -32,28 +33,27 @@ protected:
static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value);
static_cast<u8>(m_op >> 0), last_opcode);
break;
}
case cpu_disasm_mode::interpreter:
{
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
last_opcode.insert(0, fmt::format("[%08x] %02x %02x %02x %02x: ", dump_pc,
static_cast<u8>(m_op >> 24),
static_cast<u8>(m_op >> 16),
static_cast<u8>(m_op >> 8),
static_cast<u8>(m_op >> 0), value);
static_cast<u8>(m_op >> 0)));
break;
}
case cpu_disasm_mode::compiler_elf:
{
last_opcode = value + '\n';
last_opcode += '\n';
break;
}
case cpu_disasm_mode::normal:
{
last_opcode = value;
break;
}
default: fmt::throw_exception("Unreachable");
@@ -64,26 +64,27 @@ public:
std::string last_opcode{};
u32 dump_pc{};
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
cpu_disasm_mode change_mode(cpu_disasm_mode mode)
{
return T{mode, dis.m_offset, dis.m_cpu};
return std::exchange(m_mode, mode);
}
const u8* change_ptr(const u8* ptr)
{
return std::exchange(m_offset, ptr);
}
protected:
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0, const cpu_thread* cpu = nullptr)
: m_mode(mode)
, m_offset(offset)
, m_offset(offset - start_pc)
, m_start_pc(start_pc)
, m_cpu(cpu)
{
}
CPUDisAsm(const CPUDisAsm&) = delete;
CPUDisAsm& operator=(const CPUDisAsm&) = delete;
virtual ~CPUDisAsm() = default;
virtual u32 DisAsmBranchTarget(s32 /*imm*/);
// TODO: Add builtin fmt helpper for best performance
@@ -109,16 +110,17 @@ protected:
return fmt::format("%s%s", v < 0 ? "-" : "", av);
}
std::string FixOp(std::string op) const
// Signify the formatting function the minimum required amount of characters to print for an instruction
// Padding with spaces
int PadOp(std::string_view op = {}, int min_spaces = 0) const
{
if (m_mode != cpu_disasm_mode::normal)
{
op.resize(std::max<usz>(op.length(), 10), ' ');
}
return op;
return m_mode == cpu_disasm_mode::normal ? (static_cast<int>(op.size()) + min_spaces) : 10;
}
public:
virtual ~CPUDisAsm() = default;
virtual u32 disasm(u32 pc) = 0;
virtual std::pair<const void*, usz> get_memory_span() const = 0;
virtual std::unique_ptr<CPUDisAsm> copy_type_erased() const = 0;
};
+8 -6
View File
@@ -49,6 +49,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::ret: return "ret";
case cpu_flag::signal: return "sig";
case cpu_flag::memory: return "mem";
case cpu_flag::pending: return "pend";
case cpu_flag::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
@@ -411,11 +412,6 @@ void cpu_thread::operator()()
}
if (id_type() == 2)
{
if (g_cfg.core.lower_spu_priority)
{
thread_ctrl::set_native_priority(-1);
}
// force input/output denormals to zero for SPU threads (FTZ/DAZ)
_mm_setcsr( _mm_getcsr() | 0x8040 );
@@ -469,7 +465,7 @@ void cpu_thread::operator()()
cpu_thread* _cpu = get_current_cpu_thread();
// Wait flag isn't set asynchronously so this should be thread-safe
if (progress == 0 && cpu_flag::wait - _cpu->state)
if (progress == 0 && _cpu->state.none_of(cpu_flag::wait + cpu_flag::temp))
{
// Operation just started and syscall is imminent
_cpu->state += cpu_flag::wait + cpu_flag::temp;
@@ -739,6 +735,12 @@ bool cpu_thread::check_state() noexcept
cpu_counter::add(this);
}
if ((state0 & (cpu_flag::pending + cpu_flag::temp)) == cpu_flag::pending)
{
// Execute pending work
cpu_work();
}
if (retval)
{
cpu_on_stop();
+4
View File
@@ -21,6 +21,7 @@ enum class cpu_flag : u32
ret, // Callback return requested
signal, // Thread received a signal (HLE)
memory, // Thread must unlock memory mutex
pending, // Thread has postponed work
dbg_global_pause, // Emulation paused
dbg_pause, // Thread paused
@@ -169,6 +170,9 @@ public:
// Callback for cpu_flag::suspend
virtual void cpu_sleep() {}
// Callback for cpu_flag::pending
virtual void cpu_work() {}
// Callback for cpu_flag::ret
virtual void cpu_return() {}
+105 -8
View File
@@ -7,11 +7,60 @@
llvm::LLVMContext g_llvm_ctx;
llvm::Value* peek_through_bitcasts(llvm::Value* arg)
{
llvm::CastInst* i;
while ((i = llvm::dyn_cast_or_null<llvm::CastInst>(arg)) && i->getOpcode() == llvm::Instruction::BitCast)
{
arg = i->getOperand(0);
}
return arg;
}
cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
: m_context(g_llvm_ctx)
, m_module(_module)
, m_is_be(is_be)
{
register_intrinsic("x86_pshufb", [&](llvm::CallInst* ci) -> llvm::Value*
{
const auto data0 = ci->getOperand(0);
const auto index = ci->getOperand(1);
const auto zeros = llvm::ConstantAggregateZero::get(get_type<u8[16]>());
if (m_use_ssse3)
{
return m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_ssse3_pshuf_b_128), {data0, index});
}
else
{
// Emulate PSHUFB (TODO)
const auto mask = m_ir->CreateAnd(index, 0xf);
const auto loop = llvm::BasicBlock::Create(m_context, "", m_ir->GetInsertBlock()->getParent());
const auto prev = ci->getParent();
const auto next = prev->splitBasicBlock(ci->getNextNode());
llvm::cast<llvm::BranchInst>(m_ir->GetInsertBlock()->getTerminator())->setOperand(0, loop);
llvm::Value* result;
//m_ir->CreateBr(loop);
m_ir->SetInsertPoint(loop);
const auto i = m_ir->CreatePHI(get_type<u32>(), 2);
const auto v = m_ir->CreatePHI(get_type<u8[16]>(), 2);
i->addIncoming(m_ir->getInt32(0), prev);
i->addIncoming(m_ir->CreateAdd(i, m_ir->getInt32(1)), loop);
v->addIncoming(zeros, prev);
result = m_ir->CreateInsertElement(v, m_ir->CreateExtractElement(data0, m_ir->CreateExtractElement(mask, i)), i);
v->addIncoming(result, loop);
m_ir->CreateCondBr(m_ir->CreateICmpULT(i, m_ir->getInt32(16)), loop, next);
m_ir->SetInsertPoint(next->getFirstNonPHI());
result = m_ir->CreateSelect(m_ir->CreateICmpSLT(index, zeros), zeros, result);
return result;
}
});
}
void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngine& engine)
@@ -108,7 +157,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
}
template <>
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 a, u32 b)
std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32 _pos, u32 _line)
{
v128 result{};
@@ -131,12 +180,12 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
return {true, result};
}
fmt::throw_exception("[0x%x, %u] Not a vector", a, b);
fmt::throw_exception("[0x%x, %u] Not a vector", _pos, _line);
}
if (auto v = llvm::cast<llvm::VectorType>(t); v->getScalarSizeInBits() * v->getNumElements() != 128)
{
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", a, b, v->getScalarSizeInBits(), v->getNumElements());
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", _pos, _line, v->getScalarSizeInBits(), v->getNumElements());
}
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
@@ -151,10 +200,10 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
if (llvm::isa<llvm::ConstantExpr>(c))
{
// Sorry, if we cannot evaluate it we cannot use it
fmt::throw_exception("[0x%x, %u] Constant Expression!", a, b);
fmt::throw_exception("[0x%x, %u] Constant Expression!", _pos, _line);
}
fmt::throw_exception("[0x%x, %u] Unexpected constant type", a, b);
fmt::throw_exception("[0x%x, %u] Unexpected constant type", _pos, _line);
}
const auto sct = t->getScalarType();
@@ -203,14 +252,14 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
}
else
{
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", a, b);
fmt::throw_exception("[0x%x, %u] Unexpected vector element type", _pos, _line);
}
return {true, result};
}
template <>
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t, u32 _line)
{
if (const auto ct = llvm::dyn_cast<llvm::IntegerType>(t); ct && ct->getBitWidth() == 128)
{
@@ -247,7 +296,55 @@ llvm::Constant* cpu_translator::make_const_vector<v128>(v128 v, llvm::Type* t)
return llvm::ConstantDataVector::get(m_context, llvm::makeArrayRef(reinterpret_cast<const f64*>(v._bytes), 2));
}
fmt::throw_exception("No supported constant type");
fmt::throw_exception("[line %u] No supported constant type", _line);
}
void cpu_translator::replace_intrinsics(llvm::Function& f)
{
for (auto& bb : f)
{
for (auto bit = bb.begin(); bit != bb.end();)
{
if (auto ci = llvm::dyn_cast<llvm::CallInst>(&*bit))
{
if (auto cf = ci->getCalledFunction())
{
if (auto it = m_intrinsics.find(std::string_view(cf->getName().data(), cf->getName().size())); it != m_intrinsics.end())
{
m_ir->SetInsertPoint(ci);
ci->replaceAllUsesWith(it->second(ci));
bit = ci->eraseFromParent();
continue;
}
}
}
++bit;
}
}
}
void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
{
for (auto v : args)
{
for (auto it = v->use_begin(); it != v->use_end(); ++it)
{
llvm::Value* i = *it;
llvm::CastInst* bci = nullptr;
// Walk through bitcasts
while (i && (bci = llvm::dyn_cast<llvm::CastInst>(i)) && bci->getOpcode() == llvm::Instruction::BitCast)
{
i = *bci->use_begin();
}
if (auto si = llvm::dyn_cast_or_null<llvm::StoreInst>(i))
{
si->eraseFromParent();
}
}
}
}
#endif
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -161,7 +161,7 @@ f32 audio_ringbuffer::set_frequency_ratio(f32 new_ratio)
u64 audio_ringbuffer::get_timestamp()
{
return get_system_time() - Emu.GetPauseTime();
return get_system_time();
}
void audio_ringbuffer::enqueue(const float* in_buffer)
@@ -296,7 +296,7 @@ u64 audio_ringbuffer::update()
}
else
{
const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp);
const u64 play_delta = (update_timestamp ? timestamp - std::max<u64>(play_timestamp, update_timestamp) : 0);
const u64 delta_samples_tmp = play_delta * static_cast<u64>(cfg.audio_sampling_rate * frequency_ratio) + last_remainder;
last_remainder = delta_samples_tmp % 1'000'000;
@@ -972,10 +972,10 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
const float center = buf[in + 2] * m;
[[maybe_unused]]
const float low_freq = buf[in + 3] * m;
const float rear_left = buf[in + 4] * m;
const float rear_right = buf[in + 5] * m;
const float side_left = buf[in + 6] * m;
const float side_right = buf[in + 7] * m;
const float side_left = buf[in + 4] * m;
const float side_right = buf[in + 5] * m;
const float rear_left = buf[in + 6] * m;
const float rear_right = buf[in + 7] * m;
if constexpr (downmix == audio_downmix::downmix_to_stereo)
{
@@ -1017,10 +1017,10 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
const float right = buf[in + 1] * m;
const float center = buf[in + 2] * m;
const float low_freq = buf[in + 3] * m;
const float rear_left = buf[in + 4] * m;
const float rear_right = buf[in + 5] * m;
const float side_left = buf[in + 6] * m;
const float side_right = buf[in + 7] * m;
const float side_left = buf[in + 4] * m;
const float side_right = buf[in + 5] * m;
const float rear_left = buf[in + 6] * m;
const float rear_right = buf[in + 7] * m;
if constexpr (downmix == audio_downmix::downmix_to_stereo)
{
@@ -1237,7 +1237,7 @@ error_code cellAudioPortOpen(vm::ptr<CellAudioPortParam> audioParam, vm::ptr<u32
port->cur_pos = 0;
port->global_counter = g_audio.m_counter;
port->active_counter = 0;
port->timestamp = g_audio.m_last_period_end;
port->timestamp = get_guest_system_time(g_audio.m_last_period_end);
if (attr & CELL_AUDIO_PORTATTR_INITLEVEL)
{
@@ -1419,7 +1419,7 @@ error_code cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr<u64> stamp)
const u64 delta_tag_stamp = delta_tag * g_audio.cfg.audio_block_period;
// Apparently no error is returned if stamp is null
*stamp = port.timestamp - delta_tag_stamp;
*stamp = get_guest_system_time(port.timestamp - delta_tag_stamp);
return CELL_OK;
}
+1 -1
View File
@@ -4,7 +4,7 @@
#include "util/endian.hpp"
// Error codes
enum CellAudioInError
enum CellAudioInError : u32
{
CELL_AUDIO_IN_ERROR_NOT_IMPLEMENTED = 0x8002b260,
CELL_AUDIO_IN_ERROR_ILLEGAL_CONFIGURATION = 0x8002b261,
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
// Error codes
enum CellAudioOutError
enum CellAudioOutError : u32
{
CELL_AUDIO_OUT_ERROR_NOT_IMPLEMENTED = 0x8002b240,
CELL_AUDIO_OUT_ERROR_ILLEGAL_CONFIGURATION = 0x8002b241,
File diff suppressed because it is too large Load Diff
+21 -2
View File
@@ -3,6 +3,7 @@
#include "Utilities/Timer.h"
#include "Emu/Cell/lv2/sys_memory.h"
#include "Utilities/Thread.h"
#include "Emu/Io/camera_handler_base.h"
#include <map>
@@ -359,7 +360,7 @@ struct CellCameraInfo
struct CellCameraInfoEx
{
be_t<s32> format; // CellCameraFormat
be_t<CellCameraFormat> format; // CellCameraFormat
be_t<s32> resolution; // CellCameraResolution
be_t<s32> framerate;
@@ -425,17 +426,31 @@ public:
atomic_t<bool> is_open{false};
CellCameraInfoEx info{};
atomic_t<u32> pbuf_write_index = 0;
std::array<atomic_t<bool>, 2> pbuf_locked = { false, false };
u32 pbuf_next_index() const;
struct attr_t
{
u32 v1, v2;
};
attr_t attr[500]{};
atomic_t<u32> frame_num;
atomic_t<bool> has_new_frame = false;
atomic_t<u32> frame_num = 0;
atomic_t<u32> frame_timestamp = 0;
atomic_t<u32> bytes_read = 0;
atomic_t<u32> init = 0;
static constexpr auto thread_name = "Camera Thread"sv;
std::shared_ptr<camera_handler_base> handler;
bool open_camera();
bool start_camera();
bool get_camera_frame(u8* dst, u32& width, u32& height, u64& frame_number, u64& bytes_read);
void stop_camera();
void close_camera();
bool on_handler_state(camera_handler_base::camera_handler_state state);
};
using camera_thread = named_thread<camera_context>;
@@ -446,4 +461,8 @@ struct gem_camera_shared
atomic_t<s64> frame_timestamp{}; // latest read timestamp from cellCamera (cellCameraRead(Ex))
atomic_t<s32> width{640};
atomic_t<s32> height{480};
atomic_t<s32> size{0};
atomic_t<CellCameraFormat> format{CELL_CAMERA_RAW8};
};
static inline s32 get_video_buffer_size(s32 width, s32 height);
+4 -2
View File
@@ -472,7 +472,9 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
std::string dir;
psf::registry sfo;
if (Emu.GetCat() == "DG")
const std::string& cat = Emu.GetFakeCat();
if (cat == "DG")
{
*type = CELL_GAME_GAMETYPE_DISC;
*attributes = 0; // TODO
@@ -480,7 +482,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
sfo = psf::load_object(fs::file(vfs::get("/dev_bdvd/PS3_GAME/PARAM.SFO")));
}
else if (Emu.GetCat() == "GD")
else if (cat == "GD")
{
*type = CELL_GAME_GAMETYPE_DISC;
*attributes = CELL_GAME_ATTRIBUTE_PATCH; // TODO
+1 -1
View File
@@ -1570,5 +1570,5 @@ DECLARE(ppu_module_manager::cellGcmSys)("cellGcmSys", []()
REG_FUNC(cellGcmSys, cellGcmGpadCaptureSnapshot);
// Special
REG_FUNC(cellGcmSys, cellGcmCallback).flag(MFF_HIDDEN);
REG_HIDDEN_FUNC(cellGcmCallback);
});
+264 -42
View File
@@ -2,10 +2,12 @@
#include "cellGem.h"
#include "cellCamera.h"
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/Io/MouseHandler.h"
#include "Emu/system_config.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Input/pad_thread.h"
@@ -59,12 +61,39 @@ void fmt_class_string<CellGemStatus>::format(std::string& out, u64 arg)
});
}
template <>
void fmt_class_string<CellGemVideoConvertFormatEnum>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto format)
{
switch (format)
{
STR_CASE(CELL_GEM_NO_VIDEO_OUTPUT);
STR_CASE(CELL_GEM_RGBA_640x480);
STR_CASE(CELL_GEM_YUV_640x480);
STR_CASE(CELL_GEM_YUV422_640x480);
STR_CASE(CELL_GEM_YUV411_640x480);
STR_CASE(CELL_GEM_RGBA_320x240);
STR_CASE(CELL_GEM_BAYER_RESTORED);
STR_CASE(CELL_GEM_BAYER_RESTORED_RGGB);
STR_CASE(CELL_GEM_BAYER_RESTORED_RASTERIZED);
}
return unknown;
});
}
// **********************
// * HLE helper structs *
// **********************
struct gem_config
struct gem_config_data
{
public:
void operator()();
static constexpr auto thread_name = "Gem Thread"sv;
atomic_t<u32> state = 0;
struct gem_color
@@ -106,14 +135,16 @@ struct gem_config
CellGemAttribute attribute = {};
CellGemVideoConvertAttribute vc_attribute = {};
s32 video_data_out_size = -1;
std::vector<u8> video_data_in;
u64 status_flags = 0;
bool enable_pitch_correction = false;
u32 inertial_counter = 0;
std::array<gem_controller, CELL_GEM_MAX_NUM> controllers;
u32 connected_controllers = 0;
bool video_conversion_started{};
bool update_started{};
atomic_t<bool> video_conversion_in_progress{false};
atomic_t<bool> update_started{false};
u32 camera_frame{};
u32 memory_ptr{};
@@ -173,6 +204,186 @@ struct gem_config
}
};
static inline int32_t cellGemGetVideoConvertSize(s32 output_format)
{
switch (output_format)
{
case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required
return 320 * 240 * 4;
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
return 640 * 480 * 4;
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
return 640 * 480 + 640 * 480 + 640 * 480;
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
return 640 * 480 + 320 * 480 + 320 * 480;
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
return 640 * 480 + 320 * 240 + 320 * 240;
case CELL_GEM_BAYER_RESTORED: // Bayer pattern output, 640x480, gamma and white balance applied, output buffer required
case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B
case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters
return 640 * 480;
case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output
return 0;
default:
return -1;
}
}
void gem_config_data::operator()()
{
cellGem.notice("Starting thread");
while (thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
{
while (!video_conversion_in_progress && thread_ctrl::state() != thread_state::aborting && !Emu.IsStopped())
{
thread_ctrl::wait_for(1000);
}
if (thread_ctrl::state() == thread_state::aborting || Emu.IsStopped())
{
return;
}
CellGemVideoConvertAttribute vc;
{
std::scoped_lock lock(mtx);
vc = vc_attribute;
}
if (g_cfg.io.camera != camera_handler::qt)
{
video_conversion_in_progress = false;
continue;
}
const auto& shared_data = g_fxo->get<gem_camera_shared>();
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT && !vc_attribute.video_data_out)
{
video_conversion_in_progress = false;
continue;
}
extern u32 get_buffer_size_by_format(s32, s32, s32);
const u32 required_in_size = get_buffer_size_by_format(static_cast<s32>(shared_data.format), shared_data.width, shared_data.height);
const s32 required_out_size = cellGemGetVideoConvertSize(vc.output_format);
if (video_data_in.size() != required_in_size)
{
cellGem.error("convert: in_size mismatch: required=%d, actual=%d", required_in_size, video_data_in.size());
video_conversion_in_progress = false;
continue;
}
if (required_out_size < 0 || video_data_out_size != required_out_size)
{
cellGem.error("convert: out_size unknown: required=%d, format %d", required_out_size, vc.output_format);
video_conversion_in_progress = false;
continue;
}
if (required_out_size == 0)
{
video_conversion_in_progress = false;
continue;
}
switch (vc.output_format)
{
case CELL_GEM_RGBA_640x480: // RGBA output; 640*480*4-byte output buffer required
{
if (shared_data.format == CELL_CAMERA_RAW8)
{
constexpr u32 in_pitch = 640;
constexpr u32 out_pitch = 640 * 4;
for (u32 y = 0; y < 480 - 1; y += 2)
{
for (u32 x = 0; x < 640 - 1; x += 2)
{
const u32 in_offset = 1 * (y * 640 + x);
const u32 out_offset = 4 * (y * 640 + x);
const u8 b = video_data_in[in_offset + 0];
const u8 g0 = video_data_in[in_offset + 1];
const u8 g1 = video_data_in[in_offset + in_pitch + 0];
const u8 r = video_data_in[in_offset + in_pitch + 1];
// Top-Left
vc_attribute.video_data_out[out_offset + 0] = r; // R
vc_attribute.video_data_out[out_offset + 1] = g0; // G
vc_attribute.video_data_out[out_offset + 2] = b; // B
vc_attribute.video_data_out[out_offset + 3] = 255; // A
// Top-Right Pixel
vc_attribute.video_data_out[out_offset + 4] = r; // R
vc_attribute.video_data_out[out_offset + 5] = g0; // G
vc_attribute.video_data_out[out_offset + 6] = b; // B
vc_attribute.video_data_out[out_offset + 7] = 255; // A
// Bottom-Left Pixel
vc_attribute.video_data_out[out_offset + out_pitch + 0] = r; // R
vc_attribute.video_data_out[out_offset + out_pitch + 1] = g1; // G
vc_attribute.video_data_out[out_offset + out_pitch + 2] = b; // B
vc_attribute.video_data_out[out_offset + out_pitch + 3] = 255; // A
// Bottom-Right Pixel
vc_attribute.video_data_out[out_offset + out_pitch + 4] = r; // R
vc_attribute.video_data_out[out_offset + out_pitch + 5] = g1; // G
vc_attribute.video_data_out[out_offset + out_pitch + 6] = b; // B
vc_attribute.video_data_out[out_offset + out_pitch + 7] = 255; // A
}
}
}
else
{
cellGem.error("Unimplemented: Converting %s to %s", shared_data.format.load(), vc.output_format);
std::memcpy(vc_attribute.video_data_out.get_ptr(), video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
}
break;
}
case CELL_GEM_BAYER_RESTORED: // Bayer pattern output, 640x480, gamma and white balance applied, output buffer required
{
if (shared_data.format == CELL_CAMERA_RAW8)
{
std::memcpy(vc_attribute.video_data_out.get_ptr(), video_data_in.data(), std::min<usz>(required_in_size, required_out_size));
}
else
{
cellGem.error("Unimplemented: Converting %s to %s", shared_data.format.load(), vc.output_format);
}
break;
}
case CELL_GEM_RGBA_320x240: // RGBA output; 320*240*4-byte output buffer required
case CELL_GEM_YUV_640x480: // YUV output; 640*480+640*480+640*480-byte output buffer required (contiguous)
case CELL_GEM_YUV422_640x480: // YUV output; 640*480+320*480+320*480-byte output buffer required (contiguous)
case CELL_GEM_YUV411_640x480: // YUV411 output; 640*480+320*240+320*240-byte output buffer required (contiguous)
case CELL_GEM_BAYER_RESTORED_RGGB: // Restored Bayer output, 2x2 pixels rearranged into 320x240 RG1G2B
case CELL_GEM_BAYER_RESTORED_RASTERIZED: // Restored Bayer output, R,G1,G2,B rearranged into 4 contiguous 320x240 1-channel rasters
{
cellGem.error("Unimplemented: Converting %s to %s", shared_data.format.load(), vc.output_format);
break;
}
case CELL_GEM_NO_VIDEO_OUTPUT: // Disable video output
{
cellGem.trace("Ignoring frame conversion for CELL_GEM_NO_VIDEO_OUTPUT");
break;
}
default:
{
cellGem.error("Trying to convert %s to %s", shared_data.format.load(), vc.output_format);
break;
}
}
cellGem.notice("Converted video frame of format %s to %s", shared_data.format.load(), vc.output_format.get());
video_conversion_in_progress = false;
}
}
using gem_config = named_thread<gem_config_data>;
/**
* \brief Verifies that a Move controller id is valid
* \param gem_num Move controler ID to verify
@@ -519,7 +730,7 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask)
error_code cellGemConvertVideoFinish()
{
cellGem.todo("cellGemConvertVideoFinish()");
cellGem.warning("cellGemConvertVideoFinish()");
auto& gem = g_fxo->get<gem_config>();
@@ -528,19 +739,22 @@ error_code cellGemConvertVideoFinish()
return CELL_GEM_ERROR_UNINITIALIZED;
}
if (!std::exchange(gem.video_conversion_started, false))
if (!gem.video_conversion_in_progress)
{
return CELL_GEM_ERROR_CONVERT_NOT_STARTED;
}
// TODO: wait until image is converted
while (gem.video_conversion_in_progress && !Emu.IsStopped())
{
thread_ctrl::wait_for(100);
}
return CELL_OK;
}
error_code cellGemConvertVideoStart(vm::cptr<void> video_frame)
{
cellGem.todo("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame);
cellGem.warning("cellGemConvertVideoStart(video_frame=*0x%x)", video_frame);
auto& gem = g_fxo->get<gem_config>();
@@ -554,18 +768,20 @@ error_code cellGemConvertVideoStart(vm::cptr<void> video_frame)
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
//if (!video_frame.aligned(128))
//{
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
//}
if (!video_frame.aligned(128))
{
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
}
if (std::exchange(gem.video_conversion_started, true))
if (gem.video_conversion_in_progress)
{
return CELL_GEM_ERROR_CONVERT_NOT_FINISHED;
}
// TODO: start image conversion of video_frame async to gem.vc_attribute.video_data_out
const auto& shared_data = g_fxo->get<gem_camera_shared>();
gem.video_data_in.resize(shared_data.size);
std::memcpy(gem.video_data_in.data(), video_frame.get_ptr(), gem.video_data_in.size());
gem.video_conversion_in_progress = true;
return CELL_OK;
}
@@ -753,11 +969,6 @@ error_code cellGemGetCameraState(vm::ptr<CellGemCameraState> camera_state)
auto& gem = g_fxo->get<gem_config>();
if (!gem.state)
{
return CELL_GEM_ERROR_UNINITIALIZED;
}
if (!camera_state)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
@@ -826,7 +1037,7 @@ error_code cellGemGetHuePixels(vm::cptr<void> camera_frame, u32 hue, vm::ptr<u8>
error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_image_state)
{
cellGem.todo("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state);
cellGem.warning("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, gem_image_state);
auto& gem = g_fxo->get<gem_config>();
@@ -879,7 +1090,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
return CELL_GEM_ERROR_UNINITIALIZED;
}
if (!check_gem_num(gem_num) || state_flag > CELL_GEM_INERTIAL_STATE_FLAG_NEXT || !inertial_state || !gem.is_controller_ready(gem_num))
if (!check_gem_num(gem_num) || !inertial_state || !gem.is_controller_ready(gem_num))
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
@@ -1303,12 +1514,17 @@ s32 cellGemIsTrackableHue(u32 hue)
auto& gem = g_fxo->get<gem_config>();
if (!gem.state || hue > 359)
if (!gem.state)
{
return false;
return CELL_GEM_ERROR_UNINITIALIZED;
}
return true;
if (hue > 359)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
return 1;
}
error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality)
@@ -1337,7 +1553,7 @@ error_code cellGemPrepareCamera(s32 max_exposure, f32 image_quality)
error_code cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_attribute)
{
cellGem.todo("cellGemPrepareVideoConvert(vc_attribute=*0x%x)", vc_attribute);
cellGem.warning("cellGemPrepareVideoConvert(vc_attribute=*0x%x)", vc_attribute);
auto& gem = g_fxo->get<gem_config>();
@@ -1351,29 +1567,36 @@ error_code cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
const auto vc = *vc_attribute;
const CellGemVideoConvertAttribute vc = *vc_attribute;
if (!vc_attribute || vc.version != CELL_GEM_VERSION)
if (vc.version != CELL_GEM_VERSION)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
if (vc.output_format != CELL_GEM_NO_VIDEO_OUTPUT)
{
if (!vc.video_data_out || ((vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME) && !vc.buffer_memory))
if (!vc.video_data_out)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
}
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
//if (!vc.video_data_out.aligned(16) || !vc.buffer_memory.aligned(128))
//{
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
//}
if ((vc.conversion_flags & CELL_GEM_COMBINE_PREVIOUS_INPUT_FRAME) && !vc.buffer_memory)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
if (!vc.video_data_out.aligned(128) || !vc.buffer_memory.aligned(16))
{
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
}
gem.vc_attribute = vc;
const s32 buffer_size = cellGemGetVideoConvertSize(vc.output_format);
gem.video_data_out_size = buffer_size;
return CELL_OK;
}
@@ -1434,7 +1657,7 @@ error_code cellGemReset(u32 gem_num)
error_code cellGemSetRumble(u32 gem_num, u8 rumble)
{
cellGem.todo("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
cellGem.warning("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
auto& gem = g_fxo->get<gem_config>();
@@ -1468,7 +1691,7 @@ error_code cellGemSetYaw(u32 gem_num, vm::ptr<f32> z_direction)
return CELL_GEM_ERROR_UNINITIALIZED;
}
if (!z_direction)
if (!z_direction || !check_gem_num(gem_num))
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
@@ -1571,7 +1794,7 @@ error_code cellGemUpdateFinish()
std::scoped_lock lock(gem.mtx);
if (!std::exchange(gem.update_started, false))
if (!gem.update_started.exchange(false))
{
return CELL_GEM_ERROR_UPDATE_NOT_STARTED;
}
@@ -1598,16 +1821,15 @@ error_code cellGemUpdateStart(vm::cptr<void> camera_frame, u64 timestamp)
std::scoped_lock lock(gem.mtx);
// Update is starting even when camera_frame is null
if (std::exchange(gem.update_started, true))
if (gem.update_started.exchange(true))
{
return CELL_GEM_ERROR_UPDATE_NOT_FINISHED;
}
// TODO: The alignment checks seem to break Time Crisis Razing Storm [BLUS30528]
//if (!camera_frame.aligned(128))
//{
// return CELL_GEM_ERROR_INVALID_ALIGNMENT;
//}
if (!camera_frame.aligned(128))
{
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
}
gem.camera_frame = camera_frame.addr();
if (!camera_frame)
+4 -4
View File
@@ -145,7 +145,7 @@ enum
};
// Video conversion output formats
enum
enum CellGemVideoConvertFormatEnum : s32
{
CELL_GEM_NO_VIDEO_OUTPUT = 1,
CELL_GEM_RGBA_640x480 = 2,
@@ -261,13 +261,13 @@ struct CellGemState
struct CellGemVideoConvertAttribute
{
be_t<s32> version;
be_t<s32> output_format;
be_t<CellGemVideoConvertFormatEnum> output_format;
be_t<s32> conversion_flags;
be_t<f32> gain;
be_t<f32> red_gain;
be_t<f32> green_gain;
be_t<f32> blue_gain;
vm::ptr<void> buffer_memory;
vm::ptr<void> video_data_out;
vm::bptr<u8> buffer_memory;
vm::bptr<u8> video_data_out;
u8 alpha;
};
+19 -2
View File
@@ -301,9 +301,22 @@ error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data)
data->mkey = current_data.mkey;
data->len = std::min<s32>(CELL_KB_MAX_KEYCODES, current_data.len);
for (s32 i = 0; i < current_data.len; i++)
if (current_data.len > 0)
{
data->keycode[i] = current_data.keycode[i].first;
for (s32 i = 0; i < current_data.len; i++)
{
data->keycode[i] = current_data.keycode[i].first;
}
KbConfig& current_config = handler.GetConfig(port_no);
// For single character mode to work properly we need to "flush" the buffer after reading or else we'll constantly get the same key presses with each call.
// Actual key repeats are handled by adding a new key code to the buffer periodically. Key releases are handled in a similar fashion.
// Warning: Don't do this in packet mode, which is basically the mouse and keyboard gaming mode. Otherwise games like Unreal Tournament will be unplayable.
if (current_config.read_mode == CELL_KB_RMODE_INPUTCHAR)
{
current_data.len = 0;
}
}
return CELL_OK;
@@ -386,6 +399,10 @@ error_code cellKbSetReadMode(u32 port_no, u32 rmode)
KbConfig& current_config = handler.GetConfig(port_no);
current_config.read_mode = rmode;
// Key repeat must be disabled in packet mode. But let's just always enable it otherwise.
Keyboard& keyboard = handler.GetKeyboards()[port_no];
keyboard.m_key_repeat = rmode != CELL_KB_RMODE_PACKET;
// can also return CELL_KB_ERROR_SYS_SETTING_FAILED
return CELL_OK;
+1 -1
View File
@@ -949,7 +949,7 @@ error_code cellMicReadRaw(s32 dev_num, vm::ptr<void> data, s32 max_bytes)
error_code cellMicRead(s32 dev_num, vm::ptr<void> data, u32 max_bytes)
{
cellMic.todo("cellMicRead(dev_num=%d, data=0x%x, maxBytes=0x%x)", dev_num, data, max_bytes);
cellMic.warning("cellMicRead(dev_num=%d, data=0x%x, maxBytes=0x%x)", dev_num, data, max_bytes);
auto& mic_thr = g_fxo->get<mic_thread>();
const std::lock_guard lock(mic_thr.mutex);
+1 -1
View File
@@ -618,5 +618,5 @@ void cellSysutil_MsgDialog_init()
REG_FUNC(cellSysutil, cellMsgDialogAbort);
// Helper Function
REG_FUNC(cellSysutil, exit_game).flag(MFF_HIDDEN);
REG_HIDDEN_FUNC(exit_game);
}
+391 -110
View File
@@ -33,6 +33,23 @@ void fmt_class_string<CellOskDialogError>::format(std::string& out, u64 arg)
});
}
template<>
void fmt_class_string<CellOskDialogContinuousMode>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto mode)
{
switch (mode)
{
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_REMAIN_OPEN);
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE);
STR_CASE(CELL_OSKDIALOG_CONTINUOUS_MODE_SHOW);
}
return unknown;
});
}
OskDialogBase::~OskDialogBase()
{
}
@@ -41,13 +58,68 @@ struct osk_info
{
std::shared_ptr<OskDialogBase> dlg;
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{};
shared_mutex text_mtx;
atomic_t<bool> use_separate_windows = false;
atomic_t<bool> lock_ext_input = false;
atomic_t<u32> device_mask = 0; // 0 means all devices can influence the OSK
atomic_t<u32> key_layout = CELL_OSKDIALOG_10KEY_PANEL;
atomic_t<CellOskDialogInitialKeyLayout> initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
atomic_t<CellOskDialogInputDevice> initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
atomic_t<bool> clipboard_enabled = false; // For copy and paste
atomic_t<bool> half_byte_kana_enabled = false;
atomic_t<u32> supported_languages = 0; // Used to enable non-default languages in the OSK
atomic_t<bool> dimmer_enabled = true;
atomic_t<f32> base_color_red = 1.0f;
atomic_t<f32> base_color_blue = 1.0f;
atomic_t<f32> base_color_green = 1.0f;
atomic_t<f32> base_color_alpha = 1.0f;
atomic_t<bool> pointer_enabled = false;
CellOskDialogPoint pointer_pos{0.0f, 0.0f};
atomic_t<f32> initial_scale = 1.0f;
atomic_t<u32> layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
atomic_t<CellOskDialogContinuousMode> osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
atomic_t<u32> last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED; // Used for continuous seperate window dialog
atomic_t<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
stx::init_mutex init;
void reset()
{
std::lock_guard lock(text_mtx);
dlg.reset();
valid_text = {};
use_separate_windows = false;
lock_ext_input = false;
device_mask = 0;
key_layout = CELL_OSKDIALOG_10KEY_PANEL;
initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
clipboard_enabled = false;
half_byte_kana_enabled = false;
supported_languages = 0;
dimmer_enabled = true;
base_color_red = 1.0f;
base_color_blue = 1.0f;
base_color_green = 1.0f;
base_color_alpha = 1.0f;
pointer_enabled = false;
pointer_pos = {0.0f, 0.0f};
initial_scale = 1.0f;
layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED;
osk_confirm_callback.store({});
}
};
// TODO: don't use this function
@@ -76,17 +148,15 @@ std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create = false)
return osk.dlg;
}
else
const auto init = osk.init.access();
if (!init)
{
const auto init = osk.init.access();
if (!init)
{
return nullptr;
}
return osk.dlg;
return nullptr;
}
return osk.dlg;
}
error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogParam, vm::ptr<CellOskDialogInputFieldInfo> inputFieldInfo)
@@ -101,7 +171,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
auto osk = _get_osk_dialog(true);
// Can't open another dialog if this one is already open.
if (!osk || osk->state.load() == OskDialogState::Open)
if (!osk || osk->state.load() != OskDialogState::Unloaded)
{
return CELL_SYSUTIL_ERROR_BUSY;
}
@@ -115,14 +185,19 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
// Get init text and prepare return value
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
std::memset(osk->osk_text, 0, sizeof(osk->osk_text));
std::memset(osk->osk_text_old, 0, sizeof(osk->osk_text_old));
// Also clear the info text just to be sure (it should be zeroed at this point anyway)
{
auto& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
info.valid_text = {};
}
if (inputFieldInfo->init_text)
{
for (u32 i = 0; (i < maxLength) && (inputFieldInfo->init_text[i] != 0); i++)
{
osk->osk_text[i] = inputFieldInfo->init_text[i];
osk->osk_text_old[i] = inputFieldInfo->init_text[i];
}
}
@@ -142,33 +217,23 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
osk->on_osk_close = [wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
{
cellOskDialog.error("on_osk_close(status=%d)", status);
const auto osk = wptr.lock();
osk->state = OskDialogState::Closed;
if (osk->state.atomic_op([&](OskDialogState& state)
auto& info = g_fxo->get<osk_info>();
const bool keep_seperate_window_open = info.use_separate_windows.load() && (info.osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
switch (status)
{
if (state == OskDialogState::Abort)
case CELL_OSKDIALOG_CLOSE_CONFIRM:
{
if (auto ccb = info.osk_confirm_callback.exchange({}))
{
return true;
}
state = OskDialogState::Close;
return false;
}))
{
input::SetIntercepted(false);
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
return;
}
const bool accepted = status == CELL_MSGDIALOG_BUTTON_OK;
if (accepted)
{
if (auto ccb = g_fxo->get<osk_info>().osk_confirm_callback.exchange({}))
{
vm::ptr<u16> string_to_send = vm::cast(vm::alloc(CELL_OSKDIALOG_STRING_SIZE * 2, vm::main));
std::vector<u16> string_to_send(CELL_OSKDIALOG_STRING_SIZE);
atomic_t<bool> done = false;
u32 return_value;
u32 i;
for (i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
@@ -177,14 +242,16 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
if (osk->osk_text[i] == 0) break;
}
sysutil_register_cb([&, length = i](ppu_thread& cb_ppu) -> s32
sysutil_register_cb([&, length = i, string_to_send = std::move(string_to_send)](ppu_thread& cb_ppu) -> s32
{
return_value = ccb(cb_ppu, string_to_send, static_cast<s32>(length));
vm::var<u16[], vm::page_allocator<>> string_var(CELL_OSKDIALOG_STRING_SIZE, string_to_send.data());
const u32 return_value = ccb(cb_ppu, string_var.begin(), static_cast<s32>(length));
cellOskDialog.warning("osk_confirm_callback return_value=%d", return_value);
for (u32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
osk->osk_text[i] = string_to_send[i];
osk->osk_text[i] = string_var.begin()[i];
}
done = true;
@@ -198,7 +265,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
}
}
if (g_fxo->get<osk_info>().use_separate_windows.load() && osk->osk_text[0] == 0)
if (info.use_separate_windows.load() && osk->osk_text[0] == 0)
{
cellOskDialog.warning("cellOskDialogLoadAsync: input result is CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT");
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
@@ -207,41 +274,74 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
{
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
}
break;
}
else
case CELL_OSKDIALOG_CLOSE_CANCEL:
{
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_CANCELED;
break;
}
case FAKE_CELL_OSKDIALOG_CLOSE_ABORT:
{
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT;
break;
}
default:
{
cellOskDialog.fatal("on_osk_close: Unknown status (%d)", status);
break;
}
}
// Send OSK status
if (g_fxo->get<osk_info>().use_separate_windows.load() && (g_fxo->get<osk_info>().osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE))
if (keep_seperate_window_open)
{
if (accepted)
switch (status)
{
case CELL_OSKDIALOG_CLOSE_CONFIRM:
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
break;
}
else
case CELL_OSKDIALOG_CLOSE_CANCEL:
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0);
break;
}
case FAKE_CELL_OSKDIALOG_CLOSE_ABORT:
{
// Handled in cellOskDialogAbort
break;
}
default:
{
cellOskDialog.fatal("on_osk_close: Unknown status (%d)", status);
break;
}
}
if (info.osk_continuous_mode.load() == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_HIDE);
}
}
else
else if (status != FAKE_CELL_OSKDIALOG_CLOSE_ABORT) // Handled in cellOskDialogAbort
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
}
input::SetIntercepted(false);
};
osk->on_osk_input_entered = [wptr = std::weak_ptr<OskDialogBase>(osk)]()
if (auto& info = g_fxo->get<osk_info>(); info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
const auto osk = wptr.lock();
if (g_fxo->get<osk_info>().use_separate_windows.load())
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
}
};
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
return CELL_OK;
}
input::SetIntercepted(true);
@@ -250,8 +350,14 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
osk->Create(get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE), message, osk->osk_text, maxLength, prohibitFlgs, allowOskPanelFlg, firstViewPanel);
result = true;
result.notify_one();
if (g_fxo->get<osk_info>().osk_continuous_mode.load() == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_SHOW);
}
});
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
while (!result && !Emu.IsStopped())
@@ -264,7 +370,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
error_code cellOskDialogLoadAsyncExt()
{
UNIMPLEMENTED_FUNC(cellOskDialog);
cellOskDialog.todo("cellOskDialogLoadAsyncExt()");
return CELL_OK;
}
@@ -282,13 +388,28 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
auto& info = g_fxo->get<osk_info>();
const bool keep_seperate_window_open = info.use_separate_windows.load() && (info.osk_continuous_mode.load() != CELL_OSKDIALOG_CONTINUOUS_MODE_NONE);
info.text_mtx.lock();
// Update text buffer if called from cellOskDialogUnloadAsync or if the user accepted the dialog during continuous seperate window mode.
if (is_unload || (keep_seperate_window_open && info.last_dialog_state == CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED))
{
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
info.valid_text[i] = osk->osk_text[i];
}
}
if (is_unload)
{
OutputInfo->result = osk->osk_input_result.load();
}
else
{
if (memcmp(osk->osk_text_old, osk->osk_text, sizeof(osk->osk_text)) == 0)
if (info.valid_text[0] == 0)
{
OutputInfo->result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
}
@@ -298,41 +419,54 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
}
}
bool do_copy = OutputInfo->pResultString && (OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK || (is_unload && OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT));
const bool do_copy = OutputInfo->pResultString && (OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK || (is_unload && OutputInfo->result == CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT));
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
for (s32 i = 0; do_copy && i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
osk->osk_text_old[i] = osk->osk_text[i];
if (do_copy)
if (i < OutputInfo->numCharsResultString)
{
if (i < OutputInfo->numCharsResultString)
OutputInfo->pResultString[i] = info.valid_text[i];
if (info.valid_text[i] == 0)
{
if (osk->osk_text[i] == 0)
{
do_copy = false;
}
OutputInfo->pResultString[i] = osk->osk_text[i];
}
else
{
OutputInfo->pResultString[i] = 0;
do_copy = false;
break;
}
}
else
{
OutputInfo->pResultString[i] = 0;
break;
}
}
info.text_mtx.unlock();
if (is_unload)
{
// Unload should be called last, so remove the dialog here
if (const auto reset_lock = g_fxo->get<osk_info>().init.reset())
if (const auto reset_lock = info.init.reset())
{
// TODO
g_fxo->get<osk_info>().dlg.reset();
info.reset();
}
osk->state = OskDialogState::Unloaded;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_UNLOADED, 0);
}
else if (keep_seperate_window_open)
{
// Clear text buffer unless the dialog is still open during continuous seperate window mode.
switch (info.last_dialog_state)
{
case CELL_SYSUTIL_OSKDIALOG_FINISHED:
case CELL_SYSUTIL_OSKDIALOG_UNLOADED:
case CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED:
case CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED:
info.valid_text = {};
break;
default:
break;
}
}
return CELL_OK;
}
@@ -379,18 +513,12 @@ error_code cellOskDialogAbort()
const s32 result = osk->state.atomic_op([](OskDialogState& state)
{
// Check for open dialog. In this case the dialog is only "Open" if it was not aborted before.
if (state == OskDialogState::Abort)
// Check for open dialog. In this case the dialog is "Open" if it was not unloaded before.
if (state == OskDialogState::Unloaded)
{
return static_cast<s32>(CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED);
}
// If the dialog has the Open state then it is in use. Only dialogs with the Close state can be aborted.
if (state == OskDialogState::Open)
{
return static_cast<s32>(CELL_SYSUTIL_ERROR_BUSY);
}
state = OskDialogState::Abort;
return static_cast<s32>(CELL_OK);
@@ -402,7 +530,10 @@ error_code cellOskDialogAbort()
}
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_ABORT;
osk->Close(false);
osk->Close(FAKE_CELL_OSKDIALOG_CLOSE_ABORT);
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
return CELL_OK;
}
@@ -410,6 +541,13 @@ error_code cellOskDialogAbort()
error_code cellOskDialogSetDeviceMask(u32 deviceMask)
{
cellOskDialog.todo("cellOskDialogSetDeviceMask(deviceMask=0x%x)", deviceMask);
// TODO: error checks. It probably checks for use_separate_windows
g_fxo->get<osk_info>().device_mask = deviceMask;
// TODO: change osk device input
return CELL_OK;
}
@@ -422,30 +560,52 @@ error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWin
return CELL_OSKDIALOG_ERROR_PARAM;
}
if (auto& osk = g_fxo->get<osk_info>(); true)
{
osk.use_separate_windows = true;
osk.osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
}
auto& osk = g_fxo->get<osk_info>();
osk.use_separate_windows = true;
osk.osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
// TODO: handle rest of windowOption
cellOskDialog.warning("cellOskDialogSetSeparateWindowOption: continuousMode=%s)", osk.osk_continuous_mode.load());
return CELL_OK;
}
error_code cellOskDialogSetInitialInputDevice(vm::ptr<CellOskDialogInputDevice> inputDevice)
error_code cellOskDialogSetInitialInputDevice(u32 inputDevice)
{
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=*0x%x)", inputDevice);
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=%d)", inputDevice);
// TODO: error checks
g_fxo->get<osk_info>().initial_input_device = static_cast<CellOskDialogInputDevice>(inputDevice);
// TODO: use value
return CELL_OK;
}
error_code cellOskDialogSetInitialKeyLayout(vm::ptr<CellOskDialogInitialKeyLayout> initialKeyLayout)
error_code cellOskDialogSetInitialKeyLayout(u32 initialKeyLayout)
{
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=*0x%x)", initialKeyLayout);
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=%d)", initialKeyLayout);
// TODO: error checks
g_fxo->get<osk_info>().initial_key_layout = static_cast<CellOskDialogInitialKeyLayout>(initialKeyLayout);
// TODO: use value
return CELL_OK;
}
error_code cellOskDialogDisableDimmer()
{
cellOskDialog.todo("cellOskDialogDisableDimmer()");
// TODO: error checks
g_fxo->get<osk_info>().dimmer_enabled = false;
// TODO: use value
return CELL_OK;
}
@@ -458,18 +618,47 @@ error_code cellOskDialogSetKeyLayoutOption(u32 option)
return CELL_OSKDIALOG_ERROR_PARAM;
}
g_fxo->get<osk_info>().key_layout = option;
// TODO: use value
return CELL_OK;
}
error_code cellOskDialogAddSupportLanguage(u32 supportLanguage)
{
cellOskDialog.todo("cellOskDialogAddSupportLanguage(supportLanguage=0x%x)", supportLanguage);
// TODO: error checks
g_fxo->get<osk_info>().supported_languages = supportLanguage;
// TODO: disable extra languages unless they were enabled here
// Extra languages are:
// CELL_OSKDIALOG_PANELMODE_POLISH
// CELL_OSKDIALOG_PANELMODE_KOREAN
// CELL_OSKDIALOG_PANELMODE_TURKEY
// CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE
// CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE
// CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL
// CELL_OSKDIALOG_PANELMODE_DANISH
// CELL_OSKDIALOG_PANELMODE_SWEDISH
// CELL_OSKDIALOG_PANELMODE_NORWEGIAN
// CELL_OSKDIALOG_PANELMODE_FINNISH
return CELL_OK;
}
error_code cellOskDialogSetLayoutMode(s32 layoutMode)
{
cellOskDialog.todo("cellOskDialogSetLayoutMode(layoutMode=%d)", layoutMode);
// TODO: error checks
g_fxo->get<osk_info>().layout_mode = layoutMode;
// TODO: use layout mode
return CELL_OK;
}
@@ -479,12 +668,6 @@ error_code cellOskDialogGetInputText(vm::ptr<CellOskDialogCallbackReturnParam> O
return getText(OutputInfo, false);
}
error_code cellOskDialogExtInputDeviceUnlock()
{
cellOskDialog.todo("cellOskDialogExtInputDeviceUnlock()");
return CELL_OK;
}
error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
{
cellOskDialog.todo("register_keyboard_event_hook_callback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
@@ -494,6 +677,8 @@ error_code register_keyboard_event_hook_callback(u16 hookEventMode, vm::ptr<cell
return CELL_OSKDIALOG_ERROR_PARAM;
}
// TODO: register callback and and use it
return CELL_OK;
}
@@ -530,6 +715,13 @@ error_code cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
error_code cellOskDialogExtEnableClipboard()
{
cellOskDialog.todo("cellOskDialogExtEnableClipboard()");
// TODO: error checks
g_fxo->get<osk_info>().clipboard_enabled = true;
// TODO: implement copy paste
return CELL_OK;
}
@@ -539,13 +731,13 @@ error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ f
const auto osk = _get_osk_dialog();
// Check for open dialog.
if (!osk || osk->state.load() != OskDialogState::Open)
// Check for "Open" dialog.
if (!osk || osk->state.load() == OskDialogState::Unloaded)
{
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
}
osk->Close(finishReason == CELL_OSKDIALOG_CLOSE_CONFIRM);
osk->Close(finishReason);
return CELL_OK;
}
@@ -559,18 +751,56 @@ error_code cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictiona
error_code cellOskDialogExtSetInitialScale(f32 initialScale)
{
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
// TODO: error checks (CELL_OSKDIALOG_SCALE_MIN, CELL_OSKDIALOG_SCALE_MAX)
g_fxo->get<osk_info>().initial_scale = initialScale;
// TODO: implement overlay scaling
return CELL_OK;
}
error_code cellOskDialogExtInputDeviceLock()
{
cellOskDialog.todo("cellOskDialogExtInputDeviceLock()");
// TODO: error checks
g_fxo->get<osk_info>().lock_ext_input = true;
// TODO: change osk device input
return CELL_OK;
}
error_code cellOskDialogExtInputDeviceUnlock()
{
cellOskDialog.todo("cellOskDialogExtInputDeviceUnlock()");
// TODO: error checks
g_fxo->get<osk_info>().lock_ext_input = false;
// TODO: change osk device input
return CELL_OK;
}
error_code cellOskDialogExtSetBaseColor(f32 red, f32 blue, f32 green, f32 alpha)
{
cellOskDialog.todo("cellOskDialogExtSetBaseColor(red=%f, blue=%f, green=%f, alpha=%f)", red, blue, green, alpha);
// TODO: error checks
auto& osk = g_fxo->get<osk_info>();
osk.base_color_red = red;
osk.base_color_blue = blue;
osk.base_color_green = green;
osk.base_color_alpha = alpha;
// TODO: use osk base color
return CELL_OK;
}
@@ -583,10 +813,7 @@ error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDial
return CELL_OSKDIALOG_ERROR_PARAM;
}
if (auto& osk = g_fxo->get<osk_info>(); true)
{
osk.osk_confirm_callback = pCallback;
}
g_fxo->get<osk_info>().osk_confirm_callback = pCallback;
return CELL_OK;
}
@@ -594,30 +821,82 @@ error_code cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDial
error_code cellOskDialogExtUpdateInputText()
{
cellOskDialog.todo("cellOskDialogExtUpdateInputText()");
return CELL_OK;
}
error_code cellOskDialogExtDisableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
// Usually, user input is only available when the dialog was accepted.
// This function seems to be called in order to copy the current text to an internal buffer.
// Afterwards, cellOskDialogGetInputText can be called to fetch the current text regardless of
// user confirmation, even if the dialog is still in use.
// TODO: error checks
const auto osk = _get_osk_dialog();
if (osk)
{
auto& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
for (s32 i = 0; i < CELL_OSKDIALOG_STRING_SIZE - 1; i++)
{
info.valid_text[i] = osk->osk_text[i];
}
}
return CELL_OK;
}
error_code cellOskDialogExtSetPointerEnable(b8 enable)
{
cellOskDialog.todo("cellOskDialogExtSetPointerEnable(enable=%d)", enable);
// TODO: error checks
g_fxo->get<osk_info>().pointer_enabled = enable;
// TODO: use new value in osk
return CELL_OK;
}
error_code cellOskDialogExtUpdatePointerDisplayPos(/*const CellOskDialogPoint pos*/)
error_code cellOskDialogExtUpdatePointerDisplayPos(vm::cptr<CellOskDialogPoint> pos)
{
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(posX=%f, posY=%f)"/*, pos.x, pos.y*/);
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(pos=0x%x, posX=%f, posY=%f)", pos->x, pos->y);
// TODO: error checks
if (pos)
{
g_fxo->get<osk_info>().pointer_pos = *pos;
}
// TODO: use new value in osk
return CELL_OK;
}
error_code cellOskDialogExtEnableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtEnableHalfByteKana()");
// TODO: error checks
g_fxo->get<osk_info>().half_byte_kana_enabled = true;
// TODO: use new value in osk
return CELL_OK;
}
error_code cellOskDialogExtDisableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
// TODO: error checks
g_fxo->get<osk_info>().half_byte_kana_enabled = false;
// TODO: use new value in osk
return CELL_OK;
}
@@ -630,6 +909,8 @@ error_code cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForc
return CELL_OSKDIALOG_ERROR_PARAM;
}
// TODO: register and use force finish callback (PS button during continuous mode)
return CELL_OK;
}
+13 -4
View File
@@ -83,6 +83,11 @@ enum CellOskDialogFinishReason
CELL_OSKDIALOG_CLOSE_CANCEL = 1,
};
enum CellOskDialogFinishReasonFake // Helper. Must be negative values.
{
FAKE_CELL_OSKDIALOG_CLOSE_ABORT = -1,
};
enum CellOskDialogType
{
CELL_OSKDIALOG_TYPE_SINGLELINE_OSK = 0,
@@ -239,24 +244,28 @@ using cellOskDialogForceFinishCallback = class b8();
enum class OskDialogState
{
Unloaded,
Open,
Abort,
Close,
Closed
};
class OskDialogBase
{
public:
virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 prohibit_flags, u32 panel_flag, u32 first_view_panel) = 0;
virtual void Close(bool accepted) = 0;
// Closes the dialog.
// Set status to CELL_OSKDIALOG_CLOSE_CONFIRM or CELL_OSKDIALOG_CLOSE_CANCEL for user input.
// Set status to -1 if closed by the game or system.
virtual void Close(s32 status) = 0;
virtual ~OskDialogBase();
std::function<void(s32 status)> on_osk_close;
std::function<void()> on_osk_input_entered;
atomic_t<OskDialogState> state{ OskDialogState::Close };
atomic_t<OskDialogState> state{ OskDialogState::Unloaded };
atomic_t<CellOskDialogInputFieldResult> osk_input_result{ CellOskDialogInputFieldResult::CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK };
char16_t osk_text[CELL_OSKDIALOG_STRING_SIZE]{};
char16_t osk_text_old[CELL_OSKDIALOG_STRING_SIZE]{};
};
+332 -214
View File
@@ -113,6 +113,81 @@ void parse_metadata(D& dst, const utils::media_info& mi, const std::string& key,
strcpy_trunc(dst, value);
};
error_code check_search_state(search_state state, search_state action)
{
switch (action)
{
case search_state::initializing:
switch (state)
{
case search_state::not_initialized:
break;
case search_state::initializing:
return CELL_SEARCH_ERROR_BUSY;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
default:
return CELL_SEARCH_ERROR_ALREADY_INITIALIZED;
}
break;
case search_state::finalizing:
switch (state)
{
case search_state::idle:
break;
case search_state::not_initialized:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::initializing:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
}
break;
case search_state::canceling:
switch (state)
{
case search_state::in_progress:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
case search_state::idle:
return CELL_SEARCH_ERROR_ALREADY_GOT_RESULT;
default:
return CELL_SEARCH_ERROR_GENERIC;
}
break;
case search_state::in_progress:
default:
switch (state)
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
}
break;
}
return CELL_OK;
}
void populate_music_info(CellSearchMusicInfo& info, const utils::media_info& mi, const fs::dir_entry& item)
{
parse_metadata(info.artistName, mi, "artist", "Unknown Artist", CELL_SEARCH_TITLE_LEN_MAX);
@@ -296,16 +371,9 @@ error_code cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptr<Cell
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::not_initialized, search_state::initializing))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::not_initialized, search_state::initializing), search_state::initializing))
{
case search_state::not_initialized:
break;
case search_state::initializing:
return CELL_SEARCH_ERROR_BUSY;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
default:
return CELL_SEARCH_ERROR_ALREADY_INITIALIZED;
return error;
}
search.func = func;
@@ -327,20 +395,9 @@ error_code cellSearchFinalize()
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::finalizing))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::finalizing), search_state::finalizing))
{
case search_state::idle:
break;
case search_state::not_initialized:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::initializing:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
sysutil_register_cb([&search](ppu_thread& ppu) -> s32
@@ -396,20 +453,9 @@ error_code cellSearchStartListSearch(CellSearchListSearchType type, CellSearchSo
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const u32 id = *outSearchId = idm::make<search_object_t>();
@@ -465,12 +511,7 @@ error_code cellSearchStartListSearch(CellSearchListSearchType type, CellSearchSo
{
item.name = vfs::unescape(item.name);
if (item.name == "." || item.name == "..")
{
continue;
}
if (!item.is_directory)
if (item.name == "." || item.name == ".." || !item.is_directory)
{
continue;
}
@@ -623,20 +664,9 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
@@ -860,20 +890,9 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const u32 id = *outSearchId = idm::make<search_object_t>();
@@ -1033,20 +1052,23 @@ error_code cellSearchStartSceneSearchInVideo(vm::cptr<CellSearchContentId> video
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(videoId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
{
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
const u32 id = *outSearchId = idm::make<search_object_t>();
@@ -1087,22 +1109,27 @@ error_code cellSearchStartSceneSearch(CellSearchSceneSearchType searchType, vm::
return CELL_SEARCH_ERROR_PARAM;
}
if (tagNum) // TODO: find out if this is the correct location for these checks
{
if (tagNum > CELL_SEARCH_TAG_NUM_MAX || !tags)
{
return CELL_SEARCH_ERROR_TAG;
}
for (u32 n = 0; n < tagNum; n++)
{
if (!tags[tagNum] || !memchr(&tags[tagNum], '\0', CELL_SEARCH_TAG_LEN_MAX))
{
return CELL_SEARCH_ERROR_TAG;
}
}
}
auto& search = g_fxo->get<search_info>();
switch (search.state.compare_and_swap(search_state::idle, search_state::in_progress))
if (error_code error = check_search_state(search.state.compare_and_swap(search_state::idle, search_state::in_progress), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const u32 id = *outSearchId = idm::make<search_object_t>();
@@ -1137,48 +1164,38 @@ error_code cellSearchGetContentInfoByOffset(CellSearchId searchId, s32 offset, v
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
if (offset >= 0 && offset + 0u < searchObject->content_ids.size())
{
const auto& content_id = searchObject->content_ids[offset];
const auto& content_info = content_id.second;
switch (content_info->type)
{
case CELL_SEARCH_CONTENTTYPE_MUSIC:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
break;
case CELL_SEARCH_CONTENTTYPE_PHOTO:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
break;
case CELL_SEARCH_CONTENTTYPE_VIDEO:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
break;
case CELL_SEARCH_CONTENTTYPE_MUSICLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
break;
case CELL_SEARCH_CONTENTTYPE_PHOTOLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
break;
case CELL_SEARCH_CONTENTTYPE_VIDEOLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
break;
case CELL_SEARCH_CONTENTTYPE_SCENE:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
break;
default:
return CELL_SEARCH_ERROR_GENERIC;
@@ -1205,20 +1222,9 @@ error_code cellSearchGetContentInfoByContentId(vm::cptr<CellSearchContentId> con
return CELL_SEARCH_ERROR_PARAM;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
@@ -1229,25 +1235,25 @@ error_code cellSearchGetContentInfoByContentId(vm::cptr<CellSearchContentId> con
switch (content_info->type)
{
case CELL_SEARCH_CONTENTTYPE_MUSIC:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music, sizeof(content_info->data.music));
break;
case CELL_SEARCH_CONTENTTYPE_PHOTO:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo, sizeof(content_info->data.photo));
break;
case CELL_SEARCH_CONTENTTYPE_VIDEO:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video, sizeof(content_info->data.photo));
break;
case CELL_SEARCH_CONTENTTYPE_MUSICLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.music_list, sizeof(content_info->data.music_list));
break;
case CELL_SEARCH_CONTENTTYPE_PHOTOLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.photo_list, sizeof(content_info->data.photo_list));
break;
case CELL_SEARCH_CONTENTTYPE_VIDEOLIST:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.video_list, sizeof(content_info->data.video_list));
break;
case CELL_SEARCH_CONTENTTYPE_SCENE:
std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
if (infoBuffer) std::memcpy(infoBuffer.get_ptr(), &content_info->data.scene, sizeof(content_info->data.scene));
break;
default:
return CELL_SEARCH_ERROR_GENERIC;
@@ -1272,20 +1278,9 @@ error_code cellSearchGetOffsetByContentId(CellSearchId searchId, vm::cptr<CellSe
return CELL_SEARCH_ERROR_PARAM;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const auto searchObject = idm::get<search_object_t>(searchId);
@@ -1326,20 +1321,9 @@ error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm:
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
if (offset >= 0 && offset + 0u < searchObject->content_ids.size())
@@ -1367,11 +1351,38 @@ error_code cellSearchGetContentInfoGameComment(vm::cptr<CellSearchContentId> con
{
cellSearch.todo("cellSearchGetContentInfoGameComment(contentId=*0x%x, gameComment=*0x%x)", contentId, gameComment);
if (!gameComment)
if (!contentId || !gameComment)
{
return CELL_SEARCH_ERROR_PARAM;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
switch (content_info->type)
{
case CELL_SEARCH_CONTENTTYPE_MUSIC:
case CELL_SEARCH_CONTENTTYPE_PHOTO:
case CELL_SEARCH_CONTENTTYPE_VIDEO:
break;
default:
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
// TODO: retrieve gameComment
return CELL_OK;
}
@@ -1391,6 +1402,63 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
if (searchObject->content_ids.empty())
{
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
// Use the first track in order to get info about this search
const auto& first_content_id = searchObject->content_ids[0];
const auto& first_content = first_content_id.second;
ensure(first_content);
if (contentId)
{
// Try to find the specified content
const u64 content_hash = *reinterpret_cast<const u64*>(contentId->data);
auto content = std::find_if(searchObject->content_ids.begin(), searchObject->content_ids.end(), [&content_hash](const content_id_type& cid){ return cid.first == content_hash; });
if (content != searchObject->content_ids.cend() && content->second)
{
// Check if the type of the found content is correct
if (content->second->type != first_content->type)
{
return CELL_SEARCH_ERROR_NOT_SUPPORTED_CONTEXT;
}
// TODO: Use the found content
// first track/playlist of context = content->second;
}
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
{
// Abort if we can't find the playlist.
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
else
{
// TODO: Select the first track by default
// first track of context = first_content;
}
}
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
{
// Abort if we don't have the necessary info to select a playlist.
return CELL_SEARCH_ERROR_NOT_SUPPORTED_CONTEXT;
}
else
{
// TODO: Select the first track by default
// first track of context = first_content;
}
// TODO: Use repeatMode and option in our context, depending on the type (list vs single)
// TODO: assign outContext
return CELL_OK;
}
@@ -1403,6 +1471,28 @@ error_code cellSearchGetMusicSelectionContextOfSingleTrack(vm::cptr<CellSearchCo
return CELL_SEARCH_ERROR_PARAM;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
if (content_info->type != CELL_SEARCH_CONTENTTYPE_MUSIC)
{
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
// TODO: assign outContext
return CELL_OK;
}
@@ -1415,20 +1505,9 @@ error_code cellSearchGetContentInfoPath(vm::cptr<CellSearchContentId> contentId,
return CELL_SEARCH_ERROR_PARAM;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const u64 id = *reinterpret_cast<const u64*>(contentId->data);
@@ -1453,11 +1532,34 @@ error_code cellSearchGetContentInfoPathMovieThumb(vm::cptr<CellSearchContentId>
{
cellSearch.todo("cellSearchGetContentInfoPathMovieThumb(contentId=*0x%x, infoMt=*0x%x)", contentId, infoMt);
if (!infoMt)
if (!contentId || !infoMt)
{
return CELL_SEARCH_ERROR_PARAM;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
{
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
strcpy_trunc(infoMt->movieThumbnailPath, content_info->infoPath.thumbnailPath);
// TODO: set infoMt->movieThumbnailOption
return CELL_OK;
}
@@ -1471,20 +1573,10 @@ error_code cellSearchPrepareFile(vm::cptr<char> path)
}
auto& search = g_fxo->get<search_info>();
switch (search.state.load())
if (error_code error = check_search_state(search.state.load(), search_state::in_progress))
{
case search_state::idle:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
reader_lock lock(search.links_mutex);
@@ -1506,6 +1598,32 @@ error_code cellSearchGetContentInfoDeveloperData(vm::cptr<CellSearchContentId> c
return CELL_SEARCH_ERROR_PARAM;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
switch (content_info->type)
{
case CELL_SEARCH_CONTENTTYPE_VIDEO:
case CELL_SEARCH_CONTENTTYPE_SCENE:
break;
default:
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
// TODO: retrieve developerData
return CELL_OK;
}
@@ -1518,6 +1636,29 @@ error_code cellSearchGetContentInfoSharable(vm::cptr<CellSearchContentId> conten
return CELL_SEARCH_ERROR_PARAM;
}
// TODO: find out if this check is correct
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::in_progress))
{
return error;
}
auto& content_map = g_fxo->get<content_id_map>();
auto found = content_map.map.find(*reinterpret_cast<const u64*>(contentId->data));
if (found == content_map.map.end())
{
// content ID not found, perform a search first
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
}
const auto& content_info = found->second;
if (content_info->type != CELL_SEARCH_CONTENTTYPE_VIDEO)
{
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
}
// TODO: retrieve sharable
*sharable = CELL_SEARCH_SHARABLETYPE_PROHIBITED;
return CELL_OK;
}
@@ -1532,21 +1673,9 @@ error_code cellSearchCancel(CellSearchId searchId)
return CELL_SEARCH_ERROR_INVALID_SEARCHID;
}
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::canceling))
{
case search_state::in_progress:
break;
case search_state::not_initialized:
case search_state::initializing:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
case search_state::idle:
return CELL_SEARCH_ERROR_ALREADY_GOT_RESULT;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
// TODO
@@ -1558,20 +1687,9 @@ error_code cellSearchEnd(CellSearchId searchId)
{
cellSearch.todo("cellSearchEnd(searchId=0x%x)", searchId);
switch (g_fxo->get<search_info>().state.load())
if (error_code error = check_search_state(g_fxo->get<search_info>().state.load(), search_state::finalizing))
{
case search_state::idle:
break;
case search_state::not_initialized:
return CELL_SEARCH_ERROR_NOT_INITIALIZED;
case search_state::finalizing:
return CELL_SEARCH_ERROR_FINALIZING;
case search_state::in_progress:
case search_state::initializing:
case search_state::canceling:
return CELL_SEARCH_ERROR_BUSY;
default:
return CELL_SEARCH_ERROR_GENERIC;
return error;
}
const auto searchObject = idm::get<search_object_t>(searchId);
@@ -110,6 +110,15 @@ error_code cellSysutilAvc2StartVoiceDetection()
error_code cellSysutilAvc2UnloadAsync()
{
cellSysutilAvc2.todo("cellSysutilAvc2UnloadAsync()");
if (avc2_cb)
{
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32 {
avc2_cb(cb_ppu, CELL_AVC2_EVENT_UNLOAD_SUCCEEDED, 0, avc2_cb_arg);
return 0;
});
}
return CELL_OK;
}
+1 -16
View File
@@ -136,21 +136,6 @@ struct vdec_context final
, cb_func(func)
, cb_arg(arg)
{
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
// TODO: This function should be removed at some point, since ffmpeg does it automatically now.
// We'll keep it for compatibility for now until more system ffmpeg libs are up to date.
avcodec_register_all();
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
switch (type)
{
case CELL_VDEC_CODEC_TYPE_MPEG2:
@@ -1265,5 +1250,5 @@ DECLARE(ppu_module_manager::cellVdec)("libvdec", []()
REG_FUNC(libvdec, cellVdecSetFrameRateExt); // 0xcffc42a5
REG_FUNC(libvdec, cellVdecSetPts); // 0x3ce2e4f8
REG_FUNC(libvdec, vdecEntry).flag(MFF_HIDDEN);
REG_HIDDEN_FUNC(vdecEntry);
});
+460 -81
View File
@@ -1,8 +1,10 @@
#include "stdafx.h"
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/system_utils.hpp"
#include "Emu/VFS.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Utilities/StrUtil.h"
#include "sysPrxForUser.h"
#include "Emu/IdManager.h"
@@ -396,6 +398,11 @@ void fmt_class_string<SceNpError>::format(std::string& out, u64 arg)
});
}
void message_data::print() const
{
sceNp.notice("commId: %s, msgId: %d, mainType: %d, subType: %d, subject: %s, body: %s, data_size: %d", static_cast<const char *>(commId.data), msgId, mainType, subType, subject, body, data.size());
}
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
{
sceNp.warning("sceNpInit(poolsize=0x%x, poolptr=*0x%x)", poolsize, poolptr);
@@ -409,18 +416,14 @@ error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr)
return SCE_NP_ERROR_ALREADY_INITIALIZED;
}
if (poolsize == 0)
if (poolsize == 0 || !poolptr)
{
return SCE_NP_ERROR_INVALID_ARGUMENT;
}
else if (poolsize < SCE_NP_MIN_POOLSIZE)
{
return SCE_NP_ERROR_INSUFFICIENT_BUFFER;
}
if (!poolptr)
if (poolsize < SCE_NP_MIN_POOLSIZE)
{
return SCE_NP_ERROR_INVALID_ARGUMENT;
return SCE_NP_ERROR_INSUFFICIENT_BUFFER;
}
nph.init_NP(poolsize, poolptr);
@@ -450,6 +453,11 @@ error_code sceNpTerm()
error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_path)
{
if (!drm_path)
{
return SCE_NP_DRM_ERROR_INVALID_PARAM;
}
u128 k_licensee{};
if (k_licensee_addr)
@@ -458,7 +466,7 @@ error_code npDrmIsAvailable(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm_pat
sceNp.notice("npDrmIsAvailable(): KLicense key %s", std::bit_cast<be_t<u128>>(k_licensee));
}
if (Emu.GetCat() == "PE")
if (Emu.GetFakeCat() == "PE")
{
std::memcpy(&k_licensee, NP_PSP_KEY_2, sizeof(k_licensee));
sceNp.success("npDrmIsAvailable(): PSP remaster KLicense key applied.");
@@ -543,52 +551,34 @@ error_code sceNpDrmIsAvailable2(vm::cptr<u8> k_licensee_addr, vm::cptr<char> drm
return npDrmIsAvailable(k_licensee_addr, drm_path);
}
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
error_code npDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
{
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
if (!content_id)
{
return SCE_NP_DRM_ERROR_INVALID_PARAM;
}
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
sceNp.warning("npDrmVerifyUpgradeLicense(): content_id='%s'", content_id);
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense(): content_id=“%s”", content_id);
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
{
// Game hasn't been purchased therefore no RAP file present
if (!rpcs3::utils::verify_c00_unlock_edat(content_str))
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
}
// Game has been purchased and there's a RAP file present
return CELL_OK;
}
error_code sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
{
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=*0x%x)", content_id);
return npDrmVerifyUpgradeLicense(content_id);
}
error_code sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
{
sceNp.warning("sceNpDrmVerifyUpgradeLicense2(content_id=*0x%x)", content_id);
if (!content_id)
{
return SCE_NP_DRM_ERROR_INVALID_PARAM;
}
const std::string content_str(content_id.get_ptr(), std::find(content_id.get_ptr(), content_id.get_ptr() + 0x2f, '\0'));
const std::string rap_file = rpcs3::utils::get_rap_file_path(content_str);
sceNp.warning(u8"sceNpDrmVerifyUpgradeLicense2(): content_id=“%s”", content_id);
if (fs::stat_t s{}; !fs::stat(rap_file, s) || s.is_directory || s.size < 0x10)
{
// Game hasn't been purchased therefore no RAP file present
return SCE_NP_DRM_ERROR_LICENSE_NOT_FOUND;
}
// Game has been purchased and there's a RAP file present
return CELL_OK;
return npDrmVerifyUpgradeLicense(content_id);
}
error_code sceNpDrmExecuteGamePurchase()
@@ -647,7 +637,7 @@ error_code sceNpDrmProcessExitSpawn2(ppu_thread& ppu, vm::cptr<u8> klicensee, vm
error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm::ptr<SceNpBasicEventHandler> handler, vm::ptr<void> arg)
{
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x, handler=*0x%x, arg=*0x%x)", context, handler, arg);
sceNp.warning("sceNpBasicRegisterHandler(context=*0x%x(%s), handler=*0x%x, arg=*0x%x)", context, context ? static_cast<const char *>(context->data) : "", handler, arg);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@@ -656,13 +646,21 @@ error_code sceNpBasicRegisterHandler(vm::cptr<SceNpCommunicationId> context, vm:
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
nph.basic_handler = handler;
nph.basic_handler_arg = arg;
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
nph.basic_handler.handler_func = handler;
nph.basic_handler.handler_arg = arg;
nph.basic_handler.registered = true;
nph.basic_handler.context_sensitive = false;
return CELL_OK;
}
@@ -678,11 +676,22 @@ error_code sceNpBasicRegisterContextSensitiveHandler(vm::cptr<SceNpCommunication
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (!context || !handler)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
memcpy(&nph.basic_handler.context, context.get_ptr(), sizeof(nph.basic_handler.context));
nph.basic_handler.handler_func = handler;
nph.basic_handler.handler_arg = arg;
nph.basic_handler.registered = true;
nph.basic_handler.context_sensitive = true;
return CELL_OK;
}
@@ -697,10 +706,17 @@ error_code sceNpBasicUnregisterHandler()
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
nph.basic_handler.registered = false;
return CELL_OK;
}
error_code sceNpBasicSetPresence(vm::cptr<void> data, u64 size)
error_code sceNpBasicSetPresence(vm::cptr<u8> data, u64 size)
{
sceNp.todo("sceNpBasicSetPresence(data=*0x%x, size=%d)", data, size);
@@ -711,6 +727,17 @@ error_code sceNpBasicSetPresence(vm::cptr<void> data, u64 size)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
// TODO: Correct but causes issues atm(breaks bomberman ultra)
// if (!data || !data[0])
// {
// return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
// }
if (size > SCE_NP_BASIC_MAX_PRESENCE_SIZE)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
@@ -730,6 +757,11 @@ error_code sceNpBasicSetPresenceDetails(vm::cptr<SceNpBasicPresenceDetails> pres
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!pres || options > SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -754,6 +786,11 @@ error_code sceNpBasicSetPresenceDetails2(vm::cptr<SceNpBasicPresenceDetails2> pr
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!pres || options > SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -778,6 +815,11 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u64
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!to || to->handle.data[0] == '\0' || !data || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -793,7 +835,18 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u64
error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_memory_container_t containerId)
{
sceNp.todo("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
sceNp.warning("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
if (msg)
{
sceNp.notice("sceNpBasicSendMessageGui: msgId: %d, mainType: %d, subType: %d, msgFeatures: %d, count: %d", msg->msgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count);
for (u32 i = 0; i < msg->count; i++)
{
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast<char*>(&msg->npids[i].handle.data[0]));
}
sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject);
sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body);
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
@@ -802,11 +855,19 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || msg->npids.handle.data[0] == '\0' || !(msg->msgFeatures & SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES))
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!msg || msg->count > SCE_NP_BASIC_SEND_MESSAGE_MAX_RECIPIENTS || (msg->msgFeatures & ~SCE_NP_BASIC_MESSAGE_FEATURES_ALL_FEATURES) ||
msg->mainType > SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT || msg->msgId != 0ull)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
if (msg->size > SCE_NP_BASIC_MAX_MESSAGE_SIZE)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
@@ -817,6 +878,82 @@ error_code sceNpBasicSendMessageGui(vm::cptr<SceNpBasicMessageDetails> msg, sys_
return not_an_error(SCE_NP_BASIC_ERROR_NOT_CONNECTED);
}
// Prepare message data
message_data msg_data = {
.commId = nph.basic_handler.context,
.msgId = msg->msgId,
.mainType = msg->mainType,
.subType = msg->subType,
.msgFeatures = msg->msgFeatures};
std::set<std::string> npids;
for (u32 i = 0; i < msg->count; i++)
{
npids.insert(std::string(msg->npids[i].handle.data));
}
if (msg->subject)
{
msg_data.subject = std::string(msg->subject.get_ptr());
}
if (msg->body)
{
msg_data.body = std::string(msg->body.get_ptr());
}
if (msg->size)
{
msg_data.data.assign(msg->data.get_ptr(), msg->data.get_ptr() + msg->size);
}
atomic_t<bool> wake_up = false;
bool result = false;
input::SetIntercepted(true);
Emu.CallAfter([=, &wake_up, &result, msg_data = std::move(msg_data), npids = std::move(npids)]() mutable
{
auto send_dlg = Emu.GetCallbacks().get_sendmessage_dialog();
result = send_dlg->Exec(msg_data, npids);
wake_up = true;
wake_up.notify_one();
});
while (!wake_up && !Emu.IsStopped())
{
thread_ctrl::wait_on(wake_up, false);
}
input::SetIntercepted(false);
s32 callback_result = result ? 0 : -1;
s32 event = 0;
switch (msg->mainType)
{
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_DATA_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_SEND_ATTACHMENT_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_GENERAL:
event = SCE_NP_BASIC_EVENT_SEND_MESSAGE_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_ADD_FRIEND:
event = SCE_NP_BASIC_EVENT_ADD_FRIEND_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE:
event = SCE_NP_BASIC_EVENT_SEND_INVITATION_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_CUSTOM_DATA:
event = SCE_NP_BASIC_EVENT_SEND_CUSTOM_DATA_RESULT;
break;
case SCE_NP_BASIC_MESSAGE_MAIN_TYPE_URL_ATTACHMENT:
event = SCE_NP_BASIC_EVENT_SEND_URL_ATTACHMENT_RESULT;
break;
}
nph.send_basic_event(event, callback_result, 0);
return CELL_OK;
}
@@ -831,12 +968,19 @@ error_code sceNpBasicSendMessageAttachment(vm::cptr<SceNpId> to, vm::cptr<char>
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!to || to->handle.data[0] == '\0' || !data || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
if (strlen(subject.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX || strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
if (strlen(subject.get_ptr()) > SCE_NP_BASIC_SUBJECT_CHARACTER_MAX || strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
@@ -860,12 +1004,17 @@ error_code sceNpBasicRecvMessageAttachment(sys_memory_container_t containerId)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
return CELL_OK;
}
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u64> size)
error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm::ptr<u32> size)
{
sceNp.todo("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
sceNp.warning("sceNpBasicRecvMessageAttachmentLoad(id=%d, buffer=*0x%x, size=*0x%x)", id, buffer, size);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@@ -874,22 +1023,41 @@ error_code sceNpBasicRecvMessageAttachmentLoad(u32 id, vm::ptr<void> buffer, vm:
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!buffer || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
if (id > SCE_NP_BASIC_SELECTED_MESSAGE_DATA)
const auto opt_msg = nph.get_message(id);
if (!opt_msg)
{
return SCE_NP_BASIC_ERROR_INVALID_DATA_ID;
}
const auto msg_pair = opt_msg.value();
const auto msg = msg_pair->second;
const u32 orig_size = *size;
const u32 size_to_copy = std::min(static_cast<u32>(msg.data.size()), orig_size);
memcpy(buffer.get_ptr(), msg.data.data(), size_to_copy);
*size = size_to_copy;
if (size_to_copy < msg.data.size())
{
return SCE_NP_BASIC_ERROR_DATA_LOST;
}
return CELL_OK;
}
error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory_container_t containerId)
{
sceNp.todo("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
sceNp.warning("sceNpBasicRecvMessageCustom(mainType=%d, recvOptions=%d, containerId=%d)", mainType, recvOptions, containerId);
auto& nph = g_fxo->get<named_thread<np_handler>>();
@@ -898,11 +1066,66 @@ error_code sceNpBasicRecvMessageCustom(u16 mainType, u32 recvOptions, sys_memory
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!(recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED
if ((recvOptions & ~SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_ALL_OPTIONS))
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
atomic_t<bool> wake_up = false;
bool result = false;
input::SetIntercepted(true);
SceNpBasicMessageRecvAction recv_result;
u64 chosen_msg_id;
Emu.CallAfter([=, &wake_up, &result, &recv_result, &chosen_msg_id]()
{
auto recv_dlg = Emu.GetCallbacks().get_recvmessage_dialog();
result = recv_dlg->Exec(static_cast<SceNpBasicMessageMainType>(mainType), static_cast<SceNpBasicMessageRecvOptions>(recvOptions), recv_result, chosen_msg_id);
wake_up = true;
wake_up.notify_one();
});
while (!wake_up && !Emu.IsStopped())
{
thread_ctrl::wait_on(wake_up, false);
}
input::SetIntercepted(false);
if (!result)
{
return SCE_NP_BASIC_ERROR_CANCEL;
}
const auto msg_pair = nph.get_message(chosen_msg_id).value();
const auto& msg = msg_pair->second;
const u32 event_to_send = (mainType == SCE_NP_BASIC_MESSAGE_MAIN_TYPE_INVITE) ? SCE_NP_BASIC_EVENT_RECV_INVITATION_RESULT : SCE_NP_BASIC_EVENT_RECV_CUSTOM_DATA_RESULT;
np_handler::basic_event to_add{};
to_add.event = event_to_send;
strcpy_trunc(to_add.from.userId.handle.data, msg_pair->first);
strcpy_trunc(to_add.from.name.data, msg_pair->first);
to_add.data.resize(sizeof(SceNpBasicExtendedAttachmentData));
SceNpBasicExtendedAttachmentData* att_data = reinterpret_cast<SceNpBasicExtendedAttachmentData*>(to_add.data.data());
att_data->flags = 0; // ?
att_data->msgId = chosen_msg_id;
att_data->data.id = static_cast<u32>(chosen_msg_id);
att_data->data.size = static_cast<u32>(msg.data.size());
att_data->userAction = recv_result;
att_data->markedAsUsed = (recvOptions & SCE_NP_BASIC_RECV_MESSAGE_OPTIONS_PRESERVE) ? 0 : 1;
nph.queue_basic_event(to_add);
nph.send_basic_event(event_to_send, 0, 0);
return CELL_OK;
}
@@ -917,6 +1140,11 @@ error_code sceNpBasicMarkMessageAsUsed(SceNpBasicMessageId msgId)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
//if (!msgId > ?)
//{
// return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -936,6 +1164,13 @@ error_code sceNpBasicAbortGui()
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
// TODO: abort GUI interaction
return CELL_OK;
}
@@ -950,11 +1185,18 @@ error_code sceNpBasicAddFriend(vm::cptr<SceNpId> contact, vm::cptr<char> body, s
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!contact || contact->handle.data[0] == '\0')
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: SCE_NP_BASIC_ERROR_NOT_SUPPORTED, might be in between argument checks
if (strlen(body.get_ptr()) > SCE_NP_BASIC_BODY_CHARACTER_MAX)
{
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
@@ -990,8 +1232,7 @@ error_code sceNpBasicGetFriendListEntryCount(vm::ptr<u32> count)
return SCE_NP_ERROR_ID_NOT_FOUND;
}
// TODO: Check if there are any friends
*count = 0;
*count = nph.get_num_friends();
return CELL_OK;
}
@@ -1026,6 +1267,11 @@ error_code sceNpBasicGetFriendPresenceByIndex(u32 index, vm::ptr<SceNpUserInfo>
{
sceNp.todo("sceNpBasicGetFriendPresenceByIndex(index=%d, user=*0x%x, pres=*0x%x, options=%d)", index, user, pres, options);
if (!pres)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1033,7 +1279,7 @@ error_code sceNpBasicGetFriendPresenceByIndex(u32 index, vm::ptr<SceNpUserInfo>
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!user || !pres)
if (!user)
{
// TODO: check index and (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1046,6 +1292,11 @@ error_code sceNpBasicGetFriendPresenceByIndex2(u32 index, vm::ptr<SceNpUserInfo>
{
sceNp.todo("sceNpBasicGetFriendPresenceByIndex2(index=%d, user=*0x%x, pres=*0x%x, options=%d)", index, user, pres, options);
if (!pres)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1053,7 +1304,7 @@ error_code sceNpBasicGetFriendPresenceByIndex2(u32 index, vm::ptr<SceNpUserInfo>
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!user || !pres)
if (!user)
{
// TODO: check index and (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1068,12 +1319,17 @@ error_code sceNpBasicGetFriendPresenceByNpId(vm::cptr<SceNpId> npid, vm::ptr<Sce
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!pres)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
if (!nph.is_NP_init)
{
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!npid || !pres)
if (!npid)
{
// TODO: check (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1088,12 +1344,17 @@ error_code sceNpBasicGetFriendPresenceByNpId2(vm::cptr<SceNpId> npid, vm::ptr<Sc
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!pres)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
if (!nph.is_NP_init)
{
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!npid || !pres)
if (!npid)
{
// TODO: check (options & SCE_NP_BASIC_PRESENCE_OPTIONS_ALL_OPTIONS) depending on fw
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1179,6 +1440,11 @@ error_code sceNpBasicGetPlayersHistoryEntryCount(u32 options, vm::ptr<u32> count
{
sceNp.todo("sceNpBasicGetPlayersHistoryEntryCount(options=%d, count=*0x%x)", options, count);
if (options > SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1186,6 +1452,11 @@ error_code sceNpBasicGetPlayersHistoryEntryCount(u32 options, vm::ptr<u32> count
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!count)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1207,6 +1478,11 @@ error_code sceNpBasicGetPlayersHistoryEntry(u32 options, u32 index, vm::ptr<SceN
{
sceNp.warning("sceNpBasicGetPlayersHistoryEntry(options=%d, index=%d, npid=*0x%x)", options, index, npid);
if (options > SCE_NP_BASIC_PLAYERS_HISTORY_OPTIONS_ALL)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1214,6 +1490,11 @@ error_code sceNpBasicGetPlayersHistoryEntry(u32 options, u32 index, vm::ptr<SceN
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!npid)
{
// TODO: Check index
@@ -1240,6 +1521,11 @@ error_code sceNpBasicAddBlockListEntry(vm::cptr<SceNpId> npid)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!npid || npid->handle.data[0] == '\0')
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1276,7 +1562,7 @@ error_code sceNpBasicGetBlockListEntryCount(vm::ptr<u32> count)
}
// TODO: Check if there are block lists
*count = 0;
*count = nph.get_num_blocks();
return CELL_OK;
}
@@ -1318,6 +1604,11 @@ error_code sceNpBasicGetMessageAttachmentEntryCount(vm::ptr<u32> count)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!count)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1346,6 +1637,11 @@ error_code sceNpBasicGetMessageAttachmentEntry(u32 index, vm::ptr<SceNpUserInfo>
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!from)
{
// TODO: check index
@@ -1365,17 +1661,27 @@ error_code sceNpBasicGetCustomInvitationEntryCount(vm::ptr<u32> count)
{
sceNp.todo("sceNpBasicGetCustomInvitationEntryCount(count=*0x%x)", count);
if (!count)
{
return SCE_NP_AUTH_EINVAL;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
// TODO: Find the correct test which returns SCE_NP_AUTH_ESRCH
if (!nph.is_NP_init)
{
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!count)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: Find the correct test which returns SCE_NP_ERROR_ID_NOT_FOUND
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_AUTH_ESRCH;
return SCE_NP_ERROR_ID_NOT_FOUND;
}
// TODO: Check if there are custom invitations
@@ -1388,18 +1694,28 @@ error_code sceNpBasicGetCustomInvitationEntry(u32 index, vm::ptr<SceNpUserInfo>
{
sceNp.todo("sceNpBasicGetCustomInvitationEntry(index=%d, from=*0x%x)", index, from);
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
{
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!from)
{
// TODO: check index
return SCE_NP_AUTH_EINVAL;
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
// TODO: Find the correct test which returns SCE_NP_ERROR_ID_NOT_FOUND
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_AUTH_ESRCH;
return SCE_NP_ERROR_ID_NOT_FOUND;
}
return CELL_OK;
@@ -1416,6 +1732,11 @@ error_code sceNpBasicGetMatchingInvitationEntryCount(vm::ptr<u32> count)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!count)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1444,6 +1765,11 @@ error_code sceNpBasicGetMatchingInvitationEntry(u32 index, vm::ptr<SceNpUserInfo
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!from)
{
// TODO: check index
@@ -1470,6 +1796,11 @@ error_code sceNpBasicGetClanMessageEntryCount(vm::ptr<u32> count)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!count)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
@@ -1498,6 +1829,11 @@ error_code sceNpBasicGetClanMessageEntry(u32 index, vm::ptr<SceNpUserInfo> from)
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!from)
{
// TODO: check index
@@ -1517,6 +1853,12 @@ error_code sceNpBasicGetMessageEntryCount(u32 type, vm::ptr<u32> count)
{
sceNp.todo("sceNpBasicGetMessageEntryCount(type=%d, count=*0x%x)", type, count);
// TODO: verify this check and its location
if (type > SCE_NP_BASIC_MESSAGE_INFO_TYPE_BOOTABLE_CUSTOM_DATA_MESSAGE)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1545,6 +1887,12 @@ error_code sceNpBasicGetMessageEntry(u32 type, u32 index, vm::ptr<SceNpUserInfo>
{
sceNp.todo("sceNpBasicGetMessageEntry(type=%d, index=%d, from=*0x%x)", type, index, from);
// TODO: verify this check and its location
if (type > SCE_NP_BASIC_MESSAGE_INFO_TYPE_BOOTABLE_CUSTOM_DATA_MESSAGE)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np_handler>>();
if (!nph.is_NP_init)
@@ -1578,15 +1926,19 @@ error_code sceNpBasicGetEvent(vm::ptr<s32> event, vm::ptr<SceNpUserInfo> from, v
return SCE_NP_BASIC_ERROR_NOT_INITIALIZED;
}
if (!nph.basic_handler.registered)
{
return SCE_NP_BASIC_ERROR_NOT_REGISTERED;
}
if (!event || !from || !data || !size)
{
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
}
// TODO: Check for other error and pass other events
//*event = SCE_NP_BASIC_EVENT_OFFLINE; // This event only indicates a contact is offline, not the current status of the connection
return not_an_error(SCE_NP_BASIC_ERROR_NO_EVENT);
return nph.get_basic_event(event, from, data, size);
}
error_code sceNpCommerceCreateCtx(u32 version, vm::ptr<SceNpId> npId, vm::ptr<SceNpCommerceHandler> handler, vm::ptr<void> arg, vm::ptr<u32> ctx_id)
@@ -1814,11 +2166,29 @@ error_code sceNpCustomMenuRegisterActions(vm::cptr<SceNpCustomMenu> menu, vm::pt
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
}
if (!menu || !handler)
if (!menu || !handler) // TODO: handler check might come later
{
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
}
if (menu->numActions > SCE_NP_CUSTOM_MENU_ACTION_ITEMS_TOTAL_MAX)
{
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
}
for (u32 i = 0; i < menu->numActions; i++)
{
if (!menu->actions[i].name)
{
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
}
if (!memchr(menu->actions[i].name.get_ptr(), '\0', SCE_NP_CUSTOM_MENU_ACTION_CHARACTER_MAX))
{
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
}
}
return CELL_OK;
}
@@ -1833,6 +2203,8 @@ error_code sceNpCustomMenuActionSetActivation(vm::cptr<SceNpCustomMenuIndexArray
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
}
// TODO: SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED
if (!array)
{
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
@@ -1852,17 +2224,18 @@ error_code sceNpCustomMenuRegisterExceptionList(vm::cptr<SceNpCustomMenuActionEx
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
}
// TODO: SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED
if (numItems > SCE_NP_CUSTOM_MENU_EXCEPTION_ITEMS_MAX)
{
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
}
if (!items)
{
return SCE_NP_CUSTOM_MENU_ERROR_INVALID_ARGUMENT;
}
if (numItems > SCE_NP_CUSTOM_MENU_ACTION_ITEMS_MAX)
{
// TODO: what about SCE_NP_CUSTOM_MENU_ACTION_ITEMS_TOTAL_MAX
return SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX;
}
return CELL_OK;
}
@@ -1874,9 +2247,11 @@ error_code sceNpFriendlist(vm::ptr<SceNpFriendlistResultHandler> resultHandler,
if (!nph.is_NP_init)
{
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
}
// TODO: SCE_NP_FRIENDLIST_ERROR_BUSY
if (!resultHandler)
{
return SCE_NP_FRIENDLIST_ERROR_INVALID_ARGUMENT;
@@ -1893,9 +2268,11 @@ error_code sceNpFriendlistCustom(SceNpFriendlistCustomOptions options, vm::ptr<S
if (!nph.is_NP_init)
{
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
}
// TODO: SCE_NP_FRIENDLIST_ERROR_BUSY
if (!resultHandler)
{
return SCE_NP_FRIENDLIST_ERROR_INVALID_ARGUMENT;
@@ -1912,9 +2289,11 @@ error_code sceNpFriendlistAbortGui()
if (!nph.is_NP_init)
{
return SCE_NP_CUSTOM_MENU_ERROR_NOT_INITIALIZED;
return SCE_NP_FRIENDLIST_ERROR_NOT_INITIALIZED;
}
// TODO: abort friendlist GUI interaction
return CELL_OK;
}
@@ -2931,10 +3310,10 @@ error_code sceNpManagerGetTicket(vm::ptr<void> buffer, vm::ptr<u32> bufferSize)
}
const auto& ticket = nph.get_ticket();
*bufferSize = static_cast<u32>(ticket.size());
if (!buffer)
{
*bufferSize = static_cast<u32>(ticket.size());
return CELL_OK;
}
+37 -5
View File
@@ -3,6 +3,8 @@
#include "cellRtc.h"
#include "Emu/Cell/ErrorCodes.h"
#include <set>
error_code sceNpInit(u32 poolsize, vm::ptr<void> poolptr);
error_code sceNpTerm();
@@ -1022,11 +1024,11 @@ struct SceNpBasicMessageDetails
be_t<u16> mainType;
be_t<u16> subType;
be_t<u32> msgFeatures;
const SceNpId npids;
vm::bptr<SceNpId> npids;
be_t<u32> count;
const s8 subject;
const s8 body;
const be_t<u32> data;
vm::bptr<char> subject;
vm::bptr<char> body;
vm::bptr<u8> data;
be_t<u32> size;
};
@@ -1203,7 +1205,7 @@ struct SceNpSignalingNetInfo
struct SceNpCustomMenuAction
{
be_t<u32> options;
char name[SCE_NP_CUSTOM_MENU_ACTION_CHARACTER_MAX];
vm::bcptr<char> name;
be_t<SceNpCustomMenuActionMask> mask;
};
@@ -1359,3 +1361,33 @@ using SceNpMatchingGUIHandler = void(u32 ctx_id, s32 event, s32 error_code, vm::
using SceNpProfileResultHandler = s32(s32 result, vm::ptr<void> arg);
using SceNpManagerSubSigninCallback = void(s32 result, vm::ptr<SceNpId> npId, vm::ptr<void> cb_arg);
// Used to pass data to UI/RPCN
struct message_data
{
SceNpCommunicationId commId{};
u64 msgId = 0;
u16 mainType = 0;
u16 subType = 0;
u32 msgFeatures = 0;
std::string subject;
std::string body;
std::vector<u8> data;
void print() const;
};
class SendMessageDialogBase
{
public:
virtual ~SendMessageDialogBase() = default;
virtual bool Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
};
class RecvMessageDialogBase
{
public:
virtual ~RecvMessageDialogBase() = default;
virtual bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
};
+20 -4
View File
@@ -613,17 +613,23 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
switch (code)
{
case 1:
case SCE_NP_SIGNALING_CONN_INFO_RTT:
{
connInfo->rtt = 20000; // HACK
break;
}
case 2:
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
{
connInfo->bandwidth = 10'000'000; // 10 MBPS HACK
break;
}
case 5:
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
{
// TODO: need an update to whole signaling as matching2 signaling ignores npids atm
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented SCE_NP_SIGNALING_CONN_INFO_PEER_NPID");
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
{
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
@@ -631,7 +637,15 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
connInfo->address.addr.np_s_addr = si.addr;
break;
}
case 6:
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
{
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
connInfo->address.addr.np_s_addr = si.mapped_addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS:
{
connInfo->packet_loss = 1; // HACK
break;
@@ -1274,6 +1288,8 @@ error_code sceNpMatching2GetRoomDataExternalList(
return res;
}
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
return CELL_OK;
}
+4 -4
View File
@@ -95,16 +95,16 @@ error_code console_getc()
return CELL_OK;
}
void console_putc(char ch)
void console_putc(ppu_thread& ppu, char ch)
{
sysPrxForUser.trace("console_putc(ch=0x%x)", ch);
sys_tty_write(0, vm::var<char>(ch), 1, vm::var<u32>{});
sys_tty_write(ppu, 0, vm::var<char>(ch), 1, vm::var<u32>{});
}
error_code console_write(vm::ptr<char> data, u32 len)
error_code console_write(ppu_thread& ppu, vm::ptr<char> data, u32 len)
{
sysPrxForUser.trace("console_write(data=*0x%x, len=%d)", data, len);
sys_tty_write(0, data, len, vm::var<u32>{});
sys_tty_write(ppu, 0, data, len, vm::var<u32>{});
return CELL_OK;
}
+1 -1
View File
@@ -416,7 +416,7 @@ error_code _sys_printf(ppu_thread& ppu, vm::cptr<char> fmt, ppu_va_args_t va_arg
const auto buf = vm::make_str(ps3_fmt(ppu, fmt, va_args.count));
sys_tty_write(0, buf, buf.get_count() - 1, vm::var<u32>{});
sys_tty_write(ppu, 0, buf, buf.get_count() - 1, vm::var<u32>{});
return CELL_OK;
}
+124 -92
View File
@@ -5,232 +5,264 @@
class PPCDisAsm : public CPUDisAsm
{
protected:
PPCDisAsm(cpu_disasm_mode mode, const u8* offset) : CPUDisAsm(mode, offset)
PPCDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : CPUDisAsm(mode, offset, start_pc)
{
}
virtual u32 DisAsmBranchTarget(const s32 imm) override = 0;
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
usz insert_char_if(usz pos, bool insert, char c)
{
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
if (!insert)
{
return pos;
}
ensure(std::exchange(last_opcode[pos], c) == ' ' && last_opcode[pos + 1] == ' ');
return pos + 1;
}
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
usz insert_char_if(std::string_view op, bool insert, char c = '.')
{
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
return insert_char_if(op.size(), insert, c);
}
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
{
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
}
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
}
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
{
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
}
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
{
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
}
void DisAsm_V1(const std::string& op, u32 v0)
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
{
Write(fmt::format("%s v%d", FixOp(op), v0));
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
}
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
{
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
}
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
void DisAsm_V1(std::string_view op, u32 v0)
{
Write(fmt::format("%s%s cr%d,f%d,f%d", FixOp(op), (rc ? "." : ""), cr0, f0, f1));
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
}
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
{
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
}
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
{
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
{
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
}
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
{
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1);
}
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
{
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
}
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
{
Write(fmt::format("%s%s %d,r%d", FixOp(op), (rc ? "." : ""), i0, r0));
fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
insert_char_if(op, !!rc);
}
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
{
DisAsm_INT1_R1_RC(op, i0, r0, false);
}
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2, f3));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
insert_char_if(op, !!rc);
}
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1, f2));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
insert_char_if(op, !!rc);
}
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
{
DisAsm_F3_RC(op, f0, f1, f2, false);
}
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s%s f%d,f%d", FixOp(op), (rc ? "." : ""), f0, f1));
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
{
DisAsm_F2_RC(op, f0, f1, false);
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
return;
}
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s%s f%d,r%d,%s", FixOp(op), (rc ? "." : ""), f0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
insert_char_if(op, !!rc);
return;
}
Write(fmt::format("%s%s f%d,%s(r%d)", FixOp(op), (rc ? "." : ""), f0, SignedHex(imm0), r0));
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
insert_char_if(op, !!rc);
}
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
{
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
}
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
{
Write(fmt::format("%s%s f%d", FixOp(op), (rc ? "." : ""), f0));
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
insert_char_if(op, !!rc);
}
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
{
Write(fmt::format("%s%s r%d", FixOp(op), (rc ? "." : ""), r0));
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
insert_char_if(op, !!rc);
}
void DisAsm_R1(const std::string& op, u32 r0)
void DisAsm_R1(std::string_view op, u32 r0)
{
DisAsm_R1_RC(op, r0, false);
}
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 oe, u32 rc)
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 oe, u32 rc)
{
Write(fmt::format("%s%s%s r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1));
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1);
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
}
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
{
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
}
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
{
DisAsm_R2_RC(op, r0, r1, false);
}
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 oe, u32 rc)
{
Write(fmt::format("%s%s%s r%d,r%d,r%d", FixOp(op), (oe ? "o" : ""), (rc ? "." : ""), r0, r1, r2));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (oe ? 1 : 0)), op, r0, r1, r2);
insert_char_if(insert_char_if(op, !!oe, 'o'), !!rc, '.');
}
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, r2, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
{
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
}
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
{
DisAsm_R3_RC(op, r0, r1, r2, false);
}
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1, i2));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
{
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
}
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
{
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
}
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
{
Write(fmt::format("%s%s r%d,r%d,%d", FixOp(op), (rc ? "." : ""), r0, r1, i0));
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
{
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
{
if(m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
return;
}
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
}
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
{
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
}
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
{
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0);
}
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
{
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
}
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
{
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
{
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
}
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
{
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
}
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
}
void DisAsm_INT1(const std::string& op, const int i0)
void DisAsm_INT1(std::string_view op, const int i0)
{
Write(fmt::format("%s %d", FixOp(op), i0));
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
}
void DisAsm_BRANCH(const std::string& op, const int pc)
void DisAsm_BRANCH(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
}
void DisAsm_BRANCH_A(const std::string& op, const int pc)
void DisAsm_BRANCH_A(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), pc));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
}
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
{
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
}
};
+23 -1
View File
@@ -37,6 +37,8 @@ void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
}
u32 ppu_get_far_jump(u32 pc);
void ppu_module::validate(u32 reloc)
{
// Load custom PRX configuration if available
@@ -1199,6 +1201,12 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
const ppu_opcode_t op{*_ptr++};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (ppu_get_far_jump(iaddr))
{
block.second = _ptr.addr() - block.first - 4;
break;
}
if (type == ppu_itype::UNK)
{
// Invalid blocks will remain empty
@@ -1388,6 +1396,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
const ppu_opcode_t op{*_ptr++};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (ppu_get_far_jump(iaddr))
{
break;
}
if (type == ppu_itype::B || type == ppu_itype::BC)
{
const u32 target = (op.aa ? 0 : iaddr) + (type == ppu_itype::B ? +op.bt24 : +op.bt14);
@@ -1462,7 +1475,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
const ppu_opcode_t op{*_ptr++};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::UNK)
if (ppu_get_far_jump(addr))
{
_ptr.set(next);
}
else if (type == ppu_itype::UNK)
{
break;
}
@@ -1674,6 +1691,11 @@ void ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (; i_pos < lim; i_pos += 4)
{
if (ppu_get_far_jump(i_pos))
{
continue;
}
const u32 opc = vm::_ref<u32>(i_pos);
switch (auto type = s_ppu_itype.decode(opc))
+475 -28
View File
@@ -1,19 +1,53 @@
#include "stdafx.h"
#include "PPUDisAsm.h"
#include "PPUFunction.h"
#include "PPUAnalyser.h"
#include "Emu/IdManager.h"
#include "util/asm.hpp"
#include <cmath>
const ppu_decoder<PPUDisAsm> s_ppu_disasm;
const ppu_decoder<ppu_itype> s_ppu_itype;
extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map();
enum class ppu_syscall_code : u64;
extern std::shared_ptr<CPUDisAsm> make_basic_ppu_disasm()
{
return std::make_shared<PPUDisAsm>(cpu_disasm_mode::normal, vm::g_sudo_addr);
}
u32 PPUDisAsm::disasm(u32 pc)
{
last_opcode.clear();
if (pc < m_start_pc)
{
return 0;
}
if (m_offset == vm::g_sudo_addr && !vm::check_addr(pc, vm::page_executable))
{
return 0;
}
dump_pc = pc;
be_t<u32> op{};
std::memcpy(&op, m_offset + pc, 4);
m_op = op;
(this->*(s_ppu_disasm.decode(m_op)))({ m_op });
if (m_offset != vm::g_sudo_addr)
{
// Exported functions lookup is not allowed in this case
format_by_mode();
return 4;
}
const auto& map = get_exported_function_names_as_addr_indexed_map();
if (auto it = map.find(pc); it != map.end())
@@ -22,9 +56,323 @@ u32 PPUDisAsm::disasm(u32 pc)
last_opcode += it->second;
}
format_by_mode();
return 4;
}
std::pair<const void*, usz> PPUDisAsm::get_memory_span() const
{
return {m_offset + m_start_pc, (1ull << 32) - m_start_pc};
}
std::unique_ptr<CPUDisAsm> PPUDisAsm::copy_type_erased() const
{
return std::make_unique<PPUDisAsm>(*this);
}
std::pair<PPUDisAsm::const_op, u64> PPUDisAsm::try_get_const_op_gpr_value(u32 reg, u32 pc, u32 TTL) const
{
if (!TTL)
{
// Recursion limit (Time To Live)
return {};
}
if (pc == umax)
{
// Default arg: choose pc of previous instruction
if (dump_pc == 0)
{
// Do not underflow
return {};
}
pc = dump_pc - 4;
}
#if __cpp_using_enum >= 201907
using enum const_op;
#else
constexpr const_op none = const_op::none, form = const_op::form, xor_mask = const_op::xor_mask;
#endif
// Scan PPU executable memory backwards until unmapped or non-executable memory block is encountered
for (u32 i = pc; i >= m_start_pc && (m_offset != vm::g_sudo_addr || vm::check_addr(i, vm::page_executable));)
{
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
const ppu_opcode_t op{ opcode };
const auto type = s_ppu_itype.decode(opcode);
auto is_branch = [](enum ppu_itype::type itype)
{
return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR;
};
if (is_branch(type) || type == ppu_itype::UNK)
{
// TODO: Detect calls, ignore them if reg is a non-volatile register
return {};
}
// Get constant register value
#define GET_CONST_OP_REG(var, reg, op) \
{\
/* Search for the constant value of the register*/\
const auto [const_op, value] = try_get_const_op_gpr_value(reg, i - 4, TTL - 1);\
\
if (const_op != const_op::op)\
{\
/* Cannot compute constant value if register/operation is not constant*/\
return {};\
}\
\
var = value;\
} void() /*<- Require a semicolon*/
#define GET_CONST_REG(var, reg) GET_CONST_OP_REG(var, reg, form)
switch (type)
{
case ppu_itype::ADDI:
{
if (op.rd != reg)
{
// Destination register is not relevant to us
break;
}
u64 reg_ra = 0;
if (op.ra)
{
GET_CONST_REG(reg_ra, op.ra);
}
return { form, reg_ra + op.simm16 };
}
case ppu_itype::ADDIS:
{
if (op.rd != reg)
{
break;
}
u64 reg_ra = 0;
if (op.ra)
{
GET_CONST_REG(reg_ra, op.ra);
}
return { form, reg_ra + op.simm16 * 65536 };
}
case ppu_itype::ORI:
{
if (op.rs == op.ra && !op.uimm16)
{
// NO-OP
break;
}
if (op.ra != reg)
{
// Destination register is not relevant to us
break;
}
u64 reg_rs = 0;
GET_CONST_REG(reg_rs, op.rs);
return { form, reg_rs | op.uimm16 };
}
case ppu_itype::ORIS:
{
if (op.rs == op.ra && !op.uimm16)
{
// NO-OP
break;
}
if (op.ra != reg)
{
break;
}
u64 reg_rs = 0;
GET_CONST_REG(reg_rs, op.rs);
return { form, reg_rs | (u64{op.uimm16} << 16)};
}
case ppu_itype::XORIS:
{
if (op.ra != reg)
{
break;
}
const auto [const_op, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
if (const_op == none)
{
return { xor_mask, (u64{op.uimm16} << 16) };
}
if (const_op != form)
{
// Unexpected
return {};
}
return { form, reg_rs ^ (u64{op.uimm16} << 16)};
}
case ppu_itype::RLDICR:
{
if (op.ra != reg)
{
break;
}
u64 reg_rs = 0;
GET_CONST_REG(reg_rs, op.rs);
return { form, utils::rol64(reg_rs, op.sh64) & (~0ull << (op.mbe64 ^ 63)) };
}
case ppu_itype::OR:
{
if (op.rs == op.rb && op.rs == op.ra)
{
// NO-OP
break;
}
if (op.ra != reg)
{
break;
}
u64 reg_rs = 0, reg_rb = 0;
GET_CONST_REG(reg_rs, op.rs);
// Try to optimize if it's a register move operation
if (op.rs != op.rb)
{
GET_CONST_REG(reg_rb, op.rb);
}
return { form, reg_rs | reg_rb };
}
case ppu_itype::XOR:
{
if (op.ra != reg)
{
break;
}
if (op.rs == op.rb)
{
return { form, 0 };
}
const auto [const_op_rs, reg_rs] = try_get_const_op_gpr_value(op.rs, i - 4, TTL - 1);
const auto [const_op_rb, reg_rb] = try_get_const_op_gpr_value(op.rb, i - 4, TTL - 1);
if (const_op_rs == form && const_op_rb == form)
{
// Normally it is not the case
return { form, reg_rs ^ reg_rb };
}
if (const_op_rs == form && const_op_rb == none)
{
return { xor_mask, reg_rs };
}
else if (const_op_rb == form && const_op_rs == none)
{
return { xor_mask, reg_rb };
}
return {};
}
default:
{
// Ordinary test
// TODO: Proper detection of destination register(s) modification (if there are any)
if (op.ra == reg || op.rd == reg)
{
return {};
}
break;
}
}
if (i == 0)
{
return {};
}
i -= 4;
}
return {};
}
enum CellError : u32;
void comment_constant(std::string& last_opcode, u64 value, bool print_float = false)
{
// Test if potentially a CELL error
if ((value >> 28) == 0xf'ffff'fff8u || (value >> 28) == 0x8u)
{
const usz old_size = last_opcode.size();
// Comment as CELL error
fmt::append(last_opcode, " #%s (0x%xh)", CellError{static_cast<u32>(value)}, value);
// Test if failed to format (appended " #0x8".. in such case)
if (last_opcode[old_size + 2] != '0')
{
// Success
return;
}
// Revert and fallback
last_opcode.resize(old_size);
}
// Comment constant formation
fmt::append(last_opcode, " #0x%xh", value);
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu))
{
const f32 float_val = std::bit_cast<f32>(static_cast<u32>(value));
if (std::isfinite(float_val))
{
const usz old_size = last_opcode.size();
fmt::append(last_opcode, " (%.6gf)", float_val);
if (usz pos = last_opcode.find_first_of('.', old_size); pos == umax)
{
// No decimal point has been inserted, force insertion
last_opcode.insert(last_opcode.size() - 2, ".0"sv);
}
}
else
{
fmt::append(last_opcode, " (%g)", float_val);
}
}
}
constexpr std::pair<const char*, char> get_BC_info(u32 bo, u32 bi)
{
std::pair<const char*, char> info{};
@@ -903,12 +1251,41 @@ void PPUDisAsm::SUBFIC(ppu_opcode_t op)
void PPUDisAsm::CMPLI(ppu_opcode_t op)
{
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.uimm16);
DisAsm_CR1_R1_IMM(op.l10 ? "cmpldi" : "cmplwi", op.crfd, op.ra, op.uimm16);
// Try to obtain the true constant value we are comparing against, comment on success
// Upper 16/48 bits of it
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
{
// Fixup value (merge the lower 16-bits of that value)
value |= op.uimm16;
if (!op.l10)
{
value = static_cast<u32>(value);
}
comment_constant(last_opcode, value);
}
}
void PPUDisAsm::CMPI(ppu_opcode_t op)
{
DisAsm_CR1_R1_IMM(op.l10 ? "cmpdi" : "cmpwi", op.crfd, op.ra, op.simm16);
// See CMPLI
if (auto [is_xor, value] = try_get_const_xor_gpr_value(op.ra); is_xor && !(value & 0xFFFF))
{
// Signed fixup
value ^= s64{op.simm16};
if (!op.l10)
{
value = static_cast<u32>(value);
}
comment_constant(last_opcode, value);
}
}
void PPUDisAsm::ADDIC(ppu_opcode_t op)
@@ -925,6 +1302,12 @@ void PPUDisAsm::ADDI(ppu_opcode_t op)
else
{
DisAsm_R2_IMM("addi", op.rd, op.ra, op.simm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.ra); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value + op.simm16);
}
}
}
@@ -937,6 +1320,12 @@ void PPUDisAsm::ADDIS(ppu_opcode_t op)
else
{
DisAsm_R2_IMM("addis", op.rd, op.ra, op.simm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.ra); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value + op.simm16 * 65536);
}
}
}
@@ -950,7 +1339,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
return;
}
@@ -958,7 +1347,7 @@ void PPUDisAsm::BC(ppu_opcode_t op)
if (!inst)
{
Write(fmt::format("bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk));
fmt::append(last_opcode, "bc 0x%x, 0x%x, 0x%x, %d, %d", bo, bi, bd, aa, lk);
return;
}
@@ -994,12 +1383,46 @@ void PPUDisAsm::BC(ppu_opcode_t op)
void PPUDisAsm::SC(ppu_opcode_t op)
{
if (op.opcode != ppu_instructions::SC(0))
if (op.opcode != ppu_instructions::SC(0) && op.opcode != ppu_instructions::SC(1))
{
return UNK(op);
}
Write("sc");
fmt::append(last_opcode, "%-*s ", PadOp(), "sc");
if (op.lev)
{
fmt::append(last_opcode, "%u ", op.lev);
}
// Try to get constant syscall index
auto [is_const, index] = try_get_const_gpr_value(11);
if (!is_const)
{
return;
}
switch (op.lev)
{
case 0:
{
// Lv2 syscall
if (index < 1024u)
{
fmt::append(last_opcode, "#%s", ppu_syscall_code{index});
}
return;
}
case 1:
{
// Lv1 syscall
fmt::append(last_opcode, "#lv1_syscall_%u", index);
return;
}
default: return;
}
}
void PPUDisAsm::B(ppu_opcode_t op)
@@ -1010,7 +1433,7 @@ void PPUDisAsm::B(ppu_opcode_t op)
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("b 0x%x, %d, %d", li, aa, lk));
fmt::append(last_opcode, "b 0x%x, %d, %d", li, aa, lk);
return;
}
@@ -1048,7 +1471,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (bo == 0b10100)
{
Write(lk ? "blrl" : "blr");
last_opcode += (lk ? "blrl" : "blr");
return;
}
@@ -1056,7 +1479,7 @@ void PPUDisAsm::BCLR(ppu_opcode_t op)
if (!inst)
{
Write(fmt::format("bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
fmt::append(last_opcode, "bclr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
return;
}
@@ -1091,7 +1514,7 @@ void PPUDisAsm::CRANDC(ppu_opcode_t op)
void PPUDisAsm::ISYNC(ppu_opcode_t)
{
Write("isync");
last_opcode += "isync";
}
void PPUDisAsm::CRXOR(ppu_opcode_t op)
@@ -1151,7 +1574,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (bo == 0b10100)
{
Write(lk ? "bctrl" : "bctr");
last_opcode += (lk ? "bctrl" : "bctr");
return;
}
@@ -1160,7 +1583,7 @@ void PPUDisAsm::BCCTR(ppu_opcode_t op)
if (!inst || inst[1] == 'd')
{
// Invalid or unknown bcctr form
Write(fmt::format("bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk));
fmt::append(last_opcode, "bcctr %d, cr%d[%s], %d, %d", bo, bi / 4, get_partial_BI_field(bi), bh, lk);
return;
}
@@ -1199,25 +1622,49 @@ void PPUDisAsm::RLWNM(ppu_opcode_t op)
void PPUDisAsm::ORI(ppu_opcode_t op)
{
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
if (op.uimm16 == 0) return DisAsm_R2("mr", op.ra, op.rs);
DisAsm_R2_IMM("ori", op.ra, op.rs, op.uimm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value | op.uimm16);
}
}
void PPUDisAsm::ORIS(ppu_opcode_t op)
{
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) return Write("nop");
if (op.rs == 0 && op.ra == 0 && op.uimm16 == 0) { last_opcode += "nop"; return; }
DisAsm_R2_IMM("oris", op.ra, op.rs, op.uimm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value | (op.uimm16 << 16));
}
}
void PPUDisAsm::XORI(ppu_opcode_t op)
{
DisAsm_R2_IMM("xori", op.ra, op.rs, op.uimm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value ^ op.uimm16);
}
}
void PPUDisAsm::XORIS(ppu_opcode_t op)
{
DisAsm_R2_IMM("xoris", op.ra, op.rs, op.uimm16);
if (auto [is_const, value] = try_get_const_gpr_value(op.rs); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value ^ (op.uimm16 << 16));
}
}
void PPUDisAsm::ANDI(ppu_opcode_t op)
@@ -1674,7 +2121,7 @@ void PPUDisAsm::MFSPR(ppu_opcode_t op)
case 0x001: DisAsm_R1("mfxer", op.rd); break;
case 0x008: DisAsm_R1("mflr", op.rd); break;
case 0x009: DisAsm_R1("mfctr", op.rd); break;
default: DisAsm_R1_IMM("mfspr", op.rd, op.spr); break;
default: DisAsm_R1_IMM("mfspr", op.rd, n); break;
}
}
@@ -1750,10 +2197,10 @@ void PPUDisAsm::OR(ppu_opcode_t op)
{
switch (op.opcode)
{
case 0x7f9ce378: return Write("db8cyc");
case 0x7fbdeb78: return Write("db10cyc");
case 0x7fdef378: return Write("db12cyc");
case 0x7ffffb78: return Write("db16cyc");
case 0x7f9ce378: last_opcode += "db8cyc"; return;
case 0x7fbdeb78: last_opcode += "db10cyc"; return;
case 0x7fdef378: last_opcode += "db12cyc"; return;
case 0x7ffffb78: last_opcode += "db16cyc"; return;
default: DisAsm_R2_RC("mr", op.ra, op.rb, op.rc);
}
}
@@ -1782,7 +2229,7 @@ void PPUDisAsm::MTSPR(ppu_opcode_t op)
case 0x001: DisAsm_R1("mtxer", op.rs); break;
case 0x008: DisAsm_R1("mtlr", op.rs); break;
case 0x009: DisAsm_R1("mtctr", op.rs); break;
default: DisAsm_IMM_R1("mtspr", op.spr, op.rs); break;
default: DisAsm_R1_IMM("mtspr", n, op.rs); break;
}
}
@@ -1863,7 +2310,7 @@ void PPUDisAsm::LFSUX(ppu_opcode_t op)
void PPUDisAsm::SYNC(ppu_opcode_t op)
{
Write(op.l10 ? "lwsync" : "sync");
last_opcode += (op.l10 ? "lwsync" : "sync");
}
void PPUDisAsm::LFDX(ppu_opcode_t op)
@@ -1953,7 +2400,7 @@ void PPUDisAsm::LVRXL(ppu_opcode_t op)
void PPUDisAsm::DSS(ppu_opcode_t)
{
Write("dss()");
last_opcode += "dss";
}
void PPUDisAsm::SRAWI(ppu_opcode_t op)
@@ -1968,7 +2415,7 @@ void PPUDisAsm::SRADI(ppu_opcode_t op)
void PPUDisAsm::EIEIO(ppu_opcode_t)
{
Write("eieio");
last_opcode += "eieio";
}
void PPUDisAsm::STVLXL(ppu_opcode_t op)
@@ -2213,7 +2660,7 @@ void PPUDisAsm::FNMADDS(ppu_opcode_t op)
void PPUDisAsm::MTFSB1(ppu_opcode_t op)
{
Write(fmt::format("mtfsb1%s %d", op.rc ? "." : "", op.crbd));
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb1." : "mtfsb1", op.crbd);
}
void PPUDisAsm::MCRFS(ppu_opcode_t op)
@@ -2223,12 +2670,12 @@ void PPUDisAsm::MCRFS(ppu_opcode_t op)
void PPUDisAsm::MTFSB0(ppu_opcode_t op)
{
Write(fmt::format("mtfsb0%s %d", op.rc ? "." : "", op.crbd));
fmt::append(last_opcode, "%-*s %d", PadOp(), op.rc ? "mtfsb0." : "mtfsb0", op.crbd);
}
void PPUDisAsm::MTFSFI(ppu_opcode_t op)
{
Write(fmt::format("mtfsfi%s cr%d,%d,%d", op.rc ? "." : "", op.crfd, op.i, op.l15));
fmt::append(last_opcode, "%-*s cr%d,%d,%d", PadOp(), op.rc ? "mtfsfi." : "mtfsfi", op.crfd, op.i, op.l15);
}
void PPUDisAsm::MFFS(ppu_opcode_t op)
@@ -2238,7 +2685,7 @@ void PPUDisAsm::MFFS(ppu_opcode_t op)
void PPUDisAsm::MTFSF(ppu_opcode_t op)
{
Write(fmt::format("mtfsf%s %d,f%d,%d,%d", op.rc ? "." : "", op.rc, op.flm, op.frb, op.l6, op.l15));
fmt::append(last_opcode, "%-*s %d,f%d,%d,%d", PadOp(), op.rc ? "mtfsf." : "mtfsf", op.rc, op.flm, op.frb, op.l6, op.l15);
}
void PPUDisAsm::FCMPU(ppu_opcode_t op)
@@ -2367,10 +2814,10 @@ void PPUDisAsm::UNK(ppu_opcode_t)
if (dump_pc % 8 == 4 && index < ppu_function_manager::get().size())
{
Write(fmt::format("Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index));
fmt::append(last_opcode, "Function : %s (index %u)", index < g_ppu_function_names.size() ? g_ppu_function_names[index].c_str() : "?", index);
return;
}
}
Write("?? ??");
last_opcode += "?? ??";
}
+147 -108
View File
@@ -29,264 +29,303 @@ private:
}
private:
void DisAsm_V4(const std::string& op, u32 v0, u32 v1, u32 v2, u32 v3)
void DisAsm_V4(std::string_view op, u32 v0, u32 v1, u32 v2, u32 v3)
{
Write(fmt::format("%s v%d,v%d,v%d,v%d", FixOp(op), v0, v1, v2, v3));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,v%d", PadOp(), op, v0, v1, v2, v3);
}
void DisAsm_V3_UIMM(const std::string& op, u32 v0, u32 v1, u32 v2, u32 uimm)
void DisAsm_V3_UIMM(std::string_view op, u32 v0, u32 v1, u32 v2, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,v%d,%s", FixOp(op), v0, v1, v2, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d,%s", PadOp(), op, v0, v1, v2, uimm);
}
void DisAsm_V3(const std::string& op, u32 v0, u32 v1, u32 v2)
void DisAsm_V3(std::string_view op, u32 v0, u32 v1, u32 v2)
{
Write(fmt::format("%s v%d,v%d,v%d", FixOp(op), v0, v1, v2));
fmt::append(last_opcode, "%-*s v%d,v%d,v%d", PadOp(), op, v0, v1, v2);
}
void DisAsm_V2_UIMM(const std::string& op, u32 v0, u32 v1, u32 uimm)
void DisAsm_V2_UIMM(std::string_view op, u32 v0, u32 v1, u32 uimm)
{
Write(fmt::format("%s v%d,v%d,%s", FixOp(op), v0, v1, uimm));
fmt::append(last_opcode, "%-*s v%d,v%d,%s", PadOp(), op, v0, v1, uimm);
}
void DisAsm_V2(const std::string& op, u32 v0, u32 v1)
void DisAsm_V2(std::string_view op, u32 v0, u32 v1)
{
Write(fmt::format("%s v%d,v%d", FixOp(op), v0, v1));
fmt::append(last_opcode, "%-*s v%d,v%d", PadOp(), op, v0, v1);
}
void DisAsm_V1_SIMM(const std::string& op, u32 v0, s32 simm)
void DisAsm_V1_SIMM(std::string_view op, u32 v0, s32 simm)
{
Write(fmt::format("%s v%d,%s", FixOp(op), v0, SignedHex(simm)));
fmt::append(last_opcode, "%-*s v%d,%s", PadOp(), op, v0, SignedHex(simm));
}
void DisAsm_V1(const std::string& op, u32 v0)
void DisAsm_V1(std::string_view op, u32 v0)
{
Write(fmt::format("%s v%d", FixOp(op), v0));
fmt::append(last_opcode, "%-*s v%d", PadOp(), op, v0);
}
void DisAsm_V1_R2(const std::string& op, u32 v0, u32 r1, u32 r2)
void DisAsm_V1_R2(std::string_view op, u32 v0, u32 r1, u32 r2)
{
Write(fmt::format("%s v%d,r%d,r%d", FixOp(op), v0, r1, r2));
fmt::append(last_opcode, "%-*s v%d,r%d,r%d", PadOp(), op, v0, r1, r2);
}
void DisAsm_CR1_F2_RC(const std::string& op, u32 cr0, u32 f0, u32 f1, u32 rc)
void DisAsm_CR1_F2_RC(std::string_view op, u32 cr0, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s cr%d,f%d,f%d", FixOp(op + (rc ? "." : "")), cr0, f0, f1));
fmt::append(last_opcode, "%-*s cr%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, cr0, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_F2(const std::string& op, u32 cr0, u32 f0, u32 f1)
void DisAsm_CR1_F2(std::string_view op, u32 cr0, u32 f0, u32 f1)
{
DisAsm_CR1_F2_RC(op, cr0, f0, f1, false);
}
void DisAsm_INT1_R2(const std::string& op, u32 i0, u32 r0, u32 r1)
void DisAsm_INT1_R2(std::string_view op, u32 i0, u32 r0, u32 r1)
{
Write(fmt::format("%s %d,r%d,r%d", FixOp(op), i0, r0, r1));
fmt::append(last_opcode, "%-*s %d,r%d,r%d", PadOp(), op, i0, r0, r1);
}
void DisAsm_INT1_R1_IMM(const std::string& op, u32 i0, u32 r0, s32 imm0)
void DisAsm_INT1_R1_IMM(std::string_view op, u32 i0, u32 r0, s32 imm0)
{
Write(fmt::format("%s %d,r%d,%s", FixOp(op), i0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s %d,r%d,%s", PadOp(), op, i0, r0, SignedHex(imm0));
}
void DisAsm_INT1_R1_RC(const std::string& op, u32 i0, u32 r0, u32 rc)
void DisAsm_INT1_R1_RC(std::string_view op, u32 i0, u32 r0, u32 rc)
{
Write(fmt::format("%s %d,r%d", FixOp(op + (rc ? "." : "")), i0, r0));
fmt::append(last_opcode, "%-*s %d,r%d", PadOp(op, rc ? 1 : 0), op, i0, r0);
insert_char_if(op, !!rc);
}
void DisAsm_INT1_R1(const std::string& op, u32 i0, u32 r0)
void DisAsm_INT1_R1(std::string_view op, u32 i0, u32 r0)
{
DisAsm_INT1_R1_RC(op, i0, r0, false);
}
void DisAsm_F4_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
void DisAsm_F4_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 f3, u32 rc)
{
Write(fmt::format("%s f%d,f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2, f3));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2, f3);
insert_char_if(op, !!rc);
}
void DisAsm_F3_RC(const std::string& op, u32 f0, u32 f1, u32 f2, u32 rc)
void DisAsm_F3_RC(std::string_view op, u32 f0, u32 f1, u32 f2, u32 rc)
{
Write(fmt::format("%s f%d,f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1, f2));
fmt::append(last_opcode, "%-*s f%d,f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1, f2);
insert_char_if(op, !!rc);
}
void DisAsm_F3(const std::string& op, u32 f0, u32 f1, u32 f2)
void DisAsm_F3(std::string_view op, u32 f0, u32 f1, u32 f2)
{
DisAsm_F3_RC(op, f0, f1, f2, false);
}
void DisAsm_F2_RC(const std::string& op, u32 f0, u32 f1, u32 rc)
void DisAsm_F2_RC(std::string_view op, u32 f0, u32 f1, u32 rc)
{
Write(fmt::format("%s f%d,f%d", FixOp(op + (rc ? "." : "")), f0, f1));
fmt::append(last_opcode, "%-*s f%d,f%d", PadOp(op, rc ? 1 : 0), op, f0, f1);
insert_char_if(op, !!rc);
}
void DisAsm_F2(const std::string& op, u32 f0, u32 f1)
void DisAsm_F2(std::string_view op, u32 f0, u32 f1)
{
DisAsm_F2_RC(op, f0, f1, false);
}
void DisAsm_F1_R2(const std::string& op, u32 f0, u32 r0, u32 r1)
void DisAsm_F1_R2(std::string_view op, u32 f0, u32 r0, u32 r1)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,r%d", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d,r%d", PadOp(), op, f0, r0, r1);
return;
}
Write(fmt::format("%s f%d,r%d(r%d)", FixOp(op), f0, r0, r1));
fmt::append(last_opcode, "%-*s f%d,r%d(r%d)", PadOp(), op, f0, r0, r1);
}
void DisAsm_F1_IMM_R1_RC(const std::string& op, u32 f0, s32 imm0, u32 r0, u32 rc)
void DisAsm_F1_IMM_R1_RC(std::string_view op, u32 f0, s32 imm0, u32 r0, u32 rc)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s f%d,r%d,%s", FixOp(op + (rc ? "." : "")), f0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s f%d,r%d,%s", PadOp(op, rc ? 1 : 0), op, f0, r0, SignedHex(imm0));
insert_char_if(op, !!rc);
return;
}
Write(fmt::format("%s f%d,%s(r%d)", FixOp(op + (rc ? "." : "")), f0, SignedHex(imm0), r0));
fmt::append(last_opcode, "%-*s f%d,%s(r%d)", PadOp(op, rc ? 1 : 0), op, f0, SignedHex(imm0), r0);
insert_char_if(op, !!rc);
}
void DisAsm_F1_IMM_R1(const std::string& op, u32 f0, s32 imm0, u32 r0)
void DisAsm_F1_IMM_R1(std::string_view op, u32 f0, s32 imm0, u32 r0)
{
DisAsm_F1_IMM_R1_RC(op, f0, imm0, r0, false);
}
void DisAsm_F1_RC(const std::string& op, u32 f0, u32 rc)
void DisAsm_F1_RC(std::string_view op, u32 f0, u32 rc)
{
Write(fmt::format("%s f%d", FixOp(op + (rc ? "." : "")), f0));
fmt::append(last_opcode, "%-*s f%d", PadOp(op, rc ? 1 : 0), op, f0);
insert_char_if(op, !!rc);
}
void DisAsm_R1_RC(const std::string& op, u32 r0, u32 rc)
void DisAsm_R1_RC(std::string_view op, u32 r0, u32 rc)
{
Write(fmt::format("%s r%d", FixOp(op + (rc ? "." : "")), r0));
fmt::append(last_opcode, "%-*s r%d", PadOp(op, rc ? 1 : 0), op, r0);
insert_char_if(op, !!rc);
}
void DisAsm_R1(const std::string& op, u32 r0)
void DisAsm_R1(std::string_view op, u32 r0)
{
DisAsm_R1_RC(op, r0, false);
}
void DisAsm_R2_OE_RC(const std::string& op, u32 r0, u32 r1, u32 _oe, u32 rc)
void DisAsm_R2_OE_RC(std::string_view op, u32 r0, u32 r1, u32 _oe, u32 rc)
{
Write(fmt::format("%s r%d,r%d", FixOp(op + (_oe ? "o" : "") + (rc ? "." : "")), r0, r1));
fmt::append(last_opcode, "%-*s r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1);
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
}
void DisAsm_R2_RC(const std::string& op, u32 r0, u32 r1, u32 rc)
void DisAsm_R2_RC(std::string_view op, u32 r0, u32 r1, u32 rc)
{
DisAsm_R2_OE_RC(op, r0, r1, false, rc);
}
void DisAsm_R2(const std::string& op, u32 r0, u32 r1)
void DisAsm_R2(std::string_view op, u32 r0, u32 r1)
{
DisAsm_R2_RC(op, r0, r1, false);
}
void DisAsm_R3_OE_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
void DisAsm_R3_OE_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 _oe, u32 rc)
{
Write(fmt::format("%s r%d,r%d,r%d", FixOp(op + (rc ? "." : "") + (_oe ? "o" : "")), r0, r1, r2));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d", PadOp(op, (rc ? 1 : 0) + (_oe ? 1 : 0)), op, r0, r1, r2);
insert_char_if(insert_char_if(op, !!_oe, 'o'), !!rc, '.');
}
void DisAsm_R3_INT2_RC(const std::string& op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
void DisAsm_R3_INT2_RC(std::string_view op, u32 r0, u32 r1, u32 r2, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s r%d,r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, r2, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, r2, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R3_RC(const std::string& op, u32 r0, u32 r1, u32 r2, u32 rc)
void DisAsm_R3_RC(std::string_view op, u32 r0, u32 r1, u32 r2, u32 rc)
{
DisAsm_R3_OE_RC(op, r0, r1, r2, false, rc);
}
void DisAsm_R3(const std::string& op, u32 r0, u32 r1, u32 r2)
void DisAsm_R3(std::string_view op, u32 r0, u32 r1, u32 r2)
{
DisAsm_R3_RC(op, r0, r1, r2, false);
}
void DisAsm_R2_INT3_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
void DisAsm_R2_INT3_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1, i2));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1, i2);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT3(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
void DisAsm_R2_INT3(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, s32 i2)
{
DisAsm_R2_INT3_RC(op, r0, r1, i0, i1, i2, false);
}
void DisAsm_R2_INT2_RC(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
void DisAsm_R2_INT2_RC(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0, i1));
fmt::append(last_opcode, "%-*s r%d,r%d,%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0, i1);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT2(const std::string& op, u32 r0, u32 r1, s32 i0, s32 i1)
void DisAsm_R2_INT2(std::string_view op, u32 r0, u32 r1, s32 i0, s32 i1)
{
DisAsm_R2_INT2_RC(op, r0, r1, i0, i1, false);
}
void DisAsm_R2_INT1_RC(const std::string& op, u32 r0, u32 r1, s32 i0, u32 rc)
void DisAsm_R2_INT1_RC(std::string_view op, u32 r0, u32 r1, s32 i0, u32 rc)
{
Write(fmt::format("%s r%d,r%d,%d", FixOp(op + (rc ? "." : "")), r0, r1, i0));
fmt::append(last_opcode, "%-*s r%d,r%d,%d", PadOp(op, rc ? 1 : 0), op, r0, r1, i0);
insert_char_if(op, !!rc);
}
void DisAsm_R2_INT1(const std::string& op, u32 r0, u32 r1, s32 i0)
void DisAsm_R2_INT1(std::string_view op, u32 r0, u32 r1, s32 i0)
{
DisAsm_R2_INT1_RC(op, r0, r1, i0, false);
}
void DisAsm_R2_IMM(const std::string& op, u32 r0, u32 r1, s32 imm0)
void DisAsm_R2_IMM(std::string_view op, u32 r0, u32 r1, s32 imm0)
{
if (m_mode == cpu_disasm_mode::compiler_elf)
{
Write(fmt::format("%s r%d,r%d,%s", FixOp(op), r0, r1, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,r%d,%s", PadOp(), op, r0, r1, SignedHex(imm0));
return;
}
Write(fmt::format("%s r%d,%s(r%d)", FixOp(op), r0, SignedHex(imm0), r1));
fmt::append(last_opcode, "%-*s r%d,%s(r%d)", PadOp(), op, r0, SignedHex(imm0), r1);
}
void DisAsm_R1_IMM(const std::string& op, u32 r0, s32 imm0)
void DisAsm_R1_IMM(std::string_view op, u32 r0, s32 imm0)
{
Write(fmt::format("%s r%d,%s", FixOp(op), r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s r%d,%s", PadOp(), op, r0, SignedHex(imm0));
}
void DisAsm_IMM_R1(const std::string& op, s32 imm0, u32 r0)
void DisAsm_IMM_R1(std::string_view op, s32 imm0, u32 r0)
{
Write(fmt::format("%s %d,r%d #%x", FixOp(op), imm0, r0, imm0));
fmt::append(last_opcode, "%-*s %d,r%d #%x", PadOp(), op, imm0, r0, imm0);
}
void DisAsm_CR1_R1_IMM(const std::string& op, u32 cr0, u32 r0, s32 imm0)
void DisAsm_CR1_R1_IMM(std::string_view op, u32 cr0, u32 r0, s32 imm0)
{
Write(fmt::format("%s cr%d,r%d,%s", FixOp(op), cr0, r0, SignedHex(imm0)));
fmt::append(last_opcode, "%-*s cr%d,r%d,%s", PadOp(), op, cr0, r0, SignedHex(imm0));
}
void DisAsm_CR1_R2_RC(const std::string& op, u32 cr0, u32 r0, u32 r1, u32 rc)
void DisAsm_CR1_R2_RC(std::string_view op, u32 cr0, u32 r0, u32 r1, u32 rc)
{
Write(fmt::format("%s%s cr%d,r%d,r%d", FixOp(op), (rc ? "." : ""), cr0, r0, r1));
fmt::append(last_opcode, "%-*s cr%d,r%d,r%d", PadOp(op, rc ? 1 : 0), op, cr0, r0, r1);
insert_char_if(op, !!rc);
}
void DisAsm_CR1_R1(const std::string& op, u32 cr0, u32 r0)
void DisAsm_CR1_R1(std::string_view op, u32 cr0, u32 r0)
{
Write(fmt::format("%s cr%d,r%d", FixOp(op), cr0, r0));
fmt::append(last_opcode, "%-*s cr%d,r%d", PadOp(), op, cr0, r0);
}
void DisAsm_R1_CR1(const std::string& op, u32 r0, u32 cr0)
void DisAsm_R1_CR1(std::string_view op, u32 r0, u32 cr0)
{
Write(fmt::format("%s r%d,cr%d", FixOp(op), r0, cr0));
fmt::append(last_opcode, "%-*s r%d,cr%d", PadOp(), op, r0, cr0);
}
void DisAsm_CR1_R2(const std::string& op, u32 cr0, u32 r0, u32 r1)
void DisAsm_CR1_R2(std::string_view op, u32 cr0, u32 r0, u32 r1)
{
DisAsm_CR1_R2_RC(op, cr0, r0, r1, false);
}
void DisAsm_CR2(const std::string& op, u32 cr0, u32 cr1)
void DisAsm_CR2(std::string_view op, u32 cr0, u32 cr1)
{
Write(fmt::format("%s cr%d,cr%d", FixOp(op), cr0, cr1));
fmt::append(last_opcode, "%-*s cr%d,cr%d", PadOp(), op, cr0, cr1);
}
void DisAsm_BI1(const std::string& op, const int i0)
void DisAsm_BI1(std::string_view op, const int i0)
{
Write(fmt::format("%s cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0)));
fmt::append(last_opcode, "%-*s cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0));
}
void DisAsm_BI2(const std::string& op, const int i0, const int i1)
void DisAsm_BI2(std::string_view op, const int i0, const int i1)
{
Write(fmt::format("%s cr%d[%s],cr%d[%s]", FixOp(op), i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1)));
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s]", PadOp(), op, i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1));
}
void DisAsm_BI3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_BI3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s cr%d[%s],cr%d[%s],cr%d[%s]", FixOp(op),
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2)));
fmt::append(last_opcode, "%-*s cr%d[%s],cr%d[%s],cr%d[%s]", PadOp(), op,
i0 / 4, get_partial_BI_field(i0), i1 / 4, get_partial_BI_field(i1), i2 / 4, get_partial_BI_field(i2));
}
void DisAsm_INT3(const std::string& op, const int i0, const int i1, const int i2)
void DisAsm_INT3(std::string_view op, const int i0, const int i1, const int i2)
{
Write(fmt::format("%s %d,%d,%d", FixOp(op), i0, i1, i2));
fmt::append(last_opcode, "%-*s %d,%d,%d", PadOp(), op, i0, i1, i2);
}
void DisAsm_INT1(const std::string& op, const int i0)
void DisAsm_INT1(std::string_view op, const int i0)
{
Write(fmt::format("%s %d", FixOp(op), i0));
fmt::append(last_opcode, "%-*s %d", PadOp(), op, i0);
}
void DisAsm_BRANCH(const std::string& op, const int pc)
void DisAsm_BRANCH(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, DisAsmBranchTarget(pc));
}
void DisAsm_BRANCH_A(const std::string& op, const int pc)
void DisAsm_BRANCH_A(std::string_view op, const int pc)
{
Write(fmt::format("%s 0x%x", FixOp(op), pc));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, pc);
}
void DisAsm_B2_BRANCH(const std::string& op, u32 b0, u32 b1, const int pc)
void DisAsm_B2_BRANCH(std::string_view op, u32 b0, u32 b1, const int pc)
{
Write(fmt::format("%s %d,%d,0x%x ", FixOp(op), b0, b1, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s %d,%d,0x%x ", PadOp(), op, b0, b1, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, DisAsmBranchTarget(pc));
}
void DisAsm_CR_BRANCH_A(const std::string& op, u32 cr, const int pc)
void DisAsm_CR_BRANCH_A(std::string_view op, u32 cr, const int pc)
{
Write(fmt::format("%s cr%d,0x%x ", FixOp(op), cr, pc));
fmt::append(last_opcode, "%-*s cr%d,0x%x ", PadOp(), op, cr, pc);
}
void DisAsm_BI_BRANCH(const std::string& op, u32 bi, const int pc)
void DisAsm_BI_BRANCH(std::string_view op, u32 bi, const int pc)
{
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc)));
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), DisAsmBranchTarget(pc));
}
void DisAsm_BI_BRANCH_A(const std::string& op, u32 bi, const int pc)
void DisAsm_BI_BRANCH_A(std::string_view op, u32 bi, const int pc)
{
Write(fmt::format("%s cr%d[%s],0x%x ", FixOp(op), bi / 4, get_partial_BI_field(bi), pc));
fmt::append(last_opcode, "%-*s cr%d[%s],0x%x ", PadOp(), op, bi / 4, get_partial_BI_field(bi), pc);
}
public:
u32 disasm(u32 pc) override;
std::pair<const void*, usz> get_memory_span() const override;
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
enum class const_op
{
none,
form, // Cosntant formation
xor_mask, // Constant XOR mask applied (used with CMPI/CMPLI instructions, covers their limit of 16-bit immediates)
};
std::pair<const_op, u64> try_get_const_op_gpr_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
std::pair<bool, u64> try_get_const_gpr_value(u32 reg, u32 pc = -1) const
{
auto [op, res] = try_get_const_op_gpr_value(reg, pc);
return {op == const_op::form, res};
}
std::pair<bool, u64> try_get_const_xor_gpr_value(u32 reg, u32 pc = -1) const
{
auto [op, res] = try_get_const_op_gpr_value(reg, pc);
return {op == const_op::xor_mask, res};
}
void MFVSCR(ppu_opcode_t op);
void MTVSCR(ppu_opcode_t op);
+50 -30
View File
@@ -37,21 +37,23 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
// HLE function name cache
std::vector<std::string> g_ppu_function_names;
extern u32 ppu_generate_id(const char* name)
extern u32 ppu_generate_id(std::string_view name)
{
// Symbol name suffix
const auto suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A";
constexpr auto suffix = "\x67\x59\x65\x99\x04\x25\x04\x90\x56\x64\x27\x49\x94\x89\x74\x1A"sv;
sha1_context ctx;
u8 output[20];
// Compute SHA-1 hash
sha1_starts(&ctx);
sha1_update(&ctx, reinterpret_cast<const u8*>(name), std::strlen(name));
sha1_update(&ctx, reinterpret_cast<const u8*>(suffix), std::strlen(suffix));
sha1_update(&ctx, reinterpret_cast<const u8*>(name.data()), name.size());
sha1_update(&ctx, reinterpret_cast<const u8*>(suffix.data()), suffix.size());
sha1_finish(&ctx, output);
return reinterpret_cast<le_t<u32>&>(output[0]);
le_t<u32> result = 0;
std::memcpy(&result, output, sizeof(result));
return result;
}
ppu_static_module::ppu_static_module(const char* name)
@@ -331,14 +333,11 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
g_ppu_function_names[function.second.index] = fmt::format("%s:%s", function.second.name, _module->name);
}
if ((function.second.flags & MFF_HIDDEN) == 0)
{
auto& flink = linkage.functions[function.first];
auto& flink = linkage.functions[function.first];
flink.static_func = &function.second;
flink.export_addr = g_fxo->get<ppu_function_manager>().func_addr(function.second.index);
function.second.export_addr = &flink.export_addr;
}
flink.static_func = &function.second;
flink.export_addr = g_fxo->get<ppu_function_manager>().func_addr(function.second.index);
function.second.export_addr = &flink.export_addr;
}
for (auto& variable : _module->variables)
@@ -392,8 +391,21 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
// For the debugger (g_ppu_function_names shouldn't change, string_view should suffice)
extern const std::unordered_map<u32, std::string_view>& get_exported_function_names_as_addr_indexed_map()
{
static std::unordered_map<u32, std::string_view> res;
static u64 update_time = 0;
struct info_t
{
std::unordered_map<u32, std::string_view> res;
u64 update_time = 0;
};
static thread_local std::unique_ptr<info_t> info;
if (!info)
{
info = std::make_unique<info_t>();
info->res.reserve(ppu_module_manager::get().size());
}
auto& [res, update_time] = *info;
const auto link = g_fxo->try_get<ppu_linkage_info>();
const auto hle_funcs = g_fxo->try_get<ppu_function_manager>();
@@ -415,7 +427,6 @@ extern const std::unordered_map<u32, std::string_view>& get_exported_function_na
update_time = current_time;
res.clear();
res.reserve(ppu_module_manager::get().size());
for (auto& pair : ppu_module_manager::get())
{
@@ -528,6 +539,13 @@ struct ppu_prx_module_info
be_t<u32> unk5;
};
bool ppu_form_branch_to_code(u32 entry, u32 target);
extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name)
{
return g_fxo->get<ppu_linkage_info>().modules[module_name].functions[fnid].export_addr;
}
// Load and register exports; return special exports found (nameless module)
static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end)
{
@@ -606,26 +624,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
if (_sf && (_sf->flags & MFF_FORCED_HLE))
{
// Inject a branch to the HLE implementation
const u32 _entry = vm::read32(faddr);
const u32 target = g_fxo->get<ppu_function_manager>().func_addr(_sf->index) + 4;
// Set exported function
flink.export_addr = target - 4;
if ((target <= _entry && _entry - target <= 0x2000000) || (target > _entry && target - _entry < 0x2000000))
{
// Use relative branch
vm::write32(_entry, ppu_instructions::B(target - _entry));
}
else if (target < 0x2000000)
{
// Use absolute branch if possible
vm::write32(_entry, ppu_instructions::B(target, true));
}
else
{
ppu_loader.fatal("Failed to patch function at 0x%x (0x%x)", _entry, target);
}
ppu_form_branch_to_code(faddr, target);
}
else
{
@@ -1267,9 +1271,25 @@ void ppu_unload_prx(const lv2_prx& prx)
// }
//}
// Format patch name
std::string hash = fmt::format("PRX-%s", fmt::base57(prx.sha1));
for (auto& seg : prx.segs)
{
if (!seg.size) continue;
vm::dealloc(seg.addr, vm::main);
const std::string hash_seg = fmt::format("%s-%u", hash, &seg - prx.segs.data());
// Deallocatte memory used for patches
g_fxo->get<patch_engine>().unload(hash_seg);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
g_fxo->get<patch_engine>().unload(Emu.GetTitleID() + '-' + hash_seg);
}
}
}
+9 -3
View File
@@ -18,7 +18,7 @@ constexpr const char* ppu_select_name(const char* /*name*/, const char* orig_nam
}
// Generate FNID or VNID for given name
extern u32 ppu_generate_id(const char* name);
extern u32 ppu_generate_id(std::string_view name);
// Overload for REG_FNID, REG_VNID macro
constexpr u32 ppu_generate_id(u32 id)
@@ -31,7 +31,7 @@ enum ppu_static_module_flags : u32
{
MFF_FORCED_HLE = (1 << 0), // Always call HLE function
MFF_PERFECT = (1 << 1), // Indicates complete implementation and LLE interchangeability
MFF_HIDDEN = (1 << 2), // Invisible function for internal use (TODO)
MFF_HIDDEN = (1 << 2), // Invisible variable for internal use (TODO)
};
// HLE function information
@@ -293,7 +293,9 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
return func(ppu, args...);
}
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC(func, ppu.cia = static_cast<u32>(ppu.lr) & ~3), ppu_generate_id(nid))
#define BIND_FUNC_WITH_BLR(func) BIND_FUNC(func, ppu.cia = static_cast<u32>(ppu.lr) & ~3)
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func), ppu_generate_id(nid))
#define REG_FUNC(_module, func) REG_FNID(_module, #func, func)
@@ -301,6 +303,10 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
#define REG_VAR(_module, var) REG_VNID(_module, #var, var)
#define REG_HIDDEN_FUNC(func) ppu_function_manager::register_function<decltype(&func), &func>(BIND_FUNC_WITH_BLR(func))
#define REG_HIDDEN_FUNC_PURE(func) ppu_function_manager::register_function<decltype(&func), &func>(func)
#define REINIT_FUNC(func) (ppu_module_manager::find_static_function<&func>().flags = 0, ppu_module_manager::find_static_function<&func>())
#define UNIMPLEMENTED_FUNC(_module) _module.todo("%s()", __func__)
+256 -20
View File
@@ -319,6 +319,12 @@ void ppu_recompiler_fallback(ppu_thread& ppu)
while (true)
{
if (uptr func = ppu_ref(ppu.cia); (func << 17 >> 17) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
{
// We found a recompiler function at cia, return
break;
}
// Run instructions in interpreter
if (const u32 op = vm::read32(ppu.cia); ctr++, table[ppu_decode(op)](ppu, {op})) [[likely]]
{
@@ -326,12 +332,6 @@ void ppu_recompiler_fallback(ppu_thread& ppu)
continue;
}
if (uptr func = ppu_ref(ppu.cia); (func << 17 >> 17) != reinterpret_cast<uptr>(ppu_recompiler_fallback_ghc))
{
// We found a recompiler function at cia, return
break;
}
if (ppu.test_stopped())
{
break;
@@ -411,7 +411,7 @@ extern void ppu_register_range(u32 addr, u32 size)
// Register executable range at
utils::memory_commit(&ppu_ref(addr), u64{size} * 2, utils::protection::rw);
vm::page_protect(addr, size, 0, vm::page_executable);
ensure(vm::page_protect(addr, size, 0, vm::page_executable));
if (g_cfg.core.ppu_debug)
{
@@ -438,7 +438,9 @@ extern void ppu_register_range(u32 addr, u32 size)
}
}
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
static bool ppu_far_jump(ppu_thread& ppu);
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr = nullptr)
{
// Initialize specific function
if (ptr)
@@ -464,10 +466,11 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
// Initialize interpreter cache
const u64 _break = reinterpret_cast<uptr>(ppu_break);
const u64 far_jump = reinterpret_cast<uptr>(ppu_far_jump);
while (size)
{
if (ppu_ref(addr) != _break)
if (ppu_ref(addr) != _break && ppu_ref(addr) != far_jump)
{
ppu_ref(addr) = ppu_cache(addr);
}
@@ -477,6 +480,226 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr)
}
}
extern void ppu_register_function_at(u32 addr, u32 size, u64 ptr)
{
return ppu_register_function_at(addr, size, reinterpret_cast<ppu_function_t>(ptr));
}
u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name);
bool ppu_return_from_far_jump(ppu_thread& ppu)
{
auto& calls_info = ppu.hle_func_calls_with_toc_info;
ensure(!calls_info.empty());
// Branch to next instruction after far jump call entry with restored R2 and LR
const auto restore_info = &calls_info.back();
ppu.cia = restore_info->cia + 4;
ppu.lr = restore_info->saved_lr;
ppu.gpr[2] = restore_info->saved_r2;
calls_info.pop_back();
return false;
}
static const bool s_init_return_far_jump_func = []
{
REG_HIDDEN_FUNC_PURE(ppu_return_from_far_jump);
return true;
}();
struct ppu_far_jumps_t
{
struct all_info_t
{
u32 target;
bool link;
bool with_toc;
std::string module_name;
};
std::unordered_map<u32, all_info_t> vals;
mutable shared_mutex mutex;
// Get target address, 'ppu' is used in ppu_far_jump in order to modify registers
u32 get_target(const u32 pc, ppu_thread* ppu = nullptr)
{
reader_lock lock(mutex);
if (auto it = vals.find(pc); it != vals.end())
{
all_info_t& all_info = it->second;
u32 target = all_info.target;
bool link = all_info.link;
bool from_opd = all_info.with_toc;
if (!all_info.module_name.empty())
{
target = ppu_get_exported_func_addr(target, all_info.module_name);
}
if (from_opd && !vm::check_addr<sizeof(ppu_func_opd_t)>(target))
{
// Avoid reading unmapped memory under mutex
from_opd = false;
}
if (from_opd)
{
auto& opd = vm::_ref<ppu_func_opd_t>(target);
target = opd.addr;
// We modify LR to custom values here
link = false;
if (ppu)
{
auto& calls_info = ppu->hle_func_calls_with_toc_info;
// Save LR and R2
// Set LR to the this ppu_return_from_far_jump branch for restoration of registers
// NOTE: In order to clean up this information all calls must return in order
auto& saved_info = calls_info.emplace_back();
saved_info.cia = pc;
saved_info.saved_lr = std::exchange(ppu->lr, FIND_FUNC(ppu_return_from_far_jump));
saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc);
}
}
if (link && ppu)
{
ppu->lr = pc + 4;
}
return target;
}
return {};
}
};
u32 ppu_get_far_jump(u32 pc)
{
g_fxo->init<ppu_far_jumps_t>();
return g_fxo->get<ppu_far_jumps_t>().get_target(pc);
}
static bool ppu_far_jump(ppu_thread& ppu)
{
const u32 cia = g_fxo->get<ppu_far_jumps_t>().get_target(ppu.cia, &ppu);
if (!vm::check_addr(cia, vm::page_executable))
{
fmt::throw_exception("PPU far jump failed! (returned cia = 0x%08x)", cia);
}
ppu.cia = cia;
return false;
}
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link, bool with_toc, std::string module_name)
{
// Force align entry and target
entry &= -4;
// Exported functions are using target as FNID, must not be changed
if (module_name.empty())
{
target &= -4;
u32 cia_target = target;
if (with_toc)
{
ppu_func_opd_t opd{};
if (!vm::try_access(target, &opd, sizeof(opd), false))
{
// Cannot access function descriptor
return false;
}
// For now allow situations where OPD is changed later by patches or by the program itself
//cia_target = opd.addr;
// So force a valid target (executable, yet not equal to entry)
cia_target = entry ^ 8;
}
// Target CIA must be aligned, executable and not equal with
if (cia_target % 4 || entry == cia_target || !vm::check_addr(cia_target, vm::page_executable))
{
return false;
}
}
// Entry must be executable
if (!vm::check_addr(entry, vm::page_executable))
{
return false;
}
g_fxo->init<ppu_far_jumps_t>();
if (!module_name.empty())
{
// Always use function descriptor for exported functions
with_toc = true;
}
if (with_toc)
{
// Always link for calls with function descriptor
link = true;
}
// Register branch target in host memory, not guest memory
auto& jumps = g_fxo->get<ppu_far_jumps_t>();
std::lock_guard lock(jumps.mutex);
jumps.vals.insert_or_assign(entry, std::type_identity_t<typename ppu_far_jumps_t::all_info_t>{target, link, with_toc, std::move(module_name)});
ppu_register_function_at(entry, 4, &ppu_far_jump);
return true;
}
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link, bool with_toc)
{
return ppu_form_branch_to_code(entry, target, link, with_toc, std::string{});
}
bool ppu_form_branch_to_code(u32 entry, u32 target, bool link)
{
return ppu_form_branch_to_code(entry, target, link, false);
}
bool ppu_form_branch_to_code(u32 entry, u32 target)
{
return ppu_form_branch_to_code(entry, target, false);
}
void ppu_remove_hle_instructions(u32 addr, u32 size)
{
g_fxo->init<ppu_far_jumps_t>();
auto& jumps = g_fxo->get<ppu_far_jumps_t>();
std::lock_guard lock(jumps.mutex);
for (auto it = jumps.vals.begin(); it != jumps.vals.end();)
{
if (it->first >= addr && it->first <= addr + size - 1 && size)
{
it = jumps.vals.erase(it);
continue;
}
it++;
}
}
atomic_t<bool> g_debugger_pause_all_threads_on_bp = true;
// Breakpoint entry point
@@ -608,6 +831,14 @@ std::array<u32, 2> op_branch_targets(u32 pc, ppu_opcode_t op)
{
std::array<u32, 2> res{pc + 4, umax};
g_fxo->need<ppu_far_jumps_t>();
if (u32 target = g_fxo->get<ppu_far_jumps_t>().get_target(pc))
{
res[0] = target;
return res;
}
switch (const auto type = g_ppu_itype.decode(op.opcode))
{
case ppu_itype::B:
@@ -633,6 +864,8 @@ std::string ppu_thread::dump_regs() const
{
std::string ret;
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
for (uint i = 0; i < 32; ++i)
{
auto reg = gpr[i];
@@ -683,7 +916,6 @@ std::string ppu_thread::dump_regs() const
}
else
{
PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr);
dis_asm.disasm(reg);
fmt::append(ret, " -> %s", dis_asm.last_opcode);
}
@@ -1402,17 +1634,17 @@ void ppu_trap(ppu_thread& ppu, u64 addr)
// If stubbing is enabled, check current instruction and the following
if (!add || !vm::check_addr(ppu.cia, vm::page_executable) || !vm::check_addr(ppu.cia + add, vm::page_executable))
{
fmt::throw_exception("PPU Trap!");
fmt::throw_exception("PPU Trap! Sometimes tweaking the setting \"Stub PPU Traps\" can be a workaround to this crash.\nBest values depend on game code, if unsure try 1.");
}
ppu_log.error("PPU Trap: Stubbing %d instructions %s.", std::abs(static_cast<s32>(add) / 4), add >> 31 ? "backwards" : "forwards");
ppu.cia += add; // Skip instructions, hope for valid code (interprter may be invoked temporarily)
}
[[noreturn]] static void ppu_error(ppu_thread& ppu, u64 addr, u32 op)
static void ppu_error(ppu_thread& ppu, u64 addr, u32 /*op*/)
{
ppu.cia = ::narrow<u32>(addr);
fmt::throw_exception("Unknown/Illegal opcode 0x08x (0x%llx)", op, addr);
ppu_recompiler_fallback(ppu);
}
static void ppu_check(ppu_thread& ppu, u64 addr)
@@ -2597,7 +2829,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<lv2_
if (!had_ovl)
{
ensure(vm::unmap(0x3000'0000));
ensure(vm::unmap(0x3000'0000).second);
}
g_ps3_process_info.ppc_seg = ppc_seg;
@@ -2707,7 +2939,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
{
for (auto& block : func.blocks)
{
ppu_register_function_at(block.first, block.second, nullptr);
ppu_register_function_at(block.first, block.second);
}
if (g_cfg.core.ppu_debug && func.size && func.toc != umax)
@@ -3164,9 +3396,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
if (!func.size) continue;
const auto name = fmt::format("__0x%x", func.addr - reloc);
const u64 addr = ensure(jit->get(name));
jit_mod.funcs.emplace_back(reinterpret_cast<ppu_function_t>(addr));
ppu_ref(func.addr) = (addr & 0x7fff'ffff'ffffu) | (ppu_ref(func.addr) & ~0x7fff'ffff'ffffu);
const auto addr = ensure(reinterpret_cast<ppu_function_t>(jit->get(name)));
jit_mod.funcs.emplace_back(addr);
if (ppu_ref(func.addr) != reinterpret_cast<u64>(ppu_far_jump))
ppu_register_function_at(func.addr, 4, addr);
if (g_cfg.core.ppu_debug)
ppu_log.notice("Installing function %s at 0x%x: %p (reloc = 0x%x)", name, func.addr, ppu_ref(func.addr), reloc);
@@ -3183,8 +3417,10 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
{
if (!func.size) continue;
const u64 addr = ensure(reinterpret_cast<uptr>(jit_mod.funcs[index++]));
ppu_ref(func.addr) = (addr & 0x7fff'ffff'ffffu) | (ppu_ref(func.addr) & ~0x7fff'ffff'ffffu);
const u64 addr = reinterpret_cast<uptr>(ensure(jit_mod.funcs[index++]));
if (ppu_ref(func.addr) != reinterpret_cast<u64>(ppu_far_jump))
ppu_register_function_at(func.addr, 4, addr);
if (g_cfg.core.ppu_debug)
ppu_log.notice("Reinstalling function at 0x%x: %p (reloc=0x%x)", func.addr, ppu_ref(func.addr), reloc);
+9
View File
@@ -307,6 +307,15 @@ public:
static constexpr u32 call_history_max_size = 4096;
struct hle_func_call_with_toc_info_t
{
u32 cia;
u64 saved_lr;
u64 saved_r2;
};
std::vector<hle_func_call_with_toc_info_t> hle_func_calls_with_toc_info;
// For named_thread ctor
const struct thread_name_t
{
+16 -4
View File
@@ -126,6 +126,8 @@ Type* PPUTranslator::GetContextType()
return m_thread_type;
}
u32 ppu_get_far_jump(u32 pc);
Function* PPUTranslator::Translate(const ppu_function& info)
{
m_function = m_module->getFunction(info.name);
@@ -232,7 +234,16 @@ Function* PPUTranslator::Translate(const ppu_function& info)
m_rel = nullptr;
}
if (ppu_get_far_jump(m_addr + base))
{
// Branch into an HLEd instruction using the jump table
FlushRegisters();
CallFunction(0, m_reloc ? m_ir->CreateAdd(m_ir->getInt64(m_addr), m_seg0) : m_ir->getInt64(m_addr));
continue;
}
const u32 op = vm::read32(vm::cast(m_addr + base));
(this->*(s_ppu_decoder.decode(op)))({op});
if (m_rel)
@@ -251,6 +262,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
}
}
replace_intrinsics(*m_function);
return m_function;
}
@@ -995,7 +1007,7 @@ void PPUTranslator::VMADDFP(ppu_opcode_t op)
auto [a, b, c] = get_vrs<f32[4]>(op.va, op.vb, op.vc);
// Optimization: Emit only a floating multiply if the addend is zero
if (auto [ok, data] = get_const_vector(b.value, m_addr, 2000); ok)
if (auto [ok, data] = get_const_vector(b.value, m_addr); ok)
{
if (data == v128::from32p(1u << 31))
{
@@ -1306,7 +1318,7 @@ void PPUTranslator::VNMSUBFP(ppu_opcode_t op)
auto [a, b, c] = get_vrs<f32[4]>(op.va, op.vb, op.vc);
// Optimization: Emit only a floating multiply if the addend is zero
if (const auto [ok, data] = get_const_vector(b.value, m_addr, 2004); ok)
if (const auto [ok, data] = get_const_vector(b.value, m_addr); ok)
{
if (data == v128{})
{
@@ -1362,7 +1374,7 @@ void PPUTranslator::VPERM(ppu_opcode_t op)
if (m_use_avx512_icl && op.ra != op.rb)
{
const auto i = eval(~c);
set_vr(op.vd, vperm2b(b, a, i));
set_vr(op.vd, vperm2b256to128(b, a, i));
return;
}
@@ -1504,7 +1516,7 @@ void PPUTranslator::VSEL(ppu_opcode_t op)
const auto c = get_vr<u32[4]>(op.vc);
// Check if the constant mask doesn't require bit granularity
if (auto [ok, mask] = get_const_vector(c.value, m_addr, 9000); ok)
if (auto [ok, mask] = get_const_vector(c.value, m_addr); ok)
{
bool sel_32 = true;
for (u32 i = 0; i < 4; i++)
+98 -44
View File
@@ -12,30 +12,58 @@ const spu_decoder<spu_iflag> s_spu_iflag;
u32 SPUDisAsm::disasm(u32 pc)
{
last_opcode.clear();
if (pc < m_start_pc || pc >= SPU_LS_SIZE)
{
return 0;
}
dump_pc = pc;
be_t<u32> op;
std::memcpy(&op, m_offset + pc, 4);
m_op = op;
(this->*(s_spu_disasm.decode(m_op)))({ m_op });
format_by_mode();
return 4;
}
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
std::pair<const void*, usz> SPUDisAsm::get_memory_span() const
{
if (m_mode != cpu_disasm_mode::interpreter)
return {m_offset + m_start_pc, SPU_LS_SIZE - m_start_pc};
}
std::unique_ptr<CPUDisAsm> SPUDisAsm::copy_type_erased() const
{
return std::make_unique<SPUDisAsm>(*this);
}
std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc, u32 TTL) const
{
if (!TTL)
{
// Recursion limit (Time To Live)
return {};
}
if (pc == umax)
{
pc = dump_pc;
// Default arg: choose pc of previous instruction
if (dump_pc == 0)
{
// Do not underflow
return {};
}
pc = dump_pc - 4;
}
// Scan LS backwards from this instruction (until PC=0)
// Search for the first register modification or branch instruction
for (s32 i = pc - 4; i >= 0; i -= 4)
for (s32 i = static_cast<s32>(pc); i >= static_cast<s32>(m_start_pc); i -= 4)
{
const u32 opcode = *reinterpret_cast<const be_t<u32>*>(m_offset + i);
const spu_opcode_t op0{ opcode };
@@ -58,6 +86,21 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
continue;
}
// Get constant register value
#define GET_CONST_REG(var, reg) \
{\
/* Search for the constant value of the register*/\
const auto [is_const, value] = try_get_const_value(reg, i - 4, TTL - 1);\
\
if (!is_const)\
{\
/* Cannot compute constant value if register is not constant*/\
return {};\
}\
\
var = value;\
} void() /*<- Require a semicolon*/
//const auto flag = s_spu_iflag.decode(opcode);
// TODO: It detects spurious register modifications
@@ -78,6 +121,10 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
{
return { true, v128::from32p(op0.i16 << 16) };
}
case spu_itype::ILH:
{
return { true, v128::from16p(op0.i16) };
}
case spu_itype::CBD:
case spu_itype::CHD:
case spu_itype::CWD:
@@ -127,25 +174,13 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
}
case spu_itype::IOHL:
{
// Avoid multi-recursion for now
if (dump_pc != pc)
{
return {};
}
v128 reg_val{};
if (i >= 4)
{
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
// But don't limit to it
const auto [is_const, value] = try_get_const_value(reg, i);
// Search for ILHU+IOHL pattern (common pattern for 32-bit constants formation)
// But don't limit to it
GET_CONST_REG(reg_val, op0.rt);
if (is_const)
{
return { true, value | v128::from32p(op0.i16) };
}
}
return {};
return { true, reg_val | v128::from32p(op0.i16) };
}
case spu_itype::STQA:
case spu_itype::STQD:
@@ -156,6 +191,27 @@ std::pair<bool, v128> SPUDisAsm::try_get_const_value(u32 reg, u32 pc) const
// Do not modify RT
break;
}
case spu_itype::SHLQBYI:
{
if (op0.si7)
{
// Unimplemented, doubt needed
return {};
}
// Move register value operation
v128 reg_val{};
GET_CONST_REG(reg_val, op0.ra);
return { true, reg_val };
}
case spu_itype::ORI:
{
v128 reg_val{};
GET_CONST_REG(reg_val, op0.ra);
return { true, reg_val | v128::from32p(op0.si10) };
}
default: return {};
}
}
@@ -236,6 +292,8 @@ SPUDisAsm::insert_mask_info SPUDisAsm::try_get_insert_mask_info(const v128& mask
void SPUDisAsm::WRCH(spu_opcode_t op)
{
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
const auto [is_const, value] = try_get_const_value(op.rt);
if (is_const)
@@ -244,7 +302,7 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{
case MFC_Cmd:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], MFC(value._u8[12])).c_str());
fmt::append(last_opcode, " #%s", MFC(value._u8[12]));
return;
}
case MFC_WrListStallAck:
@@ -252,62 +310,60 @@ void SPUDisAsm::WRCH(spu_opcode_t op)
{
const u32 v = value._u32[3];
if (v && !(v & (v - 1)))
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s (tag=%u)", spu_reg_name[op.rt], SignedHex(v), std::countr_zero(v)).c_str()); // Single-tag mask
fmt::append(last_opcode, " #%s (tag=%u)", SignedHex(v), std::countr_zero(v)); // Single-tag mask
else
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(v)).c_str()); // Multi-tag mask (or zero)
fmt::append(last_opcode, " #%s", SignedHex(v)); // Multi-tag mask (or zero)
return;
}
case MFC_EAH:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
return;
}
case MFC_Size:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u16[6])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u16[6]));
return;
}
case MFC_TagID:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%u", spu_reg_name[op.rt], value._u8[12]).c_str());
fmt::append(last_opcode, " #%u", value._u8[12]);
return;
}
case MFC_WrTagUpdate:
{
const auto upd = fmt::format("%s", mfc_tag_update(value._u32[3]));
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], upd == "empty" ? "IMMEDIATE" : upd).c_str());
fmt::append(last_opcode, " #%s", upd == "empty" ? "IMMEDIATE" : upd);
return;
}
default:
{
DisAsm("wrch", spu_ch_name[op.ra], fmt::format("%s #%s", spu_reg_name[op.rt], SignedHex(value._u32[3])).c_str());
fmt::append(last_opcode, " #%s", SignedHex(value._u32[3]));
return;
}
}
}
DisAsm("wrch", spu_ch_name[op.ra], spu_reg_name[op.rt]);
}
enum CellError : u32;
void SPUDisAsm::IOHL(spu_opcode_t op)
{
const auto [is_const, value] = try_get_const_value(op.rt);
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
const auto [is_const, value] = try_get_const_equal_value_array<u32>(op.rt);
// Only print constant for a 4 equal 32-bit constants array
if (is_const)
{
// Only print constant for a 4 equal 32-bit constants array
if (value == v128::from32p(value._u32[0]))
{
DisAsm("iohl", spu_reg_name[op.rt], fmt::format("%s #%s", SignedHex(+op.i16), (value._u32[0] | op.i16)).c_str());
return;
}
comment_constant(last_opcode, value | op.i16);
}
DisAsm("iohl", spu_reg_name[op.rt], op.i16);
}
void SPUDisAsm::SHUFB(spu_opcode_t op)
{
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
const auto [is_const, value] = try_get_const_value(op.rc);
if (is_const)
@@ -319,15 +375,13 @@ void SPUDisAsm::SHUFB(spu_opcode_t op)
if ((size >= 4u && !src) || (size == 2u && src == 1u) || (size == 1u && src == 3u))
{
// Comment insertion pattern for CWD-alike instruction
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u]", spu_reg_name[op.rc], size * 8, dst).c_str());
fmt::append(last_opcode, " #i%u[%u]", size * 8, dst);
return;
}
// Comment insertion pattern for unknown instruction formations
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], fmt::format("%s #i%u[%u] = [%u]", spu_reg_name[op.rc], size * 8, dst, src).c_str());
fmt::append(last_opcode, " #i%u[%u] = [%u]", size * 8, dst, src);
return;
}
}
DisAsm("shufb", spu_reg_name[op.rt4], spu_reg_name[op.ra], spu_reg_name[op.rb], spu_reg_name[op.rc]);
}
+118 -45
View File
@@ -2,8 +2,9 @@
#include "PPCDisAsm.h"
#include "SPUOpcodes.h"
#include "util/v128.hpp"
union v128;
enum spu_stop_syscall : u32;
static constexpr const char* spu_reg_name[128] =
{
@@ -68,15 +69,26 @@ static constexpr const char* spu_ch_name[128] =
"ch121", "ch122", "ch123", "ch124", "ch125", "ch126", "ch127",
};
namespace utils
{
class shm;
}
void comment_constant(std::string& last_opocde, u64 value, bool print_float = true);
class SPUDisAsm final : public PPCDisAsm
{
std::shared_ptr<utils::shm> m_shm;
public:
SPUDisAsm(cpu_disasm_mode mode, const u8* offset) : PPCDisAsm(mode, offset)
SPUDisAsm(cpu_disasm_mode mode, const u8* offset, u32 start_pc = 0) : PPCDisAsm(mode, offset, start_pc)
{
}
~SPUDisAsm()
~SPUDisAsm() = default;
void set_shm(std::shared_ptr<utils::shm> shm)
{
m_shm = std::move(shm);
}
private:
@@ -85,84 +97,125 @@ private:
return spu_branch_target(dump_pc, imm);
}
static const char* BrIndirectSuffix(u32 de)
static char BrIndirectSuffix(u32 de)
{
switch (de)
{
case 0b01: return "e";
case 0b10: return "d";
//case 0b11: return "(undef)";
default: return "";
case 0b01: return 'e';
case 0b10: return 'd';
case 0b11: return '!';
default: return '\0';
}
}
std::string& FixOp(std::string& op) const
{
if (m_mode != cpu_disasm_mode::normal)
{
op.append(std::max<int>(10 - ::narrow<int>(op.size()), 0), ' ');
}
return op;
}
void DisAsm(const char* op)
{
Write(op);
last_opcode += op;
}
void DisAsm(std::string op, u32 a1)
void DisAsm(std::string_view op, u32 a1)
{
Write(fmt::format("%s 0x%x", FixOp(op), a1));
fmt::append(last_opcode, "%-*s 0x%x", PadOp(), op, a1);
}
void DisAsm(std::string op, const char* a1)
void DisAsm(std::string_view op, const char* a1)
{
Write(fmt::format("%s %s", FixOp(op), a1));
fmt::append(last_opcode, "%-*s %s", PadOp(), op, a1);
}
void DisAsm(std::string op, const char* a1, const char* a2)
void DisAsm(std::string_view op, const char* a1, const char* a2)
{
Write(fmt::format("%s %s,%s", FixOp(op), a1, a2));
fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, a2);
}
void DisAsm(std::string op, int a1, const char* a2)
void DisAsm(std::string_view op, int a1, const char* a2)
{
Write(fmt::format("%s 0x%x,%s", FixOp(op), a1, a2));
fmt::append(last_opcode, "%-*s 0x%x,%s", PadOp(), op, a1, a2);
}
void DisAsm(std::string op, const char* a1, int a2)
void DisAsm(std::string_view op, const char* a1, int a2)
{
Write(fmt::format("%s %s,%s", FixOp(op), a1, SignedHex(a2)));
fmt::append(last_opcode, "%-*s %s,%s", PadOp(), op, a1, SignedHex(a2));
}
void DisAsm(std::string op, int a1, int a2)
void DisAsm(std::string_view op, int a1, int a2)
{
Write(fmt::format("%s 0x%x,0x%x", FixOp(op), a1, a2));
fmt::append(last_opcode, "%-*s 0x%x,0x%x", PadOp(), op, a1, a2);
}
void DisAsm(std::string op, const char* a1, const char* a2, const char* a3)
void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3)
{
Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, a3));
fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, a3);
}
void DisAsm(std::string op, const char* a1, int a2, const char* a3)
void DisAsm(std::string_view op, const char* a1, int a2, const char* a3)
{
Write(fmt::format("%s %s,%s(%s)", FixOp(op), a1, SignedHex(a2), a3));
fmt::append(last_opcode, "%-*s %s,%s(%s)", PadOp(), op, a1, SignedHex(a2), a3);
}
void DisAsm(std::string op, const char* a1, const char* a2, int a3)
void DisAsm(std::string_view op, const char* a1, const char* a2, int a3)
{
Write(fmt::format("%s %s,%s,%s", FixOp(op), a1, a2, SignedHex(a3)));
fmt::append(last_opcode, "%-*s %s,%s,%s", PadOp(), op, a1, a2, SignedHex(a3));
}
void DisAsm(std::string op, const char* a1, const char* a2, const char* a3, const char* a4)
void DisAsm(std::string_view op, const char* a1, const char* a2, const char* a3, const char* a4)
{
Write(fmt::format("%s %s,%s,%s,%s", FixOp(op), a1, a2, a3, a4));
fmt::append(last_opcode, "%-*s %s,%s,%s,%s", PadOp(), op, a1, a2, a3, a4);
}
using field_de_t = decltype(spu_opcode_t::de);
void DisAsm(std::string op, field_de_t de, const char* a1)
void DisAsm(std::string_view op, field_de_t de, const char* a1)
{
Write(fmt::format("%s %s", FixOp(op.append(BrIndirectSuffix(de))), a1));
const char c = BrIndirectSuffix(de);
if (c == '!')
{
// Invalid
fmt::append(last_opcode, "?? ?? (%s)", op);
return;
}
fmt::append(last_opcode, "%-*s %s", PadOp(op, c ? 1 : 0), op, a1);
insert_char_if(op, !!c, c);
}
void DisAsm(std::string op, field_de_t de, const char* a1, const char* a2)
void DisAsm(std::string_view op, field_de_t de, const char* a1, const char* a2)
{
Write(fmt::format("%s %s,%s", FixOp(op.append(BrIndirectSuffix(de))), a1, a2));
const char c = BrIndirectSuffix(de);
if (c == '!')
{
fmt::append(last_opcode, "?? ?? (%s)", op);
return;
}
fmt::append(last_opcode, "%-*s %s,%s", PadOp(op, c ? 1 : 0), op, a1, a2);
insert_char_if(op, !!c, c);
}
public:
u32 disasm(u32 pc) override;
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1) const;
std::pair<const void*, usz> get_memory_span() const override;
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
// Get constant value if the original array is made of only repetitions of the same value
template <typename T> requires (sizeof(T) < sizeof(v128) && !(sizeof(v128) % sizeof(T)))
std::pair<bool, T> try_get_const_equal_value_array(u32 reg, u32 pc = -1, u32 TTL = 10) const
{
auto [ok, res] = try_get_const_value(reg, pc, TTL);
if (!ok)
{
return {};
}
v128 test_value{};
T sample{};
std::memcpy(&sample, res._bytes, sizeof(T));
for (u32 i = 0; i < sizeof(v128); i += sizeof(T))
{
std::memcpy(test_value._bytes + i, &sample, sizeof(T));
}
if (test_value != res)
{
return {};
}
return {ok, sample};
}
struct insert_mask_info
{
@@ -176,7 +229,7 @@ public:
//0 - 10
void STOP(spu_opcode_t op)
{
op.rb ? UNK(op) : DisAsm("stop", op.opcode & 0x3fff);
op.rb ? UNK(op) : DisAsm("stop", fmt::format("0x%x #%s", op.opcode & 0x3fff, spu_stop_syscall{op.opcode & 0x3fff}).c_str());
}
void LNOP(spu_opcode_t /*op*/)
{
@@ -819,10 +872,12 @@ public:
void ILHU(spu_opcode_t op)
{
DisAsm("ilhu", spu_reg_name[op.rt], op.i16);
comment_constant(last_opcode, op.i16 << 16);
}
void ILH(spu_opcode_t op)
{
DisAsm("ilh", spu_reg_name[op.rt], op.i16);
comment_constant(last_opcode, (op.i16 << 16) | op.i16);
}
void IOHL(spu_opcode_t op);
@@ -838,6 +893,12 @@ public:
}
DisAsm("ori", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(+op.ra); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value | static_cast<u32>(op.si10));
}
}
void ORHI(spu_opcode_t op)
{
@@ -870,6 +931,12 @@ public:
void AI(spu_opcode_t op)
{
DisAsm("ai", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value + static_cast<u32>(op.si10));
}
}
void AHI(spu_opcode_t op)
{
@@ -886,6 +953,12 @@ public:
void XORI(spu_opcode_t op)
{
DisAsm("xori", spu_reg_name[op.rt], spu_reg_name[op.ra], op.si10);
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
{
// Comment constant formation
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10));
}
}
void XORHI(spu_opcode_t op)
{
@@ -991,6 +1064,6 @@ public:
void UNK(spu_opcode_t /*op*/)
{
Write("?? ??");
DisAsm("?? ??");
}
};
+22 -3
View File
@@ -116,13 +116,22 @@ void spu_interpreter::set_interrupt_status(spu_thread& spu, spu_opcode_t op)
spu.set_interrupt_status(false);
}
spu.check_mfc_interrupts(spu.pc);
if (spu.check_mfc_interrupts(spu.pc) && spu.state & cpu_flag::pending)
{
spu.do_mfc();
}
}
bool spu_interpreter::STOP(spu_thread& spu, spu_opcode_t op)
{
if (!spu.stop_and_signal(op.opcode & 0x3fff))
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
const bool advance_pc = spu.stop_and_signal(op.opcode & 0x3fff);
spu.allow_interrupts_in_cpu_work = allow;
if (!advance_pc)
{
return false;
}
@@ -163,8 +172,12 @@ bool spu_interpreter::MFSPR(spu_thread& spu, spu_opcode_t op)
bool spu_interpreter::RDCH(spu_thread& spu, spu_opcode_t op)
{
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
const s64 result = spu.get_ch_value(op.ra);
spu.allow_interrupts_in_cpu_work = allow;
if (result < 0)
{
return false;
@@ -425,7 +438,13 @@ bool spu_interpreter::MTSPR(spu_thread&, spu_opcode_t)
bool spu_interpreter::WRCH(spu_thread& spu, spu_opcode_t op)
{
if (!spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]))
const bool allow = std::exchange(spu.allow_interrupts_in_cpu_work, false);
const bool advance_pc = spu.set_ch_value(op.ra, spu.gpr[op.rt]._u32[3]);
spu.allow_interrupts_in_cpu_work = allow;
if (!advance_pc)
{
return false;
}
File diff suppressed because it is too large Load Diff
+215 -33
View File
@@ -1216,6 +1216,8 @@ std::string spu_thread::dump_regs() const
const bool floats_only = debugger_float_mode.load();
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
for (u32 i = 0; i < 128; i++, ret += '\n')
{
fmt::append(ret, "%s: ", spu_reg_name[i]);
@@ -1269,7 +1271,6 @@ std::string spu_thread::dump_regs() const
if (i3 >= 0x80 && is_exec_code(i3))
{
SPUDisAsm dis_asm(cpu_disasm_mode::normal, ls);
dis_asm.disasm(i3);
fmt::append(ret, " -> %s", dis_asm.last_opcode);
}
@@ -1642,6 +1643,8 @@ void spu_thread::cpu_task()
{
ensure(spu_runtime::g_interpreter);
allow_interrupts_in_cpu_work = true;
while (true)
{
if (state) [[unlikely]]
@@ -1652,6 +1655,68 @@ void spu_thread::cpu_task()
spu_runtime::g_interpreter(*this, _ptr<u8>(0), nullptr);
}
allow_interrupts_in_cpu_work = false;
}
}
void spu_thread::cpu_work()
{
if (std::exchange(in_cpu_work, true))
{
return;
}
const u32 old_iter_count = cpu_work_iteration_count++;
const auto timeout = +g_cfg.core.mfc_transfers_timeout;
bool work_left = false;
if (u32 shuffle_count = g_cfg.core.mfc_transfers_shuffling)
{
// If either MFC size exceeds limit or timeout has been reached execute pending MFC commands
if (mfc_size > shuffle_count || (timeout && get_system_time() - mfc_last_timestamp >= timeout))
{
work_left = do_mfc(false, false);
}
else
{
work_left = mfc_size != 0; // TODO: Optimize
}
}
bool gen_interrupt = false;
// Check interrupts every 16 iterations
if (!(old_iter_count % 16) && allow_interrupts_in_cpu_work)
{
if (u32 mask = ch_events.load().mask & SPU_EVENT_INTR_BUSY_CHECK)
{
// LR check is expensive, do it once in a while
if (old_iter_count /*% 256*/)
{
mask &= ~SPU_EVENT_LR;
}
get_events(mask);
}
gen_interrupt = check_mfc_interrupts(pc);
work_left |= interrupts_enabled;
}
in_cpu_work = false;
if (!work_left)
{
state -= cpu_flag::pending;
}
if (gen_interrupt)
{
// Interrupt! escape everything and restart execution
spu_runtime::g_escape(this);
}
}
@@ -1710,12 +1775,12 @@ spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u
{
if (!group)
{
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, 0x200));
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, vm::page_size_64k));
}
else
{
// 0x1000 indicates falloc to allocate page with no access rights in base memory
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, 0x1200));
// alloc_hidden indicates falloc to allocate page with no access rights in base memory
ensure(vm::get(vm::spu)->falloc(vm_offset(), SPU_LS_SIZE, &shm, vm::page_size_64k | vm::alloc_hidden));
}
// Try to guess free area
@@ -2948,14 +3013,15 @@ void spu_thread::do_putlluc(const spu_mfc_cmd& args)
vm::reservation_notifier(addr).notify_all(-128);
}
void spu_thread::do_mfc(bool /*wait*/)
bool spu_thread::do_mfc(bool can_escape, bool must_finish)
{
u32 removed = 0;
u32 barrier = 0;
u32 fence = 0;
u16 exec_mask = 0;
bool pending = false;
// Process enqueued commands
static_cast<void>(std::remove_if(mfc_queue + 0, mfc_queue + mfc_size, [&](spu_mfc_cmd& args)
auto process_command = [&](spu_mfc_cmd& args)
{
// Select tag bit in the tag mask or the stall mask
const u32 mask = utils::rol32(1, args.tag);
@@ -2992,6 +3058,20 @@ void spu_thread::do_mfc(bool /*wait*/)
return false;
}
// If command is not enabled in execution mask, execute it later
if (!(exec_mask & (1u << (&args - mfc_queue))))
{
if (args.cmd & MFC_BARRIER_MASK)
{
barrier |= mask;
}
// Fence is set for any command
fence |= mask;
pending = true;
return false;
}
if (args.cmd & MFC_LIST_MASK)
{
if (!(args.tag & 0x80))
@@ -3028,32 +3108,68 @@ void spu_thread::do_mfc(bool /*wait*/)
removed++;
return true;
}));
};
mfc_size -= removed;
mfc_barrier = barrier;
mfc_fence = fence;
if (removed && ch_tag_upd)
auto get_exec_mask = [&size = mfc_size]
{
const u32 completed = get_mfc_completed();
// Get commands' execution mask
// Mask bits are always set when mfc_transfers_shuffling is 0
return static_cast<u16>((0 - (1u << std::min<u32>(g_cfg.core.mfc_transfers_shuffling, size))) | __rdtsc());
};
if (completed && ch_tag_upd == MFC_TAG_UPDATE_ANY)
// Process enqueued commands
while (true)
{
removed = 0;
barrier = 0;
fence = 0;
// Shuffle commands execution (if enabled), explicit barriers are obeyed
pending = false;
exec_mask = get_exec_mask();
static_cast<void>(std::remove_if(mfc_queue + 0, mfc_queue + mfc_size, process_command));
mfc_size -= removed;
mfc_barrier = barrier;
mfc_fence = fence;
if (removed && ch_tag_upd)
{
ch_tag_stat.set_value(completed);
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
const u32 completed = get_mfc_completed();
if (completed && ch_tag_upd == MFC_TAG_UPDATE_ANY)
{
ch_tag_stat.set_value(completed);
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
}
else if (completed == ch_tag_mask && ch_tag_upd == MFC_TAG_UPDATE_ALL)
{
ch_tag_stat.set_value(completed);
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
}
}
else if (completed == ch_tag_mask && ch_tag_upd == MFC_TAG_UPDATE_ALL)
if (can_escape && check_mfc_interrupts(pc + 4))
{
ch_tag_stat.set_value(completed);
ch_tag_upd = MFC_TAG_UPDATE_IMMEDIATE;
spu_runtime::g_escape(this);
}
if (!pending)
{
break;
}
if (!must_finish && g_cfg.core.mfc_shuffling_in_steps)
{
// Exit early, not all pending commands have to be executed at a single iteration
// Update last timestamp so the next MFC timeout check will use the current time
mfc_last_timestamp = get_system_time();
return true;
}
}
if (check_mfc_interrupts(pc + 4))
{
spu_runtime::g_escape(this);
}
return false;
}
bool spu_thread::check_mfc_interrupts(u32 next_pc)
@@ -3109,6 +3225,22 @@ bool spu_thread::process_mfc_cmd()
// Stall infinitely if MFC queue is full
while (mfc_size >= 16) [[unlikely]]
{
// Reset MFC timestamp in the case of full queue
mfc_last_timestamp = 0;
if (test_stopped())
{
return false;
}
// Process MFC commands
do_mfc();
if (mfc_size < 16)
{
break;
}
auto old = state.add_fetch(cpu_flag::wait);
if (is_stopped(old))
@@ -3205,7 +3337,7 @@ bool spu_thread::process_mfc_cmd()
{
state += cpu_flag::wait + cpu_flag::temp;
std::this_thread::yield();
!check_state();
static_cast<void>(check_state());
}
}())
{
@@ -3382,12 +3514,18 @@ bool spu_thread::process_mfc_cmd()
{
if (do_dma_check(ch_mfc_cmd)) [[likely]]
{
if (ch_mfc_cmd.size)
if (!g_cfg.core.mfc_transfers_shuffling)
{
do_dma_transfer(this, ch_mfc_cmd, ls);
if (ch_mfc_cmd.size)
{
do_dma_transfer(this, ch_mfc_cmd, ls);
}
return true;
}
return true;
if (!state.test_and_set(cpu_flag::pending))
mfc_last_timestamp = get_system_time();
}
mfc_queue[mfc_size++] = ch_mfc_cmd;
@@ -3429,9 +3567,17 @@ bool spu_thread::process_mfc_cmd()
if (do_dma_check(cmd)) [[likely]]
{
if (!cmd.size || do_list_transfer(cmd)) [[likely]]
if (!g_cfg.core.mfc_transfers_shuffling)
{
return true;
if (!cmd.size || do_list_transfer(cmd)) [[likely]]
{
return true;
}
}
else
{
if (!state.test_and_set(cpu_flag::pending))
mfc_last_timestamp = get_system_time();
}
}
@@ -3445,6 +3591,7 @@ bool spu_thread::process_mfc_cmd()
if (check_mfc_interrupts(pc + 4))
{
do_mfc(false);
spu_runtime::g_escape(this);
}
@@ -3647,9 +3794,20 @@ void spu_thread::set_interrupt_status(bool enable)
if (enable)
{
// Detect enabling interrupts with events masked
if (auto mask = ch_events.load().mask; mask & ~SPU_EVENT_INTR_IMPLEMENTED)
if (auto mask = ch_events.load().mask; mask & SPU_EVENT_INTR_BUSY_CHECK)
{
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)", mask);
if (g_cfg.core.spu_decoder != spu_decoder_type::precise && g_cfg.core.spu_decoder != spu_decoder_type::fast)
{
fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x): Use interpreterts", mask);
}
spu_log.trace("SPU Interrupts (mask=0x%x) are using CPU busy checking mode", mask);
// Process interrupts in cpu_work()
if (state.none_of(cpu_flag::pending))
{
state += cpu_flag::pending;
}
}
}
@@ -3714,6 +3872,11 @@ s64 spu_thread::get_ch_value(u32 ch)
state += cpu_flag::wait + cpu_flag::temp;
}
if (state & cpu_flag::pending)
{
do_mfc();
}
for (int i = 0; i < 10 && channel.get_count() == 0; i++)
{
busy_wait();
@@ -3739,6 +3902,11 @@ s64 spu_thread::get_ch_value(u32 ch)
while (true)
{
if (state & cpu_flag::pending)
{
do_mfc();
}
for (int i = 0; i < 10 && ch_in_mbox.get_count() == 0; i++)
{
busy_wait();
@@ -3770,13 +3938,17 @@ s64 spu_thread::get_ch_value(u32 ch)
case MFC_RdTagStat:
{
if (state & cpu_flag::pending)
{
do_mfc();
}
if (u32 out; ch_tag_stat.try_read(out))
{
ch_tag_stat.set_value(0, false);
return out;
}
// Will stall infinitely
return read_channel(ch_tag_stat);
}
@@ -3929,6 +4101,11 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
{
if (get_type() >= spu_type::raw)
{
if (state & cpu_flag::pending)
{
do_mfc();
}
if (ch_out_intr_mbox.get_count())
{
state += cpu_flag::wait;
@@ -4060,6 +4237,11 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
case SPU_WrOutMbox:
{
if (state & cpu_flag::pending)
{
do_mfc();
}
if (ch_out_mbox.get_count())
{
state += cpu_flag::wait;
+10 -3
View File
@@ -82,8 +82,7 @@ enum : u32
SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN | SPU_EVENT_S1 | SPU_EVENT_S2, // Mask of implemented events
SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN,
SPU_EVENT_INTR_TEST = SPU_EVENT_INTR_IMPLEMENTED,
SPU_EVENT_INTR_BUSY_CHECK = SPU_EVENT_IMPLEMENTED & ~SPU_EVENT_INTR_IMPLEMENTED,
};
// SPU Class 0 Interrupts
@@ -631,6 +630,7 @@ public:
virtual std::string dump_misc() const override;
virtual void cpu_task() override final;
virtual void cpu_return() override;
virtual void cpu_work() override;
virtual ~spu_thread() override;
void cleanup();
void cpu_init();
@@ -668,6 +668,9 @@ public:
u32 mfc_barrier = -1;
u32 mfc_fence = -1;
// Timestamp of the first postponed command (transfers shuffling related)
u64 mfc_last_timestamp = 0;
// MFC proxy command data
spu_mfc_cmd mfc_prxy_cmd;
shared_mutex mfc_prxy_mtx;
@@ -774,6 +777,10 @@ public:
u64 mfc_dump_idx = 0;
static constexpr u32 max_mfc_dump_idx = 2048;
bool in_cpu_work = false;
bool allow_interrupts_in_cpu_work = false;
u8 cpu_work_iteration_count = 0;
std::array<v128, 0x4000> stack_mirror; // Return address information
const char* current_func{}; // Current STOP or RDCH blocking function
@@ -787,7 +794,7 @@ public:
bool do_list_transfer(spu_mfc_cmd& args);
void do_putlluc(const spu_mfc_cmd& args);
bool do_putllc(const spu_mfc_cmd& args);
void do_mfc(bool wait = true);
bool do_mfc(bool can_escape = true, bool must_finish = true);
u32 get_mfc_completed() const;
bool process_mfc_cmd();
+72
View File
@@ -936,9 +936,81 @@ const std::array<std::pair<ppu_function_t, std::string_view>, 1024> g_ppu_syscal
#undef BIND_SYSC
#undef NULL_FUNC
// TODO: more enums
enum CellAdecError : u32;
enum CellAtracError : u32;
enum CellAtracMultiError : u32;
enum CellAudioError : u32;
enum CellAudioOutError : u32;
enum CellAudioInError : u32;
enum CellVideoOutError : u32;
enum CellSpursCoreError : u32;
enum CellSpursPolicyModuleError : u32;
enum CellSpursTaskError : u32;
enum CellSpursJobError : u32;
enum CellGameError : u32;
enum CellGameDataError : u32;
enum CellDiscGameError : u32;
enum CellHddGameError : u32;
enum SceNpTrophyError : u32;
enum SceNpError : u32;
template <u64 EnumMin, typename E>
constexpr auto formatter_of = std::make_pair(EnumMin, &fmt_class_string<E>::format);
const std::map<u64, void(*)(std::string&, u64)> s_error_codes_formatting_by_type
{
formatter_of<0x80610000, CellAdecError>,
formatter_of<0x80612100, CellAdecError>,
formatter_of<0x80610300, CellAtracError>,
formatter_of<0x80610b00, CellAtracMultiError>,
formatter_of<0x80310700, CellAudioError>,
formatter_of<0x8002b240, CellAudioOutError>,
formatter_of<0x8002b260, CellAudioInError>,
formatter_of<0x8002b220, CellVideoOutError>,
formatter_of<0x80410700, CellSpursCoreError>,
formatter_of<0x80410800, CellSpursPolicyModuleError>,
formatter_of<0x80410900, CellSpursTaskError>,
formatter_of<0x80410A00, CellSpursJobError>,
formatter_of<0x8002cb00, CellGameError>,
formatter_of<0x8002b600, CellGameDataError>,
formatter_of<0x8002bd00, CellDiscGameError>,
formatter_of<0x8002ba00, CellHddGameError>,
formatter_of<0x80022900, SceNpTrophyError>,
formatter_of<0x80029500, SceNpError>,
};
template<>
void fmt_class_string<CellError>::format(std::string& out, u64 arg)
{
// Test if can be formatted by this formatter
const bool lv2_cell_error = (arg >> 8) == 0x800100u;
if (!lv2_cell_error)
{
// Format by external enum formatters
auto upper = s_error_codes_formatting_by_type.upper_bound(arg);
if (upper == s_error_codes_formatting_by_type.begin())
{
// Format as unknown by another enum formatter
upper->second(out, arg);
return;
}
// Find the formatter whose base is the highest that is not more than arg
const auto found = std::prev(upper);
found->second(out, arg);
return;
}
format_enum(out, arg, [](auto error)
{
switch (error)
+62 -18
View File
@@ -23,19 +23,27 @@ lv2_fs_mount_point g_mp_sys_dev_flash{"", 512, 8192, lv2_mp_flag::read_only + lv
lv2_fs_mount_point g_mp_sys_dev_flash2{ "", 512, 8192, lv2_mp_flag::no_uid_gid }; // TODO confirm
lv2_fs_mount_point g_mp_sys_dev_flash3{ "", 512, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid }; // TODO confirm
template<>
void fmt_class_string<lv2_file_type>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](lv2_file_type type)
{
switch (type)
{
case lv2_file_type::regular: return "Regular file";
case lv2_file_type::sdata: return "SDATA";
case lv2_file_type::edata: return "EDATA";
}
return unknown;
});
}
template<>
void fmt_class_string<lv2_file>::format(std::string& out, u64 arg)
{
const auto& file = get_object(arg);
std::string_view type_s;
switch (file.type)
{
case lv2_file_type::regular: type_s = "Regular file"; break;
case lv2_file_type::sdata: type_s = "SDATA"; break;
case lv2_file_type::edata: type_s = "EDATA"; break;
}
auto get_size = [](u64 size) -> std::string
{
if (size == umax)
@@ -61,7 +69,7 @@ void fmt_class_string<lv2_file>::format(std::string& out, u64 arg)
const usz pos = file.file ? file.file.pos() : umax;
const usz size = file.file ? file.file.size() : umax;
fmt::append(out, u8"%s, “%s”, Mode: 0x%x, Flags: 0x%x, Pos: %s, Size: %s", type_s, file.name.data(), file.mode, file.flags, get_size(pos), get_size(size));
fmt::append(out, u8"%s, “%s”, Mode: 0x%x, Flags: 0x%x, Pos: %s, Size: %s", file.type, file.name.data(), file.mode, file.flags, get_size(pos), get_size(size));
}
template<>
@@ -637,12 +645,25 @@ error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, v
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file || file->flags & CELL_FS_O_WRONLY)
if (!file || (nbytes && file->flags & CELL_FS_O_WRONLY))
{
nread.try_write(0); // nread writing is allowed to fail, error code is unchanged
return CELL_EBADF;
}
if (!nbytes)
{
// Whole function is skipped, only EBADF and EBUSY are checked
if (file->lock == 1)
{
nread.try_write(0);
return CELL_EBUSY;
}
*nread = 0;
return CELL_OK;
}
std::lock_guard lock(file->mp->mutex);
if (!file->file)
@@ -681,12 +702,30 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file || !(file->flags & CELL_FS_O_ACCMODE))
if (!file || (nbytes && !(file->flags & CELL_FS_O_ACCMODE)))
{
nwrite.try_write(0); // nwrite writing is allowed to fail, error code is unchanged
return CELL_EBADF;
}
if (!nbytes)
{
// Whole function is skipped, only EBADF and EBUSY are checked
if (file->lock == 1)
{
nwrite.try_write(0);
return CELL_EBUSY;
}
*nwrite = 0;
return CELL_OK;
}
if (file->type != lv2_file_type::regular)
{
sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, nbytes, file->name.data());
}
if (file->mp->flags & lv2_mp_flag::read_only)
{
nwrite.try_write(0);
@@ -727,13 +766,11 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
ppu.state += cpu_flag::wait;
lv2_obj::sleep(ppu);
sys_fs.trace("sys_fs_close(fd=%d)", fd);
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
if (!file)
{
return CELL_EBADF;
return {CELL_EBADF, fd};
}
{
@@ -741,7 +778,7 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
if (!file->file)
{
return CELL_EBADF;
return {CELL_EBADF, fd};
}
if (!(file->mp->flags & (lv2_mp_flag::read_only + lv2_mp_flag::cache)) && file->flags & CELL_FS_O_ACCMODE)
@@ -772,12 +809,14 @@ error_code sys_fs_close(ppu_thread& ppu, u32 fd)
if (!ret || ret.ret == CELL_EBADF)
{
return CELL_EBADF;
return {CELL_EBADF, fd};
}
sys_fs.warning("sys_fs_close(fd=%u): path='%s'", fd, file->name.data());
if (file->lock == 1)
{
return CELL_EBUSY;
return {CELL_EBUSY, fd};
}
return CELL_OK;
@@ -1396,6 +1435,11 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
return CELL_EROFS;
}
if (op == 0x8000000b && file->type != lv2_file_type::regular && arg->size)
{
sys_fs.error("%s type: Writing %u bytes to FD=%d (path=%s)", file->type, arg->size, file->name.data());
}
std::lock_guard lock(file->mp->mutex);
if (!file->file)
@@ -1467,7 +1511,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
return nullptr;
}
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, file.flags, file.real_path, lv2_file_type::sdata);
return std::make_shared<lv2_file>(file, std::move(stream), file.mode, CELL_FS_O_RDONLY, file.real_path, lv2_file_type::sdata);
}))
{
arg->out_code = CELL_OK;
+2 -2
View File
@@ -12,7 +12,7 @@ error_code _sys_lwmutex_create(ppu_thread& ppu, vm::ptr<u32> lwmutex_id, u32 pro
{
ppu.state += cpu_flag::wait;
sys_lwmutex.warning(u8"_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, has_name=0x%x, name=0x%llx (“%s”))", lwmutex_id, protocol, control, has_name, name, lv2_obj::name64(std::bit_cast<be_t<u64>>(name)));
sys_lwmutex.trace(u8"_sys_lwmutex_create(lwmutex_id=*0x%x, protocol=0x%x, control=*0x%x, has_name=0x%x, name=0x%llx (“%s”))", lwmutex_id, protocol, control, has_name, name, lv2_obj::name64(std::bit_cast<be_t<u64>>(name)));
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_RETRY && protocol != SYS_SYNC_PRIORITY)
{
@@ -38,7 +38,7 @@ error_code _sys_lwmutex_destroy(ppu_thread& ppu, u32 lwmutex_id)
{
ppu.state += cpu_flag::wait;
sys_lwmutex.warning("_sys_lwmutex_destroy(lwmutex_id=0x%x)", lwmutex_id);
sys_lwmutex.trace("_sys_lwmutex_destroy(lwmutex_id=0x%x)", lwmutex_id);
std::shared_ptr<lv2_lwmutex> _mutex;
+22 -4
View File
@@ -462,24 +462,30 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
auto& pf_events = g_fxo->get<page_fault_event_entries>();
std::lock_guard pf_lock(pf_events.pf_mutex);
const auto mem = vm::get(vm::any, addr);
if (!mem || mem->addr != addr)
{
return {CELL_EINVAL, addr};
}
for (const auto& ev : pf_events.events)
{
auto mem = vm::get(vm::any, addr);
if (mem && addr <= ev.second && ev.second <= addr + mem->size - 1)
if (addr <= ev.second && ev.second <= addr + mem->size - 1)
{
return CELL_EBUSY;
}
}
// Try to unmap area
const auto area = vm::unmap(addr, true);
const auto [area, success] = vm::unmap(addr, true, &mem);
if (!area)
{
return {CELL_EINVAL, addr};
}
if (area.use_count() != 1)
if (!success)
{
return CELL_EBUSY;
}
@@ -586,6 +592,12 @@ error_code sys_mmapper_map_shared_memory(ppu_thread& ppu, u32 addr, u32 mem_id,
if (!area->falloc(addr, mem->size, &mem->shm, mem->align == 0x10000 ? SYS_MEMORY_PAGE_SIZE_64K : SYS_MEMORY_PAGE_SIZE_1M))
{
mem->counter--;
if (!area->is_valid())
{
return {CELL_EINVAL, addr};
}
return CELL_EBUSY;
}
@@ -634,6 +646,12 @@ error_code sys_mmapper_search_and_map(ppu_thread& ppu, u32 start_addr, u32 mem_i
if (!addr)
{
mem->counter--;
if (!area->is_valid())
{
return {CELL_EINVAL, start_addr};
}
return CELL_ENOMEM;
}
+40 -13
View File
@@ -978,7 +978,7 @@ struct network_thread
WSADATA wsa_data;
WSAStartup(MAKEWORD(2, 2), &wsa_data);
#endif
if (g_cfg.net.psn_status == np_psn_status::rpcn)
if (g_cfg.net.psn_status == np_psn_status::psn_rpcn)
list_p2p_ports.emplace(std::piecewise_construct, std::forward_as_tuple(3658), std::forward_as_tuple(3658));
}
@@ -1603,6 +1603,13 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
std::memcpy(addr_buf.buf, addr.get_ptr(), 16);
name.sin_port = std::bit_cast<u16>(psa_in->sin_port);
name.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
#ifdef _WIN32
// Windows doesn't support sending packets to 0.0.0.0 but it works on unixes, send to 127.0.0.1 instead
if (name.sin_addr.s_addr == 0x00000000)
{
name.sin_addr.s_addr = 0x0100007F;
}
#endif
}
else
{
@@ -1672,7 +1679,7 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
sock.p2ps.op_vport = dst_vport;
sock.p2ps.cur_seq = send_hdr.seq + 1;
sock.p2ps.data_beg_seq = 0;
sock.p2ps.data_available = 0;
sock.p2ps.data_available = 0u;
sock.p2ps.received_data.clear();
sock.p2ps.status = lv2_socket::p2ps_i::stream_status::stream_handshaking;
@@ -1896,7 +1903,7 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
return -SYS_NET_EINVAL;
}
::sockaddr_storage native_addr;
::sockaddr_storage native_addr{};
::socklen_t native_addrlen = sizeof(native_addr);
lv2_socket_type type;
@@ -1909,6 +1916,12 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
type = sock.type;
p2p_vport = sock.p2p.vport;
// Unbound P2P socket special case
if ((sock.type == SYS_NET_SOCK_DGRAM_P2P || sock.type == SYS_NET_SOCK_STREAM_P2P) && sock.socket == 0)
{
return {};
}
if (::getsockname(sock.socket, reinterpret_cast<struct sockaddr*>(&native_addr), &native_addrlen) == 0)
{
ensure(native_addr.ss_family == AF_INET);
@@ -1921,8 +1934,6 @@ error_code sys_net_bnet_getsockname(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sock
// windows doesn't support getsockname for sockets that are not bound
if (get_native_error() == WSAEINVAL)
{
memset(&native_addr, 0, native_addrlen);
native_addr.ss_family = AF_INET;
return {};
}
}
@@ -2320,8 +2331,8 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
{
const auto get_data = [&](unsigned char *dest_buf)
{
const u32 to_give = std::min(sock.p2ps.data_available, len);
sys_net.trace("STREAM-P2P socket had %d available, given %d", sock.p2ps.data_available, to_give);
const u32 to_give = std::min<u32>(sock.p2ps.data_available, len);
sys_net.trace("STREAM-P2P socket had %u available, given %u", sock.p2ps.data_available, to_give);
u32 left_to_give = to_give;
while (left_to_give)
@@ -2608,6 +2619,14 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
name.sin_port = std::bit_cast<u16>(psa_in->sin_port);
name.sin_addr.s_addr = std::bit_cast<u32>(psa_in->sin_addr);
#ifdef _WIN32
// Windows doesn't support sending packets to 0.0.0.0 but it works on unixes, send to 127.0.0.1 instead
if (name.sin_addr.s_addr == 0x00000000)
{
name.sin_addr.s_addr = 0x0100007F;
}
#endif
char ip_str[16];
inet_ntop(AF_INET, &name.sin_addr, ip_str, sizeof(ip_str));
sys_net.trace("Sending to %s:%d", ip_str, psa_in->sin_port);
@@ -2652,7 +2671,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
}
else if (type == SYS_NET_SOCK_STREAM_P2P)
{
constexpr s64 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp)));
constexpr u32 max_data_len = (65535 - (sizeof(u16) + sizeof(lv2_socket::p2ps_i::encapsulated_tcp)));
// Prepare address
name.sin_family = AF_INET;
@@ -2664,10 +2683,10 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
tcp_header.dst_port = sock.p2ps.op_vport;
// chop it up
std::vector<std::vector<u8>> stream_packets;
s64 cur_total_len = len;
u32 cur_total_len = len;
while(cur_total_len > 0)
{
s64 cur_data_len;
u32 cur_data_len;
if (cur_total_len >= max_data_len)
cur_data_len = max_data_len;
else
@@ -2676,7 +2695,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
tcp_header.length = cur_data_len;
tcp_header.seq = sock.p2ps.cur_seq;
auto packet = nt_p2p_port::generate_u2s_packet(tcp_header, &_buf[len - cur_total_len], cur_data_len);
auto packet = nt_p2p_port::generate_u2s_packet(tcp_header, &_buf[len - cur_total_len], cur_data_len);
nt_p2p_port::send_u2s_packet(sock, s, std::move(packet), &name, tcp_header.seq);
cur_total_len -= cur_data_len;
@@ -3273,7 +3292,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
{
if ((fds[i].events & SYS_NET_POLLIN) && sock->p2ps.data_available)
{
sys_net.trace("[P2PS] p2ps has %d bytes available", sock->p2ps.data_available);
sys_net.trace("[P2PS] p2ps has %u bytes available", sock->p2ps.data_available);
fds_buf[i].revents |= SYS_NET_POLLIN;
}
@@ -3444,7 +3463,15 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
if (exceptfds)
{
sys_net.error("sys_net_bnet_select(): exceptfds not implemented");
struct log_t
{
atomic_t<bool> logged = false;
};
if (!g_fxo->get<log_t>().logged.exchange(true))
{
sys_net.error("sys_net_bnet_select(): exceptfds not implemented");
}
}
sys_net_fd_set rread{}, _readfds{};
+2 -2
View File
@@ -414,10 +414,10 @@ struct lv2_socket final
u32 op_addr = 0;
u64 data_beg_seq = 0; // Seq of first byte of received_data
u32 data_available = 0; // Amount of continuous data available(calculated on ACK send)
u64 data_available = 0; // Amount of continuous data available(calculated on ACK send)
std::map<u64, std::vector<u8>> received_data; // holds seq/data of data received
u32 cur_seq = 0; // SEQ of next packet to be sent
u64 cur_seq = 0; // SEQ of next packet to be sent
} p2ps;
// Value keepers
+3 -2
View File
@@ -411,7 +411,8 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
if (disc.empty() && !Emu.GetTitleID().empty())
disc = vfs::get(Emu.GetDir());
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc), hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>().devKlic.load()]() mutable
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc)
, hdd1 = std::move(hdd1), klic = g_fxo->get<loaded_npdrm_keys>().devKlic.load(), old_config = Emu.GetUsedConfig()]() mutable
{
sys_process.success("Process finished -> %s", argv[0]);
Emu.SetForceBoot(true);
@@ -429,7 +430,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
Emu.SetForceBoot(true);
auto res = Emu.BootGame(path, "", true);
auto res = Emu.BootGame(path, "", true, false, cfg_mode::continuous, old_config);
if (res != game_boot_result::no_errors)
{
+55 -6
View File
@@ -473,7 +473,13 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys
}
pOpt->entry.set(prx->start ? prx->start.addr() : ~0ull);
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
// This check is probably for older fw
if (pOpt->size != 0x20u)
{
pOpt->entry2.set(prx->prologue ? prx->prologue.addr() : ~0ull);
}
return CELL_OK;
}
@@ -495,6 +501,14 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
return CELL_EINVAL;
}
auto set_entry2 = [&](u64 addr)
{
if (pOpt->size != 0x20u)
{
pOpt->entry2.set(addr);
}
};
switch (pOpt->cmd & 0xf)
{
case 1:
@@ -511,7 +525,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
}
pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
set_entry2(prx->epilogue ? prx->epilogue.addr() : ~0ull);
return CELL_OK;
}
case 2:
@@ -550,7 +564,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
if (pOpt->cmd == 4u)
{
pOpt->entry.set(prx->stop ? prx->stop.addr() : ~0ull);
pOpt->entry2.set(prx->epilogue ? prx->epilogue.addr() : ~0ull);
set_entry2(prx->epilogue ? prx->epilogue.addr() : ~0ull);
}
else
{
@@ -787,7 +801,17 @@ error_code _sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<
return CELL_EFAULT;
}
if (pOpt->size != pOpt.size() || !pOpt->info)
if (pOpt->size != pOpt.size())
{
return CELL_EINVAL;
}
if (!pOpt->info)
{
return CELL_EFAULT;
}
if (pOpt->info->size != pOpt->info.size())
{
return CELL_EINVAL;
}
@@ -843,8 +867,33 @@ error_code _sys_prx_get_module_id_by_address(ppu_thread& ppu, u32 addr)
{
ppu.state += cpu_flag::wait;
sys_prx.todo("_sys_prx_get_module_id_by_address(addr=0x%x)", addr);
return CELL_OK;
sys_prx.warning("_sys_prx_get_module_id_by_address(addr=0x%x)", addr);
if (!vm::check_addr(addr))
{
// Fast check for an invalid argument
return {CELL_PRX_ERROR_UNKNOWN_MODULE, addr};
}
const auto [prx, id] = idm::select<lv2_obj, lv2_prx>([&](u32 id, lv2_prx& prx) -> u32
{
for (const ppu_segment& seg : prx.segs)
{
if (seg.size && addr >= seg.addr && addr < seg.addr + seg.size)
{
return id;
}
}
return 0;
});
if (!id)
{
return {CELL_PRX_ERROR_UNKNOWN_MODULE, addr};
}
return not_an_error(id);
}
error_code _sys_prx_start(ppu_thread& ppu)
+19
View File
@@ -48,6 +48,25 @@ void fmt_class_string<spu_group_status>::format(std::string& out, u64 arg)
});
}
template <>
void fmt_class_string<spu_stop_syscall>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](spu_stop_syscall value)
{
switch (value)
{
case SYS_SPU_THREAD_STOP_YIELD: return "sys_spu_thread_yield";
case SYS_SPU_THREAD_STOP_GROUP_EXIT: return "sys_spu_thread_group_exit";
case SYS_SPU_THREAD_STOP_THREAD_EXIT: return "sys_spu_thread_thread_exit";
case SYS_SPU_THREAD_STOP_RECEIVE_EVENT: return "sys_spu_thread_receive_event";
case SYS_SPU_THREAD_STOP_TRY_RECEIVE_EVENT: return "sys_spu_thread_tryreceive_event";
case SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE: return "sys_spu_thread_switch_system_module";
}
return unknown;
});
}
void sys_spu_image::load(const fs::file& stream)
{
const spu_exec_object obj{stream, 0, elf_opt::no_sections + elf_opt::no_data};

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