Compare commits

...

254 Commits

Author SHA1 Message Date
Ani ef8afa7873 rpcs3_version: Bump to 0.0.31 2024-03-05 00:40:22 +01:00
nastys df4fc947ec Improve macOS < 14.3 Rosetta blocker message (#15258) 2024-03-01 20:01:21 +01:00
InvoxiPlayGames eecfbf3fa6 add Wii Harmonix instruments to whitelist 2024-03-01 19:07:02 +01:00
Elad.Ash cfbe7669b4 Update serialization.hpp 2024-03-01 14:58:48 +02:00
Elad.Ash ccaeb87b01 Update serialization.hpp 2024-03-01 14:58:48 +02:00
Elad.Ash fc98f35ea7 Update serialization.hpp 2024-03-01 14:58:48 +02:00
Elad Ashkenazi 998f867c29 serialization.hpp: Fix HLE serilization 2024-03-01 14:58:48 +02:00
capriots eb8325afcb cellPamf implementation (#15143) 2024-02-29 21:45:28 +01:00
RipleyTom 7674c29c78 Fix for sceNpScoreGetGameData 2024-02-29 15:12:22 +02:00
RipleyTom 573734fab8 Send initial presence for offline friends. 2024-02-28 17:41:25 +01:00
Megamouse 2cbd983b4c GL: remove unused params 2024-02-28 14:25:20 +01:00
Megamouse 18983f5476 Fix warnings: Remove unused decryption code 2024-02-28 14:25:20 +01:00
Elad.Ash 75ef154c12 SPU LLVM: Improve runtime SPU compilation preferences (#15250) 2024-02-28 08:28:49 +02:00
RipleyTom 414a354511 Improve cellSysutilAvc2SetAttribute logging 2024-02-28 02:14:37 +01:00
RipleyTom 2168511e5b Add check for score transactions being reused 2024-02-28 02:14:37 +01:00
Mrlinkwii f64d6bc687 Misc: Update FW Latest Version 2024-02-27 12:52:53 +01:00
oltolm 3e83729cb2 xaudio2: use system library instead of xaudio2redist 2024-02-27 05:21:32 +02:00
kd-11 83256cdacb Make clang happy 2024-02-26 18:15:06 +03:00
kd-11 f748fe688c gl: Allow selecting FSR1 upscaling from the settings 2024-02-26 18:15:06 +03:00
kd-11 188f5c7c3c gl: FSR1 cleanup, making it functional 2024-02-26 18:15:06 +03:00
kd-11 838346e4f7 gl: Always log the current renderer visibly. 2024-02-26 18:15:06 +03:00
kd-11 12dc03a903 rsx: Improve simple array performance for small objects
- Also adds map/collect transformation
2024-02-26 18:15:06 +03:00
kd-11 e17c2f2397 gl: Fix compilation 2024-02-26 18:15:06 +03:00
kd-11 071baf63b0 rsx: Implement FSR1 integration for OpenGL 2024-02-26 18:15:06 +03:00
kd-11 1795d5bcc9 gl: Rework the upscalers a bit and define the FSR interface 2024-02-26 18:15:06 +03:00
kd-11 27125c6f72 gl: Remove upscaling 'dst' binding and clean up some awful code in GLPresent 2024-02-26 18:15:06 +03:00
kd-11 12694dcf69 gl: Introduce the concept of scaling passes to the backend 2024-02-26 18:15:06 +03:00
kd-11 e7d8ef924f rsx/vk: Refactor FSR integration to support multiple backends 2024-02-26 18:15:06 +03:00
RipleyTom 0f682b7da1 Fix Apple Clang build 2024-02-24 20:54:55 +02:00
nastys 2b385f28f0 Block Rosetta on macOS 14.0, 14.1, and 14.2 (#15237) 2024-02-24 15:15:19 +01:00
RipleyTom ac8e914a25 improves sig_ctx handling, sys_net logging and fixes udpp2p protocol (#15235) 2024-02-24 12:40:53 +01:00
Megamouse 3067c86d65 gui/input: only init and quit SDL once for all instances
Calling SDL_Quit while two handlers are active can cause controllers to disconnect
2024-02-22 19:31:00 +01:00
Megamouse 96afc15fa2 gui/input; fix log message delta type 2024-02-22 19:31:00 +01:00
Megamouse 66f694bb39 gui/input: include Carbon.h on apple 2024-02-22 19:31:00 +01:00
Megamouse 06025cd19d gui/input: let pad handlers know whether they are part of emulation
Also fix unique_ptr vs shared_ptr nonsense in pad_threads
2024-02-22 19:31:00 +01:00
Megamouse fc698a4df2 gui/input: implement pad navigation for maxOs 2024-02-22 19:31:00 +01:00
Megamouse 2cd47c0415 gui/input: use uinput for linux in gui_pad_thread 2024-02-22 19:31:00 +01:00
Megamouse b032f2dd87 gui/input: add gui input with native events 2024-02-22 19:31:00 +01:00
RipleyTom 3acd442c2e sceNpBasicSendMessage fix 2024-02-22 05:33:29 +02:00
Megamouse 6b43e5c80e Qt: add Q_OBJECT macro to midi_creator
This fixes the localization of its strings
2024-02-20 10:06:02 +01:00
Megamouse 183380fe57 Qt: localize default pad names 2024-02-20 10:06:02 +01:00
kd-11 7abc5f3ead vk: Move driver properties to properties section from features section 2024-02-16 18:11:30 +03:00
Megamouse 1867f9aa1f Qt 6.6.2 2024-02-16 11:29:38 +01:00
Eladash f47c7e0ef1 PPU LLVM: Relax MSELF entry name constraints
These names are a hint
2024-02-16 10:42:57 +02:00
Eladash 36ab866c2c SPU Analyzer: forgive invalid code, use interpreter 2024-02-16 10:42:57 +02:00
Eladash f943deb2c3 PPU LLVM: Do not use possible duplicates in MSELF 2024-02-16 10:42:57 +02:00
Eladash 27ea25a999 Add debug info to ENOMEMs 2024-02-16 10:42:57 +02:00
Eladash 989b353fbd Fixup lv2_file::file_view::read() regression 2024-02-16 10:42:57 +02:00
Eladash c1607d14dc Add debug info to sys_fs_lseek 2024-02-16 10:42:57 +02:00
Eladash 85884d14cd PPU LLVM: Add more MSELF file checks
Do not crash on invalid files.
2024-02-16 10:42:57 +02:00
Eladash 08cf275bc8 Fix lv2_file::file_view::size() 2024-02-16 10:42:57 +02:00
Eladash 88ba3c47c2 ELF.h: Avoid using seek operations 2024-02-16 10:42:57 +02:00
Eladash 4aee44b1c0 sys_prx/overlay: Add some debugging steps 2024-02-16 10:42:57 +02:00
Megamouse e3d4c067df raw_mouse: adjust relative mouse position if the window size changed 2024-02-14 21:10:26 +01:00
kd-11 dfc626c99c rsx/vk: Fix WCB/Blit flush deadlock 2024-02-14 21:09:02 +03:00
kd-11 8786516c58 vk: Implement fast RSX release if there is no pending GPU work 2024-02-14 21:09:02 +03:00
Eladash 34e945128f Crypto\EDATA: Optimize decrypt_block to avoid an allocation 2024-02-14 15:11:52 +02:00
Eladash 7db6165b46 Fix lv2_file::file_view::read_at 2024-02-14 15:11:52 +02:00
Eladash 883f2e92ae EDATA: Add more logging 2024-02-14 15:11:52 +02:00
Eladash 3e8b003515 Add logging for cellFsSdataOpenByFd 2024-02-14 15:11:52 +02:00
Eladash ed66e9e9b9 Fix EDATADecrypter::ReadData 2024-02-14 15:11:52 +02:00
Eladash f87e11d16a Savestates/sys_fs: Save NPDRM decryption key 2024-02-14 15:11:52 +02:00
RipleyTom 5a64556fbf Add <optional> to RB3MidiDrums.h 2024-02-14 14:14:09 +02:00
Megamouse d71f4b33d7 raw_mouse: center cursors on init 2024-02-14 01:13:44 +01:00
Megamouse c91bc03532 sys_usbd: silence warning 2024-02-14 01:13:44 +01:00
Megamouse 348fab33a8 overlays: fix raw mouse debug overlay pos type 2024-02-14 01:13:44 +01:00
Megamouse 68f3df4975 input: implement raw_mouse_handler 2024-02-14 01:13:44 +01:00
Megamouse f6f8996aa9 overlays: use signed short for position to allow negative positions 2024-02-14 00:12:55 +01:00
nswarm 8533f962c6 Add limited support for midi drums w/ rockband 3 (#15054)
* add support for midi drums
* rb3drums: use dpad to disambiguate cymbal/drum hits instead of staggering.
* rb3drums: configurable min velocity and hihat up behavior
* rb3drums: configurable combos, combo duration, note overrides
* rb3drums: move config to separate file.
* rb3drums: apply stagger to double cymbal hits
* rb3drums: support midi CC note mapping
* rb3drums: add config to cmakelists. only load rb3drums config once.
* rb3drums: HihatWithPedalUp defaults to yellow cymbal instead of blue
2024-02-13 23:08:22 +01:00
RipleyTom af33c0c69f Revert FM(a*re_accurate) shortcut to relaxed 2024-02-13 20:08:03 +02:00
RipleyTom 50b1402d66 Fixes full fm(a, re_accurate) pattern 2024-02-13 11:37:20 +02:00
RipleyTom cfd5c62386 Improve re_accurate 2024-02-13 11:37:20 +02:00
RipleyTom 569f72c360 Disable updates for local build 2024-02-12 19:02:52 +01:00
kd-11 df74d49f72 vk: Silence compiler warning 2024-02-12 12:28:16 +03:00
xujibbs eac31dfd03 Update skylander_dialog.cpp
Make strings translatable
2024-02-12 09:54:07 +02:00
Megamouse 0b1a7ee346 overlays: localize sendmessage confirmation 2024-02-12 00:04:46 +01:00
Eladash 77ba2b40e7 Thread.cpp: Explicitly state difference type in thread_ctrl::wait_for_accurate 2024-02-11 23:01:40 +02:00
Eladash 94239f5526 Fixup HW thread switch delay condition for performance 2024-02-11 23:01:40 +02:00
Elad.Ash 148edc005a Improve cpu_flag::yield handling 2024-02-11 20:09:01 +02:00
Elad Ashkenazi ec59f4d37e PPU/LV2: Implement HW threads switching delay when signaling higher priority threads
In real PS3 (it seems), when a thread with a higher priority than the caller is signaled and that there is available space on the running queue for the other hardware thread to start It prioritizes signaled thread caller's hardware thread switches instantly to the new thread code while signaling to the other hardware thread to execute the caller's code.
Resulting in a delay to the caller after such thread is signaled
2024-02-11 20:09:01 +02:00
kd-11 2c03878c3b vk: Don't panic on encountering the exotic mesa drivers 2024-02-11 17:21:56 +03:00
kd-11 c8c845a659 vk: Emit a strong warning if dozen is detected as the currently loaded driver.
- In most cases, the user does not intend to actually use dozen and has installed a compatibility pack by mistake.
2024-02-11 17:21:56 +03:00
kd-11 a54f1970f1 vk: Make more features "optional"
- This allows some non-conformant drivers to at least finish swapchain creation.
2024-02-11 17:21:56 +03:00
kd-11 7f602937a3 vk: Improve driver detection for dozen and lavapipe 2024-02-11 17:21:56 +03:00
kd-11 c2631c914d rsx: Lower some performance warnings to trace to reduce log spam 2024-02-11 14:24:33 +01:00
kd-11 c17014a055 Buildfiles improvements 2024-02-11 14:24:33 +01:00
kd-11 c572226b0d Stop including x86 intrinsics in other architectures 2024-02-11 14:24:33 +01:00
kd-11 6718b0683e Don't include x64 options when building arm64 2024-02-11 14:24:33 +01:00
RipleyTom 3735df4f53 Update CMake requirements in BUILDING.md 2024-02-11 10:09:24 +02:00
kd-11 4746ef09cd Fix linux build 2024-02-10 15:15:02 +03:00
kd-11 ed93bbd16d vk: Improve compute->compute barrier before detiling 2024-02-10 15:15:02 +03:00
kd-11 041daab068 vk: Tighten some memory bounds checks 2024-02-10 15:15:02 +03:00
kd-11 e5c831a800 rsx/vk: Fix GPU tile encoding
- Decoding also needs some love, but currently WIP
2024-02-10 15:15:02 +03:00
kd-11 b8ed3eb824 rsx: Fix cpu detiler 2024-02-10 15:15:02 +03:00
kd-11 95f9f04612 rsx: Provide tile alignment utility (align to 64 * pitch blocks) 2024-02-10 15:15:02 +03:00
kd-11 823c72bf27 rsx/vk: Redefine the (de)tiler config interface 2024-02-10 15:15:02 +03:00
RipleyTom 9b387485b5 Avoid modifying any memory on SYS_NET_EINTR cases 2024-02-09 18:07:18 +02:00
RipleyTom 56e27bdd49 Restrict file creation in /dev_hdd0 2024-02-09 16:13:46 +02:00
Elad.Ash 30e8c3e951 SPU LLVM: Allow swapped FMA and multiplications args in match context 2024-02-07 13:45:29 +02:00
Megamouse 96b7e4c67f Update libpng to 1.6.42 2024-02-06 22:13:22 +01:00
RipleyTom 65d93c97ea Add various SPU patterns 2024-02-06 22:08:44 +02:00
RipleyTom a92b8acba7 Accurate FI 2024-02-06 22:08:44 +02:00
Megamouse 85f4c38b4e Update submodules
- Updates SDL to 2.30.0
- Updates curl to 8.6.0
- Updates libusb to 1.0.27
- Updates zlib to 1.3.1
- Updates FAudio to 24.02
2024-02-05 23:47:38 +01:00
Eladash 40c51a1f05 Crypto/unedat.cpp: remove extract_all_data 2024-02-05 21:56:04 +02:00
Eladash a6c2e995af Crypto/PKG installer: Fix potential RAM shortage when extracing EDAT files 2024-02-05 21:56:04 +02:00
Eladash 596e671973 Crypto/sys_fs: Fix potential RAM shortage in NPDRM decryption
Previous code was structured very naively: if 8GB read is requested, it would allocate 8GB of memory. Without considering recycling memory for each block.
2024-02-05 21:56:04 +02:00
Eladash d38b2eb8ef Crypto/sys_fs: Remove some seek operations 2024-02-05 21:56:04 +02:00
RipleyTom bb9444e19a Add sceNpPlus to registered modules 2024-02-05 15:28:38 +01:00
RipleyTom 26d406fec3 RPCN 1.1 2024-02-05 09:44:29 +01:00
Megamouse dda0b8af45 cellGem: Allow to use multiple controllers as fake PS-Move
- Allows to use the last 4 ports/players as PS Move in fake mode
- Should in theory react to re/disconnected controllers
- Should allow to use player 1-3 as normal controller simultaneously
2024-02-03 21:47:40 +01:00
Megamouse d5ce4e0f6b sceNp: fix atomic type 2024-02-03 17:59:19 +01:00
Megamouse e5cfe710fa sceNp: correct type for data attachment 2024-02-03 17:59:19 +01:00
Megamouse 2a0e2ce0a2 sceNp: don't preserve messages in sceNpBasicRecvMessageAttachment 2024-02-03 17:59:19 +01:00
Megamouse ef56433684 overlays: ask for user confirmation before sending an invite
This shows subject and body as well
2024-02-03 17:59:19 +01:00
Megamouse 8bc3a39586 sceNp: implement sceNpBasicAbortGui 2024-02-03 17:59:19 +01:00
Megamouse e13a671f86 sceNp: Implement sceNpBasicRecvMessageAttachment 2024-02-03 17:59:19 +01:00
Megamouse 48f82c75c1 np: fix signed/unsigned warning
who the heck thought returning signed on a counter makes sense...
2024-02-03 17:59:19 +01:00
Megamouse a5955657c5 overlays: add subject and body to recvmessage dialog 2024-02-03 09:45:55 +01:00
RipleyTom 319657e815 Review fixes 2024-02-03 08:45:29 +01:00
RipleyTom c589001dff Add player history 2024-02-03 08:45:29 +01:00
Megamouse c04cd2228e Qt: Show NP ID in rpcn dialogs 2024-02-02 19:05:56 +01:00
Megamouse cba51b4a5e rsx/glsl: use const for left/right 2024-02-02 10:07:23 +01:00
kd-11 8962ddca01 rsx/glsl: Fix typo 2024-02-02 10:07:23 +01:00
kd-11 1808a4373b rsx: Improvements to the video out passes
- Use shared VS for vulkan as well as GL
- Simplify binding model and give control back to the cpp side
2024-02-02 10:07:23 +01:00
kd-11 bb22afb7f1 gl: Vectorize GL_TEMP_IMAGE_SLOT expression 2024-02-02 10:07:23 +01:00
kd-11 ce571cb6ca vk: Allow indexed lookup for sampler and IA binding points in overlay passes 2024-02-02 10:07:23 +01:00
Megamouse ac9afbfede glsl: use fragCoord to decide interlacing 2024-02-02 10:07:23 +01:00
Megamouse 4e8130bd99 glsl: use textureSize instead of height 2024-02-02 10:07:23 +01:00
Megamouse 00c9321b1d glsl: use const in for params 2024-02-02 10:07:23 +01:00
Megamouse 8e8b582747 glsl: move anaglpyh to separate functions 2024-02-02 10:07:23 +01:00
Megamouse 6f2e843193 vk/gl: add amber-blue anaglyph (ColorCode 3D) 2024-02-02 10:07:23 +01:00
Megamouse 7d9460a560 vk/gl: cleanup formatting in glsl, move some common magic numbers to variables 2024-02-02 10:07:23 +01:00
Megamouse 32f2b14429 vk/gl: add interlaced 3D mode 2024-02-02 10:07:23 +01:00
Megamouse 651f8512eb vk/gl: add more anaglyph 3D modes 2024-02-02 10:07:23 +01:00
Megamouse 3dac7d16e7 vk/gl: unite video_out_calibration_pass fragment shader to new glsl 2024-02-02 10:07:23 +01:00
Megamouse 240f80b467 gl: add missing else in calibration pass fragment shader 2024-02-02 10:07:23 +01:00
Megamouse 17b5a36b59 Add update_helper.sh to VS filters 2024-02-02 10:07:23 +01:00
Megamouse ea326a5f29 overlays: clean up includes 2024-01-31 16:33:29 +01:00
Megamouse c271415328 sceNp: decrease some log levels from todo to warning 2024-01-31 16:33:29 +01:00
Megamouse f28c7d029c overlays: use timestamp for fade animations
Fixes jojo when using sceNp dialogs
2024-01-31 16:33:29 +01:00
Megamouse d7f317fa58 Add UI to sceNpBasicSendMessageAttachment and sceNpBasicAddFriend 2024-01-31 16:33:29 +01:00
Megamouse 3f2c34edd2 sceNp: silence some errors 2024-01-31 16:33:29 +01:00
Megamouse 566a8ac5ac scnNp: add ppu wait before opening blocking dialogs 2024-01-31 16:33:29 +01:00
Megamouse 71b5bc4fe6 sceNp: add more error checks 2024-01-31 16:33:29 +01:00
Megamouse a427783bac overlays: remove separators from send/recv dialogs 2024-01-31 16:33:29 +01:00
Megamouse 23dc6a44f6 overlays: implement native sendmessage dialog 2024-01-31 16:33:29 +01:00
Megamouse 9ef5a01de4 overlays: implement native recvmessage dialog 2024-01-31 16:33:29 +01:00
Megamouse cdfe3ee7c8 Name some threads 2024-01-31 16:33:29 +01:00
kd-11 c3c73c7e24 rsx: Allow native UI render even when we have pending draw calls if FIFO queue is empty. 2024-01-30 16:36:58 +03:00
RipleyTom af8d308a69 Terminate RPCN connection cleanly 2024-01-29 13:01:09 +01:00
Florin9doi a7da245656 Don't search games when the dialog is cancelled 2024-01-28 13:18:35 +01:00
RipleyTom e9713f9635 Fix sceNpCommerce2 init 2024-01-28 10:52:26 +01:00
Megamouse 3dbde04d18 sceNpCommerce: add init checks 2024-01-27 08:31:16 +01:00
Megamouse 1611588682 sceNpCommerce2: add some error checks 2024-01-27 08:31:16 +01:00
RipleyTom 93c72822a3 Fix Apple clang 2024-01-26 17:59:53 +01:00
RipleyTom 6f5bcf12c6 Arm version of trigger_write_page_fault 2024-01-26 09:32:15 +02:00
RipleyTom 07dba02897 FreeBSD workaround for libusb 2024-01-26 02:51:41 +01:00
RipleyTom db685879af Return no file to TSS apis 2024-01-26 02:51:41 +01:00
RipleyTom 94e8d6fe13 Mini sceNpCommerce2Init fix 2024-01-25 22:53:21 +01:00
Megamouse 0d1fbfb755 VFS: Fix some potential .back() segfaults 2024-01-25 17:03:58 +01:00
Megamouse 10c52cf569 sys_usbd: add libusb log callback 2024-01-25 17:03:58 +01:00
RipleyTom 7661df0847 Enable Discord presence on Linux 2024-01-25 09:49:17 +01:00
pastalian e8ad6ec673 Add missing headers (#15075)
Some systems must explicitly include these headers.
2024-01-24 17:22:37 +01:00
Steveice10 3ef48cbdd5 rpcs3qt: Add macOS support to the updater. 2024-01-24 09:44:56 +01:00
Megamouse 5fe36872c5 System: don't consider games as registered if they were already in games.yml
Also, show message if nothing was added
2024-01-24 08:18:57 +01:00
RipleyTom d33955c290 Asmjit FI 2024-01-23 12:24:38 +02:00
RipleyTom 606a60e901 Accurate frest and frsqest 2024-01-23 12:24:38 +02:00
Megamouse 93f5154429 Qt: trim whitespace from textinput before writing to config 2024-01-22 22:25:06 +01:00
Megamouse a6e046c6c3 update submodules 2024-01-22 22:25:06 +01:00
kd-11 8eae8cb86c rsx: Do not emit rounding code when not needed 2024-01-21 12:30:37 +03:00
kd-11 abe5013f1d rsx: Fix compiling of mixed-precision shaders 2024-01-21 12:30:37 +03:00
kd-11 ca7de4bcb1 rsx: Aesthetic changes 2024-01-21 12:30:37 +03:00
kd-11 9a9b5860df rsx: Forcefully disable writes to channels disabled on host
- Some formats may have dummy channels on host. Never write to them!
- This means that XRGB formats correctly only write 24 bits, not 32.
2024-01-21 12:30:37 +03:00
kd-11 0971636c07 rsx: More pipeine refactoring 2024-01-21 12:30:37 +03:00
Megamouse 50fcec63e0 fs: fix get_executable_dir 2024-01-21 00:11:16 +01:00
Megamouse 8a87dc63b1 Emu: Add more info to BlockingCallFromMainThread 2024-01-21 00:11:16 +01:00
Megamouse efea3aa385 Qt: revert accidental settings_dialog check-in 2024-01-20 21:59:02 +01:00
Megamouse 86bab5b7c3 cellImeJp: fix character deletion after change to cursor 2024-01-20 21:59:02 +01:00
Megamouse 675ed82c0b cellImeJp: fix deadlock 2024-01-20 21:59:02 +01:00
Megamouse 58dd0198dd cellImeJp: add candidate list functions 2024-01-20 21:59:02 +01:00
Megamouse 16f833a516 cellImeJp: add cursor 2024-01-20 21:59:02 +01:00
Megamouse 0392830950 cellImeJp: fix focus area 2024-01-20 21:59:02 +01:00
Megamouse 04efc6b4a4 cellImeJp: static functions to silence annoying VS 2024-01-20 21:59:02 +01:00
Megamouse 8edd7d54cf fs: fix warning. fix size argument in report_fatal_error 2024-01-20 21:59:02 +01:00
Eladash f19744ae88 Savestates: Fix restart when suspend mode is off
Add a new menu button for explicit exit (mostly for debug purposes)
2024-01-20 22:15:07 +02:00
Eladash 01ee119c59 Savestates: Dont patch memory on load 2024-01-20 22:15:07 +02:00
Eladash 4cd60aca22 Savestates: Add some logging for sys_overlay 2024-01-20 22:15:07 +02:00
Eladash 81ba791ee7 Simplify fatal dialog's error a bit 2024-01-20 22:15:07 +02:00
Eladash c7a94a80df Savestates/net: fix P2P socket data saving 2024-01-20 22:15:07 +02:00
Eladash c509226bab Firmware installer: Early protection against disk space shortage complications 2024-01-20 22:15:07 +02:00
Eladash 312b35051d sys_fs: Aggregate safe file reads (optimization) 2024-01-20 22:15:07 +02:00
Eladash e401d85af7 progress dialog: minor fix 2024-01-20 22:15:07 +02:00
Eladash e4c9af176b fixup hdd1 cache 2024-01-20 22:15:07 +02:00
Steveice10 9c354ee269 Utilities: Add support for portable user directory. (#15064) 2024-01-20 10:04:25 +02:00
kd-11 96d880839a rsx: Properly propagate surface properties on surface reuse. 2024-01-19 12:34:31 +03:00
Megamouse e23db7efbd Mac: Update MoltenVk to 1.2.7
Remove deprecated extension VK_MVK_moltenvk
2024-01-16 22:52:57 +01:00
Megamouse fd540f1820 Mac: silence deprecation warning 2024-01-16 22:52:57 +01:00
RipleyTom 7d55a85fa6 Replaces page fault triggers with assembly 2024-01-16 12:04:47 +02:00
Megamouse fa7ef870dd sceNp: disable Canonicalize functions for RPCN 2024-01-14 22:55:03 +01:00
Megamouse ecacb874e4 sceNpCommerce: more error checks 2024-01-14 22:55:03 +01:00
Megamouse 0b2e0a1b28 sceNpCommerce: add error constants 2024-01-14 22:55:03 +01:00
Megamouse a08d60fbbe sceNpFriendlist: more checks 2024-01-14 22:55:03 +01:00
Megamouse ef6041c14f sceNpMatching: simplify linked list for loops 2024-01-14 22:55:03 +01:00
Megamouse 25954758d7 sceNpMatching: add error checks 2024-01-14 22:55:03 +01:00
oltolm 0ae1578dce split JIT.cpp and SPURecompiler.cpp 2024-01-14 17:21:39 +01:00
oltolm 2039b85be3 prepare split 2024-01-14 17:21:39 +01:00
oltolm 8eae73dd02 add comments where to split 2024-01-14 17:21:39 +01:00
RipleyTom fe9a24a1b8 RPCN 1.0 (#15045) 2024-01-14 12:36:23 +01:00
Megamouse 3d974eed03 fs: move buf from stack to heap to silence VS warning 2024-01-14 06:40:35 +01:00
Megamouse 329b58c8cd fs: remove duplicate to_utf8 function 2024-01-14 06:40:35 +01:00
Megamouse 2d82e3ef9a Qt: use fs::get_temp_dir during updates
GetTempPathW was falsely using size in bytes instead of string length.
2024-01-14 06:40:35 +01:00
Megamouse faaf644331 Qt/input: show all mmjoy devices with connection in dropdown
This fixes weird issues with disconnected pads while also showing the status.
2024-01-14 02:50:45 +01:00
Megamouse d7aedb439e Qt/Input: Keep configured device name in config when no pad is connected 2024-01-14 02:50:45 +01:00
Megamouse 9c11840e40 input: implement dynamic mmjoy device reconnection 2024-01-14 02:50:45 +01:00
RipleyTom 4c36b80300 Misc NP fixes 2024-01-13 15:44:28 +01:00
Megamouse f7fa5aab70 media_utils: fix segfault when stopping a recording 2024-01-12 19:53:20 +01:00
Megamouse 17aeefe1b0 cellFont: some stubs 2024-01-11 22:35:00 +01:00
Eladash 825ae5b8aa Ensure HDD1 is never damaged or corrupted due to unexpected RPCS3/game termination 2024-01-11 20:24:53 +02:00
Megamouse 3513f6d83c cellMouse: silence some log spam 2024-01-10 22:34:13 +01:00
Megamouse 4ad4badcfe audio_decoder: fix more memleaks and dangling files 2024-01-10 22:34:13 +01:00
Megamouse b67837c0ef cellMusicDecode: fix random pop at the end of a stream
Observed in SSX, which seems to ignore the readSize sometimes
2024-01-10 22:34:13 +01:00
Megamouse b5f3f910f6 cellSearch: fix vfs music and video paths 2024-01-10 22:34:13 +01:00
Megamouse b0a7448394 get_media_info: improve logging 2024-01-10 22:34:13 +01:00
Megamouse bed7763a3b audio_decoder: ignore first invalid data error 2024-01-10 22:34:13 +01:00
Megamouse 572c075a4b audio_decoder: improve logging 2024-01-10 22:34:13 +01:00
Megamouse bad7756af2 audio_decoder: properly free packets 2024-01-10 22:34:13 +01:00
Megamouse 45c89030ac audio_decoder: check for failed av_packet_alloc 2024-01-10 22:34:13 +01:00
Megamouse 7aad4f4253 overlays: decrease log level 2024-01-10 22:34:13 +01:00
Megamouse 9e9385a877 cellVideoExport: export movies to /video/ instead of /movie 2024-01-10 22:34:13 +01:00
Megamouse 431d69ac87 Windows: remove QtCore5Compat dependency 2024-01-10 22:34:13 +01:00
Megamouse 3be24f1e6f Update FAudio to 24.01 2024-01-10 22:34:13 +01:00
Megamouse c45deb3f87 sdl: log version 2024-01-10 22:34:13 +01:00
brian218 213674511e sys_ss: Corrected sys_ss_update_manager(0x6011) version data format 2024-01-10 12:37:11 +01:00
brian218 6548e3f7d0 sys_fs: Improved get_device_root() path identifying security 2024-01-10 12:37:11 +01:00
brian218 a269ae7e30 sys_fs: Prevented duplicate device mounting 2024-01-10 12:37:11 +01:00
brian218 862295d9f3 sys_fs: Fixed sys_fs_unlink()'s failing to delete files in dev_usbXXX
sys_fs: Fixed get_mp() up for dev_usbXXX
2024-01-10 12:37:11 +01:00
RipleyTom 61e130e68c Honk if you knew memset(nullptr, 0, 0) was UB 2024-01-09 23:06:23 +01:00
RipleyTom 236e32fb3d Fix weird cmp in np_handler.cpp 2024-01-09 23:06:23 +01:00
RipleyTom 067ea68979 Fix games passing invalid npids 2024-01-09 23:06:23 +01:00
Megamouse 7504503d5f sceNp: don't return error if assignedReqId is null 2024-01-09 02:58:00 +01:00
Megamouse 108d0d49ec sceNp: add missing roomPassword check 2024-01-09 02:58:00 +01:00
Megamouse 2c424a0911 sceNp: add missing withPassword check 2024-01-09 02:58:00 +01:00
Megamouse ffe1b287a8 sceNp: add missing check for roomSlotInfo 2024-01-09 02:58:00 +01:00
RipleyTom 485fca34b0 Misc NP fixes 2024-01-08 14:03:15 +01:00
RipleyTom 0b984e9a23 Add -Werror=implicit-fallthrough to cmake build 2024-01-07 17:29:43 +01:00
RipleyTom 74e084c893 Improve sceNpEula 2024-01-07 14:50:45 +01:00
RipleyTom 47ae192cf8 Impl sceNpBasicSendMessage 2024-01-07 13:36:27 +01:00
RipleyTom 80b7fd5f3f Check room event callback has been set before calling it 2024-01-05 22:34:52 +01:00
RipleyTom 0227b16d06 Fix sceNpBasicSetPresence when data is nullptr and size is 0 2024-01-05 22:34:52 +01:00
kd-11 2a1af17f67 vk: Force GPU texture processing if the input is already GPU-resident (Tiled RCB read) 2024-01-05 16:29:53 +03:00
RipleyTom b6e9746198 RPCN 0.9 (#15002) 2024-01-05 09:43:26 +01:00
kd-11 bc141831f7 gl: Implement display output format correction 2024-01-04 13:03:32 +03:00
kd-11 acd3fd0c3e vk: Implement display output format correction 2024-01-04 13:03:32 +03:00
kd-11 2369266b8a rsx: Add an option to force hardware MSAA resolve 2024-01-03 19:32:45 +03:00
325 changed files with 24574 additions and 12320 deletions
+1
View File
@@ -43,6 +43,7 @@ cmake .. \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SDL=ON \
-DUSE_SYSTEM_FFMPEG=OFF \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
-DLLVM_DIR=/opt/llvm/lib/cmake/llvm \
-DSTATIC_LINK_LLVM=ON \
+2 -2
View File
@@ -13,8 +13,8 @@ arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetyp
arch -x86_64 /usr/local/bin/brew install llvm@16 glew cmake sdl2 vulkan-headers coreutils
arch -x86_64 /usr/local/bin/brew link -f llvm@16 ffmpeg@5
# moltenvk based on commit for 1.2.6 release
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/4ac0cfaca4c2505abe2fcbcc0ce5816572103a6c/Formula/m/molten-vk.rb
# moltenvk based on commit for 1.2.7 release
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/05a8770c483cfddf5b077667a392d846bc2ad719/Formula/m/molten-vk.rb
arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb
#export MACOSX_DEPLOYMENT_TARGET=12.0
export CXX=clang++
-2
View File
@@ -19,7 +19,6 @@ QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}"
QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}"
QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}"
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
QT_5CMP_URL="${QT_HOST}${QT_PREFIX}qt5compat.${QT_PREFIX_2}qt5compat${QT_SUFFIX}"
LLVMLIBS_URL='https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-16.0.1/llvmlibs_mt.7z'
GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z'
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-1.3.268.0-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
@@ -30,7 +29,6 @@ DEP_URLS=" \
$QT_TOOL_URL \
$QT_MM_URL \
$QT_SVG_URL \
$QT_5CMP_URL \
$LLVMLIBS_URL \
$GLSLANG_URL \
$VULKAN_SDK_URL"
+3 -3
View File
@@ -7,7 +7,7 @@ env:
BUILD_SOURCEBRANCHNAME: $CIRRUS_BRANCH
RPCS3_TOKEN: ENCRYPTED[!a4c3850e29ab150692286a74bec29819d25971a7ec431b86de2a35f7ed90c5b2ab3c93469f9298e30924d843599110e9!]
QT_VER_MAIN: '6'
QT_VER: '6.6.1'
QT_VER: '6.6.2'
# windows_task:
# matrix:
@@ -21,7 +21,7 @@ env:
# COMPILER: msvc
# BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}\artifacts\
# QT_VER_MSVC: 'msvc2019'
# QT_DATE: '202311210527'
# QT_DATE: '202402121135'
# QTDIR: C:\Qt\${QT_VER}\${QT_VER_MSVC}_64
# VULKAN_VER: '1.3.268.0'
# VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
@@ -56,7 +56,7 @@ env:
# linux_task:
# container:
# image: rpcs3/rpcs3-ci-focal:1.5
# image: rpcs3/rpcs3-ci-focal:1.6
# cpu: 4
# memory: 16G
# env:
+1 -1
View File
@@ -1,5 +1,5 @@
# 7z sdk
if(WIN32)
if(WIN32 OR APPLE)
add_library(3rdparty_7z STATIC EXCLUDE_FROM_ALL
src/7zAlloc.c
src/7zArcIn.c
+1 -1
View File
@@ -4,7 +4,7 @@ include(ExternalProject)
ExternalProject_Add(moltenvk
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
GIT_TAG 9e4ee9e
GIT_TAG 66f6ff1
BUILD_IN_SOURCE 1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
File diff suppressed because it is too large Load Diff
Binary file not shown.
+2
View File
@@ -1,6 +1,8 @@
# DiscordRPC
add_library(3rdparty_discordRPC INTERFACE)
check_cxx_compiler_flag("-msse -msse2 -mcx16" COMPILER_X86)
# We don't want Discord Rich Presence on the BSDs and other OSes
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE) AND COMPILER_X86)
if (WIN32 AND NOT MSVC)
-2
View File
@@ -4,11 +4,9 @@ if (NOT USE_SYSTEM_LIBPNG)
# We use libpng's static library and don't need to build the shared library and run the tests
set(PNG_SHARED OFF CACHE BOOL "Build shared lib")
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests")
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found")
set(SKIP_INSTALL_ALL ON)
add_subdirectory(libpng EXCLUDE_FROM_ALL)
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
target_link_libraries(png_static 3rdparty::zlib)
set(LIBPNG_TARGET png_static PARENT_SCOPE)
else()
+21 -1
View File
@@ -11,6 +11,12 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
<ClCompile Include="SDL\src\joystick\SDL_steam_virtual_gamepad.c" />
<ClCompile Include="SDL\src\joystick\steam\SDL_steamcontroller.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c" />
<ClInclude Include="SDL\include\begin_code.h" />
<ClInclude Include="SDL\include\close_code.h" />
<ClInclude Include="SDL\include\SDL.h" />
@@ -133,7 +139,9 @@
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_rumble.h" />
<ClInclude Include="SDL\src\joystick\SDL_gamecontrollerdb.h" />
<ClInclude Include="SDL\src\joystick\SDL_joystick_c.h" />
<ClInclude Include="SDL\src\joystick\SDL_steam_virtual_gamepad.h" />
<ClInclude Include="SDL\src\joystick\SDL_sysjoystick.h" />
<ClInclude Include="SDL\src\joystick\steam\SDL_steamcontroller.h" />
<ClInclude Include="SDL\src\joystick\usb_ids.h" />
<ClInclude Include="SDL\src\joystick\virtual\SDL_virtualjoystick_c.h" />
<ClInclude Include="SDL\src\joystick\windows\SDL_dinputjoystick_c.h" />
@@ -188,6 +196,13 @@
<ClInclude Include="SDL\src\video\dummy\SDL_nullevents_c.h" />
<ClInclude Include="SDL\src\video\dummy\SDL_nullframebuffer_c.h" />
<ClInclude Include="SDL\src\video\dummy\SDL_nullvideo.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codecs_common.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_decode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_encode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_decode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_encode.h" />
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_icd.h" />
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_layer.h" />
<ClInclude Include="SDL\src\video\khronos\vulkan\vk_platform.h" />
@@ -238,7 +253,13 @@
<ClInclude Include="SDL\src\video\windows\SDL_windowswindow.h" />
<ClInclude Include="SDL\src\video\windows\wmmsg.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse_func.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std_func.h" />
</ItemGroup>
<ItemGroup>
@@ -418,7 +439,6 @@
<ClCompile Include="SDL\src\video\windows\SDL_windowsvideo.c" />
<ClCompile Include="SDL\src\video\windows\SDL_windowsvulkan.c" />
<ClCompile Include="SDL\src\video\windows\SDL_windowswindow.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}</ProjectGuid>
+21 -1
View File
@@ -178,10 +178,15 @@
<ClCompile Include="SDL\src\video\windows\SDL_windowsvideo.c" />
<ClCompile Include="SDL\src\video\windows\SDL_windowsvulkan.c" />
<ClCompile Include="SDL\src\video\windows\SDL_windowswindow.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb.c" />
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_ps3.c" />
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steam.c" />
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_wii.c" />
<ClCompile Include="SDL\src\joystick\steam\SDL_steamcontroller.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.c" />
<ClCompile Include="SDL\src\video\yuv2rgb\yuv_rgb_std.c" />
<ClCompile Include="SDL\src\joystick\hidapi\SDL_hidapi_steamdeck.c" />
<ClCompile Include="SDL\src\joystick\SDL_steam_virtual_gamepad.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="SDL\include\begin_code.h" />
@@ -409,5 +414,20 @@
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse_func.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std_func.h" />
<ClInclude Include="SDL\src\joystick\hidapi\SDL_hidapi_nintendo.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_common.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_internal.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_lsx_func.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_sse.h" />
<ClInclude Include="SDL\src\video\yuv2rgb\yuv_rgb_std.h" />
<ClInclude Include="SDL\src\joystick\steam\SDL_steamcontroller.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_decode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h264std_encode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_decode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codec_h265std_encode.h" />
<ClInclude Include="SDL\src\video\khronos\vk_video\vulkan_video_codecs_common.h" />
<ClInclude Include="SDL\src\joystick\SDL_steam_virtual_gamepad.h" />
</ItemGroup>
</Project>
-2
View File
@@ -27,10 +27,8 @@
#include <sys/un.h>
#include <arpa/inet.h>
#include <poll.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <unistd.h>
#endif
#endif
#ifdef _WIN32
constexpr SOCKET invalid_socket = INVALID_SOCKET;
+4 -3
View File
@@ -1,10 +1,9 @@
if (USE_SYSTEM_ZLIB)
message(STATUS "RPCS3: Using system ZLIB")
find_package(ZLIB QUIET)
find_package(ZLIB QUIET REQUIRED)
add_library(3rdparty_zlib INTERFACE)
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARIES})
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(3rdparty_zlib INTERFACE ZLIB::ZLIB)
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
else()
message(STATUS "RPCS3: Using builtin ZLIB")
@@ -15,4 +14,6 @@ else()
target_link_libraries(3rdparty_zlib INTERFACE zlibstatic)
target_include_directories(3rdparty_zlib INTERFACE zlib ${CMAKE_CURRENT_BINARY_DIR}/zlib)
target_compile_definitions(3rdparty_zlib INTERFACE -DZLIB_CONST=1)
set(ZLIB_ROOT ${CMAKE_CURRENT_BINARY_DIR}/zlib PARENT_SCOPE)
endif()
+6 -6
View File
@@ -7,21 +7,21 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
### Windows 7 or later
* [CMake 3.16.9+](https://www.cmake.org/download/) (add to PATH)
* [CMake 3.28.0+](https://www.cmake.org/download/) (add to PATH)
* [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
* [Qt 6.6.1](https://www.qt.io/download-qt-installer)
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
* [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community) (or at least Visual Studio 2019 16.11.xx+ as C++20 is not included in previous versions)
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.1\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\6.6.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
* Clang 12+ or GCC 11+
* [CMake 3.16.9+](https://www.cmake.org/download/)
* [Qt 6.6.1](https://www.qt.io/download-qt-installer)
* [CMake 3.28.0+](https://www.cmake.org/download/)
* [Qt 6.6.2](https://www.qt.io/download-qt-installer)
* [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
* [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@@ -96,7 +96,7 @@ git submodule update --init
#### Configuring the Qt plugin (if used)
1) Go to `Extensions->Qt VS Tools->Qt Versions`.
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.1\msvc2019_64`, version will fill in automatically.
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.6.2\msvc2019_64`, version will fill in automatically.
3) Go to `Extensions->Qt VS Tools->Options->Legacy Project Format`.
4) Set `Build: Run pre-build setup` to `true`.
+186 -36
View File
@@ -1,12 +1,14 @@
#include "File.h"
#include "mutex.h"
#include "StrFmt.h"
#include "StrUtil.h"
#include "Crypto/sha1.h"
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <map>
#include <iostream>
#include "util/asm.hpp"
#include "util/coro.hpp"
@@ -49,23 +51,6 @@ static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
return buffer;
}
static void to_utf8(std::string& out, const wchar_t* source)
{
// String size
const usz length = std::wcslen(source);
// Safe buffer size for max possible output length (including null terminator)
const int buf_size = narrow<int>(length * 3 + 1);
// Resize buffer
out.resize(buf_size - 1);
const int result = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &out.front(), buf_size, nullptr, nullptr);
// Fix the size
out.resize(ensure(result) - 1);
}
static time_t to_time(const ULARGE_INTEGER& ft)
{
return ft.QuadPart / 10000000ULL - 11644473600ULL;
@@ -153,6 +138,7 @@ static fs::error to_error(DWORD e)
#if defined(__APPLE__)
#include <copyfile.h>
#include <mach-o/dyld.h>
#include <limits.h>
#elif defined(__linux__) || defined(__sun)
#include <sys/sendfile.h>
#include <sys/syscall.h>
@@ -371,6 +357,12 @@ namespace fs
return false;
}
bool device_base::create_symlink(const std::string&)
{
g_tls_error = error::readonly;
return false;
}
bool device_base::rename(const std::string&, const std::string&)
{
g_tls_error = error::readonly;
@@ -531,7 +523,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
// Handle drives specially
if (epath.find_first_of(delim) == umax && epath.ends_with(':'))
{
WIN32_FILE_ATTRIBUTE_DATA attrs;
WIN32_FILE_ATTRIBUTE_DATA attrs{};
// Must end with a delimiter
if (!GetFileAttributesExW(to_wchar(std::string(epath) + '/').get(), GetFileExInfoStandard, &attrs))
@@ -553,8 +545,6 @@ bool fs::get_stat(const std::string& path, stat_t& info)
return true;
}
WIN32_FIND_DATA attrs;
// Allowed by FindFirstFileExW but we should not allow it
if (epath.ends_with("*"))
{
@@ -562,6 +552,8 @@ bool fs::get_stat(const std::string& path, stat_t& info)
return false;
}
WIN32_FIND_DATA attrs{};
const auto wchar_ptr = to_wchar(std::string(epath));
const std::wstring_view wpath_view = wchar_ptr.get();
@@ -595,6 +587,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
}
info.is_directory = (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_symlink = (attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
info.is_writable = (attrs.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = attrs.nFileSizeLow | (u64{attrs.nFileSizeHigh} << 32);
info.atime = to_time(attrs.ftLastAccessTime);
@@ -609,6 +602,7 @@ bool fs::get_stat(const std::string& path, stat_t& info)
}
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_symlink = S_ISLNK(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
info.atime = file_info.st_atime;
@@ -662,6 +656,23 @@ bool fs::is_dir(const std::string& path)
return true;
}
bool fs::is_symlink(const std::string& path)
{
fs::stat_t info{};
if (!fs::get_stat(path, info))
{
return false;
}
if (!info.is_symlink)
{
g_tls_error = error::exist;
return false;
}
return true;
}
bool fs::statfs(const std::string& path, fs::device_stat& info)
{
if (auto device = get_virtual_device(path))
@@ -761,7 +772,7 @@ bool fs::create_path(const std::string& path)
#ifdef _WIN32
// Workaround: don't call is_dir with naked drive letter
if (parent.size() < path.size() && parent.back() != ':' && !is_dir(parent) && !create_path(parent))
if (parent.size() < path.size() && (parent.empty() || (parent.back() != ':' && !is_dir(parent) && !create_path(parent))))
#else
if (parent.size() < path.size() && !is_dir(parent) && !create_path(parent))
#endif
@@ -797,10 +808,35 @@ bool fs::remove_dir(const std::string& path)
g_tls_error = to_error(GetLastError());
return false;
}
#else
if (::rmdir(path.c_str()) != 0)
{
g_tls_error = to_error(errno);
return false;
}
#endif
return true;
}
bool fs::create_symlink(const std::string& path, const std::string& target)
{
if (auto device = get_virtual_device(path))
{
return device->create_symlink(path);
}
#ifdef _WIN32
const DWORD flags = is_dir(target) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;
if (!CreateSymbolicLinkW(to_wchar(path).get(), to_wchar(target).get(), flags))
{
g_tls_error = to_error(GetLastError());
return false;
}
return true;
#else
if (::rmdir(path.c_str()) != 0)
if (::symlink(target.c_str(), path.c_str()) != 0)
{
g_tls_error = to_error(errno);
return false;
@@ -1789,7 +1825,7 @@ bool fs::dir::open(const std::string& path)
{
dir_entry info;
to_utf8(info.name, found.cFileName);
info.name = wchar_to_utf8(found.cFileName);
info.is_directory = (found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_writable = (found.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = (static_cast<u64>(found.nFileSizeHigh) << 32) | static_cast<u64>(found.nFileSizeLow);
@@ -1905,9 +1941,9 @@ bool fs::dir::open(const std::string& path)
return true;
}
bool fs::file::strict_read_check(u64 _size, u64 type_size) const
bool fs::file::strict_read_check(u64 offset, u64 _size, u64 type_size) const
{
if (usz pos0 = pos(), size0 = size(); (pos0 >= size0 ? 0 : (size0 - pos0)) / type_size < _size)
if (usz pos0 = offset, size0 = size(); (pos0 >= size0 ? 0 : (size0 - pos0)) / type_size < _size)
{
fs::g_tls_error = fs::error::inval;
return false;
@@ -1916,6 +1952,72 @@ bool fs::file::strict_read_check(u64 _size, u64 type_size) const
return true;
}
std::string fs::get_executable_path()
{
// Use magic static
static const std::string s_exe_path = []
{
#if defined(_WIN32)
constexpr DWORD size = 32767;
std::vector<wchar_t> buffer(size);
GetModuleFileNameW(nullptr, buffer.data(), size);
return wchar_to_utf8(buffer.data());
#elif defined(__APPLE__)
char bin_path[PATH_MAX];
uint32_t bin_path_size = sizeof(bin_path);
if (_NSGetExecutablePath(bin_path, &bin_path_size) != 0)
{
std::cerr << "Failed to find app binary path" << std::endl;
return std::string{};
}
// App bundle directory is three levels up from the binary.
return get_parent_dir(bin_path, 3);
#else
if (const char* appimage_path = ::getenv("APPIMAGE"))
{
std::cout << "Found AppImage path: " << appimage_path << std::endl;
return std::string(appimage_path);
}
std::cout << "No AppImage path found, checking for executable" << std::endl;
char exe_path[PATH_MAX];
const ssize_t len = ::readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
if (len == -1)
{
std::cerr << "Failed to find executable path" << std::endl;
return std::string{};
}
exe_path[len] = '\0';
std::cout << "Found exec path: " << exe_path << std::endl;
return std::string(exe_path);
#endif
}();
return s_exe_path;
}
std::string fs::get_executable_dir()
{
// Use magic static
static const std::string s_exe_dir = []
{
std::string exe_path = get_executable_path();
if (exe_path.empty())
{
return exe_path;
}
return get_parent_dir(exe_path) + "/";
}();
return s_exe_dir;
}
const std::string& fs::get_config_dir()
{
// Use magic static
@@ -1923,17 +2025,52 @@ const std::string& fs::get_config_dir()
{
std::string dir;
#ifdef _WIN32
wchar_t buf[32768];
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
GetModuleFileName(nullptr, buf, size) - 1 >= size - 1)
// Check if a portable directory exists.
std::string portable_dir = get_executable_dir() + "/portable/";
if (is_dir(portable_dir))
{
MessageBoxA(nullptr, fmt::format("GetModuleFileName() failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
return dir; // empty
return portable_dir;
}
to_utf8(dir, buf); // Convert to UTF-8
#ifdef _WIN32
std::vector<wchar_t> buf;
// Check if RPCS3_CONFIG_DIR is set and get the required buffer size
DWORD size = GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", nullptr, 0);
if (size > 0)
{
// Resize buffer and fetch RPCS3_CONFIG_DIR
buf.resize(size);
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf.data(), size) != (size - 1))
{
// Clear buffer on failure and notify user
MessageBoxA(nullptr, fmt::format("GetEnvironmentVariable(RPCS3_CONFIG_DIR) failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
buf.clear();
}
}
// Fallback to executable path if needed
for (DWORD buf_size = MAX_PATH; size == 0; buf_size += MAX_PATH)
{
buf.resize(buf_size);
size = GetModuleFileName(nullptr, buf.data(), buf_size);
if (size == 0)
{
MessageBoxA(nullptr, fmt::format("GetModuleFileName() failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
return dir; // empty
}
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// Try again with increased buffer size
size = 0;
continue;
}
}
dir = wchar_to_utf8(buf.data());
std::replace(dir.begin(), dir.end(), '\\', '/');
@@ -2018,10 +2155,23 @@ const std::string& fs::get_temp_dir()
return dir; // empty
}
to_utf8(dir, buf);
dir = wchar_to_utf8(buf);
#else
// TODO
dir = get_cache_dir();
const char* tmp_dir = getenv("TMPDIR");
if (tmp_dir == nullptr || tmp_dir[0] == '\0')
{
// Fall back to cache directory
dir = get_cache_dir();
}
else
{
dir = tmp_dir;
if (!dir.ends_with("/"))
{
// Ensure path ends with a separator
dir += "/";
}
}
#endif
return dir;
+24 -6
View File
@@ -66,6 +66,7 @@ namespace fs
struct stat_t
{
bool is_directory;
bool is_symlink;
bool is_writable;
u64 size;
s64 atime;
@@ -155,6 +156,7 @@ namespace fs
virtual bool statfs(const std::string& path, device_stat& info) = 0;
virtual bool remove_dir(const std::string& path);
virtual bool create_dir(const std::string& path);
virtual bool create_symlink(const std::string& path);
virtual bool rename(const std::string& from, const std::string& to);
virtual bool remove(const std::string& path);
virtual bool trunc(const std::string& path, u64 length);
@@ -197,6 +199,9 @@ namespace fs
// Check whether the directory exists and is NOT a file
bool is_dir(const std::string& path);
// Check whether the path points to an existing symlink
bool is_symlink(const std::string& path);
// Get filesystem information
bool statfs(const std::string& path, device_stat& info);
@@ -209,6 +214,9 @@ namespace fs
// Create directories
bool create_path(const std::string& path);
// Create symbolic link
bool create_symlink(const std::string& path, const std::string& target);
// Rename (move) file or directory
bool rename(const std::string& from, const std::string& to, bool overwrite);
@@ -306,7 +314,7 @@ namespace fs
}
// Check if the handle is capable of reading (size * type_size) of bytes at the time of calling
bool strict_read_check(u64 size, u64 type_size = 1) const;
bool strict_read_check(u64 offset, u64 size, u64 type_size) const;
// Read the data from the file and return the amount of data written in buffer
u64 read(void* buffer, u64 count,
@@ -424,7 +432,7 @@ namespace fs
if (_size != umax)
{
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(pos(), _size, sizeof(T)))
{
return false;
}
@@ -449,7 +457,7 @@ namespace fs
// Read POD std::vector
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
bool read(std::vector<T>& vec, usz _size = umax,
bool read(std::vector<T>& vec, usz _size = umax, bool use_offs = false, usz offset = umax,
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION(),
u32 line = __builtin_LINE(),
@@ -460,7 +468,7 @@ namespace fs
if (_size != umax)
{
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(use_offs ? offset : pos(), _size, sizeof(T)))
{
return false;
}
@@ -468,6 +476,11 @@ namespace fs
vec.resize(_size);
}
if (use_offs)
{
return read_at(offset, vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
}
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
}
@@ -510,8 +523,7 @@ namespace fs
{
std::vector<T> result;
result.resize(size() / sizeof(T));
seek(0);
if (!read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
if (!read(result, result.size(), true, 0, file, func, line, col)) xfail({line, col, file, func});
return result;
}
@@ -660,6 +672,12 @@ namespace fs
}
};
// Get executable path
std::string get_executable_path();
// Get executable containing directory
std::string get_executable_dir();
// Get configuration directory
const std::string& get_config_dir();
+1 -1
View File
@@ -554,4 +554,4 @@ public:
static std::string triple2();
};
#endif
#endif // LLVM_AVAILABLE
+1 -649
View File
@@ -4,16 +4,13 @@
#include "StrFmt.h"
#include "File.h"
#include "util/logs.hpp"
#include "mutex.h"
#include "util/vm.hpp"
#include "util/asm.hpp"
#include "util/v128.hpp"
#include "util/simd.hpp"
#include "Crypto/unzip.h"
#include <charconv>
#ifdef __linux__
#include <unistd.h>
#define CAN_OVERCOMMIT
#endif
@@ -845,648 +842,3 @@ void asmjit::simd_builder::vec_extract_gpr(u32 esize, const x86::Gp& dst, const
}
#endif /* X86 */
#ifdef LLVM_AVAILABLE
#include <unordered_map>
#include <unordered_set>
#include <deque>
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic ignored "-Wredundant-decls"
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#endif
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolSize.h"
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
const bool jit_initialize = []() -> bool
{
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
LLVMLinkInMCJIT();
return true;
}();
[[noreturn]] static void null(const char* name)
{
fmt::throw_exception("Null function: %s", name);
}
namespace vm
{
extern u8* const g_sudo_addr;
}
static shared_mutex null_mtx;
static std::unordered_map<std::string, u64> null_funcs;
static u64 make_null_function(const std::string& name)
{
if (name.starts_with("__0x"))
{
u32 addr = -1;
auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16);
if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000)
{
// Point the garbage to reserved, non-executable memory
return reinterpret_cast<u64>(vm::g_sudo_addr + addr);
}
}
std::lock_guard lock(null_mtx);
if (u64& func_ptr = null_funcs[name]) [[likely]]
{
// Already exists
return func_ptr;
}
else
{
using namespace asmjit;
// Build a "null" function that contains its name
const auto func = build_function_asm<void (*)()>("NULL", [&](native_asm& c, auto& args)
{
#if defined(ARCH_X64)
Label data = c.newLabel();
c.lea(args[0], x86::qword_ptr(data, 0));
c.jmp(Imm(&null));
c.align(AlignMode::kCode, 16);
c.bind(data);
// Copy function name bytes
for (char ch : name)
c.db(ch);
c.db(0);
c.align(AlignMode::kData, 16);
#else
// AArch64 implementation
Label jmp_address = c.newLabel();
Label data = c.newLabel();
// Force absolute jump to prevent out of bounds PC-rel jmp
c.ldr(args[0], arm::ptr(jmp_address));
c.br(args[0]);
c.align(AlignMode::kCode, 16);
c.bind(data);
c.embed(name.c_str(), name.size());
c.embedUInt8(0U);
c.bind(jmp_address);
c.embedUInt64(reinterpret_cast<u64>(&null));
c.align(AlignMode::kData, 16);
#endif
});
func_ptr = reinterpret_cast<u64>(func);
return func_ptr;
}
}
struct JITAnnouncer : llvm::JITEventListener
{
void notifyObjectLoaded(u64, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& info) override
{
using namespace llvm;
object::OwningBinary<object::ObjectFile> debug_obj_ = info.getObjectForDebug(obj);
if (!debug_obj_.getBinary())
{
#ifdef __linux__
jit_log.error("LLVM: Failed to announce JIT events (no debug object)");
#endif
return;
}
const object::ObjectFile& debug_obj = *debug_obj_.getBinary();
for (const auto& [sym, size] : computeSymbolSizes(debug_obj))
{
Expected<object::SymbolRef::Type> type_ = sym.getType();
if (!type_ || *type_ != object::SymbolRef::ST_Function)
continue;
Expected<StringRef> name = sym.getName();
if (!name)
continue;
Expected<u64> addr = sym.getAddress();
if (!addr)
continue;
jit_announce(*addr, size, {name->data(), name->size()});
}
}
};
// Simple memory manager
struct MemoryManager1 : llvm::RTDyldMemoryManager
{
// 256 MiB for code or data
static constexpr u64 c_max_size = 0x20000000 / 2;
// Allocation unit (2M)
static constexpr u64 c_page_size = 2 * 1024 * 1024;
// Reserve 512 MiB
u8* const ptr = static_cast<u8*>(utils::memory_reserve(c_max_size * 2));
u64 code_ptr = 0;
u64 data_ptr = c_max_size;
MemoryManager1() = default;
MemoryManager1(const MemoryManager1&) = delete;
MemoryManager1& operator=(const MemoryManager1&) = delete;
~MemoryManager1() override
{
// Hack: don't release to prevent reuse of address space, see jit_announce
utils::memory_decommit(ptr, c_max_size * 2);
}
llvm::JITSymbol findSymbol(const std::string& name) override
{
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
if (!addr)
{
addr = make_null_function(name);
if (!addr)
{
fmt::throw_exception("Failed to link '%s'", name);
}
}
return {addr, llvm::JITSymbolFlags::Exported};
}
u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot)
{
if (align > c_page_size)
{
jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align);
return nullptr;
}
const u64 olda = utils::align(oldp, align);
const u64 newp = utils::align(olda + size, align);
if ((newp - 1) / c_max_size != oldp / c_max_size)
{
jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align);
return nullptr;
}
if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size)
{
// Allocate pages on demand
const u64 pagea = utils::align(oldp, c_page_size);
const u64 psize = utils::align(newp - pagea, c_page_size);
utils::memory_commit(this->ptr + pagea, psize, prot);
}
// Update allocation counter
oldp = newp;
return this->ptr + olda;
}
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
{
return allocate(code_ptr, size, align, utils::protection::wx);
}
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
{
return allocate(data_ptr, size, align, utils::protection::rw);
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8*, u64, usz) override
{
}
void deregisterEHFrames() override
{
}
};
// Simple memory manager
struct MemoryManager2 : llvm::RTDyldMemoryManager
{
MemoryManager2() = default;
~MemoryManager2() override
{
}
llvm::JITSymbol findSymbol(const std::string& name) override
{
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
if (!addr)
{
addr = make_null_function(name);
if (!addr)
{
fmt::throw_exception("Failed to link '%s' (MM2)", name);
}
}
return {addr, llvm::JITSymbolFlags::Exported};
}
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
{
return jit_runtime::alloc(size, align, true);
}
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
{
return jit_runtime::alloc(size, align, false);
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8*, u64, usz) override
{
}
void deregisterEHFrames() override
{
}
};
// Helper class
class ObjectCache final : public llvm::ObjectCache
{
const std::string& m_path;
public:
ObjectCache(const std::string& path)
: m_path(path)
{
}
~ObjectCache() override = default;
void notifyObjectCompiled(const llvm::Module* _module, llvm::MemoryBufferRef obj) override
{
std::string name = m_path;
name.append(_module->getName().data());
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
name.append(".gz");
fs::file module_file(name, fs::rewrite);
if (!module_file)
{
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
return;
}
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
{
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
module_file.close();
fs::remove_file(name);
return;
}
jit_log.notice("LLVM: Created module: %s", _module->getName().data());
}
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
{
if (fs::file cached{path + ".gz", fs::read})
{
const std::vector<u8> cached_data = cached.to_vector<u8>();
if (cached_data.empty()) [[unlikely]]
{
return nullptr;
}
const std::vector<u8> out = unzip(cached_data);
if (out.empty())
{
jit_log.error("LLVM: Failed to unzip module: '%s'", path);
return nullptr;
}
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(out.size());
std::memcpy(buf->getBufferStart(), out.data(), out.size());
return buf;
}
if (fs::file cached{path, fs::read})
{
if (cached.size() == 0) [[unlikely]]
{
return nullptr;
}
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(buf->getBufferStart(), buf->getBufferSize());
return buf;
}
return nullptr;
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* _module) override
{
std::string path = m_path;
path.append(_module->getName().data());
if (auto buf = load(path))
{
jit_log.notice("LLVM: Loaded module: %s", _module->getName().data());
return buf;
}
return nullptr;
}
};
std::string jit_compiler::cpu(const std::string& _cpu)
{
std::string m_cpu = _cpu;
if (m_cpu.empty())
{
m_cpu = llvm::sys::getHostCPUName().str();
if (m_cpu == "sandybridge" ||
m_cpu == "ivybridge" ||
m_cpu == "haswell" ||
m_cpu == "broadwell" ||
m_cpu == "skylake" ||
m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cooperlake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server" ||
m_cpu == "tigerlake" ||
m_cpu == "rocketlake" ||
m_cpu == "alderlake" ||
m_cpu == "raptorlake" ||
m_cpu == "meteorlake")
{
// Downgrade if AVX is not supported by some chips
if (!utils::has_avx())
{
m_cpu = "nehalem";
}
}
if (m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cooperlake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server" ||
m_cpu == "tigerlake" ||
m_cpu == "rocketlake")
{
// Downgrade if AVX-512 is disabled or not supported
if (!utils::has_avx512())
{
m_cpu = "skylake";
}
}
if (m_cpu == "znver1" && utils::has_clwb())
{
// Upgrade
m_cpu = "znver2";
}
if ((m_cpu == "znver3" || m_cpu == "goldmont" || m_cpu == "alderlake" || m_cpu == "raptorlake" || m_cpu == "meteorlake") && utils::has_avx512_icl())
{
// Upgrade
m_cpu = "icelake-client";
}
if (m_cpu == "goldmont" && utils::has_avx2())
{
// Upgrade
m_cpu = "alderlake";
}
}
return m_cpu;
}
std::string jit_compiler::triple1()
{
#if defined(_WIN32)
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#elif defined(__APPLE__) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#else
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#endif
}
std::string jit_compiler::triple2()
{
#if defined(_WIN32) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(_WIN32) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#else
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#endif
}
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
: m_context(new llvm::LLVMContext)
, m_cpu(cpu(_cpu))
{
std::string result;
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
null_mod->setTargetTriple(jit_compiler::triple1());
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
if (_link.empty())
{
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
if (flags & 0x1)
{
mem = std::make_unique<MemoryManager1>();
}
else
{
mem = std::make_unique<MemoryManager2>();
null_mod->setTargetTriple(jit_compiler::triple2());
}
}
else
{
mem = std::make_unique<MemoryManager1>();
}
{
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
.setErrorStr(&result)
.setEngineKind(llvm::EngineKind::JIT)
.setMCJITMemoryManager(std::move(mem))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
#ifdef __APPLE__
//.setCodeModel(llvm::CodeModel::Large)
#endif
.setRelocationModel(llvm::Reloc::Model::PIC_)
.setMCPU(m_cpu)
.create());
}
if (!_link.empty())
{
for (auto&& [name, addr] : _link)
{
m_engine->updateGlobalMapping(name, addr);
}
}
if (!_link.empty() || !(flags & 0x1))
{
m_engine->RegisterJITEventListener(llvm::JITEventListener::createIntelJITEventListener());
m_engine->RegisterJITEventListener(new JITAnnouncer);
}
if (!m_engine)
{
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
}
}
jit_compiler::~jit_compiler()
{
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string& path)
{
ObjectCache cache{path};
m_engine->setObjectCache(&cache);
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
m_engine->generateCodeForModule(ptr);
m_engine->setObjectCache(nullptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
{
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
m_engine->generateCodeForModule(ptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
}
void jit_compiler::add(const std::string& path)
{
auto cache = ObjectCache::load(path);
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
{
m_engine->addObjectFile(llvm::object::OwningBinary<llvm::object::ObjectFile>(std::move(*object_file), std::move(cache)));
}
else
{
jit_log.error("ObjectCache: Adding failed: %s", path);
}
}
bool jit_compiler::check(const std::string& path)
{
if (auto cache = ObjectCache::load(path))
{
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
{
return true;
}
if (fs::remove_file(path))
{
jit_log.error("ObjectCache: Removed damaged file: %s", path);
}
}
return false;
}
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
{
m_engine->updateGlobalMapping(name, addr);
}
void jit_compiler::fin()
{
m_engine->finalizeObject();
}
u64 jit_compiler::get(const std::string& name)
{
return m_engine->getGlobalValueAddress(name);
}
#endif
+656
View File
@@ -0,0 +1,656 @@
#include "util/types.hpp"
#include "util/sysinfo.hpp"
#include "JIT.h"
#include "StrFmt.h"
#include "File.h"
#include "util/logs.hpp"
#include "mutex.h"
#include "util/vm.hpp"
#include "util/asm.hpp"
#include "Crypto/unzip.h"
#include <charconv>
LOG_CHANNEL(jit_log, "JIT");
#ifdef LLVM_AVAILABLE
#include <unordered_map>
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#pragma GCC diagnostic ignored "-Wredundant-decls"
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#endif
#include "llvm/Support/TargetSelect.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolSize.h"
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
const bool jit_initialize = []() -> bool
{
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();
LLVMLinkInMCJIT();
return true;
}();
[[noreturn]] static void null(const char* name)
{
fmt::throw_exception("Null function: %s", name);
}
namespace vm
{
extern u8* const g_sudo_addr;
}
static shared_mutex null_mtx;
static std::unordered_map<std::string, u64> null_funcs;
static u64 make_null_function(const std::string& name)
{
if (name.starts_with("__0x"))
{
u32 addr = -1;
auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16);
if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000)
{
// Point the garbage to reserved, non-executable memory
return reinterpret_cast<u64>(vm::g_sudo_addr + addr);
}
}
std::lock_guard lock(null_mtx);
if (u64& func_ptr = null_funcs[name]) [[likely]]
{
// Already exists
return func_ptr;
}
else
{
using namespace asmjit;
// Build a "null" function that contains its name
const auto func = build_function_asm<void (*)()>("NULL", [&](native_asm& c, auto& args)
{
#if defined(ARCH_X64)
Label data = c.newLabel();
c.lea(args[0], x86::qword_ptr(data, 0));
c.jmp(Imm(&null));
c.align(AlignMode::kCode, 16);
c.bind(data);
// Copy function name bytes
for (char ch : name)
c.db(ch);
c.db(0);
c.align(AlignMode::kData, 16);
#else
// AArch64 implementation
Label jmp_address = c.newLabel();
Label data = c.newLabel();
// Force absolute jump to prevent out of bounds PC-rel jmp
c.ldr(args[0], arm::ptr(jmp_address));
c.br(args[0]);
c.align(AlignMode::kCode, 16);
c.bind(data);
c.embed(name.c_str(), name.size());
c.embedUInt8(0U);
c.bind(jmp_address);
c.embedUInt64(reinterpret_cast<u64>(&null));
c.align(AlignMode::kData, 16);
#endif
});
func_ptr = reinterpret_cast<u64>(func);
return func_ptr;
}
}
struct JITAnnouncer : llvm::JITEventListener
{
void notifyObjectLoaded(u64, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& info) override
{
using namespace llvm;
object::OwningBinary<object::ObjectFile> debug_obj_ = info.getObjectForDebug(obj);
if (!debug_obj_.getBinary())
{
#ifdef __linux__
jit_log.error("LLVM: Failed to announce JIT events (no debug object)");
#endif
return;
}
const object::ObjectFile& debug_obj = *debug_obj_.getBinary();
for (const auto& [sym, size] : computeSymbolSizes(debug_obj))
{
Expected<object::SymbolRef::Type> type_ = sym.getType();
if (!type_ || *type_ != object::SymbolRef::ST_Function)
continue;
Expected<StringRef> name = sym.getName();
if (!name)
continue;
Expected<u64> addr = sym.getAddress();
if (!addr)
continue;
jit_announce(*addr, size, {name->data(), name->size()});
}
}
};
// Simple memory manager
struct MemoryManager1 : llvm::RTDyldMemoryManager
{
// 256 MiB for code or data
static constexpr u64 c_max_size = 0x20000000 / 2;
// Allocation unit (2M)
static constexpr u64 c_page_size = 2 * 1024 * 1024;
// Reserve 512 MiB
u8* const ptr = static_cast<u8*>(utils::memory_reserve(c_max_size * 2));
u64 code_ptr = 0;
u64 data_ptr = c_max_size;
MemoryManager1() = default;
MemoryManager1(const MemoryManager1&) = delete;
MemoryManager1& operator=(const MemoryManager1&) = delete;
~MemoryManager1() override
{
// Hack: don't release to prevent reuse of address space, see jit_announce
utils::memory_decommit(ptr, c_max_size * 2);
}
llvm::JITSymbol findSymbol(const std::string& name) override
{
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
if (!addr)
{
addr = make_null_function(name);
if (!addr)
{
fmt::throw_exception("Failed to link '%s'", name);
}
}
return {addr, llvm::JITSymbolFlags::Exported};
}
u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot)
{
if (align > c_page_size)
{
jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align);
return nullptr;
}
const u64 olda = utils::align(oldp, align);
const u64 newp = utils::align(olda + size, align);
if ((newp - 1) / c_max_size != oldp / c_max_size)
{
jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align);
return nullptr;
}
if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size)
{
// Allocate pages on demand
const u64 pagea = utils::align(oldp, c_page_size);
const u64 psize = utils::align(newp - pagea, c_page_size);
utils::memory_commit(this->ptr + pagea, psize, prot);
}
// Update allocation counter
oldp = newp;
return this->ptr + olda;
}
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
{
return allocate(code_ptr, size, align, utils::protection::wx);
}
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
{
return allocate(data_ptr, size, align, utils::protection::rw);
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8*, u64, usz) override
{
}
void deregisterEHFrames() override
{
}
};
// Simple memory manager
struct MemoryManager2 : llvm::RTDyldMemoryManager
{
MemoryManager2() = default;
~MemoryManager2() override
{
}
llvm::JITSymbol findSymbol(const std::string& name) override
{
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
if (!addr)
{
addr = make_null_function(name);
if (!addr)
{
fmt::throw_exception("Failed to link '%s' (MM2)", name);
}
}
return {addr, llvm::JITSymbolFlags::Exported};
}
u8* allocateCodeSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/) override
{
return jit_runtime::alloc(size, align, true);
}
u8* allocateDataSection(uptr size, uint align, uint /*sec_id*/, llvm::StringRef /*sec_name*/, bool /*is_ro*/) override
{
return jit_runtime::alloc(size, align, false);
}
bool finalizeMemory(std::string* = nullptr) override
{
return false;
}
void registerEHFrames(u8*, u64, usz) override
{
}
void deregisterEHFrames() override
{
}
};
// Helper class
class ObjectCache final : public llvm::ObjectCache
{
const std::string& m_path;
public:
ObjectCache(const std::string& path)
: m_path(path)
{
}
~ObjectCache() override = default;
void notifyObjectCompiled(const llvm::Module* _module, llvm::MemoryBufferRef obj) override
{
std::string name = m_path;
name.append(_module->getName().data());
//fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
name.append(".gz");
fs::file module_file(name, fs::rewrite);
if (!module_file)
{
jit_log.error("LLVM: Failed to create module file: %s (%s)", name, fs::g_tls_error);
return;
}
if (!zip(obj.getBufferStart(), obj.getBufferSize(), module_file))
{
jit_log.error("LLVM: Failed to compress module: %s", _module->getName().data());
module_file.close();
fs::remove_file(name);
return;
}
jit_log.notice("LLVM: Created module: %s", _module->getName().data());
}
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
{
if (fs::file cached{path + ".gz", fs::read})
{
const std::vector<u8> cached_data = cached.to_vector<u8>();
if (cached_data.empty()) [[unlikely]]
{
return nullptr;
}
const std::vector<u8> out = unzip(cached_data);
if (out.empty())
{
jit_log.error("LLVM: Failed to unzip module: '%s'", path);
return nullptr;
}
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(out.size());
std::memcpy(buf->getBufferStart(), out.data(), out.size());
return buf;
}
if (fs::file cached{path, fs::read})
{
if (cached.size() == 0) [[unlikely]]
{
return nullptr;
}
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
cached.read(buf->getBufferStart(), buf->getBufferSize());
return buf;
}
return nullptr;
}
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* _module) override
{
std::string path = m_path;
path.append(_module->getName().data());
if (auto buf = load(path))
{
jit_log.notice("LLVM: Loaded module: %s", _module->getName().data());
return buf;
}
return nullptr;
}
};
std::string jit_compiler::cpu(const std::string& _cpu)
{
std::string m_cpu = _cpu;
if (m_cpu.empty())
{
m_cpu = llvm::sys::getHostCPUName().str();
if (m_cpu == "sandybridge" ||
m_cpu == "ivybridge" ||
m_cpu == "haswell" ||
m_cpu == "broadwell" ||
m_cpu == "skylake" ||
m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cooperlake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server" ||
m_cpu == "tigerlake" ||
m_cpu == "rocketlake" ||
m_cpu == "alderlake" ||
m_cpu == "raptorlake" ||
m_cpu == "meteorlake")
{
// Downgrade if AVX is not supported by some chips
if (!utils::has_avx())
{
m_cpu = "nehalem";
}
}
if (m_cpu == "skylake-avx512" ||
m_cpu == "cascadelake" ||
m_cpu == "cooperlake" ||
m_cpu == "cannonlake" ||
m_cpu == "icelake" ||
m_cpu == "icelake-client" ||
m_cpu == "icelake-server" ||
m_cpu == "tigerlake" ||
m_cpu == "rocketlake")
{
// Downgrade if AVX-512 is disabled or not supported
if (!utils::has_avx512())
{
m_cpu = "skylake";
}
}
if (m_cpu == "znver1" && utils::has_clwb())
{
// Upgrade
m_cpu = "znver2";
}
if ((m_cpu == "znver3" || m_cpu == "goldmont" || m_cpu == "alderlake" || m_cpu == "raptorlake" || m_cpu == "meteorlake") && utils::has_avx512_icl())
{
// Upgrade
m_cpu = "icelake-client";
}
if (m_cpu == "goldmont" && utils::has_avx2())
{
// Upgrade
m_cpu = "alderlake";
}
}
return m_cpu;
}
std::string jit_compiler::triple1()
{
#if defined(_WIN32)
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#elif defined(__APPLE__) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#else
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#endif
}
std::string jit_compiler::triple2()
{
#if defined(_WIN32) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(_WIN32) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_X64)
return llvm::Triple::normalize("x86_64-unknown-linux-gnu");
#elif defined(__APPLE__) && defined(ARCH_ARM64)
return llvm::Triple::normalize("aarch64-unknown-linux-gnu");
#else
return llvm::Triple::normalize(llvm::sys::getProcessTriple());
#endif
}
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
: m_context(new llvm::LLVMContext)
, m_cpu(cpu(_cpu))
{
std::string result;
auto null_mod = std::make_unique<llvm::Module> ("null_", *m_context);
null_mod->setTargetTriple(jit_compiler::triple1());
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
if (_link.empty())
{
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
if (flags & 0x1)
{
mem = std::make_unique<MemoryManager1>();
}
else
{
mem = std::make_unique<MemoryManager2>();
null_mod->setTargetTriple(jit_compiler::triple2());
}
}
else
{
mem = std::make_unique<MemoryManager1>();
}
{
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
.setErrorStr(&result)
.setEngineKind(llvm::EngineKind::JIT)
.setMCJITMemoryManager(std::move(mem))
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
#ifdef __APPLE__
//.setCodeModel(llvm::CodeModel::Large)
#endif
.setRelocationModel(llvm::Reloc::Model::PIC_)
.setMCPU(m_cpu)
.create());
}
if (!_link.empty())
{
for (auto&& [name, addr] : _link)
{
m_engine->updateGlobalMapping(name, addr);
}
}
if (!_link.empty() || !(flags & 0x1))
{
m_engine->RegisterJITEventListener(llvm::JITEventListener::createIntelJITEventListener());
m_engine->RegisterJITEventListener(new JITAnnouncer);
}
if (!m_engine)
{
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
}
}
jit_compiler::~jit_compiler()
{
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string& path)
{
ObjectCache cache{path};
m_engine->setObjectCache(&cache);
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
m_engine->generateCodeForModule(ptr);
m_engine->setObjectCache(nullptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
{
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
m_engine->generateCodeForModule(ptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
}
void jit_compiler::add(const std::string& path)
{
auto cache = ObjectCache::load(path);
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
{
m_engine->addObjectFile(llvm::object::OwningBinary<llvm::object::ObjectFile>(std::move(*object_file), std::move(cache)));
}
else
{
jit_log.error("ObjectCache: Adding failed: %s", path);
}
}
bool jit_compiler::check(const std::string& path)
{
if (auto cache = ObjectCache::load(path))
{
if (auto object_file = llvm::object::ObjectFile::createObjectFile(*cache))
{
return true;
}
if (fs::remove_file(path))
{
jit_log.error("ObjectCache: Removed damaged file: %s", path);
}
}
return false;
}
void jit_compiler::update_global_mapping(const std::string& name, u64 addr)
{
m_engine->updateGlobalMapping(name, addr);
}
void jit_compiler::fin()
{
m_engine->finalizeObject();
}
u64 jit_compiler::get(const std::string& name)
{
return m_engine->getGlobalValueAddress(name);
}
#endif // LLVM_AVAILABLE
+10
View File
@@ -35,6 +35,9 @@ std::string wchar_to_utf8(std::wstring_view src)
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -52,6 +55,8 @@ std::u16string utf8_to_utf16(std::string_view src)
}
#ifdef _MSC_VER
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#else
#pragma GCC diagnostic pop
#endif
@@ -652,6 +657,11 @@ std::string fmt::to_lower(std::string_view string)
return result;
}
std::string fmt::truncate(std::string_view src, usz length)
{
return std::string(src.begin(), src.begin() + std::min(src.size(), length));
}
bool fmt::match(const std::string& source, const std::string& mask)
{
usz source_position = 0, mask_position = 0;
+2
View File
@@ -178,6 +178,8 @@ namespace fmt
std::string to_upper(std::string_view string);
std::string to_lower(std::string_view string);
std::string truncate(std::string_view src, usz length);
bool match(const std::string& source, const std::string& mask);
struct buf_to_hexstring
+2 -1
View File
@@ -46,6 +46,7 @@ DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDesc
#include <time.h>
#endif
#ifdef __linux__
#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <unistd.h>
#endif
@@ -2426,7 +2427,7 @@ void thread_ctrl::wait_for_accurate(u64 usec)
break;
}
usec = (until - current).count();
usec = std::chrono::duration_cast<std::chrono::microseconds>(until - current).count();
}
#endif
}
+3 -3
View File
@@ -130,6 +130,9 @@ public:
const native_entry entry_point;
// Set name for debugger
static void set_name(std::string);
private:
// Thread handle (platform-specific)
atomic_t<u64> m_thread{0};
@@ -160,9 +163,6 @@ private:
// Cleanup after possibly deleting the thread instance
static native_entry finalize(u64 _self) noexcept;
// Set name for debugger
static void set_name(std::string);
// Make entry point
static native_entry make_trampoline(u64(*entry)(thread_base* _base));
+13 -3
View File
@@ -379,8 +379,8 @@ public:
return m_head != 0;
}
template <typename... Args>
void push(Args&&... args)
template <bool Notify = true, typename... Args>
bool push(Args&&... args)
{
auto oldv = m_head.load();
auto item = new lf_queue_item<T>(load(oldv), std::forward<Args>(args)...);
@@ -390,9 +390,19 @@ public:
item->m_link = load(oldv);
}
if (!oldv)
if (!oldv && Notify)
{
// Notify only if queue was empty
notify(true);
}
return !oldv;
}
void notify(bool force = false)
{
if (force || operator bool())
{
utils::bless<atomic_t<u32>>(&m_head)[1].notify_one();
}
}
+5 -5
View File
@@ -38,13 +38,13 @@ jobs:
displayName: ccache
- bash: |
docker pull --quiet rpcs3/rpcs3-ci-focal:1.5
docker pull --quiet rpcs3/rpcs3-ci-focal:1.6
docker run \
-v $(pwd):/rpcs3 \
--env-file .ci/docker.env \
-v $CCACHE_DIR:/root/.ccache \
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
rpcs3/rpcs3-ci-focal:1.5 \
rpcs3/rpcs3-ci-focal:1.6 \
/rpcs3/.ci/build-linux.sh
displayName: Docker setup and build
@@ -69,9 +69,9 @@ jobs:
variables:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.6.1'
QT_VER: '6.6.2'
QT_VER_MSVC: 'msvc2019'
QT_DATE: '202311210527'
QT_DATE: '202402121135'
QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
@@ -132,7 +132,7 @@ jobs:
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-mac"
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
ARTDIR: $(Build.ArtifactStagingDirectory)
QT_VER: '6.6.1'
QT_VER: '6.6.2'
QT_VER_MAIN: '6'
pool:
Binary file not shown.
+6 -4
View File
@@ -33,16 +33,17 @@ else()
add_compile_options(-fno-exceptions)
add_compile_options(-fstack-protector)
if (COMPILER_X86)
add_compile_options(-msse -msse2 -mcx16)
endif()
if (COMPILER_ARM)
# This section needs a review. Apple claims armv8.5-a on M-series but doesn't support SVE.
# Note that compared to the rest of the 8.x family, 8.1 is very restrictive and we'll have to bump the requirement in future to get anything meaningful.
if (APPLE)
add_compile_options(-march=armv8.4-a)
else()
add_compile_options(-march=armv8.1-a)
endif()
elseif(COMPILER_X86)
# Some compilers will set both X86 and ARM, so check explicitly for ARM first
add_compile_options(-msse -msse2 -mcx16)
endif()
add_compile_options(-Werror=old-style-cast)
@@ -51,6 +52,7 @@ else()
add_compile_options(-Werror=return-type)
add_compile_options(-Werror=overloaded-virtual)
add_compile_options(-Werror=missing-noreturn)
add_compile_options(-Werror=implicit-fallthrough)
add_compile_options(-Wunused-parameter)
add_compile_options(-Wignored-qualifiers)
add_compile_options(-Wredundant-move)
+3
View File
@@ -1,4 +1,7 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#import <Foundation/Foundation.h>
#pragma GCC diagnostic pop
namespace Darwin_Version
{
+4 -1
View File
@@ -45,7 +45,8 @@ if(WIN32)
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
elseif(APPLE)
add_executable(rpcs3 MACOSX_BUNDLE)
target_sources(rpcs3 PRIVATE rpcs3.icns)
target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh)
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set_target_properties(rpcs3
PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
@@ -70,10 +71,12 @@ target_sources(rpcs3
Input/dualsense_pad_handler.cpp
Input/evdev_joystick_handler.cpp
Input/evdev_gun_handler.cpp
Input/gui_pad_thread.cpp
Input/hid_pad_handler.cpp
Input/keyboard_pad_handler.cpp
Input/mm_joystick_handler.cpp
Input/pad_thread.cpp
Input/raw_mouse_handler.cpp
Input/sdl_pad_handler.cpp
Input/skateboard_pad_handler.cpp
Input/xinput_pad_handler.cpp
+11 -2
View File
@@ -106,7 +106,7 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
case "NPD\0"_u32:
{
// EDAT / SDAT
elf_file = DecryptEDAT(elf_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]), true);
elf_file = DecryptEDAT(elf_file, old_path, key_it != 0 ? 8 : 1, reinterpret_cast<u8*>(&m_klics[key_it]));
if (!elf_file)
{
@@ -139,7 +139,16 @@ usz decrypt_binaries_t::decrypt(std::string klic_input)
if (fs::file new_file{new_path, fs::rewrite})
{
new_file.write(elf_file.to_string());
// 16MB buffer
std::vector<u8> buffer(std::min<usz>(elf_file.size(), 1u << 24));
elf_file.seek(0);
while (usz read_size = elf_file.read(buffer.data(), buffer.size()))
{
new_file.write(buffer.data(), read_size);
}
dec_log.success("Decrypted %s -> %s", old_path, new_path);
std::cout << "Decrypted " << old_path << " -> " << new_path << std::endl; // For CLI
m_index++;
+181 -249
View File
@@ -157,37 +157,30 @@ u128 get_block_key(int block, NPD_HEADER *npd)
// for out data, allocate a buffer the size of 'edat->block_size'
// Also, set 'in file' to the beginning of the encrypted data, which may be offset if inside another file, but normally just reset to beginning of file
// returns number of bytes written, -1 for error
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left)
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left, bool is_out_buffer_aligned = false)
{
// Get metadata info and setup buffers.
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
const int metadata_offset = 0x100;
std::unique_ptr<u8[]> enc_data;
std::unique_ptr<u8[]> dec_data;
u8 hash[0x10] = { 0 };
u8 key_result[0x10] = { 0 };
u8 hash_result[0x14] = { 0 };
u64 offset = 0;
u64 metadata_sec_offset = 0;
s32 length = 0;
u64 length = 0;
s32 compression_end = 0;
unsigned char empty_iv[0x10] = {};
const u64 file_offset = in->pos();
memset(hash_result, 0, 0x14);
// Decrypt the metadata.
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
{
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
in->seek(file_offset + metadata_sec_offset);
u8 metadata[0x20]{};
unsigned char metadata[0x20];
memset(metadata, 0, 0x20);
in->read(metadata, 0x20);
in->read_at(metadata_sec_offset, metadata, 0x20);
// If the data is compressed, decrypt the metadata.
// NOTE: For NPD version 1 the metadata is not encrypted.
@@ -202,18 +195,17 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
std::tie(offset, length, compression_end) = dec_section(metadata);
}
memcpy(hash_result, metadata, 0x10);
std::memcpy(hash_result, metadata, 0x10);
}
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
{
// If FLAG 0x20, the metadata precedes each data block.
metadata_sec_offset = metadata_offset + u64{block_num} * (metadata_section_size + edat->block_size);
in->seek(file_offset + metadata_sec_offset);
unsigned char metadata[0x20];
memset(metadata, 0, 0x20);
in->read(metadata, 0x20);
memcpy(hash_result, metadata, 0x14);
u8 metadata[0x20]{};
in->read_at(metadata_sec_offset, metadata, 0x20);
std::memcpy(hash_result, metadata, 0x14);
// If FLAG 0x20 is set, apply custom xor.
for (int j = 0; j < 0x10; j++)
@@ -228,9 +220,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
else
{
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
in->seek(file_offset + metadata_sec_offset);
in->read(hash_result, 0x10);
in->read_at(metadata_sec_offset, hash_result, 0x10);
offset = metadata_offset + u64{block_num} * edat->block_size + total_blocks * metadata_section_size;
length = edat->block_size;
@@ -239,59 +231,82 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
}
// Locate the real data.
const int pad_length = length;
length = (pad_length + 0xF) & 0xFFFFFFF0;
const usz pad_length = length;
length = utils::align<usz>(pad_length, 0x10);
// Setup buffers for decryption and read the data.
enc_data.reset(new u8[length]{ 0 });
dec_data.reset(new u8[length]{ 0 });
memset(hash, 0, 0x10);
memset(key_result, 0, 0x10);
std::vector<u8> enc_data_buf(is_out_buffer_aligned || length == pad_length ? 0 : length);
std::vector<u8> dec_data_buf(length);
in->seek(file_offset + offset);
in->read(enc_data.get(), length);
// Try to use out buffer for file reads if no padding is needed instead of a new buffer
u8* enc_data = enc_data_buf.empty() ? out : enc_data_buf.data();
// Variable to avoid copies when possible
u8* dec_data = dec_data_buf.data();
std::memset(hash, 0, 0x10);
std::memset(key_result, 0, 0x10);
in->read_at(offset, enc_data, length);
// Generate a key for the current block.
auto b_key = get_block_key(block_num, npd);
// Encrypt the block key with the crypto key.
aesecb128_encrypt(crypt_key, reinterpret_cast<uchar*>(&b_key), key_result);
if ((edat->flags & EDAT_FLAG_0x10) != 0)
{
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
}
else
memcpy(hash, key_result, 0x10);
{
std::memcpy(hash, key_result, 0x10);
}
// Setup the crypto and hashing mode based on the extra flags.
int crypto_mode = ((edat->flags & EDAT_FLAG_0x02) == 0) ? 0x2 : 0x1;
int hash_mode;
if ((edat->flags & EDAT_FLAG_0x10) == 0)
if ((edat->flags & EDAT_FLAG_0x10) == 0)
hash_mode = 0x02;
else if ((edat->flags & EDAT_FLAG_0x20) == 0)
hash_mode = 0x04;
else
hash_mode = 0x01;
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
{
crypto_mode |= 0x10000000;
hash_mode |= 0x10000000;
}
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
const bool should_decompress = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end;
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
{
// Reset the flags.
crypto_mode |= 0x01000000;
hash_mode |= 0x01000000;
// Simply copy the data without the header or the footer.
memcpy(dec_data.get(), enc_data.get(), length);
if (should_decompress)
{
std::memcpy(dec_data, enc_data, length);
}
else
{
// Optimize when decompression is not needed by avoiding 2 copies
dec_data = enc_data;
}
}
else
{
// IV is null if NPD version is 1 or 0.
u8* iv = (npd->version <= 1) ? empty_iv : npd->digest;
// Call main crypto routine on this data block.
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data.get(), dec_data.get(), length, key_result, iv, hash, hash_result))
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result))
{
edat_log.error("Block at offset 0x%llx has invalid hash!", offset);
return -1;
@@ -299,9 +314,9 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
}
// Apply additional de-compression if needed and write the decrypted data.
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end)
if (should_decompress)
{
const int res = decompress(out, dec_data.get(), edat->block_size);
const int res = decompress(out, dec_data, edat->block_size);
size_left -= res;
@@ -313,42 +328,20 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
return -1;
}
}
return res;
}
else
{
memcpy(out, dec_data.get(), pad_length);
return pad_length;
}
}
// EDAT/SDAT decryption.
// reset file to beginning of data before calling
int decrypt_data(const fs::file* in, const fs::file* out, EDAT_HEADER *edat, NPD_HEADER *npd, unsigned char* crypt_key, bool /*verbose*/)
{
const int total_blocks = static_cast<int>((edat->file_size + edat->block_size - 1) / edat->block_size);
u64 size_left = edat->file_size;
std::unique_ptr<u8[]> data(new u8[edat->block_size]);
for (int i = 0; i < total_blocks; i++)
if (dec_data != out)
{
in->seek(0);
memset(data.get(), 0, edat->block_size);
u64 res = decrypt_block(in, data.get(), edat, npd, crypt_key, i, total_blocks, size_left);
if (res == umax)
{
edat_log.error("Decrypt Block failed!");
return 1;
}
size_left -= res;
out->write(data.get(), res);
std::memcpy(out, dec_data, pad_length);
}
return 0;
return pad_length;
}
// set file offset to beginning before calling
int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs::file* f, bool verbose)
bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f, bool verbose)
{
u8 header[0xA0] = { 0 };
u8 empty_header[0xA0] = { 0 };
@@ -363,7 +356,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if (edat->flags & 0x7EFFFFFE)
{
edat_log.error("Bad header flags!");
return 1;
return false;
}
}
else if (npd->version == 2)
@@ -371,7 +364,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if (edat->flags & 0x7EFFFFE0)
{
edat_log.error("Bad header flags!");
return 1;
return false;
}
}
else if ((npd->version == 3) || (npd->version == 4))
@@ -379,13 +372,13 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if (edat->flags & 0x7EFFFFC0)
{
edat_log.error("Bad header flags!");
return 1;
return false;
}
}
else
{
edat_log.error("Unknown version!");
return 1;
return false;
}
// Read in the file header.
@@ -421,7 +414,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
{
edat_log.error("RAP/RIF/KLIC key is invalid!");
return 1;
return false;
}
}
@@ -435,7 +428,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if (!edat->block_size)
{
return 1;
return false;
}
const usz block_num = utils::aligned_div<u64>(edat->file_size, edat->block_size);
@@ -445,7 +438,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if (utils::add_saturate<u64>(utils::add_saturate<u64>(file_offset, metadata_section_offset), metadata_size) > f->size())
{
return 1;
return false;
}
u64 bytes_read = 0;
@@ -455,10 +448,10 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
while (bytes_read < metadata_size)
{
// Locate the metadata blocks.
f->seek(file_offset + metadata_section_offset);
const usz offset = file_offset + metadata_section_offset;
// Read in the metadata.
f->read(metadata.get() + bytes_read, metadata_section_size);
f->read_at(offset, metadata.get() + bytes_read, metadata_section_size);
// Adjust sizes.
bytes_read += metadata_section_size;
@@ -492,15 +485,17 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
ecdsa_set_pub(VSH_PUB);
// Read in the metadata and header signatures.
f->seek(file_offset + 0xB0);
f->seek(0xB0);
f->read(metadata_signature, 0x28);
f->read(header_signature, 0x28);
// Checking metadata signature.
// Setup signature r and s.
memcpy(signature_r + 01, metadata_signature, 0x14);
memcpy(signature_s + 01, metadata_signature + 0x14, 0x14);
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
signature_r[0] = 0;
signature_s[0] = 0;
std::memcpy(signature_r + 1, metadata_signature, 0x14);
std::memcpy(signature_s + 1, metadata_signature + 0x14, 0x14);
if ((!std::memcmp(signature_r, zero_buf, 0x15)) || (!std::memcmp(signature_s, zero_buf, 0x15)))
{
edat_log.warning("Metadata signature is invalid!");
}
@@ -510,10 +505,12 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
{
const usz metadata_buf_size = block_num * 0x10;
std::unique_ptr<u8[]> metadata_buf(new u8[metadata_buf_size]);
f->seek(file_offset + metadata_offset);
f->read(metadata_buf.get(), metadata_buf_size);
sha1(metadata_buf.get(), metadata_buf_size, signature_hash);
std::vector<u8> metadata_buf(metadata_buf_size);
f->read_at(file_offset + metadata_offset, metadata_buf.data(), metadata_buf_size);
sha1(metadata_buf.data(), metadata_buf_size, signature_hash);
}
else
sha1(metadata.get(), metadata_size, signature_hash);
@@ -528,22 +525,23 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
// Checking header signature.
// Setup header signature r and s.
memset(signature_r, 0, 0x15);
memset(signature_s, 0, 0x15);
memcpy(signature_r + 01, header_signature, 0x14);
memcpy(signature_s + 01, header_signature + 0x14, 0x14);
signature_r[0] = 0;
signature_s[0] = 0;
std::memcpy(signature_r + 1, header_signature, 0x14);
std::memcpy(signature_s + 1, header_signature + 0x14, 0x14);
if ((!memcmp(signature_r, zero_buf, 0x15)) || (!memcmp(signature_s, zero_buf, 0x15)))
if ((!std::memcmp(signature_r, zero_buf, 0x15)) || (!std::memcmp(signature_s, zero_buf, 0x15)))
{
edat_log.warning("Header signature is invalid!");
}
else
{
// Setup header signature hash.
memset(signature_hash, 0, 20);
u8 header_buf[0xD8];
f->seek(file_offset);
f->read(header_buf, 0xD8);
std::memset(signature_hash, 0, 20);
u8 header_buf[0xD8]{};
f->read_at(file_offset, header_buf, 0xD8);
sha1(header_buf, 0xD8, signature_hash);
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
@@ -551,7 +549,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
}
}
return 0;
return true;
}
bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
@@ -585,21 +583,25 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
return cmac_hash_compare(reinterpret_cast<uchar*>(&key), 0x10, dev, 0x60, npd->dev_hash, 0x10);
}
bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, EDAT_HEADER* edat, bool verbose)
bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HEADER* npd, EDAT_HEADER* edat, bool verbose)
{
if (!file_name)
{
fmt::throw_exception("Empty filename");
}
// Ignore header validation in DEBUG data.
if (edat->flags & EDAT_DEBUG_DATA_FLAG)
{
return true;
}
const usz file_name_length = std::strlen(file_name);
const usz buf_len = 0x30 + file_name_length;
if (!validate_dev_klic(klicensee, npd))
{
return false;
}
if (file_name.empty())
{
return true;
}
const usz buf_len = 0x30 + file_name.size();
std::unique_ptr<u8[]> buf(new u8[buf_len]);
std::unique_ptr<u8[]> buf_lower(new u8[buf_len]);
@@ -607,12 +609,12 @@ bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER
// Build the title buffer (content_id + file_name).
std::memcpy(buf.get(), npd->content_id, 0x30);
std::memcpy(buf.get() + 0x30, file_name, file_name_length);
std::memcpy(buf.get() + 0x30, file_name.data(), file_name.size());
std::memcpy(buf_lower.get(), buf.get(), buf_len);
std::memcpy(buf_upper.get(), buf.get(), buf_len);
for (usz i = std::basic_string_view<u8>(buf.get() + 0x30, file_name_length).find_last_of('.'); i < buf_len; i++)
for (usz i = std::basic_string_view<u8>(buf.get() + 0x30, file_name.size()).find_last_of('.'); i < buf_len; i++)
{
const u8 c = static_cast<u8>(buf[i]);
buf_upper[i] = std::toupper(c);
@@ -634,17 +636,17 @@ bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER
edat_log.warning("NPD title hash is invalid!");
}
const bool dev_hash_result = validate_dev_klic(klicensee, npd);
return title_hash_result && dev_hash_result;
return title_hash_result;
}
void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT)
{
char npd_header[0x80];
char edat_header[0x10];
input->read(npd_header, sizeof(npd_header));
input->read(edat_header, sizeof(edat_header));
char npd_header[0x80]{};
char edat_header[0x10]{};
usz pos = input->pos();
pos += input->read_at(pos, npd_header, sizeof(npd_header));
input->read_at(pos, edat_header, sizeof(edat_header));
std::memcpy(&NPD.magic, npd_header, 4);
NPD.version = read_from_ptr<be_t<s32>>(npd_header, 4);
@@ -662,124 +664,6 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
EDAT.file_size = read_from_ptr<be_t<u64>>(edat_header, 8);
}
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;
EDAT_HEADER EDAT;
// Read in the NPD and EDAT/SDAT headers.
read_npd_edat_header(input, NPD, EDAT);
if (NPD.magic != "NPD\0"_u32)
{
edat_log.error("%s has invalid NPD header or already decrypted.", input_file_name);
return true;
}
if (verbose)
{
edat_log.notice("NPD HEADER");
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.
u128 key{};
// Check EDAT/SDAT flag.
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
{
if (verbose)
{
edat_log.notice("SDAT HEADER");
edat_log.notice("SDAT flags: 0x%08X", EDAT.flags);
edat_log.notice("SDAT block size: 0x%08X", EDAT.block_size);
edat_log.notice("SDAT file size: 0x%08X", EDAT.file_size);
}
// Generate SDAT key.
key = std::bit_cast<u128>(NPD.dev_hash) ^ std::bit_cast<u128>(SDAT_KEY);
}
else
{
if (verbose)
{
edat_log.notice("EDAT HEADER");
edat_log.notice("EDAT flags: 0x%08X", EDAT.flags);
edat_log.notice("EDAT block size: 0x%08X", EDAT.block_size);
edat_log.notice("EDAT file size: 0x%08X", EDAT.file_size);
}
// Perform header validation (EDAT only).
char real_file_name[CRYPTO_MAX_PATH];
extract_file_name(input_file_name, real_file_name);
if (!validate_npd_hashes(real_file_name, devklic, &NPD, &EDAT, verbose))
{
edat_log.error("NPD hash validation failed!");
return true;
}
// Select EDAT key.
if ((NPD.license & 0x3) == 0x3) // Type 3: Use supplied devklic.
{
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! (license=%d)", NPD.license);
return true;
}
if (verbose)
{
edat_log.notice("RIFKEY: %s", std::bit_cast<be_t<u128>>(key));
}
}
if (verbose)
{
be_t<u128> data;
std::memcpy(&data, devklic, sizeof(data));
edat_log.notice("DEVKLIC: %s", data);
}
}
if (verbose)
{
edat_log.notice("DECRYPTION KEY: %s", std::bit_cast<be_t<u128>>(key));
}
input->seek(0);
if (check_data(reinterpret_cast<uchar*>(&key), &EDAT, &NPD, input, verbose))
{
edat_log.error("Data parsing failed!");
return true;
}
input->seek(0);
if (decrypt_data(input, output, &EDAT, &NPD, reinterpret_cast<uchar*>(&key), verbose))
{
edat_log.error("Data decryption failed!");
return true;
}
return false;
}
u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
{
u128 rapkey{};
@@ -814,7 +698,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu
}
// Perform header validation (EDAT only).
char real_file_name[CRYPTO_MAX_PATH];
char real_file_name[CRYPTO_MAX_PATH]{};
extract_file_name(input_file_name.c_str(), real_file_name);
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, &EDAT, false))
{
@@ -834,7 +718,7 @@ 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, u8 *custom_klic, bool verbose)
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic)
{
if (!input)
{
@@ -890,25 +774,29 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
}
// 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), verbose))
auto data = std::make_unique<EDATADecrypter>(input, devklic, input_file_name, false);
if (!data->ReadHeader())
{
output.release();
return fs::file{};
}
output.seek(0);
fs::file output;
output.reset(std::move(data));
return output;
}
bool EDATADecrypter::ReadHeader()
{
edata_file.seek(0);
// Read in the NPD and EDAT/SDAT headers.
read_npd_edat_header(&edata_file, npdHeader, edatHeader);
if (npdHeader.magic != "NPD\0"_u32)
{
edat_log.error("Not an NPDRM file");
return false;
}
@@ -920,25 +808,56 @@ bool EDATADecrypter::ReadHeader()
}
else
{
// verify key
if (!validate_dev_klic(reinterpret_cast<uchar*>(&dec_key), &npdHeader))
// extract key from RIF
char real_file_name[CRYPTO_MAX_PATH]{};
extract_file_name(m_file_name.c_str(), real_file_name);
if (!validate_npd_hashes(real_file_name, reinterpret_cast<const u8*>(&dec_key), &npdHeader, &edatHeader, false))
{
edat_log.error("Failed validating klic");
return false;
edat_log.error("NPD hash validation failed!");
return true;
}
// Use provided dec_key
// Select EDAT key.
if (m_is_key_final)
{
// Already provided
}
// Type 3: Use supplied dec_key.
else if ((npdHeader.license & 0x3) == 0x3)
{
//
}
// Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
else
{
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.content_id);
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(dec_key))
{
dec_key = GetEdatRifKeyFromRapFile(rap);
}
// Make sure we don't have an empty RIF key.
if (!dec_key)
{
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", npdHeader.license);
return true;
}
edat_log.trace("RIFKEY: %s", std::bit_cast<be_t<u128>>(dec_key));
}
}
edata_file.seek(0);
// k the ecdsa_verify function in this check_data function takes a ridiculous amount of time
// like it slows down load time by a factor of x20, at least, so its ignored for now
/*if (check_data(dec_key._bytes, &edatHeader, &npdHeader, &sdata_file, false))
{
return false;
}*/
//if (!check_data(reinterpret_cast<u8*>(&dec_key), &edatHeader, &npdHeader, &edata_file, false))
//{
// edat_log.error("NPDRM check_data() failed!");
// return false;
//}
file_size = edatHeader.file_size;
total_blocks = ::narrow<u32>(utils::aligned_div(edatHeader.file_size, edatHeader.block_size));
@@ -948,6 +867,7 @@ bool EDATADecrypter::ReadHeader()
if (file_size && !ReadData(0, data_sample, 1))
{
edat_log.error("NPDRM ReadData() failed!");
return false;
}
@@ -967,28 +887,40 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
const u64 startOffset = pos % edatHeader.block_size;
const u64 num_blocks = utils::aligned_div(startOffset + size, edatHeader.block_size);
data_buf.resize(num_blocks * edatHeader.block_size);
// Find and decrypt block range covering pos + size
const u32 starting_block = ::narrow<u32>(pos / edatHeader.block_size);
const u32 ending_block = ::narrow<u32>(std::min<u64>(starting_block + num_blocks, total_blocks));
u64 writeOffset = 0;
for (u32 i = starting_block; i < ending_block; ++i)
std::vector<u8> data_buf(edatHeader.block_size + 16);
for (u32 i = starting_block; i < ending_block; i++)
{
edata_file.seek(0);
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size);
u64 res = decrypt_block(&edata_file, data_buf.data(), &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size, true);
if (res == umax)
{
edat_log.error("Error Decrypting data");
return 0;
}
writeOffset += res;
const usz skip_start = (i == starting_block ? startOffset : 0);
if (skip_start >= res)
{
break;
}
const usz end_pos = (i != total_blocks - 1 ? edatHeader.block_size : (edatHeader.file_size - 1) % edatHeader.block_size + 1);
const usz read_end = std::min<usz>(res, i == ending_block - 1 ? std::min<usz>(end_pos, (startOffset + size - 1) % edatHeader.block_size + 1) : end_pos);
std::memcpy(data + writeOffset, data_buf.data() + skip_start, read_end - skip_start);
std::memset(data_buf.data(), 0, read_end - skip_start);
writeOffset += read_end - skip_start;
}
const u64 bytesWrote = std::min<u64>(writeOffset - startOffset, size);
memcpy(data, &data_buf[startOffset], bytesWrote);
return bytesWrote;
return writeOffset;
}
+25 -7
View File
@@ -18,6 +18,7 @@ struct loaded_npdrm_keys
{
atomic_t<u128> dec_keys[16]{};
atomic_t<u64> dec_keys_pos = 0;
u128 one_time_key{}; // For savestates
atomic_t<u32> npdrm_fds{0};
void install_decryption_key(u128 key)
@@ -61,7 +62,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, u8 *custom_klic, bool verbose);
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic);
extern void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT);
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, NPD_HEADER *npd_out = nullptr);
@@ -71,7 +72,10 @@ u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
struct EDATADecrypter final : fs::file_base
{
// file stream
fs::file edata_file;
fs::file m_edata_file;
const fs::file& edata_file;
std::string m_file_name;
bool m_is_key_final = true;
u64 file_size{0};
u32 total_blocks{0};
u64 pos{0};
@@ -79,14 +83,23 @@ struct EDATADecrypter final : fs::file_base
NPD_HEADER npdHeader{};
EDAT_HEADER edatHeader{};
// Internal data buffers.
std::vector<u8> data_buf{};
u128 dec_key{};
public:
EDATADecrypter(fs::file&& input, u128 dec_key = {})
: edata_file(std::move(input))
EDATADecrypter(fs::file&& input, u128 dec_key = {}, std::string file_name = {}, bool is_key_final = true) noexcept
: m_edata_file(std::move(input))
, edata_file(m_edata_file)
, m_file_name(std::move(file_name))
, m_is_key_final(is_key_final)
, dec_key(dec_key)
{
}
EDATADecrypter(const fs::file& input, u128 dec_key = {}, std::string file_name = {}, bool is_key_final = true) noexcept
: m_edata_file(fs::file{})
, edata_file(input)
, m_file_name(std::move(file_name))
, m_is_key_final(is_key_final)
, dec_key(dec_key)
{
}
@@ -150,4 +163,9 @@ public:
id.type.insert(0, "EDATADecrypter: "sv);
return id;
}
u128 get_key() const
{
return dec_key;
}
};
+178 -23
View File
@@ -862,10 +862,12 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
return true;
}
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic);
void package_reader::extract_worker(thread_key thread_data_key)
{
std::vector<u8> read_cache;
while (m_num_failures == 0 && !m_aborted)
{
// Make sure m_entry_indexer does not exceed m_install_entries
@@ -941,42 +943,195 @@ void package_reader::extract_worker(thread_key thread_data_key)
pkg_log.warning("NPDRM EDAT!");
}
if (fs::file out = is_buffered ? fs::make_stream<std::vector<u8>>() : fs::file{ path, did_overwrite ? fs::rewrite : fs::write_new })
if (fs::file out{ path, did_overwrite ? fs::rewrite : fs::write_new })
{
bool extract_success = true;
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
struct pkg_file_reader : fs::file_base
{
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
const std::function<u64(u64, void*, u64)> m_read_func;
const install_entry& m_entry;
usz m_pos;
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
if (data_span.size() != block_size)
explicit pkg_file_reader(std::function<u64(u64, void* buffer, u64)> read_func, const install_entry& entry) noexcept
: m_read_func(std::move(read_func))
, m_entry(entry)
, m_pos(0)
{
extract_success = false;
pkg_log.error("Failed to extract file %s (data_span.size=%d, block_size=%d)", path, data_span.size(), block_size);
break;
}
if (out.write(data_span.data(), block_size) != block_size)
fs::stat_t get_stat() override
{
extract_success = false;
pkg_log.error("Failed to write file %s (error=%s)", path, fs::g_tls_error);
break;
fs::stat_t stat{};
stat.size = m_entry.file_size;
return stat;
}
m_written_bytes += block_size;
}
bool trunc(u64) override
{
return false;
}
u64 read(void* buffer, u64 size) override
{
const u64 result = pkg_file_reader::read_at(m_pos, buffer, size);
m_pos += result;
return result;
}
u64 read_at(u64 offset, void* buffer, u64 size) override
{
return m_read_func(offset, buffer, size);
}
u64 write(const void*, u64) override
{
return 0;
}
u64 seek(s64 offset, fs::seek_mode whence) override
{
const s64 new_pos =
whence == fs::seek_set ? offset :
whence == fs::seek_cur ? offset + m_pos :
whence == fs::seek_end ? offset + size() : -1;
if (new_pos < 0)
{
fs::g_tls_error = fs::error::inval;
return -1;
}
m_pos = new_pos;
return m_pos;
}
u64 size() override
{
return m_entry.file_size;
}
fs::file_id get_id() override
{
fs::file_id id{};
id.type.insert(0, "pkg_file_reader: "sv);
return id;
}
};
read_cache.clear();
auto reader = std::make_unique<pkg_file_reader>([&, cache_off = u64{umax}](usz pos, void* ptr, usz size) mutable -> u64
{
if (pos >= entry.file_size || !size)
{
return 0;
}
size = std::min<u64>(entry.file_size - pos, size);
u64 size_cache_end = 0;
u64 read_size = 0;
// Check if exists in cache
if (!read_cache.empty() && cache_off <= pos && pos < cache_off + read_cache.size())
{
read_size = std::min<u64>(pos + size, cache_off + read_cache.size()) - pos;
std::memcpy(ptr, read_cache.data() + (pos - cache_off), read_size);
pos += read_size;
}
else if (!read_cache.empty() && cache_off < pos + size && cache_off + read_cache.size() >= pos + size)
{
size_cache_end = size - (std::max<u64>(cache_off, pos) - pos);
std::memcpy(static_cast<u8*>(ptr) + (cache_off - pos), read_cache.data(), size_cache_end);
size -= size_cache_end;
}
if (pos >= entry.file_size || !size)
{
return read_size + size_cache_end;
}
// Try to cache for later
if (size <= BUF_SIZE && !size_cache_end && !read_size)
{
const u64 block_size = std::min<u64>({BUF_SIZE, std::max<u64>(size * 5 / 3, 65536), entry.file_size - pos});
read_cache.resize(block_size);
cache_off = pos;
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
if (data_span.empty())
{
cache_off = umax;
read_cache.clear();
return 0;
}
read_cache.resize(data_span.size());
std::memcpy(read_cache.data(), data_span.data(), data_span.size());
size = std::min<usz>(data_span.size(), size);
std::memcpy(ptr, data_span.data(), size);
return size;
}
while (read_size < size)
{
const u64 block_size = std::min<u64>(BUF_SIZE, size - read_size);
const std::span<const char> data_span = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), thread_data_key);
if (data_span.empty())
{
break;
}
std::memcpy(static_cast<u8*>(ptr) + read_size, data_span.data(), data_span.size());
read_size += data_span.size();
pos += data_span.size();
}
return read_size + size_cache_end;
}, entry);
fs::file in_data;
in_data.reset(std::move(reader));
fs::file final_data;
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))
{
m_num_failures++;
pkg_log.error("Failed to create file %s (error=%s)", path, fs::g_tls_error);
break;
}
final_data = DecryptEDAT(in_data, name, 1, reinterpret_cast<u8*>(&m_header.klicensee));
}
else
{
final_data = std::move(in_data);
}
if (!final_data)
{
m_num_failures++;
pkg_log.error("Failed to decrypt EDAT file %s (error=%s)", path, fs::g_tls_error);
break;
}
// 16MB buffer
std::vector<u8> buffer(std::min<usz>(entry.file_size, 1u << 24));
while (usz read_size = final_data.read(buffer.data(), buffer.size()))
{
out.write(buffer.data(), read_size);
m_written_bytes += read_size;
}
final_data.close();
out.close();
if (extract_success)
{
@@ -12,10 +12,6 @@
#include <Windows.h>
#include <system_error>
#ifdef _MSC_VER
#pragma comment(lib, "xaudio2_9redist.lib")
#endif
#ifndef XAUDIO2_USE_DEFAULT_PROCESSOR
#define XAUDIO2_USE_DEFAULT_PROCESSOR XAUDIO2_DEFAULT_PROCESSOR
#endif
-4
View File
@@ -8,12 +8,8 @@
#include "Utilities/mutex.h"
#include "Emu/Audio/AudioBackend.h"
#ifdef _MSC_VER
#include <xaudio2redist.h>
#else
#include <initguid.h>
#include <xaudio2.h>
#endif
#include <wrl/client.h>
#include <MMDeviceAPI.h>
+20 -30
View File
@@ -56,7 +56,8 @@ target_sources(rpcs3_emu PRIVATE
../../Utilities/cond.cpp
../../Utilities/Config.cpp
../../Utilities/File.cpp
../../Utilities/JIT.cpp
../../Utilities/JITASM.cpp
../../Utilities/JITLLVM.cpp
../../Utilities/LUrlParser.cpp
../../Utilities/mutex.cpp
../../Utilities/rXml.cpp
@@ -79,29 +80,15 @@ target_link_libraries(rpcs3_emu
PUBLIC
3rdparty::pugixml)
if(MSVC)
set_source_files_properties("../../Utilities/JIT.cpp" PROPERTIES
COMPILE_FLAGS /GR-
SKIP_PRECOMPILE_HEADERS ON
)
else()
set_source_files_properties("../../Utilities/JIT.cpp" PROPERTIES
COMPILE_FLAGS -fno-rtti
SKIP_PRECOMPILE_HEADERS ON
)
endif()
set_source_files_properties("../../Utilities/JITLLVM.cpp" "../../Utilities/JITASM.cpp" PROPERTIES
COMPILE_FLAGS "$<IF:$<CXX_COMPILER_ID:MSVC>,/GR-,-fno-rtti>"
SKIP_PRECOMPILE_HEADERS ON
)
if(MSVC)
set_source_files_properties("../util/yaml.cpp" PROPERTIES
COMPILE_FLAGS /EHsc
SKIP_PRECOMPILE_HEADERS ON
)
else()
set_source_files_properties("../util/yaml.cpp" PROPERTIES
COMPILE_FLAGS -fexceptions
SKIP_PRECOMPILE_HEADERS ON
)
endif()
set_source_files_properties("../util/yaml.cpp" PROPERTIES
COMPILE_FLAGS "$<IF:$<CXX_COMPILER_ID:MSVC>,/EHsc,-fexceptions>"
SKIP_PRECOMPILE_HEADERS ON
)
# Crypto
target_sources(rpcs3_emu PRIVATE
@@ -154,12 +141,8 @@ if(USE_FAUDIO)
endif()
if(WIN32)
target_include_directories(rpcs3_emu PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/XAudio2Redist/include")
if(MSVC)
target_link_libraries(rpcs3_emu PRIVATE "${PROJECT_SOURCE_DIR}/3rdparty/XAudio2Redist/libs/xaudio2_9redist.lib")
target_link_options(rpcs3_emu PRIVATE /DELAYLOAD:xaudio2_9redist.dll)
else()
target_link_libraries(rpcs3_emu PRIVATE xaudio2_8)
if(NOT MSVC)
target_link_libraries(rpcs3_emu PRIVATE xaudio2_9)
endif()
target_sources(rpcs3_emu PRIVATE
Audio/XAudio2/XAudio2Backend.cpp
@@ -199,7 +182,8 @@ target_sources(rpcs3_emu PRIVATE
Cell/SPUASMJITRecompiler.cpp
Cell/SPUDisAsm.cpp
Cell/SPUInterpreter.cpp
Cell/SPURecompiler.cpp
Cell/SPUCommonRecompiler.cpp
Cell/SPULLVMRecompiler.cpp
Cell/SPUThread.cpp
Cell/lv2/lv2.cpp
Cell/lv2/sys_bdemu.cpp
@@ -359,6 +343,7 @@ target_sources(rpcs3_emu PRIVATE
Cell/Modules/sceNpCommerce2.cpp
Cell/Modules/sceNp.cpp
Cell/Modules/sceNpMatchingInt.cpp
Cell/Modules/sceNpPlus.cpp
Cell/Modules/sceNpSns.cpp
Cell/Modules/sceNpTrophy.cpp
Cell/Modules/sceNpTus.cpp
@@ -425,6 +410,8 @@ target_sources(rpcs3_emu PRIVATE
Io/midi_config_types.cpp
Io/RB3MidiKeyboard.cpp
Io/RB3MidiGuitar.cpp
Io/RB3MidiDrums.cpp
Io/rb3drums_config.cpp
Io/Buzz.cpp
Io/Turntable.cpp
Io/usio.cpp
@@ -483,6 +470,8 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp
RSX/Overlays/Network/overlay_recvmessage_dialog.cpp
RSX/Overlays/Network/overlay_sendmessage_dialog.cpp
RSX/Overlays/overlay_animated_icon.cpp
RSX/Overlays/overlay_animation.cpp
RSX/Overlays/overlay_controls.cpp
@@ -525,6 +514,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/GL/glutils/program.cpp
RSX/GL/glutils/ring_buffer.cpp
RSX/GL/glutils/sampler.cpp
RSX/GL/upscalers/fsr1/fsr_pass.cpp
RSX/GL/GLCommonDecompiler.cpp
RSX/GL/GLCompute.cpp
RSX/GL/GLDraw.cpp
+9 -1
View File
@@ -952,7 +952,7 @@ bool cpu_thread::check_state() noexcept
continue;
}
if (state0 & cpu_flag::yield && cpu_flag::wait - state0 && cpu_can_stop)
if (state0 & cpu_flag::yield && cpu_can_stop)
{
if (auto spu = try_get<spu_thread>())
{
@@ -964,6 +964,14 @@ bool cpu_thread::check_state() noexcept
}
else if (auto ppu = try_get<ppu_thread>())
{
if (u32 usec = ppu->hw_sleep_time)
{
thread_ctrl::wait_for_accurate(usec);
ppu->hw_sleep_time = 0;
ppu->raddr = 0; // Also lose reservation if there is any (reservation is unsaved on hw thread switch)
continue;
}
if (ppu->raddr && ppu->rtime == vm::reservation_acquire(ppu->raddr))
{
// Same
+90 -5
View File
@@ -43,6 +43,9 @@
#include <functional>
#include <unordered_map>
// Helper function
llvm::Value* peek_through_bitcasts(llvm::Value*);
enum class i2 : char
{
};
@@ -147,7 +150,7 @@ struct llvm_value_t
std::tuple<> match(llvm::Value*& value, llvm::Module*) const
{
if (value != this->value)
if (peek_through_bitcasts(value) != peek_through_bitcasts(this->value))
{
value = nullptr;
}
@@ -503,9 +506,6 @@ using llvm_common_t = std::enable_if_t<(is_llvm_expr_of<T, Types>::ok && ...), t
template <typename... Args>
using llvm_match_tuple = decltype(std::tuple_cat(std::declval<llvm_expr_t<Args>&>().match(std::declval<llvm::Value*&>(), nullptr)...));
// Helper function
llvm::Value* peek_through_bitcasts(llvm::Value*);
template <typename T, typename U = llvm_common_t<llvm_value_t<T>>>
struct llvm_match_t
{
@@ -532,7 +532,7 @@ struct llvm_match_t
std::tuple<> match(llvm::Value*& value, llvm::Module*) const
{
if (value != this->value)
if (peek_through_bitcasts(value) != peek_through_bitcasts(this->value))
{
value = nullptr;
}
@@ -685,6 +685,18 @@ struct llvm_add
return std::tuple_cat(r1, r2);
}
}
v1 = i->getOperand(0);
v2 = i->getOperand(1);
// Argument order does not matter here, try when swapped
if (auto r1 = a1.match(v2, _m); v2)
{
if (auto r2 = a2.match(v1, _m); v1)
{
return std::tuple_cat(r1, r2);
}
}
}
value = nullptr;
@@ -858,6 +870,18 @@ struct llvm_mul
return std::tuple_cat(r1, r2);
}
}
v1 = i->getOperand(0);
v2 = i->getOperand(1);
// Argument order does not matter here, try when swapped
if (auto r1 = a1.match(v2, _m); v2)
{
if (auto r2 = a2.match(v1, _m); v1)
{
return std::tuple_cat(r1, r2);
}
}
}
value = nullptr;
@@ -2239,6 +2263,18 @@ struct llvm_add_sat
return std::tuple_cat(r1, r2);
}
}
v1 = i->getOperand(0);
v2 = i->getOperand(1);
// Argument order does not matter here, try when swapped
if (auto r1 = a1.match(v2, _m); v2)
{
if (auto r2 = a2.match(v1, _m); v1)
{
return std::tuple_cat(r1, r2);
}
}
}
value = nullptr;
@@ -2868,6 +2904,22 @@ struct llvm_fmuladd
}
}
}
v1 = i->getOperand(0);
v2 = i->getOperand(1);
v3 = i->getOperand(2);
// With multiplication args swapped
if (auto r1 = a1.match(v2, _m); v2)
{
if (auto r2 = a2.match(v1, _m); v1)
{
if (auto r3 = a3.match(v3, _m); v3)
{
return std::tuple_cat(r1, r2, r3);
}
}
}
}
value = nullptr;
@@ -2884,6 +2936,18 @@ struct llvm_calli
std::tuple<llvm_expr_t<A>...> a;
std::array<usz, sizeof...(A)> order_equality_hint = []()
{
std::array<usz, sizeof...(A)> r{};
for (usz i = 0; i < r.size(); i++)
{
r[i] = i;
}
return r;
}();
llvm::Value*(*c)(llvm::Value**, llvm::IRBuilder<>*){};
llvm::Value* eval(llvm::IRBuilder<>* ir) const
@@ -2917,6 +2981,13 @@ struct llvm_calli
return *this;
}
template <typename... Args> requires (sizeof...(Args) == sizeof...(A))
llvm_calli& set_order_equality_hint(Args... args)
{
order_equality_hint = {static_cast<usz>(args)...};
return *this;
}
llvm_match_tuple<A...> match(llvm::Value*& value, llvm::Module* _m) const
{
return match(value, _m, std::make_index_sequence<sizeof...(A)>());
@@ -2939,6 +3010,20 @@ struct llvm_calli
{
return std::tuple_cat(std::get<I>(r)...);
}
if constexpr (sizeof...(A) >= 2)
{
if (order_equality_hint[0] == order_equality_hint[1])
{
// Test if it works with the first pair swapped
((v[I <= 1 ? I ^ 1 : I] = i->getOperand(I)), ...);
if (((std::get<I>(r) = std::get<I>(a).match(v[I], _m), v[I]) && ...))
{
return std::tuple_cat(std::get<I>(r)...);
}
}
}
}
}
+13 -10
View File
@@ -278,7 +278,7 @@ public:
#endif
AVCodecContext* ctx = nullptr;
AVFormatContext* fmt = nullptr;
u8* io_buf;
u8* io_buf = nullptr;
struct AudioReader
{
@@ -371,17 +371,16 @@ public:
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
avcodec_free_context(&ctx);
}
if (io_buf)
{
av_freep(&io_buf);
}
if (fmt)
{
if (io_buf)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
if (fmt->pb) av_freep(&fmt->pb);
avformat_close_input(&fmt);
}
}
@@ -451,7 +450,11 @@ public:
}
AVPacket* packet = av_packet_alloc();
std::unique_ptr<AVPacket, decltype([](AVPacket* p){av_packet_unref(p);})> packet_(packet);
std::unique_ptr<AVPacket, decltype([](AVPacket* p)
{
av_packet_unref(p);
av_packet_free(&p);
})> packet_(packet);
if (just_started && just_finished)
{
File diff suppressed because it is too large Load Diff
+104 -3
View File
@@ -94,12 +94,30 @@ enum
CELL_FONT_TYPE_SEURAT_CAPIE_MARU_GOTHIC_VAGR2_SET = 0x00300224,
};
enum
{
CELL_FONT_LIBRARY_TYPE_NONE = 0
};
enum
{
CELL_FONT_MAP_FONT = 0,
CELL_FONT_MAP_UNICODE = 1,
};
enum
{
CELL_FONT_OPEN_MODE_DEFAULT = 0,
CELL_FONT_OPEN_MODE_IGNORE_VERTICAL_METRICS = 1,
};
enum
{
CELL_FONT_GRAPHICS_DRAW_TYPE_MONO = 0,
CELL_FONT_GRAPHICS_DRAW_TYPE_COLOR = 1,
CELL_FONT_GRAPHICS_DRAW_TYPE_COLOR_REVERSE = 2,
};
//Custom enum to determine the origin of a CellFont object
enum
{
@@ -109,6 +127,8 @@ enum
CELL_FONT_OPEN_MEMORY,
};
constexpr f32 CELL_FONT_GLYPH_OUTLINE_CONTROL_DISTANCE_DEFAULT = 0.125f;
using CellFontMallocCallback = vm::ptr<void>(vm::ptr<void> arg, u32 size);
using CellFontFreeCallback = void(vm::ptr<void> arg, vm::ptr<void> ptr);
@@ -148,7 +168,7 @@ struct CellFontLibrary
{
be_t<u32> libraryType;
be_t<u32> libraryVersion;
// ...
vm::bptr<u32> SystemClosed;
};
struct CellFontType
@@ -171,6 +191,24 @@ struct CellFontVerticalLayout
be_t<f32> effectWidth;
};
struct CellFontVertexesGlyphSubHeader
{
be_t<u32> size;
be_t<f32> SystemReserved[11];
};
struct CellFontVertexesGlyphData
{
const be_t<u32> size;
vm::bptr<f32> SystemClosed;
};
struct CellFontVertexesGlyph
{
vm::bptr<CellFontVertexesGlyphData> data;
vm::bptr<CellFontVertexesGlyphSubHeader> subHeader;
};
struct CellFontGlyphMetrics
{
be_t<f32> width;
@@ -185,6 +223,64 @@ struct CellFontGlyphMetrics
be_t<f32> v_advance;
};
struct CellFontGlyphOutline
{
be_t<s16> contoursCount;
be_t<s16> pointsCount;
struct Point
{
be_t<f32> x;
be_t<f32> y;
};
vm::bptr<Point> points;
vm::bptr<u8> pointTags;
vm::bptr<u16> contourIndexs;
be_t<u32> flags;
vm::bptr<void> generateEnv;
};
using CellFontGetOutlineVertexCallback = vm::ptr<void>(vm::ptr<void> arg, s32 contourN, s32 vertexNumber, s32 vertexAttr, f32 x, f32 y);
struct CellFontGetOutlineVertexesIF
{
vm::bptr<CellFontGetOutlineVertexCallback> callback;
vm::bptr<void> arg;
};
struct CellFontGlyphBoundingBox
{
be_t<f32> min_x;
be_t<f32> min_y;
be_t<f32> max_x;
be_t<f32> max_y;
};
struct CellFontKerning
{
be_t<f32> offsetX;
be_t<f32> offsetY;
};
struct CellFontGlyphStyle
{
be_t<f32> scale_widthPixel;
be_t<f32> scale_heightPixel;
be_t<f32> effect_weight;
be_t<f32> effect_slant;
};
struct CellFontGlyph
{
be_t<u16> CF_type;
be_t<u16> type;
be_t<u32> size;
CellFontGlyphMetrics Metrics;
CellFontGlyphOutline Outline;
};
struct CellFontRenderSurface
{
vm::bptr<void> buffer;
@@ -235,11 +331,16 @@ struct CellFontRendererConfig
struct CellFontRenderer
{
void *systemReserved[64];
vm::bptr<void> systemReserved[64];
};
struct CellFontGraphics
{
be_t<u32> graphicsType;
// ...
vm::bptr<uint32_t> SystemClosed;
};
struct CellFontGraphicsDrawContext
{
vm::bptr<void> SystemReserved[64];
};
+49 -12
View File
@@ -5,30 +5,67 @@
LOG_CHANNEL(cellFontFT);
error_code cellFontInitLibraryFreeType()
{
UNIMPLEMENTED_FUNC(cellFontFT);
return CELL_OK;
}
error_code cellFontInitLibraryFreeTypeWithRevision(u64 revisionFlags, vm::ptr<CellFontLibraryConfigFT> config, vm::pptr<CellFontLibrary> lib)
{
cellFontFT.warning("cellFontInitLibraryFreeTypeWithRevision(revisionFlags=0x%llx, config=*0x%x, lib=**0x%x)", revisionFlags, config, lib);
cellFontFT.todo("cellFontInitLibraryFreeTypeWithRevision(revisionFlags=0x%llx, config=*0x%x, lib=**0x%x)", revisionFlags, config, lib);
if (!lib)
{
return CELL_FONT_ERROR_INVALID_PARAMETER;
}
*lib = {};
if (!config)
{
return CELL_FONT_ERROR_INVALID_PARAMETER;
}
if (false) // TODO
{
return CELL_FONT_ERROR_UNINITIALIZED;
}
lib->set(vm::alloc(sizeof(CellFontLibrary), vm::main));
return CELL_OK;
}
error_code cellFontFTGetRevisionFlags()
error_code cellFontInitLibraryFreeType(vm::ptr<CellFontLibraryConfigFT> config, vm::pptr<CellFontLibrary> lib)
{
UNIMPLEMENTED_FUNC(cellFontFT);
return CELL_OK;
cellFontFT.todo("cellFontInitLibraryFreeType(config=*0x%x, lib=**0x%x)", config, lib);
uint64_t revisionFlags = 0LL;
//cellFontFTGetStubRevisionFlags(&revisionFlags);
return cellFontInitLibraryFreeTypeWithRevision(revisionFlags, config, lib);
}
error_code cellFontFTGetInitializedRevisionFlags()
void cellFontFTGetRevisionFlags(vm::ptr<u64> revisionFlags)
{
UNIMPLEMENTED_FUNC(cellFontFT);
cellFontFT.notice("cellFontFTGetRevisionFlags(revisionFlags=*0x%x)", revisionFlags);
if (revisionFlags)
{
*revisionFlags = 0x43;
}
}
error_code cellFontFTGetInitializedRevisionFlags(vm::ptr<u64> revisionFlags)
{
cellFontFT.notice("cellFontFTGetInitializedRevisionFlags(revisionFlags=*0x%x)", revisionFlags);
if (!revisionFlags)
{
return CELL_FONT_ERROR_INVALID_PARAMETER;
}
if (false) // TODO
{
return CELL_FONT_ERROR_UNINITIALIZED;
}
//*revisionFlags = something; // TODO
return CELL_OK;
}
+105 -24
View File
@@ -114,6 +114,20 @@ void fmt_class_string<CellGemVideoConvertFormatEnum>::format(std::string& out, u
});
}
// last 4 out of 7 ports (7,6,5,4). index starts at 1
static u32 port_num(u32 gem_num)
{
ensure(gem_num < CELL_GEM_MAX_NUM);
return CELL_PAD_MAX_PORT_NUM - gem_num;
}
// last 4 out of 7 ports (6,5,4,3). index starts at 0
static u32 pad_num(u32 gem_num)
{
ensure(gem_num < CELL_GEM_MAX_NUM);
return (CELL_PAD_MAX_PORT_NUM - 1) - gem_num;
}
// **********************
// * HLE helper structs *
// **********************
@@ -278,12 +292,44 @@ public:
return;
}
bool is_connected = false;
switch (g_cfg.io.move)
{
case move_handler::fake:
case move_handler::mouse:
{
connected_controllers = 1;
connected_controllers = 0;
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
for (u32 i = 0; i < std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM); i++)
{
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
if (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
connected_controllers++;
if (gem_num == i)
{
is_connected = true;
}
}
}
break;
}
case move_handler::mouse:
case move_handler::raw_mouse:
{
auto& handler = g_fxo->get<MouseHandlerBase>();
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM));
const MouseInfo& info = handler.GetInfo();
connected_controllers = std::min<u32>({ info.now_connect, attribute.max_connect, CELL_GEM_MAX_NUM });
if (gem_num < connected_controllers)
{
is_connected = true;
}
break;
}
#ifdef HAVE_LIBEVDEV
@@ -291,8 +337,13 @@ public:
{
gun_thread& gun = g_fxo->get<gun_thread>();
std::scoped_lock lock(gun.handler.mutex);
connected_controllers = gun.handler.init() ? gun.handler.get_num_guns() : 0;
gun.num_devices = connected_controllers;
gun.num_devices = gun.handler.init() ? gun.handler.get_num_guns() : 0;
connected_controllers = std::min<u32>(std::min<u32>(attribute.max_connect, CELL_GEM_MAX_NUM), gun.num_devices);
if (gem_num < connected_controllers)
{
is_connected = true;
}
break;
}
#endif
@@ -306,10 +357,10 @@ public:
controller.sphere_rgb = gem_color::get_default_color(gem_num);
// Assign status and port number
if (gem_num < connected_controllers)
if (is_connected)
{
controller.status = CELL_GEM_STATUS_READY;
controller.port = CELL_PAD_MAX_PORT_NUM - gem_num;
controller.port = port_num(gem_num);
}
}
@@ -541,8 +592,8 @@ static bool check_gem_num(const u32 gem_num)
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller&, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
{
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
const s16 x = static_cast<s16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
const s16 y = static_cast<s16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
// Note: We shouldn't use sphere_rgb here. The game will set it to black in many cases.
const gem_config_data::gem_color& rgb = gem_config_data::gem_color::get_default_color(gem_num);
@@ -638,12 +689,12 @@ extern bool is_input_allowed();
/**
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
* \param port_no DS3 port number to use
* \param gem_num gem index to use
* \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
* \return true on success, false if port_no controller is invalid
* \return true on success, false if controller is disconnected
*/
static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
static void ds3_input_to_pad(const u32 gem_num, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
{
digital_buttons = 0;
analog_t = 0;
@@ -656,14 +707,14 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
return;
}
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{
if (!pressed)
@@ -707,12 +758,12 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
constexpr u16 ds3_max_x = 255;
constexpr u16 ds3_max_y = 255;
static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
{
x_pos = 0;
y_pos = 0;
const auto& cfg = ::at32(g_cfg_gem.players, port_no);
const auto& cfg = ::at32(g_cfg_gem.players, gem_num);
cfg->handle_input(pad, true, [&](gem_btn btn, u16 value, bool pressed)
{
if (!pressed)
@@ -733,7 +784,7 @@ static inline void ds3_get_stick_values(u32 port_no, const std::shared_ptr<Pad>&
}
template <typename T>
static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_controller& controller, T& gem_state)
static void ds3_pos_to_gem_state(u32 gem_num, const gem_config::gem_controller& controller, T& gem_state)
{
if (!gem_state || !is_input_allowed())
{
@@ -743,7 +794,7 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
@@ -751,15 +802,15 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
}
s32 ds3_pos_x, ds3_pos_y;
ds3_get_stick_values(port_no, pad, ds3_pos_x, ds3_pos_y);
ds3_get_stick_values(gem_num, pad, ds3_pos_x, ds3_pos_y);
if constexpr (std::is_same<T, vm::ptr<CellGemState>>::value)
{
pos_to_gem_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
else if constexpr (std::is_same<T, vm::ptr<CellGemImageState>>::value)
{
pos_to_gem_image_state(port_no, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
}
}
@@ -767,11 +818,11 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
* \brief Maps external Move controller data to DS3 input. (This can be input from any physical pad, not just the DS3)
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
* libPad, so no translation is needed.
* \param port_no DS3 port number to use
* \param gem_num gem index to use
* \param ext External data to modify
* \return true on success, false if port_no controller is invalid
* \return true on success, false if controller is disconnected
*/
static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
static void ds3_input_to_ext(const u32 gem_num, const gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
ext = {};
@@ -783,7 +834,7 @@ static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
const auto& pad = ::at32(handler->GetPads(), port_no);
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
@@ -1372,6 +1423,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_image_state);
break;
#ifdef HAVE_LIBEVDEV
@@ -1426,6 +1478,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
ds3_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_input_to_pad(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
break;
#ifdef HAVE_LIBEVDEV
@@ -1460,6 +1513,33 @@ error_code cellGemGetInfo(vm::ptr<CellGemInfo> info)
}
// TODO: Support connecting PlayStation Move controllers
if (g_cfg.io.move == move_handler::fake)
{
gem.connected_controllers = 0;
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
for (u32 i = 0; i < CELL_GEM_MAX_NUM; i++)
{
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
const bool connected = (pad && (pad->m_port_status & CELL_PAD_STATUS_CONNECTED) && i < gem.attribute.max_connect);
if (connected)
{
gem.connected_controllers++;
gem.controllers[i].status = CELL_GEM_STATUS_READY;
gem.controllers[i].port = port_num(i);
}
else
{
gem.controllers[i].status = CELL_GEM_STATUS_DISCONNECTED;
gem.controllers[i].port = 0;
}
}
}
info->max_connect = gem.attribute.max_connect;
info->now_connect = gem.connected_controllers;
@@ -1603,6 +1683,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
ds3_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_input_to_pad(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T);
mouse_pos_to_gem_state(gem_num, gem.controllers[gem_num], gem_state);
break;
+258 -135
View File
@@ -40,7 +40,7 @@ ime_jp_manager::ime_jp_manager()
bool ime_jp_manager::addChar(u16 c)
{
if (!c || cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1) || cursor > input_string.length())
if (!c || cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1ULL) || cursor > input_string.length())
return false;
std::u16string tmp;
@@ -49,11 +49,29 @@ bool ime_jp_manager::addChar(u16 c)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
input_string.insert(cursor++, tmp);
input_string.insert(cursor, tmp);
#if defined(__GNUG__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
cursor_end = cursor;
const usz cursor_old = cursor;
const bool cursor_was_in_focus = cursor >= focus_begin && cursor <= (focus_begin + focus_length);
move_cursor(1);
if (cursor_was_in_focus)
{
// Add this char to the focus
move_focus_end(1, false);
}
else
{
// Let's just move the focus to the cursor, so that it contains the new char.
focus_begin = cursor_old;
focus_length = 1;
move_focus(0); // Sanitize focus
}
input_state = CELL_IMEJP_BEFORE_CONVERT;
return true;
}
@@ -74,59 +92,138 @@ bool ime_jp_manager::addString(vm::cptr<u16> str)
bool ime_jp_manager::backspaceWord()
{
if (!cursor || cursor > CELL_IMEJP_STRING_MAXLENGTH || cursor > input_string.length())
return false;
input_string.erase(--cursor);
cursor_end = cursor;
if (input_string.empty())
input_state = CELL_IMEJP_BEFORE_INPUT;
return true;
return remove_character(false);
}
bool ime_jp_manager::deleteWord()
{
if (cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1) || cursor > (input_string.length() - 1))
return false;
return remove_character(true);
}
input_string.erase(cursor);
cursor_end = cursor;
bool ime_jp_manager::remove_character(bool forward)
{
if (!forward && !cursor)
{
return false;
}
const usz pos = forward ? cursor : (cursor - 1);
if (pos >= (CELL_IMEJP_STRING_MAXLENGTH - 1ULL) || pos >= input_string.length())
{
return false;
}
// Delete the character at the position
input_string.erase(pos, 1);
// Move cursor and focus
const bool deleted_part_of_focus = pos > focus_begin && pos <= (focus_begin + focus_length);
if (!forward)
{
move_cursor(-1);
}
if (deleted_part_of_focus)
{
move_focus_end(-1, false);
}
else if (focus_begin > pos)
{
move_focus(-1);
}
if (input_string.empty())
{
input_state = CELL_IMEJP_BEFORE_INPUT;
}
return true;
}
void ime_jp_manager::moveCursor(s8 amount)
void ime_jp_manager::clear_input()
{
cursor = 0;
focus_begin = 0;
focus_length = 0;
input_string.clear();
converted_string.clear();
}
void ime_jp_manager::move_cursor(s8 amount)
{
cursor = std::max(0, std::min(static_cast<s32>(cursor) + amount, ::narrow<s32>(input_string.length())));
}
void ime_jp_manager::move_focus(s8 amount)
{
focus_begin = std::max(0, std::min(static_cast<s32>(focus_begin) + amount, ::narrow<s32>(input_string.length())));
move_focus_end(amount, false);
}
void ime_jp_manager::move_focus_end(s8 amount, bool wrap_around)
{
if (focus_begin >= input_string.length())
{
focus_length = 0;
return;
}
constexpr usz min_length = 1;
const usz max_length = input_string.length() - focus_begin;
if (amount > 0)
{
cursor = std::min(cursor + amount, input_string.length() - 1);
cursor_end = std::min(cursor_end + amount, input_string.length() - 1);
if (wrap_around && focus_length >= max_length)
{
focus_length = min_length;
}
else
{
focus_length += static_cast<usz>(amount);
}
}
else if (amount < 0)
{
cursor = std::max(0, static_cast<s32>(cursor) + amount);
cursor_end = std::max(static_cast<s32>(cursor), static_cast<s32>(cursor_end) + amount);
if (wrap_around && focus_length <= min_length)
{
focus_length = max_length;
}
else
{
focus_length = std::max(0, static_cast<s32>(focus_length) + amount);
}
}
focus_length = std::max(min_length, std::min(max_length, focus_length));
}
void ime_jp_manager::moveCursorEnd(s8 amount)
std::vector<ime_jp_manager::candidate> ime_jp_manager::get_candidate_list() const
{
if (amount > 0)
{
cursor_end = std::max(static_cast<s32>(cursor), std::min<s32>(static_cast<s32>(cursor_end) + amount, ::narrow<s32>(input_string.length()) - 1));
}
else if (amount < 0)
{
cursor_end = std::max(static_cast<s32>(cursor), static_cast<s32>(cursor_end) + amount);
}
std::vector<candidate> candidates;
if (input_string.empty() || focus_length == 0 || focus_begin >= input_string.length())
return candidates;
// TODO: we just fake this with one candidate for now
candidates.push_back(candidate{
.text = get_focus_string(),
.offset = 0
});
return candidates;
}
error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
std::u16string ime_jp_manager::get_focus_string() const
{
if (input_string.empty() || focus_length == 0 || focus_begin >= input_string.length())
return {};
return input_string.substr(focus_begin, focus_length);
}
static error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
{
cellImeJp.todo("cellImeJpOpen(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
@@ -156,7 +253,7 @@ error_code cellImeJpOpen(sys_memory_container_t container_id, vm::ptr<CellImeJpH
return CELL_OK;
}
error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
static error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cptr<CellImeJpAddDic> addDicPath)
{
cellImeJp.todo("cellImeJpOpen2(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
@@ -187,7 +284,7 @@ error_code cellImeJpOpen2(sys_memory_container_t container_id, vm::ptr<CellImeJp
return CELL_OK;
}
error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cpptr<CellImeJpAddDic> addDicPath)
static error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJpHandle> hImeJpHandle, vm::cpptr<CellImeJpAddDic> addDicPath)
{
cellImeJp.todo("cellImeJpOpen3(container_id=*0x%x, hImeJpHandle=*0x%x, addDicPath=*0x%x)", container_id, hImeJpHandle, addDicPath);
@@ -224,13 +321,13 @@ error_code cellImeJpOpen3(sys_memory_container_t container_id, vm::ptr<CellImeJp
return CELL_OK;
}
error_code cellImeJpOpenExt()
static error_code cellImeJpOpenExt()
{
cellImeJp.todo("cellImeJpOpenExt()");
return CELL_OK;
}
error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpClose(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -243,17 +340,14 @@ error_code cellImeJpClose(CellImeJpHandle hImeJpHandle)
}
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
manager.input_string.clear();
manager.converted_string.clear();
manager.clear_input();
manager.confirmed_string.clear();
manager.cursor = 0;
manager.cursor_end = 0;
manager.is_initialized = false;
return CELL_OK;
}
error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOption)
static error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOption)
{
cellImeJp.todo("cellImeJpSetKanaInputMode(hImeJpHandle=*0x%x, inputOption=%d)", hImeJpHandle, inputOption);
@@ -275,7 +369,7 @@ error_code cellImeJpSetKanaInputMode(CellImeJpHandle hImeJpHandle, s16 inputOpti
return CELL_OK;
}
error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeOption)
static error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeOption)
{
cellImeJp.todo("cellImeJpSetInputCharType(hImeJpHandle=*0x%x, charTypeOption=%d)", hImeJpHandle, charTypeOption);
@@ -292,7 +386,7 @@ error_code cellImeJpSetInputCharType(CellImeJpHandle hImeJpHandle, s16 charTypeO
return CELL_OK;
}
error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMode)
static error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMode)
{
cellImeJp.todo("cellImeJpSetFixInputMode(hImeJpHandle=*0x%x, fixInputMode=%d)", hImeJpHandle, fixInputMode);
@@ -309,7 +403,7 @@ error_code cellImeJpSetFixInputMode(CellImeJpHandle hImeJpHandle, s16 fixInputMo
return CELL_OK;
}
error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 extensionCharacters)
static error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 extensionCharacters)
{
cellImeJp.todo("cellImeJpSetFixInputMode(hImeJpHandle=*0x%x, extensionCharacters=%d)", hImeJpHandle, extensionCharacters);
@@ -331,7 +425,7 @@ error_code cellImeJpAllowExtensionCharacters(CellImeJpHandle hImeJpHandle, s16 e
return CELL_OK;
}
error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpReset(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -344,16 +438,13 @@ error_code cellImeJpReset(CellImeJpHandle hImeJpHandle)
}
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
manager.input_string.clear();
manager.converted_string.clear();
manager.clear_input();
manager.confirmed_string.clear();
manager.cursor = 0;
manager.cursor_end = 0;
return CELL_OK;
}
error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputStatus)
static error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputStatus)
{
cellImeJp.warning("cellImeJpGetStatus(hImeJpHandle=*0x%x, pInputStatus=%d)", hImeJpHandle, pInputStatus);
@@ -375,7 +466,7 @@ error_code cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pInputS
return CELL_OK;
}
error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
static error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
{
cellImeJp.todo("cellImeJpEnterChar(hImeJpHandle=*0x%x, inputChar=%d, pOutputStatus=%d)", hImeJpHandle, inputChar, pOutputStatus);
@@ -404,13 +495,13 @@ error_code cellImeJpEnterChar(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::p
return CELL_OK;
}
error_code cellImeJpEnterCharExt(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
static error_code cellImeJpEnterCharExt(CellImeJpHandle hImeJpHandle, u16 inputChar, vm::ptr<s16> pOutputStatus)
{
cellImeJp.todo("cellImeJpEnterCharExt(hImeJpHandle=*0x%x, inputChar=%d, pOutputStatus=%d", hImeJpHandle, inputChar, pOutputStatus);
return cellImeJpEnterChar(hImeJpHandle, inputChar, pOutputStatus);
}
error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
static error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
{
cellImeJp.todo("cellImeJpEnterString(hImeJpHandle=*0x%x, pInputString=*0x%x, pOutputStatus=%d", hImeJpHandle, pInputString, pOutputStatus);
@@ -439,13 +530,13 @@ error_code cellImeJpEnterString(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInp
return CELL_OK;
}
error_code cellImeJpEnterStringExt(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
static error_code cellImeJpEnterStringExt(CellImeJpHandle hImeJpHandle, vm::cptr<u16> pInputString, vm::ptr<s16> pOutputStatus)
{
cellImeJp.todo("cellImeJpEnterStringExt(hImeJpHandle=*0x%x, pInputString=*0x%x, pOutputStatus=%d", hImeJpHandle, pInputString, pOutputStatus);
return cellImeJpEnterString(hImeJpHandle, pInputString, pOutputStatus);
}
error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpModeCaretRight(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -462,12 +553,12 @@ error_code cellImeJpModeCaretRight(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
manager.moveCursor(1);
manager.move_cursor(1);
return CELL_OK;
}
error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpModeCaretLeft(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -484,12 +575,12 @@ error_code cellImeJpModeCaretLeft(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
manager.moveCursor(-1);
manager.move_cursor(-1);
return CELL_OK;
}
error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpBackspaceWord(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -511,7 +602,7 @@ error_code cellImeJpBackspaceWord(CellImeJpHandle hImeJpHandle)
return CELL_OK;
}
error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpDeleteWord(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -533,7 +624,7 @@ error_code cellImeJpDeleteWord(CellImeJpHandle hImeJpHandle)
return CELL_OK;
}
error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpAllDeleteConvertString(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -550,16 +641,13 @@ error_code cellImeJpAllDeleteConvertString(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
manager.cursor = 0;
manager.cursor_end = 0;
manager.input_string.clear();
manager.converted_string.clear();
manager.clear_input();
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
return CELL_OK;
}
error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpConvertForward(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -581,7 +669,7 @@ error_code cellImeJpConvertForward(CellImeJpHandle hImeJpHandle)
return CELL_OK;
}
error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpConvertBackward(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -603,7 +691,7 @@ error_code cellImeJpConvertBackward(CellImeJpHandle hImeJpHandle)
return CELL_OK;
}
error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listItem)
static error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listItem)
{
cellImeJp.todo("cellImeJpCurrentPartConfirm(hImeJpHandle=*0x%x, listItem=%d)", hImeJpHandle, listItem);
@@ -623,7 +711,7 @@ error_code cellImeJpCurrentPartConfirm(CellImeJpHandle hImeJpHandle, s16 listIte
return CELL_OK;
}
error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpAllConfirm(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -642,16 +730,13 @@ error_code cellImeJpAllConfirm(CellImeJpHandle hImeJpHandle)
// Use input_string for now
manager.confirmed_string = manager.input_string;
manager.cursor = 0;
manager.cursor_end = 0;
manager.input_string.clear();
manager.converted_string.clear();
manager.clear_input();
manager.input_state = CELL_IMEJP_BEFORE_INPUT;
return CELL_OK;
}
error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpAllConvertCancel(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -674,7 +759,7 @@ error_code cellImeJpAllConvertCancel(CellImeJpHandle hImeJpHandle)
return CELL_OK;
}
error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpConvertCancel(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -691,11 +776,13 @@ error_code cellImeJpConvertCancel(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
// TODO: only cancel all if cursor is at 0
return cellImeJpAllConvertCancel(hImeJpHandle);
manager.converted_string.clear();
manager.input_state = CELL_IMEJP_BEFORE_CONVERT;
return CELL_OK;
}
error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpExtendConvertArea(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -712,12 +799,13 @@ error_code cellImeJpExtendConvertArea(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
manager.moveCursorEnd(1);
// Move end of the focus by one. Wrap around if the focus end is already at the end of the input string.
manager.move_focus_end(1, true);
return CELL_OK;
}
error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
static error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
{
cellImeJp.todo("cellImeJpShortenConvertArea(hImeJpHandle=*0x%x)", hImeJpHandle);
@@ -734,12 +822,13 @@ error_code cellImeJpShortenConvertArea(CellImeJpHandle hImeJpHandle)
return CELL_IMEJP_ERROR_ERR;
}
manager.moveCursorEnd(-1);
// Move end of focus by one. Wrap around if the focus end is already at the beginning of the input string.
manager.move_focus_end(-1, true);
return CELL_OK;
}
error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectIndex)
static error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectIndex)
{
cellImeJp.todo("cellImeJpTemporalConfirm(hImeJpHandle=*0x%x, selectIndex=%d)", hImeJpHandle, selectIndex);
@@ -759,7 +848,7 @@ error_code cellImeJpTemporalConfirm(CellImeJpHandle hImeJpHandle, s16 selectInde
return CELL_OK;
}
error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
static error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
{
cellImeJp.todo("cellImeJpPostConvert(hImeJpHandle=*0x%x, postType=%d)", hImeJpHandle, postType);
@@ -779,7 +868,7 @@ error_code cellImeJpPostConvert(CellImeJpHandle hImeJpHandle, s16 postType)
return CELL_OK;
}
error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
static error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
{
cellImeJp.todo("cellImeJpMoveFocusClause(hImeJpHandle=*0x%x, moveType=%d)", hImeJpHandle, moveType);
@@ -799,17 +888,17 @@ error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
switch (moveType)
{
case CELL_IMEJP_FOCUS_NEXT:
manager.moveCursor(1);
manager.move_focus(1);
break;
case CELL_IMEJP_FOCUS_BEFORE:
manager.moveCursor(-1);
manager.move_focus(-1);
break;
case CELL_IMEJP_FOCUS_TOP:
manager.moveCursor(-1 * ::narrow<s8>(manager.input_string.length()));
manager.move_focus(-1 * ::narrow<s8>(manager.input_string.length()));
break;
case CELL_IMEJP_FOCUS_END:
manager.moveCursor(::narrow<s8>(manager.input_string.length()));
manager.moveCursor(-1);
manager.move_focus(::narrow<s8>(manager.input_string.length()));
manager.move_focus(-1);
break;
default:
break;
@@ -820,7 +909,7 @@ error_code cellImeJpMoveFocusClause(CellImeJpHandle hImeJpHandle, s16 moveType)
return CELL_OK;
}
error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusTop)
static error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusTop)
{
cellImeJp.todo("cellImeJpGetFocusTop(hImeJpHandle=*0x%x, pFocusTop=*0x%x)", hImeJpHandle, pFocusTop);
@@ -837,12 +926,12 @@ error_code cellImeJpGetFocusTop(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocu
return CELL_IMEJP_ERROR_CONTEXT;
}
*pFocusTop = static_cast<u16>(manager.cursor * 2); // offset in bytes
*pFocusTop = static_cast<u16>(manager.focus_begin * 2); // offset in bytes
return CELL_OK;
}
error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusLength)
static error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pFocusLength)
{
cellImeJp.todo("cellImeJpGetFocusLength(hImeJpHandle=*0x%x, pFocusLength=*0x%x)", hImeJpHandle, pFocusLength);
@@ -859,19 +948,12 @@ error_code cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pF
return CELL_IMEJP_ERROR_CONTEXT;
}
if (manager.cursor >= (CELL_IMEJP_STRING_MAXLENGTH - 1))
{
*pFocusLength = 0;
}
else
{
*pFocusLength = static_cast<u16>((manager.cursor_end - manager.cursor + 1) * 2); // offset in bytes
}
*pFocusLength = ::narrow<s16>(manager.focus_length * 2); // offset in bytes
return CELL_OK;
}
error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
static error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
{
cellImeJp.todo("cellImeJpGetConfirmYomiString(hImeJpHandle=*0x%x, pYomiString=*0x%x)", hImeJpHandle, pYomiString);
@@ -888,22 +970,22 @@ error_code cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u
return CELL_IMEJP_ERROR_CONTEXT;
}
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pYomiString[i] = 0;
}
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.confirmed_string.length());
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.confirmed_string.length());
for (u32 i = 0; i < max_len; i++)
{
pYomiString[i] = manager.confirmed_string[i];
}
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pYomiString[i] = 0;
}
return CELL_OK;
}
error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConfirmString)
static error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConfirmString)
{
cellImeJp.todo("cellImeJpGetConfirmString(hImeJpHandle=*0x%x, pConfirmString=*0x%x)", hImeJpHandle, pConfirmString);
@@ -920,22 +1002,22 @@ error_code cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16>
return CELL_IMEJP_ERROR_CONTEXT;
}
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pConfirmString[i] = 0;
}
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.confirmed_string.length());
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.confirmed_string.length());
for (u32 i = 0; i < max_len; i++)
{
pConfirmString[i] = manager.confirmed_string[i];
}
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pConfirmString[i] = 0;
}
return CELL_OK;
}
error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
static error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
{
cellImeJp.todo("cellImeJpGetConvertYomiString(hImeJpHandle=*0x%x, pYomiString=*0x%x)", hImeJpHandle, pYomiString);
@@ -952,22 +1034,22 @@ error_code cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u
return CELL_IMEJP_ERROR_CONTEXT;
}
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pYomiString[i] = 0;
}
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.input_string.length());
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.input_string.length());
for (u32 i = 0; i < max_len; i++)
{
pYomiString[i] = manager.input_string[i];
}
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pYomiString[i] = 0;
}
return CELL_OK;
}
error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConvertString)
static error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConvertString)
{
cellImeJp.warning("cellImeJpGetConvertString(hImeJpHandle=*0x%x, pConvertString=*0x%x)", hImeJpHandle, pConvertString);
@@ -984,22 +1066,22 @@ error_code cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16>
return CELL_IMEJP_ERROR_CONTEXT;
}
for (u32 i = 0; i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pConvertString[i] = 0;
}
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1, manager.input_string.length());
const usz max_len = std::min<usz>(CELL_IMEJP_STRING_MAXLENGTH - 1ULL, manager.input_string.length());
for (u32 i = 0; i < max_len; i++)
{
pConvertString[i] = manager.input_string[i];
}
for (u32 i = static_cast<u32>(max_len); i < CELL_IMEJP_STRING_MAXLENGTH; i++)
{
pConvertString[i] = 0;
}
return CELL_OK;
}
error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pListSize)
static error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pListSize)
{
cellImeJp.todo("cellImeJpGetCandidateListSize(hImeJpHandle=*0x%x, pListSize=*0x%x)", hImeJpHandle, pListSize);
@@ -1021,12 +1103,23 @@ error_code cellImeJpGetCandidateListSize(CellImeJpHandle hImeJpHandle, vm::ptr<s
return CELL_IMEJP_ERROR_ERR;
}
*pListSize = 0;
// Add focus string size, including null terminator
const std::u16string focus_string = manager.get_focus_string();
usz size = sizeof(u16) * (focus_string.length() + 1);
// Add candidates, including null terminators and offsets
for (const ime_jp_manager::candidate& can : manager.get_candidate_list())
{
constexpr usz offset_size = sizeof(u16);
size += offset_size + (can.text.size() + 1) * sizeof(u16);
}
*pListSize = ::narrow<s16>(size);
return CELL_OK;
}
error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> plistNum, vm::ptr<u16> pCandidateString)
static error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> plistNum, vm::ptr<u16> pCandidateString)
{
cellImeJp.todo("cellImeJpGetCandidateList(hImeJpHandle=*0x%x, plistNum=*0x%x, pCandidateString=*0x%x)", hImeJpHandle, plistNum, pCandidateString);
@@ -1048,12 +1141,42 @@ error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::ptr<s16>
return CELL_IMEJP_ERROR_ERR;
}
*plistNum = 0;
// First, copy the focus string
u32 pos = 0;
const std::u16string focus_string = manager.get_focus_string();
for (u32 i = pos; i < focus_string.length(); i++)
{
pCandidateString[i] = focus_string[i];
}
pos += ::narrow<u32>(focus_string.length());
// Add null terminator
pCandidateString[pos++] = 0;
// Add list of candidates
const std::vector<ime_jp_manager::candidate> list = manager.get_candidate_list();
for (const ime_jp_manager::candidate& can : list)
{
// Copy the candidate
for (u32 i = pos; i < can.text.length(); i++)
{
pCandidateString[i] = can.text[i];
}
pos += ::narrow<u32>(can.text.length());
// Add null terminator
pCandidateString[pos++] = 0;
// Add offset
pCandidateString[pos++] = can.offset;
}
*plistNum = ::narrow<s16>(list.size());
return CELL_OK;
}
error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pIndex)
static error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pIndex)
{
cellImeJp.todo("cellImeJpGetCandidateSelect(hImeJpHandle=*0x%x, pIndex=*0x%x)", hImeJpHandle, pIndex);
@@ -1080,7 +1203,7 @@ error_code cellImeJpGetCandidateSelect(CellImeJpHandle hImeJpHandle, vm::ptr<s16
return CELL_OK;
}
error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pYomiString, s32 itemNum, vm::ptr<s32> plistCount, vm::ptr<CellImeJpPredictItem> pPredictItem)
static error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pYomiString, s32 itemNum, vm::ptr<s32> plistCount, vm::ptr<CellImeJpPredictItem> pPredictItem)
{
cellImeJp.todo("cellImeJpGetPredictList(hImeJpHandle=*0x%x, pYomiString=*0x%x, itemNum=%d, plistCount=*0x%x, pPredictItem=*0x%x)", hImeJpHandle, pYomiString, itemNum, plistCount, pPredictItem);
@@ -1102,7 +1225,7 @@ error_code cellImeJpGetPredictList(CellImeJpHandle hImeJpHandle, vm::ptr<s16> pY
return CELL_OK;
}
error_code cellImeJpConfirmPrediction(CellImeJpHandle hImeJpHandle, vm::ptr<CellImeJpPredictItem> pPredictItem)
static error_code cellImeJpConfirmPrediction(CellImeJpHandle hImeJpHandle, vm::ptr<CellImeJpPredictItem> pPredictItem)
{
cellImeJp.todo("cellImeJpConfirmPrediction(hImeJpHandle=*0x%x, pPredictItem=*0x%x)", hImeJpHandle, pPredictItem);
+19 -7
View File
@@ -115,11 +115,12 @@ struct ime_jp_manager
u32 input_state = CELL_IMEJP_BEFORE_INPUT;
std::vector<std::string> dictionary_paths;
std::u16string confirmed_string;
std::u16string converted_string;
std::u16string input_string;
usz cursor = 0;
usz cursor_end = 0;
std::u16string confirmed_string; // Confirmed part of the string (first part of the entire string)
std::u16string converted_string; // Converted part of the unconfirmed input string
std::u16string input_string; // Unconfirmed part of the string (second part of the entire string)
usz cursor = 0; // The cursor. Can move across the entire input string.
usz focus_begin = 0; // Begin of the focus string
usz focus_length = 0; // Length of the focus string
s16 fix_input_mode = CELL_IMEJP_FIXINMODE_OFF;
s16 input_char_type = CELL_IMEJP_DSPCHAR_HIRA;
s16 kana_input_mode = CELL_IMEJP_ROMAN_INPUT;
@@ -131,6 +132,17 @@ struct ime_jp_manager
bool addString(vm::cptr<u16> str);
bool backspaceWord();
bool deleteWord();
void moveCursor(s8 amount);
void moveCursorEnd(s8 amount);
bool remove_character(bool forward);
void clear_input();
void move_cursor(s8 amount); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
void move_focus(s8 amount); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
void move_focus_end(s8 amount, bool wrap_around); // s8 because CELL_IMEJP_STRING_MAXLENGTH is 128
struct candidate
{
std::u16string text; // Actual text of the candidate
u16 offset = 0; // The offset of the next character after the candidate replaced part of the current focus.
};
std::vector<candidate> get_candidate_list() const;
std::u16string get_focus_string() const;
};
+2 -2
View File
@@ -259,7 +259,7 @@ error_code cellMouseGetData(u32 port_no, vm::ptr<CellMouseData> data)
error_code cellMouseGetDataList(u32 port_no, vm::ptr<CellMouseDataList> data)
{
sys_io.notice("cellMouseGetDataList(port_no=%d, data=0x%x)", port_no, data);
sys_io.trace("cellMouseGetDataList(port_no=%d, data=0x%x)", port_no, data);
auto& handler = g_fxo->get<MouseHandlerBase>();
@@ -406,7 +406,7 @@ error_code cellMouseGetTabletDataList(u32 port_no, vm::ptr<CellMouseTabletDataLi
error_code cellMouseGetRawData(u32 port_no, vm::ptr<CellMouseRawData> data)
{
sys_io.notice("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
sys_io.trace("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
auto& handler = g_fxo->get<MouseHandlerBase>();
@@ -227,6 +227,12 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.read_pos], size_to_read);
if (size_to_read < reqSize)
{
// Set the rest of the buffer to zero to prevent loud pops at the end of the stream if the game ignores the readSize.
std::memset(vm::static_ptr_cast<u8>(buf).get_ptr() + size_to_read, 0, reqSize - size_to_read);
}
dec.read_pos += size_to_read;
s64 start_time_ms = 0;
File diff suppressed because it is too large Load Diff
+288 -92
View File
@@ -24,14 +24,30 @@ enum
enum CellPamfStreamType
{
CELL_PAMF_STREAM_TYPE_AVC = 0,
CELL_PAMF_STREAM_TYPE_M2V = 1,
CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2,
CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3,
CELL_PAMF_STREAM_TYPE_AC3 = 4,
CELL_PAMF_STREAM_TYPE_USER_DATA = 5,
CELL_PAMF_STREAM_TYPE_VIDEO = 20,
CELL_PAMF_STREAM_TYPE_AUDIO = 21,
CELL_PAMF_STREAM_TYPE_AVC = 0,
CELL_PAMF_STREAM_TYPE_M2V = 1,
CELL_PAMF_STREAM_TYPE_ATRAC3PLUS = 2,
CELL_PAMF_STREAM_TYPE_PAMF_LPCM = 3,
CELL_PAMF_STREAM_TYPE_AC3 = 4,
CELL_PAMF_STREAM_TYPE_USER_DATA = 5,
CELL_PAMF_STREAM_TYPE_PSMF_AVC = 6,
CELL_PAMF_STREAM_TYPE_PSMF_ATRAC3PLUS = 7,
CELL_PAMF_STREAM_TYPE_PSMF_LPCM = 8,
CELL_PAMF_STREAM_TYPE_PSMF_USER_DATA = 9,
CELL_PAMF_STREAM_TYPE_VIDEO = 20,
CELL_PAMF_STREAM_TYPE_AUDIO = 21,
CELL_PAMF_STREAM_TYPE_UNK = 22,
};
enum PamfStreamCodingType : u8
{
PAMF_STREAM_CODING_TYPE_M2V = 0x02,
PAMF_STREAM_CODING_TYPE_AVC = 0x1b,
PAMF_STREAM_CODING_TYPE_PAMF_LPCM = 0x80,
PAMF_STREAM_CODING_TYPE_AC3 = 0x81,
PAMF_STREAM_CODING_TYPE_ATRAC3PLUS = 0xdc,
PAMF_STREAM_CODING_TYPE_USER_DATA = 0xdd,
PAMF_STREAM_CODING_TYPE_PSMF = 0xff,
};
enum
@@ -144,7 +160,7 @@ struct CellCodecTimeStamp
be_t<u32> lower;
};
static const u64 CODEC_TS_INVALID = 0xffffffffffffffffull;
constexpr u32 CODEC_TS_INVALID = umax;
// Entry point information
struct CellPamfEp
@@ -155,13 +171,21 @@ struct CellPamfEp
be_t<u64> rpnOffset;
};
struct CellPamfEpUnk // Speculative name, only used in two undocumented functions
{
CellPamfEp ep;
be_t<u64> nextRpnOffset;
};
CHECK_SIZE(CellPamfEpUnk, 0x20);
// Entry point iterator
struct CellPamfEpIterator
{
b8 isPamf;
be_t<u32> index;
be_t<u32> num;
be_t<u32> pCur_addr;
vm::bcptr<void> pCur;
};
struct CellCodecEsFilterId
@@ -252,18 +276,23 @@ struct CellPamfLpcmInfo
be_t<u16> bitsPerSample;
};
CHECK_SIZE(CellPamfLpcmInfo, 8);
// PAMF file structs, everything here is not aligned (LLE uses exclusively u8 pointers)
struct PamfStreamHeader
{
u8 type;
u8 unknown[3];
u8 fid_major;
u8 fid_minor;
u8 unknown1;
u8 unknown2;
be_t<u32> ep_offset; // offset of EP section in header
be_t<u32> ep_num; // count of EPs
u8 stream_coding_type;
u8 reserved[3];
u8 stream_id;
u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd)
be_t<u16, 1> p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size
be_t<u32, 1> ep_offset; // offset of EP section in header
be_t<u32, 1> ep_num; // count of EPs
union
{
@@ -276,32 +305,20 @@ struct PamfStreamHeader
u8 levelIdc;
u8 x2; // contains frameMbsOnlyFlag, videoSignalInfoFlag, frameRateInfo
u8 aspectRatioIdc;
u32 x4; // 0 (not used)
be_t<u16> horizontalSize; // divided by 16
be_t<u16> verticalSize; // divided by 16
be_t<u16> frameCropLeftOffset;
be_t<u16> frameCropRightOffset;
be_t<u16> frameCropTopOffset;
be_t<u16> frameCropBottomOffset;
union
{
struct
{
be_t<u16> width;
be_t<u16> height;
}
sarInfo;
struct
{
u8 x14; // contains videoFormat and videoFullRangeFlag
u8 colourPrimaries;
u8 transferCharacteristics;
u8 matrixCoefficients;
};
};
be_t<u16, 1> sarWidth;
be_t<u16, 1> sarHeight;
u8 reserved1;
u8 horizontalSize; // divided by 16
u8 reserved2;
u8 verticalSize; // divided by 16
be_t<u16, 1> frameCropLeftOffset;
be_t<u16, 1> frameCropRightOffset;
be_t<u16, 1> frameCropTopOffset;
be_t<u16, 1> frameCropBottomOffset;
u8 x14; // contains videoFormat and videoFullRangeFlag
u8 colourPrimaries;
u8 transferCharacteristics;
u8 matrixCoefficients;
u8 x18; // contains entropyCodingModeFlag, deblockingFilterFlag, minNumSlicePerPictureIdc, nfwIdc
u8 maxMeanBitrate;
}
@@ -314,13 +331,15 @@ struct PamfStreamHeader
u8 x1; // not used
u8 x2; // contains progressiveSequence, videoSignalInfoFlag, frameRateInfo
u8 aspectRatioIdc;
be_t<u16> sarWidth;
be_t<u16> sarHeight;
be_t<u16> horizontalSize;
be_t<u16> verticalSize;
be_t<u16> horizontalSizeValue;
be_t<u16> verticalSizeValue;
u32 x10; // not used
be_t<u16, 1> sarWidth;
be_t<u16, 1> sarHeight;
u8 reserved1;
u8 horizontalSize; // in units of 16 pixels
u8 reserved2;
u8 verticalSize; // in units of 16 pixels
be_t<u16, 1> horizontalSizeValue;
be_t<u16, 1> verticalSizeValue;
be_t<u32, 1> x10; // not used
u8 x14; // contains videoFormat and videoFullRangeFlag
u8 colourPrimaries;
u8 transferCharacteristics;
@@ -331,7 +350,7 @@ struct PamfStreamHeader
// Audio specific information
struct
{
u16 unknown; // 0
be_t<u16, 1> unknown; // 0
u8 channels; // number of channels (1, 2, 6, 8)
u8 freq; // 1 (always 48000)
u8 bps; // LPCM only
@@ -340,59 +359,236 @@ struct PamfStreamHeader
};
};
CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 4);
CHECK_SIZE_ALIGN(PamfStreamHeader, 48, 1);
struct PamfGroup
{
be_t<u32, 1> size; // doesn't include this field
u8 reserved;
u8 stream_num; // same value as in PamfSequenceInfo
PamfStreamHeader streams;
};
CHECK_SIZE_ALIGN(PamfGroup, 6 + sizeof(PamfStreamHeader), 1);
struct PamfGroupingPeriod
{
be_t<u32, 1> size; // doesn't include this field
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> start_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod
be_t<u16, 1> end_pts_high; // unused due to bug
be_t<u32, 1> end_pts_low; // same value as in PamfSequenceInfo, since there is only one PamfGroupingPeriod
u8 reserved;
u8 group_num; // always 1
PamfGroup groups;
};
CHECK_SIZE_ALIGN(PamfGroupingPeriod, 0x12 + sizeof(PamfGroup), 1);
struct PamfSequenceInfo
{
be_t<u32, 1> size; // doesn't include this field
be_t<u16, 1> reserved1;
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> start_pts_low; // Presentation Time Stamp (start)
be_t<u16, 1> end_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> end_pts_low; // Presentation Time Stamp (end)
be_t<u32, 1> mux_rate_bound; // multiplex bitrate in units of 50 bytes per second
be_t<u32, 1> std_delay_bound; // buffer delay in units of 1/90000 seconds
be_t<u32, 1> total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PamfGroup
u8 reserved2;
u8 grouping_period_num; // always 1
PamfGroupingPeriod grouping_periods;
};
CHECK_SIZE_ALIGN(PamfSequenceInfo, 0x20 + sizeof(PamfGroupingPeriod), 1);
struct PamfHeader
{
u32 magic; //"PAMF"
u32 version; //"0041" (is it const?)
be_t<u32> data_offset; //== 2048 >> 11, PAMF headers seem to be always 2048 bytes in size
be_t<u32> data_size; //== ((fileSize - 2048) >> 11)
u32 reserved[16];
be_t<u32> table_size; //== size of mapping-table
u16 reserved1;
be_t<u16> start_pts_high;
be_t<u32, 2> start_pts_low; //Presentation Time Stamp (start)
be_t<u16> end_pts_high;
be_t<u32, 2> end_pts_low; //Presentation Time Stamp (end)
be_t<u32, 2> mux_rate_max; //== 0x01D470 (400 bps per unit, == 48000000 bps)
be_t<u32, 2> mux_rate_min; //== 0x0107AC (?????)
u16 reserved2; // ?????
u8 reserved3;
u8 stream_count; //total stream count (reduced to 1 byte)
be_t<u16> unk1; //== 1 (?????)
be_t<u32, 2> table_data_size; //== table_size - 0x20 == 0x14 + (0x30 * total_stream_num) (?????)
//TODO: check relative offset of stream structs (could be from 0x0c to 0x14, currently 0x14)
be_t<u16> start_pts_high2; //????? (probably same values)
be_t<u32, 2> start_pts_low2; //?????
be_t<u16> end_pts_high2; //?????
be_t<u32, 2> end_pts_low2; //?????
be_t<u32> unk2; //== 0x10000 (?????)
be_t<u16> unk3; // ?????
be_t<u16> unk4; // == stream_count
//==========================
PamfStreamHeader stream_headers[256];
be_t<u32, 1> magic; // "PAMF"
be_t<u32, 1> version; // "0040" or "0041"
be_t<u32, 1> header_size; // in units of 2048 bytes
be_t<u32, 1> data_size; // in units of 2048 bytes
be_t<u32, 1> psmf_marks_offset; // always 0
be_t<u32, 1> psmf_marks_size; // always 0
be_t<u32, 1> unk_offset; // always 0
be_t<u32, 1> unk_size; // always 0
u8 reserved[0x30];
PamfSequenceInfo seq_info;
};
CHECK_SIZE_ALIGN(PamfHeader, 136 + sizeof(PamfHeader::stream_headers), 4);
CHECK_SIZE_ALIGN(PamfHeader, 0x50 + sizeof(PamfSequenceInfo), 1);
struct PamfEpHeader
{
be_t<u16> value0; //mixed indexN (probably left 2 bits) and nThRefPictureOffset
be_t<u16> pts_high;
be_t<u32> pts_low;
be_t<u32> rpnOffset;
be_t<u16, 1> value0; // 2 bits: indexN, 1 bit: unused, 13 bits: nThRefPictureOffset in units of 2048 bytes
be_t<u16, 1> pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> pts_low;
be_t<u32, 1> rpnOffset; // in units of 2048 bytes
};
CHECK_SIZE_ALIGN(PamfEpHeader, 12, 4);
CHECK_SIZE_ALIGN(PamfEpHeader, 12, 1);
// PSMF specific
struct PsmfStreamHeader
{
u8 stream_id;
u8 private_stream_id; // for streams multiplexed as private data streams (stream_id == 0xbd)
be_t<u16, 1> p_std_buffer; // 2 bits: unused ??? "00", 1 bit: P_STD_buffer_scale, 13 bits: P_STD_buffer_size
be_t<u32, 1> ep_offset; // offset of EP section in header
be_t<u32, 1> ep_num; // count of EPs
union
{
// Video specific information
struct
{
u8 horizontalSize; // in units of 16 pixels
u8 verticalSize; // in units of 16 pixels
}
video;
// Audio specific information
struct
{
be_t<u16, 1> unknown; // 0
u8 channelConfiguration; // 1 = mono, 2 = stereo
u8 samplingFrequency; // 2 = 44.1kHz
}
audio;
};
};
CHECK_SIZE_ALIGN(PsmfStreamHeader, 0x10, 1);
struct PsmfGroup
{
be_t<u32, 1> size; // doesn't include this field
u8 reserved;
u8 stream_num; // same value as in PsmfSequenceInfo
PsmfStreamHeader streams;
};
CHECK_SIZE_ALIGN(PsmfGroup, 6 + sizeof(PsmfStreamHeader), 1);
struct PsmfGroupingPeriod
{
be_t<u32, 1> size; // doesn't include this field
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> start_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod
be_t<u16, 1> end_pts_high; // unused due to bug
be_t<u32, 1> end_pts_low; // same value as in PsmfSequenceInfo, since there is only one PsmfGroupingPeriod
u8 reserved;
u8 group_num; // always 1
PsmfGroup groups;
};
CHECK_SIZE_ALIGN(PsmfGroupingPeriod, 0x12 + sizeof(PsmfGroup), 1);
struct PsmfSequenceInfo
{
be_t<u32, 1> size; // doesn't include this field
be_t<u16, 1> start_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> start_pts_low; // Presentation Time Stamp (start)
be_t<u16, 1> end_pts_high; // always 0, greatest valid pts is UINT32_MAX
be_t<u32, 1> end_pts_low; // Presentation Time Stamp (end)
be_t<u32, 1> mux_rate_bound; // multiplex bitrate in units of 50 bytes per second
be_t<u32, 1> std_delay_bound; // buffer delay in units of 1/90000 seconds
u8 total_stream_num; // across all groups; since there is always only one group, this is equal stream_count in PsmfGroup
u8 grouping_period_num; // always 1
PsmfGroupingPeriod grouping_periods;
};
CHECK_SIZE_ALIGN(PsmfSequenceInfo, 0x1a + sizeof(PsmfGroupingPeriod), 1);
struct PsmfHeader
{
be_t<u32, 1> magic; // "PSMF"
be_t<u32, 1> version; // "0012", "0013", "0014" or "0015"
be_t<u32, 1> header_size; // not scaled, unlike PAMF
be_t<u32, 1> data_size; // not scaled, unlike PAMF
be_t<u32, 1> psmf_marks_offset;
be_t<u32, 1> psmf_marks_size;
be_t<u32, 1> unk[2];
u8 reserved[0x30];
PsmfSequenceInfo seq_info;
};
CHECK_SIZE_ALIGN(PsmfHeader, 0x50 + sizeof(PsmfSequenceInfo), 1);
struct PsmfEpHeader
{
be_t<u16, 1> value0; // 2 bits: indexN, 2 bits: unused, 11 bits: nThRefPictureOffset in units of 1024 bytes, 1 bit: pts_high
be_t<u32, 1> pts_low;
be_t<u32, 1> rpnOffset; // in units of 2048 bytes
};
CHECK_SIZE_ALIGN(PsmfEpHeader, 10, 1);
// not directly accessed by virtual CPU, fields are unknown
struct CellPamfReader
{
vm::cptr<PamfHeader> pAddr;
s32 stream;
u64 fileSize;
u32 internalData[28];
be_t<u64> headerSize;
be_t<u64> dataSize;
be_t<u32> attribute;
be_t<u16> isPsmf;
be_t<u16> version;
be_t<u32> currentGroupingPeriodIndex;
be_t<u32> currentGroupIndex;
be_t<u32> currentStreamIndex;
union
{
struct
{
vm::bcptr<PamfHeader> header;
vm::bcptr<PamfSequenceInfo> sequenceInfo;
vm::bcptr<PamfGroupingPeriod> currentGroupingPeriod;
vm::bcptr<PamfGroup> currentGroup;
vm::bcptr<PamfStreamHeader> currentStream;
}
pamf;
struct
{
vm::bcptr<PsmfHeader> header;
vm::bcptr<PsmfSequenceInfo> sequenceInfo;
vm::bcptr<PsmfGroupingPeriod> currentGroupingPeriod;
vm::bcptr<PsmfGroup> currentGroup;
vm::bcptr<PsmfStreamHeader> currentStream;
}
psmf;
};
u32 reserved[18];
};
CHECK_SIZE(CellPamfReader, 128);
+1 -2
View File
@@ -1657,9 +1657,8 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
//CELL_SAVEDATA_RECREATE_NO = overwrite and let the user know, not data is corrupt.
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
//TODO: if this is a save, and it's not auto, then show a dialog
// fallthrough
[[fallthrough]];
}
case CELL_SAVEDATA_RECREATE_NO_NOBROKEN:
{
break;
+4 -4
View File
@@ -879,7 +879,7 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
{
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC;
const std::string path = vfs::get(vpath) + "/" + item.name;
const std::string path = vfs::get(item_path);
const auto [success, mi] = utils::get_media_info(path, 1); // AVMEDIA_TYPE_AUDIO
if (!success)
{
@@ -900,7 +900,7 @@ error_code cellSearchStartContentSearchInList(vm::cptr<CellSearchContentId> list
{
curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO;
const std::string path = vfs::get(vpath) + "/" + item.name;
const std::string path = vfs::get(item_path);
const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO
if (!success)
{
@@ -1120,7 +1120,7 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
{
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC;
const std::string path = vfs::get(vpath) + "/" + item.name;
const std::string path = vfs::get(item_path);
const auto [success, mi] = utils::get_media_info(path, 1); // AVMEDIA_TYPE_AUDIO
if (!success)
{
@@ -1141,7 +1141,7 @@ error_code cellSearchStartContentSearch(CellSearchContentSearchType type, CellSe
{
curr_find->type = CELL_SEARCH_CONTENTTYPE_VIDEO;
const std::string path = vfs::get(vpath) + "/" + item.name;
const std::string path = vfs::get(item_path);
const auto [success, mi] = utils::get_media_info(path, 0); // AVMEDIA_TYPE_VIDEO
if (!success)
{
+105 -12
View File
@@ -31,6 +31,8 @@ void fmt_class_string<CellSysCacheError>::format(std::string& out, u64 arg)
extern lv2_fs_mount_point g_mp_sys_dev_hdd1;
extern std::string get_syscache_state_corruption_indicator_file_path(std::string_view dir_path);
struct syscache_info
{
const std::string cache_root = rpcs3::utils::get_hdd1_dir() + "/caches/";
@@ -39,6 +41,8 @@ struct syscache_info
std::string cache_id;
bool retain_caches = false;
syscache_info() noexcept
{
// Check if dev_hdd1 is mounted by parent process
@@ -47,10 +51,15 @@ struct syscache_info
const auto lock = init.init();
// Extract cache id from path
cache_id = Emu.hdd1;
if (cache_id.back() == '/')
cache_id.resize(cache_id.size() - 1);
cache_id = cache_id.substr(cache_id.find_last_of('/') + 1);
std::string_view id = Emu.hdd1;
id = id.substr(0, id.find_last_not_of(fs::delim) + 1);
id = id.substr(id.find_last_of(fs::delim) + 1);
cache_id = std::string{id};
if (!Emu.DeserialManager() && !fs::write_file<true>(get_syscache_state_corruption_indicator_file_path(Emu.hdd1), fs::write_new))
{
fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", Emu.hdd1, fs::g_tls_error);
}
cellSysutil.success("Retained cache from parent process: %s", Emu.hdd1);
return;
@@ -63,7 +72,17 @@ struct syscache_info
{
if (entry.is_directory && entry.name.starts_with(prefix))
{
cache_id = std::move(entry.name);
cache_id = vfs::unescape(entry.name);
if (fs::is_file(get_syscache_state_corruption_indicator_file_path(cache_root + '/' + cache_id)))
{
// State is not complete
clear(true);
cache_id.clear();
continue;
}
cellSysutil.notice("Retained cache from past data: %s", cache_root + '/' + cache_id);
break;
}
}
@@ -89,8 +108,50 @@ struct syscache_info
});
}
}
~syscache_info() noexcept
{
if (cache_id.empty())
{
return;
}
if (!retain_caches)
{
vfs::host::remove_all(cache_root + cache_id, cache_root, &g_mp_sys_dev_hdd1, true, false, true);
return;
}
idm::select<lv2_fs_object, lv2_file>([](u32 /*id*/, lv2_file& file)
{
if (file.file && file.mp->flags & lv2_mp_flag::cache && file.flags & CELL_FS_O_ACCMODE)
{
file.file.sync();
}
});
fs::remove_file(get_syscache_state_corruption_indicator_file_path(cache_root + cache_id));
}
};
extern std::string get_syscache_state_corruption_indicator_file_path(std::string_view dir_path)
{
constexpr std::u8string_view append_path = u8"/hdd0_temp_state_indicator";
const std::string_view filename = reinterpret_cast<const char*>(append_path.data());
if (dir_path.empty())
{
return rpcs3::utils::get_hdd1_dir() + "/caches/" + ensure(g_fxo->try_get<syscache_info>())->cache_id + "/" + filename.data();
}
return std::string{dir_path} + filename.data();
}
extern void signal_system_cache_can_stay()
{
ensure(g_fxo->try_get<syscache_info>())->retain_caches = true;
}
error_code cellSysCacheClear()
{
cellSysutil.notice("cellSysCacheClear()");
@@ -116,17 +177,26 @@ error_code cellSysCacheClear()
error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
{
cellSysutil.notice("cellSysCacheMount(param=*0x%x)", param);
cellSysutil.notice("cellSysCacheMount(param=*0x%x ('%s'))", param, param.ptr(&CellSysCacheParam::cacheId));
auto& cache = g_fxo->get<syscache_info>();
if (!param || (param->cacheId[0] && sysutil_check_name_string(param->cacheId, 1, CELL_SYSCACHE_ID_SIZE) != 0))
if (!param)
{
return CELL_SYSCACHE_ERROR_PARAM;
}
std::string cache_name;
ensure(vm::read_string(param.ptr(&CellSysCacheParam::cacheId).addr(), sizeof(param->cacheId), cache_name), "Access violation");
if (!cache_name.empty() && sysutil_check_name_string(cache_name.data(), 1, CELL_SYSCACHE_ID_SIZE) != 0)
{
return CELL_SYSCACHE_ERROR_PARAM;
}
// Full virtualized cache id (with title id included)
std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + param->cacheId);
std::string cache_id = vfs::escape(Emu.GetTitleID() + '_' + cache_name);
// Full path to virtual cache root (/dev_hdd1)
std::string new_path = cache.cache_root + cache_id + '/';
@@ -141,8 +211,8 @@ error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
std::lock_guard lock0(g_mp_sys_dev_hdd1.mutex);
// Check if can reuse existing cache (won't if cache id is an empty string)
if (param->cacheId[0] && cache_id == cache.cache_id)
// Check if can reuse existing cache (won't if cache id is an empty string or cache is damaged/incomplete)
if (!cache_name.empty() && cache_id == cache.cache_id)
{
// Isn't mounted yet on first call to cellSysCacheMount
if (vfs::mount("/dev_hdd1", new_path))
@@ -152,15 +222,38 @@ error_code cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
return not_an_error(CELL_SYSCACHE_RET_OK_RELAYED);
}
// Clear existing cache
const bool can_create = cache.cache_id != cache_id || !cache.cache_id.empty();
if (!cache.cache_id.empty())
{
// Clear previous cache
cache.clear(true);
}
// Set new cache id
cache.cache_id = std::move(cache_id);
fs::create_dir(new_path);
if (can_create)
{
const bool created = fs::create_dir(new_path);
if (!created)
{
if (fs::g_tls_error != fs::error::exist)
{
fmt::throw_exception("Failed to create HDD1 cache! (path='%s', reason='%s')", new_path, fs::g_tls_error);
}
// Clear new cache
cache.clear(false);
}
}
if (!fs::write_file<true>(get_syscache_state_corruption_indicator_file_path(new_path), fs::write_new))
{
fmt::throw_exception("Failed to create HDD1 corruption indicator file! (path='%s', reason='%s')", new_path, fs::g_tls_error);
}
if (vfs::mount("/dev_hdd1", new_path))
g_fxo->get<lv2_fs_mount_info_map>().add("/dev_hdd1", &g_mp_sys_dev_hdd1);
+29 -2
View File
@@ -35,6 +35,30 @@ void fmt_class_string<CellSysutilAvc2Error>::format(std::string& out, u64 arg)
});
}
template<>
void fmt_class_string<CellSysutilAvc2AttributeId>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](CellSysutilAvc2AttributeId value)
{
switch (value)
{
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_EVENT_TYPE);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_INTERVAL_TIME);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DETECT_SIGNAL_LEVEL);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_MAX_BITRATE);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DATA_FEC);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_PACKET_CONTENTION);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_DTX_MODE);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_MIC_STATUS_DETECTION);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_MIC_SETTING_NOTIFICATION);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_VOICE_MUTING_NOTIFICATION);
STR_CASE(CELL_SYSUTIL_AVC2_ATTRIBUTE_CAMERA_STATUS_DETECTION);
}
return unknown;
});
}
vm::ptr<CellSysutilAvc2Callback> avc2_cb{};
vm::ptr<void> avc2_cb_arg{};
@@ -655,10 +679,13 @@ error_code cellSysutilAvc2Load(SceNpMatching2ContextId ctx_id, u32 container, vm
error_code cellSysutilAvc2SetAttribute(vm::cptr<CellSysutilAvc2Attribute> attr)
{
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(attr=*0x%x)", attr);
if (!attr)
{
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(attr=*0x%x)", attr);
return CELL_AVC2_ERROR_INVALID_ARGUMENT;
}
cellSysutilAvc2.todo("cellSysutilAvc2SetAttribute(%s=%d)", attr->attr_id, attr->attr_param.int_param);
switch (attr->attr_id)
{
+62 -40
View File
@@ -1,74 +1,96 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "cellSysutil.h"
#include "sceNp.h"
#include "Emu/IdManager.h"
LOG_CHANNEL(cellSysutilNpEula);
struct sceNpEulaStructUnk0
enum SceNpEulaStatus
{
char buf[0xc];
SCE_NP_EULA_UNKNOWN = 0,
SCE_NP_EULA_ACCEPTED = 1,
SCE_NP_EULA_ALREADY_ACCEPTED = 2,
SCE_NP_EULA_REJECTED = 3,
SCE_NP_EULA_ABORTED = 4,
SCE_NP_EULA_ERROR = 5,
};
// All error codes are unknown at this point in implementation and just guesses
enum cellSysutilNpEulaError : u32
using SceNpEulaVersion = u32;
using SceNpEulaCheckEulaStatusCallback = void(SceNpEulaStatus status, u32 errorCode, SceNpEulaVersion version, vm::ptr<void> arg);
struct sceNpEulaCallbacksRegistered
{
CELL_SYSUTIL_NP_EULA_NOT_INIT = 0x8002E500,
CELL_SYSUTIL_NP_EULA_INVALID_PARAM = 0x8002E501,
CELL_SYSUTIL_NP_EULA_UNKNOWN_502 = 0x8002E502,
CELL_SYSUTIL_NP_EULA_UNKNOWN_503 = 0x8002E503,
atomic_t<SceNpEulaStatus> status = SCE_NP_EULA_UNKNOWN;
atomic_t<bool> sceNpEulaCheckEulaStatus_callback_registered = false;
atomic_t<bool> sceNpEulaShowCurrentEula_callback_registered = false;
};
template<>
void fmt_class_string<cellSysutilNpEulaError>::format(std::string& out, u64 arg)
error_code sceNpEulaCheckEulaStatus(vm::cptr<SceNpCommunicationId> communicationId, u32 arg2, u64 arg3, vm::ptr<SceNpEulaCheckEulaStatusCallback> cbFunc, vm::ptr<void> cbFuncArg)
{
format_enum(out, arg, [](auto error)
cellSysutilNpEula.warning("sceNpEulaCheckEulaStatus(communicationId=*0x%x, arg2=0x%x, arg3=0x%x, cbFunc=*0x%x, cbFuncArg=*0x%x)", communicationId, arg2, arg3, cbFunc, cbFuncArg);
if (!communicationId || !cbFunc)
{
switch (error)
{
STR_CASE(CELL_SYSUTIL_NP_EULA_NOT_INIT);
STR_CASE(CELL_SYSUTIL_NP_EULA_INVALID_PARAM);
STR_CASE(CELL_SYSUTIL_NP_EULA_UNKNOWN_502);
STR_CASE(CELL_SYSUTIL_NP_EULA_UNKNOWN_503);
}
return SCE_NP_EULA_ERROR_INVALID_ARGUMENT;
}
return unknown;
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
if (cb_infos.sceNpEulaCheckEulaStatus_callback_registered || cb_infos.sceNpEulaShowCurrentEula_callback_registered)
{
return SCE_NP_EULA_ERROR_ALREADY_INITIALIZED;
}
cb_infos.sceNpEulaCheckEulaStatus_callback_registered = true;
cb_infos.status = SCE_NP_EULA_ALREADY_ACCEPTED;
sysutil_register_cb([=](ppu_thread& cb_ppu) -> s32
{
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
cbFunc(cb_ppu, cb_infos.status, CELL_OK, 1, cbFuncArg);
cb_infos.sceNpEulaCheckEulaStatus_callback_registered = false;
return 0;
});
}
// Seen on: Resistance 3, Uncharted 2
error_code sceNpEulaCheckEulaStatus(vm::ptr<sceNpEulaStructUnk0> arg1, u32 arg2, u64 arg3, u32 arg4, vm::ptr<CellSysutilCallback> callback1)
{
cellSysutilNpEula.todo("sceNpEulaCheckEulaStatus(arg1=*0x%x, arg2=0x%x, arg3=0x%x, arg4=0x%x, callback1=*0x%x)", arg1, arg2, arg3, arg4, callback1);
if (!arg1)
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
if (arg4 == 0)
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
return CELL_OK;
}
// Seen on: Resistance 3
error_code sceNpEulaAbort()
{
cellSysutilNpEula.todo("sceNpEulaAbort()");
cellSysutilNpEula.warning("sceNpEulaAbort()");
// Can return CELL_SYSUTIL_NP_EULA_NOT_INIT
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
if (!cb_infos.sceNpEulaCheckEulaStatus_callback_registered && !cb_infos.sceNpEulaShowCurrentEula_callback_registered)
{
return SCE_NP_EULA_ERROR_NOT_INITIALIZED;
}
// It would forcefully abort the dialog/process of getting the eula but since we don't show the dialog, just alter the status returned
cb_infos.status = SCE_NP_EULA_ABORTED;
return CELL_OK;
}
// Seen on: Resistance 3, Uncharted 2
error_code sceNpEulaShowCurrentEula(vm::ptr<sceNpEulaStructUnk0> arg1, u64 arg2, vm::ptr<CellSysutilCallback> callback1, vm::ptr<CellSysutilCallback> callback2)
error_code sceNpEulaShowCurrentEula(vm::cptr<SceNpCommunicationId> communicationId, u64 arg2, vm::ptr<CellSysutilCallback> cbFunc, vm::ptr<void> cbFuncArg)
{
cellSysutilNpEula.todo("sceNpEulaShowCurrentEula(arg1=*0x%x, arg2=0x%x, callback1=*0x%x, callback2=*0x%x)", arg1, arg2, callback1, callback2);
cellSysutilNpEula.todo("sceNpEulaShowCurrentEula(communicationId=*0x%x, arg2=0x%x, cbFunc=*0x%x, cbFuncArg=*0x%x)", communicationId, arg2, cbFunc, cbFuncArg);
if (!arg1)
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
if (!communicationId || !cbFunc)
{
return SCE_NP_EULA_ERROR_INVALID_ARGUMENT;
}
if (!callback1)
return CELL_SYSUTIL_NP_EULA_INVALID_PARAM;
auto& cb_infos = g_fxo->get<sceNpEulaCallbacksRegistered>();
if (cb_infos.sceNpEulaCheckEulaStatus_callback_registered || cb_infos.sceNpEulaShowCurrentEula_callback_registered)
{
return SCE_NP_EULA_ERROR_ALREADY_INITIALIZED;
}
// Call callback (Unknown parameters)
return CELL_OK;
}
-1
View File
@@ -280,7 +280,6 @@ struct vdec_context final
~vdec_context()
{
avcodec_close(ctx);
avcodec_free_context(&ctx);
sws_freeContext(sws);
}
+3 -1
View File
@@ -113,7 +113,9 @@ bool check_movie_path(const std::string& file_path)
std::string get_available_movie_path(const std::string& filename)
{
const std::string movie_dir = "/dev_hdd0/movie/";
// TODO: Find out how to build this path properly. Apparently real hardware doesn't add a suffix,
// but just randomly puts each video into a separate 2-Letter subdirectory like /video/hd/ or /video/ee/
const std::string movie_dir = "/dev_hdd0/video/";
std::string dst_path = vfs::get(movie_dir + filename);
// Do not overwrite existing files. Add a suffix instead.
+1
View File
@@ -156,6 +156,7 @@ error_code cellVoiceCreatePort(vm::ptr<u32> portId, vm::cptr<CellVoicePortParam>
return CELL_VOICE_ERROR_ARGUMENT_INVALID;
}
}
break;
}
case CELLVOICE_PORTTYPE_IN_MIC:
case CELLVOICE_PORTTYPE_OUT_SECONDARY:
File diff suppressed because it is too large Load Diff
+292 -3
View File
@@ -242,6 +242,85 @@ enum SceNpError : u32
SCE_NP_COMMUNITY_SERVER_ERROR_TITLE_USER_STORAGE_MAINTENANCE = 0x8002a4ac,
SCE_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x8002a4ff,
// COMMERCE
SCE_NP_COMMERCE_ERROR_NOT_INITIALIZED = 0x80029401,
SCE_NP_COMMERCE_ERROR_ALREADY_INITIALIZED = 0x80029402,
SCE_NP_COMMERCE_ERROR_OUT_OF_MEMORY = 0x80029403,
SCE_NP_COMMERCE_ERROR_UNSUPPORTED_VERSION = 0x80029404,
SCE_NP_COMMERCE_ERROR_CTX_MAX = 0x80029405,
SCE_NP_COMMERCE_ERROR_CTX_NOT_FOUND = 0x80029406,
SCE_NP_COMMERCE_ERROR_CTXID_NOT_AVAILABLE = 0x80029407,
SCE_NP_COMMERCE_ERROR_REQ_MAX = 0x80029408,
SCE_NP_COMMERCE_ERROR_REQ_NOT_FOUND = 0x80029409,
SCE_NP_COMMERCE_ERROR_REQID_NOT_AVAILABLE = 0x8002940a,
SCE_NP_COMMERCE_ERROR_INVALID_CATEGORY_ID = 0x8002940b,
SCE_NP_COMMERCE_ERROR_INVALID_LANG_CODE = 0x8002940c,
SCE_NP_COMMERCE_ERROR_REQ_BUSY = 0x8002940d,
SCE_NP_COMMERCE_ERROR_INSUFFICIENT_BUFFER = 0x8002940e,
SCE_NP_COMMERCE_ERROR_INVALID_REQ_STATE = 0x8002940f,
SCE_NP_COMMERCE_ERROR_INVALID_CTX_STATE = 0x80029410,
SCE_NP_COMMERCE_ERROR_UNKNOWN = 0x80029411,
SCE_NP_COMMERCE_ERROR_INVALID_REQ_TYPE = 0x80029412,
SCE_NP_COMMERCE_ERROR_INVALID_MEMORY_CONTAINER = 0x80029413,
SCE_NP_COMMERCE_ERROR_INSUFFICIENT_MEMORY_CONTAINER = 0x80029414,
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_TYPE = 0x80029415,
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_STATE = 0x80029416,
SCE_NP_COMMERCE_ERROR_DATA_FLAG_NUM_NOT_FOUND = 0x80029417,
SCE_NP_COMMERCE_ERROR_DATA_FLAG_INFO_NOT_FOUND = 0x80029418,
SCE_NP_COMMERCE_ERROR_INVALID_PROVIDER_ID = 0x80029419,
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_NUM = 0x8002941a,
SCE_NP_COMMERCE_ERROR_INVALID_SKU_ID = 0x8002941b,
SCE_NP_COMMERCE_ERROR_INVALID_DATA_FLAG_ID = 0x8002941c,
SCE_NP_COMMERCE_ERROR_GPC_SEND_REQUEST = 0x8002941d,
SCE_NP_COMMERCE_ERROR_GDF_SEND_REQUEST = 0x8002941e,
SCE_NP_COMMERCE_ERROR_SDF_SEND_REQUEST = 0x8002941f,
SCE_NP_COMMERCE_ERROR_PARSE_PRODUCT_CATEGORY = 0x80029421,
SCE_NP_COMMERCE_ERROR_CURRENCY_INFO_NOT_FOUND = 0x80029422,
SCE_NP_COMMERCE_ERROR_CATEGORY_INFO_NOT_FOUND = 0x80029423,
SCE_NP_COMMERCE_ERROR_CHILD_CATEGORY_COUNT_NOT_FOUND = 0x80029424,
SCE_NP_COMMERCE_ERROR_CHILD_CATEGORY_INFO_NOT_FOUND = 0x80029425,
SCE_NP_COMMERCE_ERROR_SKU_COUNT_NOT_FOUND = 0x80029426,
SCE_NP_COMMERCE_ERROR_SKU_INFO_NOT_FOUND = 0x80029427,
SCE_NP_COMMERCE_ERROR_PLUGIN_LOAD_FAILURE = 0x80029428,
SCE_NP_COMMERCE_ERROR_INVALID_SKU_NUM = 0x80029429,
SCE_NP_COMMERCE_ERROR_INVALID_GPC_PROTOCOL_VERSION = 0x8002942a,
SCE_NP_COMMERCE_ERROR_CHECKOUT_UNEXPECTED = 0x80029430,
SCE_NP_COMMERCE_ERROR_CHECKOUT_OUT_OF_SERVICE = 0x80029431,
SCE_NP_COMMERCE_ERROR_CHECKOUT_INVALID_SKU = 0x80029432,
SCE_NP_COMMERCE_ERROR_CHECKOUT_SERVER_BUSY = 0x80029433,
SCE_NP_COMMERCE_ERROR_CHECKOUT_MAINTENANCE = 0x80029434,
SCE_NP_COMMERCE_ERROR_CHECKOUT_ACCOUNT_SUSPENDED = 0x80029435,
SCE_NP_COMMERCE_ERROR_CHECKOUT_OVER_SPENDING_LIMIT = 0x80029436,
SCE_NP_COMMERCE_ERROR_CHECKOUT_NOT_ENOUGH_MONEY = 0x80029437,
SCE_NP_COMMERCE_ERROR_CHECKOUT_UNKNOWN = 0x80029438,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_UNKNOWN = 0x80029600,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029601,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INVALID_CATEGORY_ID = 0x80029602,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_END = 0x80029603,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_STOP = 0x80029604,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_SERVICE_BUSY = 0x80029605,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029606,
SCE_NP_COMMERCE_BROWSE_SERVER_ERROR_INTERNAL_SERVER = 0x80029680,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_UNKNOWN = 0x80029d00,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029d01,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_INVALID_FLAGLIST = 0x80029d02,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_END = 0x80029d03,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_STOP = 0x80029d04,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_SERVICE_BUSY = 0x80029d05,
SCE_NP_COMMERCE_GDF_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029d06,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_UNKNOWN = 0x80029e00,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_INVALID_CREDENTIALS = 0x80029e01,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_INVALID_FLAGLIST = 0x80029e02,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_END = 0x80029e03,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_STOP = 0x80029e04,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_SERVICE_BUSY = 0x80029e05,
SCE_NP_COMMERCE_SDF_SERVER_ERROR_UNSUPPORTED_VERSION = 0x80029e06,
// DRM
SCE_NP_DRM_ERROR_OUT_OF_MEMORY = 0x80029501,
SCE_NP_DRM_ERROR_INVALID_PARAM = 0x80029502,
@@ -454,6 +533,197 @@ enum SceNpError : u32
SCE_NP_CUSTOM_MENU_ERROR_NOT_REGISTERED = 0x80023b0b,
SCE_NP_CUSTOM_MENU_ERROR_EXCEEDS_MAX = 0x80023b0c,
SCE_NP_CUSTOM_MENU_ERROR_INVALID_CHARACTER = 0x80023b0d,
// EULA
SCE_NP_EULA_ERROR_UNKNOWN = 0x8002e500,
SCE_NP_EULA_ERROR_INVALID_ARGUMENT = 0x8002e501,
SCE_NP_EULA_ERROR_NOT_INITIALIZED = 0x8002e502,
SCE_NP_EULA_ERROR_ALREADY_INITIALIZED = 0x8002e503,
SCE_NP_EULA_ERROR_OUT_OF_MEMORY = 0x8002e504,
SCE_NP_EULA_ERROR_BUSY = 0x8002e505,
SCE_NP_EULA_ERROR_EULA_NOT_FOUND = 0x8002e5a0,
SCE_NP_EULA_ERROR_NET_OUT_OF_MEMORY = 0x8002e5a1,
SCE_NP_EULA_ERROR_CONF_FORMAT = 0x8002e5b0,
SCE_NP_EULA_ERROR_CONF_INVALID_FILENAME = 0x8002e5b1,
SCE_NP_EULA_ERROR_CONF_TOO_MANY_EULA_FILES = 0x8002e5b2,
SCE_NP_EULA_ERROR_CONF_INVALID_LANGUAGE = 0x8002e5b3,
SCE_NP_EULA_ERROR_CONF_INVALID_COUNTRY = 0x8002e5b4,
SCE_NP_EULA_ERROR_CONF_INVALID_NPCOMMID = 0x8002e5b5,
SCE_NP_EULA_ERROR_CONF_INVALID_EULA_VERSION = 0x8002e5b6,
// Matching
SCE_NP_MATCHING_ERROR_NOT_INITIALIZED = 0x8002a701,
SCE_NP_MATCHING_ERROR_ALREADY_INITIALIZED = 0x8002a702,
SCE_NP_MATCHING_ERROR_INVALID_ARG = 0x8002a703,
SCE_NP_MATCHING_ERROR_TERMINATED = 0x8002a704,
SCE_NP_MATCHING_ERROR_TIMEOUT = 0x8002a705,
SCE_NP_MATCHING_ERROR_OUT_OF_MEMORY = 0x8002a706,
SCE_NP_MATCHING_ERROR_CTXID_NOT_AVAIL = 0x8002a707,
SCE_NP_MATCHING_ERROR_CTX_ALREADY_EXIST = 0x8002a708,
SCE_NP_MATCHING_ERROR_CTX_NOT_FOUND = 0x8002a709,
SCE_NP_MATCHING_ERROR_LOBBY_NOT_FOUND = 0x8002a70a,
SCE_NP_MATCHING_ERROR_ROOM_NOT_FOUND = 0x8002a70b,
SCE_NP_MATCHING_ERROR_MEMBER_NOT_FOUND = 0x8002a70c,
SCE_NP_MATCHING_ERROR_TOO_BIG_VALUE = 0x8002a70d,
SCE_NP_MATCHING_ERROR_IVALID_ATTR_TYPE = 0x8002a70e,
SCE_NP_MATCHING_ERROR_INVALID_ATTR_ID = 0x8002a70f,
SCE_NP_MATCHING_ERROR_ALREADY_REQUESTED = 0x8002a710,
SCE_NP_MATCHING_ERROR_LIMITTED_SEATING = 0x8002a711,
SCE_NP_MATCHING_ERROR_LOCKED = 0x8002a712,
SCE_NP_MATCHING_ERROR_CTX_STILL_RUNNING = 0x8002a713,
SCE_NP_MATCHING_ERROR_INSUFFICIENT_BUFFER = 0x8002a714,
SCE_NP_MATCHING_ERROR_REQUEST_NOT_ALLOWED = 0x8002a715,
SCE_NP_MATCHING_ERROR_CTX_MAX = 0x8002a716,
SCE_NP_MATCHING_ERROR_INVALID_REQ_ID = 0x8002a717,
SCE_NP_MATCHING_ERROR_RESULT_NOT_FOUND = 0x8002a718,
SCE_NP_MATCHING_ERROR_BUSY = 0x8002a719,
SCE_NP_MATCHING_ERROR_ALREADY_JOINED_ROOM = 0x8002a71a,
SCE_NP_MATCHING_ERROR_ROOM_MAX = 0x8002a71b,
SCE_NP_MATCHING_ERROR_QUICK_MATCH_PLAYER_NOT_FOUND = 0x8002a71c,
SCE_NP_MATCHING_ERROR_COND_MAX = 0x8002a71d,
SCE_NP_MATCHING_ERROR_INVALID_COND = 0x8002a71e,
SCE_NP_MATCHING_ERROR_INVALID_ATTR = 0x8002a71f,
SCE_NP_MATCHING_ERROR_COMP_OP_INEQUALITY_MAX = 0x8002a720,
SCE_NP_MATCHING_ERROR_RESULT_OVERFLOWED = 0x8002a721,
SCE_NP_MATCHING_ERROR_HTTPXML_TIMEOUT = 0x8002a722,
SCE_NP_MATCHING_ERROR_CANCELED = 0x8002a723,
SCE_NP_MATCHING_ERROR_SEARCH_JOIN_ROOM_NOT_FOUND = 0x8002a724,
SCE_NP_MATCHING_ERROR_INVALID_COMP_OP = 0x8002a725,
SCE_NP_MATCHING_ERROR_INVALID_COMP_TYPE = 0x8002a726,
SCE_NP_MATCHING_ERROR_REQUEST_NOT_FOUND = 0x8002a727,
SCE_NP_MATCHING_ERROR_INTERNAL_ERROR = 0x8002a728,
SCE_NP_MATCHING_ERROR_INVALID_PROTOCOL_ID = 0x8002a729,
SCE_NP_MATCHING_ERROR_ATTR_NOT_SPECIFIED = 0x8002a72a,
SCE_NP_MATCHING_ERROR_SYSUTIL_INVALID_RESULT = 0x8002a72b,
SCE_NP_MATCHING_ERROR_PLUGIN_LOAD_FAILURE = 0x8002a72c,
SCE_NP_MATCHING_ERROR_INVALID_ATTR_VALUE = 0x8002a72d,
SCE_NP_MATCHING_ERROR_DUPLICATE = 0x8002a72e,
SCE_NP_MATCHING_ERROR_INVALID_MEMORY_CONTAINER = 0x8002a72f,
SCE_NP_MATCHING_ERROR_SHUTDOWN = 0x8002a730,
SCE_NP_MATCHING_ERROR_SYSUTIL_SERVER_BUSY = 0x8002a731,
SCE_NP_MATCHING_ERROR_SEND_INVITATION_PARTIALLY_FAILED = 0x8002a732,
SCE_NP_MATCHING_ERROR_UTILITY_UNAVAILABLE = 0x8002a733,
SCE_NP_MATCHING_SERVER_ERROR_OUT_OF_SERVICE = 0x8002a740,
SCE_NP_MATCHING_SERVER_ERROR_MAINTENANCE = 0x8002a741,
SCE_NP_MATCHING_SERVER_ERROR_SERVER_BUSY = 0x8002a742,
SCE_NP_MATCHING_SERVER_ERROR_ACCESS_FORBIDDEN = 0x8002a743,
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_SERVER = 0x8002a744,
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_LOBBY = 0x8002a745,
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_ROOM = 0x8002a746,
SCE_NP_MATCHING_SERVER_ERROR_NO_SUCH_USER = 0x8002a747,
SCE_NP_MATCHING_SERVER_ERROR_NOT_ALLOWED = 0x8002a748,
SCE_NP_MATCHING_SERVER_ERROR_UNKNOWN = 0x8002a749,
SCE_NP_MATCHING_SERVER_ERROR_BAD_REQUEST_STANZA = 0x8002a74a,
SCE_NP_MATCHING_SERVER_ERROR_REQUEST_FORBIDDEN = 0x8002a74b,
SCE_NP_MATCHING_SERVER_ERROR_INTERNAL_ERROR = 0x8002a74c,
SCE_NP_MATCHING_SERVER_ERROR_ROOM_OVER = 0x8002a74d,
SCE_NP_MATCHING_SERVER_ERROR_ROOM_CLOSED = 0x8002a74e,
};
enum
{
SCE_NP_COMMERCE_CURRENCY_CODE_LEN = 4,
SCE_NP_COMMERCE_CATEGORY_ID_LEN = 56,
SCE_NP_COMMERCE_CATEGORY_NAME_LEN = 192,
SCE_NP_COMMERCE_CATEGORY_DESCRIPTION_LEN = 384,
SCE_NP_COMMERCE_CATEGORY_IMAGEURL_LEN = 128,
SCE_NP_COMMERCE_PRODUCT_ID_LEN = 48,
SCE_NP_COMMERCE_PRODUCT_NAME_LEN = 192,
SCE_NP_COMMERCE_SKU_ID_LEN = 56,
SCE_NP_COMMERCE_SKU_NAME_LEN = 64,
SCE_NP_COMMERCE_SKU_DESCRIPTION_LEN = 384,
SCE_NP_COMMERCE_SKU_IMAGEURL_LEN = 128,
SCE_NP_COMMERCE_SKU_USER_DATA_LEN = 128,
};
enum
{
SCE_NP_COMMERCE_EVENT_REQUEST_ERROR = 0x0001,
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_READY = 0x0011,
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_DONE = 0x0012,
SCE_NP_COMMERCE_EVENT_PRODUCT_CATEGORY_ABORT = 0x0013,
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_STARTED = 0x0021,
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_SUCCESS = 0x0022,
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_BACK = 0x0023,
SCE_NP_COMMERCE_EVENT_DO_CHECKOUT_FINISHED = 0x0025,
};
enum
{
SCE_NP_MATCHING_EVENT_ERROR = 0x0001,
SCE_NP_MATCHING_EVENT_LEAVE_ROOM_DONE = 0x0002,
SCE_NP_MATCHING_EVENT_RESERVED1 = 0x0003,
SCE_NP_MATCHING_EVENT_RESERVED2 = 0x0004,
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_NEW_MEMBER = 0x0005,
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_MEMBER_LEAVE = 0x0006,
SCE_NP_MATCHING_EVENT_ROOM_DISAPPEARED = 0x0007,
SCE_NP_MATCHING_EVENT_RESERVED3 = 0x0008,
SCE_NP_MATCHING_EVENT_GET_ROOM_INFO_DONE = 0x0009,
SCE_NP_MATCHING_EVENT_SET_ROOM_INFO_DONE = 0x000a,
SCE_NP_MATCHING_EVENT_GRANT_OWNER_DONE = 0x000b,
SCE_NP_MATCHING_EVENT_ROOM_UPDATE_OWNER_CHANGE = 0x000c,
SCE_NP_MATCHING_EVENT_KICK_MEMBER_DONE = 0x000d,
SCE_NP_MATCHING_EVENT_ROOM_KICKED = 0x000e,
SCE_NP_MATCHING_EVENT_GET_ROOM_SEARCH_FLAG_DONE = 0x000f,
SCE_NP_MATCHING_EVENT_SET_ROOM_SEARCH_FLAG_DONE = 0x0010,
};
enum
{
SCE_NP_MATCHING_ATTR_TYPE_BASIC_BIN = 1,
SCE_NP_MATCHING_ATTR_TYPE_BASIC_NUM = 2,
SCE_NP_MATCHING_ATTR_TYPE_GAME_BIN = 3,
SCE_NP_MATCHING_ATTR_TYPE_GAME_NUM = 4,
};
enum
{
SCE_NP_MATCHING_ROOM_ATTR_ID_TOTAL_SLOT = 1,
SCE_NP_MATCHING_ROOM_ATTR_ID_PRIVATE_SLOT = 2,
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_TOTAL_NUM = 3,
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_PUBLIC_NUM = 4,
SCE_NP_MATCHING_ROOM_ATTR_ID_CUR_PRIVATE_NUM = 5,
SCE_NP_MATCHING_ROOM_ATTR_ID_PRIVILEGE_TYPE = 6,
SCE_NP_MATCHING_ROOM_ATTR_ID_ROOM_SEARCH_FLAG = 7,
};
enum
{
SCE_NP_MATCHING_ROOM_SLOT_TYPE_PUBLIC = 0,
SCE_NP_MATCHING_ROOM_SLOT_TYPE_PRIVATE = 1,
};
enum
{
SCE_NP_MATCHING_ROOM_PRIVILEGE_TYPE_NO_AUTO_GRANT = 0,
SCE_NP_MATCHING_ROOM_PRIVILEGE_TYPE_AUTO_GRANT = 1,
};
enum
{
SCE_NP_MATCHING_ROOM_SEARCH_FLAG_OPEN = 0,
SCE_NP_MATCHING_ROOM_SEARCH_FLAG_STEALTH = 1,
};
enum
{
SCE_NP_MATCHING_CONDITION_SEARCH_EQ = 0,
SCE_NP_MATCHING_CONDITION_SEARCH_NE = 1,
SCE_NP_MATCHING_CONDITION_SEARCH_LT = 2,
SCE_NP_MATCHING_CONDITION_SEARCH_LE = 3,
SCE_NP_MATCHING_CONDITION_SEARCH_GT = 4,
SCE_NP_MATCHING_CONDITION_SEARCH_GE = 5,
};
enum
{
SCE_NP_MATCHING_CONDITION_TYPE_VALUE = 0,
};
enum
{
SCE_NP_MATCHING_INVITATION_DESTINATION_MAX = 12,
};
// Basic presence options
@@ -592,7 +862,7 @@ enum
enum : SceNpBasicAttachmentDataId
{
SCE_NP_BASIC_INVALID_ATTACHMENT_DATA_ID = 0,
SCE_NP_BASIC_INVALID_MESSAGE_ID = 0,
SCE_NP_BASIC_INVALID_MESSAGE_ID = 0,
SCE_NP_BASIC_SELECTED_INVITATION_DATA = 1,
SCE_NP_BASIC_SELECTED_MESSAGE_DATA = 2,
};
@@ -753,6 +1023,7 @@ enum
SCE_NP_UTIL_NPID_VERSION = 1,
SCE_NP_UTIL_NPLOBBYID_VERSION = 1,
SCE_NP_UTIL_NPROOMID_VERSION = 1,
SCE_NP_COMMERCE_VERSION = 1,
};
// Languages
@@ -1431,12 +1702,26 @@ struct message_data
void print() const;
};
struct np_state
{
atomic_t<bool> abort_gui_flag = false;
};
namespace rpcn
{
class rpcn_client;
}
class SendMessageDialogBase
{
public:
virtual ~SendMessageDialogBase() = default;
virtual bool Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
virtual error_code Exec(message_data& msg_data, std::set<std::string>& npids) = 0;
virtual void callback_handler(u16 ntype, const std::string& username, bool status) = 0;
protected:
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
};
class RecvMessageDialogBase
@@ -1444,5 +1729,9 @@ class RecvMessageDialogBase
public:
virtual ~RecvMessageDialogBase() = default;
virtual bool Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
virtual error_code Exec(SceNpBasicMessageMainType type, SceNpBasicMessageRecvOptions options, SceNpBasicMessageRecvAction& recv_result, u64& chosen_msg_id) = 0;
virtual void callback_handler(const std::shared_ptr<std::pair<std::string, message_data>> new_msg, u64 msg_id) = 0;
protected:
std::shared_ptr<rpcn::rpcn_client> m_rpcn;
};
+178 -59
View File
@@ -179,14 +179,14 @@ error_code sceNpMatching2Init2(u32 stackSize, s32 priority, vm::ptr<SceNpMatchin
error_code sceNpMatching2Term(ppu_thread& ppu);
error_code sceNpMatching2Term2();
error_code generic_match2_error_check(const named_thread<np::np_handler>& nph, SceNpMatching2ContextId ctxId, vm::cptr<void> reqParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
error_code generic_match2_error_check(const named_thread<np::np_handler>& nph, SceNpMatching2ContextId ctxId, vm::cptr<void> reqParam)
{
if (!nph.is_NP2_Match2_init)
{
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!reqParam || !assignedReqId)
if (!reqParam)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
@@ -313,7 +313,10 @@ error_code sceNpMatching2Term2()
nph.is_NP2_Match2_init = false;
}
// TODO: for all contexts: sceNpMatching2DestroyContext
idm::clear<match2_ctx>();
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.clear_match2_ctx();
return CELL_OK;
}
@@ -332,6 +335,9 @@ error_code sceNpMatching2DestroyContext(SceNpMatching2ContextId ctxId)
if (!destroy_match2_context(ctxId))
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.remove_match2_ctx(ctxId);
return CELL_OK;
}
@@ -341,7 +347,7 @@ error_code sceNpMatching2LeaveLobby(
sceNp2.todo("sceNpMatching2LeaveLobby(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -369,7 +375,7 @@ error_code sceNpMatching2GetWorldInfoList(
sceNp2.warning("sceNpMatching2GetWorldInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -379,7 +385,12 @@ error_code sceNpMatching2GetWorldInfoList(
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
}
*assignedReqId = nph.get_world_list(ctxId, optParam, reqParam->serverId);
const u32 request_id = nph.get_world_list(ctxId, optParam, reqParam->serverId);
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -404,7 +415,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternalList(SceNpMatching2ContextId
sceNp2.todo("sceNpMatching2GetLobbyMemberDataInternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -418,18 +429,23 @@ error_code sceNpMatching2SearchRoom(
sceNp2.warning("sceNpMatching2SearchRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.search_room(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.search_room(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
error_code sceNpMatching2SignalingGetConnectionStatus(
SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId memberId, vm::ptr<int> connStatus, vm::ptr<np_in_addr> peerAddr, vm::ptr<np_in_port_t> peerPort)
SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, SceNpMatching2RoomMemberId memberId, vm::ptr<s32> connStatus, vm::ptr<np_in_addr> peerAddr, vm::ptr<np_in_port_t> peerPort)
{
sceNp2.warning("sceNpMatching2SignalingGetConnectionStatus(ctxId=%d, roomId=%d, memberId=%d, connStatus=*0x%x, peerAddr=*0x%x, peerPort=*0x%x)", ctxId, roomId, memberId, connStatus, peerAddr, peerPort);
@@ -494,14 +510,21 @@ error_code sceNpMatching2SignalingGetConnectionStatus(
error_code sceNpMatching2SetUserInfo(
SceNpMatching2ContextId ctxId, vm::cptr<SceNpMatching2SetUserInfoRequest> reqParam, vm::cptr<SceNpMatching2RequestOptParam> optParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
{
sceNp2.todo("sceNpMatching2SetUserInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
sceNp2.warning("sceNpMatching2SetUserInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
const u32 request_id = nph.set_userinfo(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -525,7 +548,7 @@ error_code sceNpMatching2GetLobbyMemberDataInternal(
sceNp2.todo("sceNpMatching2GetLobbyMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -566,7 +589,7 @@ error_code sceNpMatching2CreateServerContext(
sceNp2.warning("sceNpMatching2CreateServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -576,7 +599,12 @@ error_code sceNpMatching2CreateServerContext(
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
}
*assignedReqId = nph.create_server_context(ctxId, optParam, reqParam->serverId);
const u32 request_id = nph.create_server_context(ctxId, optParam, reqParam->serverId);
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -601,12 +629,17 @@ error_code sceNpMatching2LeaveRoom(
sceNp2.warning("sceNpMatching2LeaveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.leave_room(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.leave_room(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -617,12 +650,17 @@ error_code sceNpMatching2SetRoomDataExternal(
sceNp2.warning("sceNpMatching2SetRoomDataExternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.set_roomdata_external(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.set_roomdata_external(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -713,12 +751,17 @@ error_code sceNpMatching2SendRoomMessage(
sceNp2.warning("sceNpMatching2SendRoomMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.send_room_message(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.send_room_message(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -729,7 +772,7 @@ error_code sceNpMatching2JoinLobby(
sceNp2.todo("sceNpMatching2JoinLobby(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -743,7 +786,7 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c
sceNp2.todo("sceNpMatching2GetRoomMemberDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -784,12 +827,17 @@ error_code sceNpMatching2GetServerInfo(
sceNp2.warning("sceNpMatching2GetServerInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.get_server_status(ctxId, optParam, reqParam->serverId);
const u32 request_id = nph.get_server_status(ctxId, optParam, reqParam->serverId);
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -824,6 +872,11 @@ error_code sceNpMatching2GetRoomSlotInfoLocal(SceNpMatching2ContextId ctxId, con
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!roomSlotInfo)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
if (!ctxId)
{
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
@@ -857,7 +910,7 @@ error_code sceNpMatching2SendLobbyChatMessage(
sceNp2.todo("sceNpMatching2SendLobbyChatMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -933,12 +986,17 @@ error_code sceNpMatching2JoinRoom(
sceNp2.warning("sceNpMatching2JoinRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.join_room(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.join_room(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1009,7 +1067,7 @@ error_code sceNpMatching2KickoutRoomMember(
sceNp2.todo("sceNpMatching2KickoutRoomMember(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1055,7 +1113,7 @@ error_code sceNpMatching2SetSignalingOptParam(
sceNp2.todo("sceNpMatching2SetSignalingOptParam(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1095,7 +1153,7 @@ error_code sceNpMatching2SendRoomChatMessage(
sceNp2.todo("sceNpMatching2SendRoomChatMessage(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1109,12 +1167,17 @@ error_code sceNpMatching2SetRoomDataInternal(
sceNp2.warning("sceNpMatching2SetRoomDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.set_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.set_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1125,12 +1188,17 @@ error_code sceNpMatching2GetRoomDataInternal(
sceNp2.warning("sceNpMatching2GetRoomDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.get_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.get_roomdata_internal(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1141,12 +1209,17 @@ error_code sceNpMatching2SignalingGetPingInfo(
sceNp2.warning("sceNpMatching2SignalingGetPingInfo(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.get_ping_info(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.get_ping_info(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1206,7 +1279,7 @@ error_code sceNpMatching2GrantRoomOwner(
sceNp2.todo("sceNpMatching2GrantRoomOwner(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1266,8 +1339,19 @@ error_code sceNpMatching2RegisterSignalingCallback(SceNpMatching2ContextId ctxId
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
auto ctx = get_match2_context(ctxId);
if (!ctx)
{
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
}
std::lock_guard lock(ctx->mutex);
ctx->signaling_cb = cbFunc;
ctx->signaling_cb_arg = cbFuncArg;
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.set_sig2_cb(ctxId, cbFunc, cbFuncArg);
sigh.add_match2_ctx(ctxId);
return CELL_OK;
}
@@ -1292,7 +1376,7 @@ error_code sceNpMatching2GetUserInfoList(
sceNp2.todo("sceNpMatching2GetUserInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1303,14 +1387,21 @@ error_code sceNpMatching2GetUserInfoList(
error_code sceNpMatching2GetRoomMemberDataInternal(
SceNpMatching2ContextId ctxId, vm::cptr<SceNpMatching2GetRoomMemberDataInternalRequest> reqParam, vm::cptr<SceNpMatching2RequestOptParam> optParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
{
sceNp2.todo("sceNpMatching2GetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
sceNp2.warning("sceNpMatching2GetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
const u32 request_id = nph.get_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1320,12 +1411,17 @@ error_code sceNpMatching2SetRoomMemberDataInternal(
sceNp2.warning("sceNpMatching2SetRoomMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.set_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.set_roommemberdata_internal(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1336,13 +1432,18 @@ error_code sceNpMatching2JoinProhibitiveRoom(
sceNp2.warning("sceNpMatching2JoinProhibitiveRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
// TODO: add blocked users
*assignedReqId = nph.join_room(ctxId, optParam, &reqParam->joinParam);
const u32 request_id = nph.join_room(ctxId, optParam, &reqParam->joinParam);
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1367,7 +1468,7 @@ error_code sceNpMatching2DeleteServerContext(
sceNp2.warning("sceNpMatching2DeleteServerContext(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1377,7 +1478,12 @@ error_code sceNpMatching2DeleteServerContext(
return SCE_NP_MATCHING2_ERROR_INVALID_SERVER_ID;
}
*assignedReqId = nph.delete_server_context(ctxId, optParam, reqParam->serverId);
const u32 request_id = nph.delete_server_context(ctxId, optParam, reqParam->serverId);
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1460,12 +1566,15 @@ error_code sceNpMatching2GetRoomPasswordLocal(SceNpMatching2ContextId ctxId, Sce
if (password)
{
*withPassword = true;
memcpy(roomPassword.get_ptr(), &*password, sizeof(SceNpMatching2SessionPassword));
if (withPassword) *withPassword = true;
if (roomPassword)
{
std::memcpy(roomPassword.get_ptr(), &*password, sizeof(SceNpMatching2SessionPassword));
}
}
else
{
*withPassword = false;
if (withPassword) *withPassword = false;
}
return CELL_OK;
@@ -1477,12 +1586,17 @@ error_code sceNpMatching2GetRoomDataExternalList(
sceNp2.warning("sceNpMatching2GetRoomDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.get_roomdata_external_list(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1493,12 +1607,17 @@ error_code sceNpMatching2CreateJoinRoom(
sceNp2.warning("sceNpMatching2CreateJoinRoom(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
*assignedReqId = nph.create_join_room(ctxId, optParam, reqParam.get_ptr());
const u32 request_id = nph.create_join_room(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@@ -1523,7 +1642,7 @@ error_code sceNpMatching2GetLobbyInfoList(
sceNp2.todo("sceNpMatching2GetLobbyInfoList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1552,7 +1671,7 @@ error_code sceNpMatching2SendLobbyInvitation(
sceNp2.todo("sceNpMatching2SendLobbyInvitation(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
@@ -1562,7 +1681,7 @@ error_code sceNpMatching2SendLobbyInvitation(
error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId)
{
sceNp2.todo("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
sceNp2.warning("sceNpMatching2ContextStop(ctxId=%d)", ctxId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@@ -1596,7 +1715,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal(
sceNp2.todo("sceNpMatching2SetLobbyMemberDataInternal(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam, assignedReqId); res != CELL_OK)
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
{
return res;
}
+659 -19
View File
@@ -6,6 +6,8 @@
#include "sceNp.h"
#include "cellSysutil.h"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/NP/np_handler.h"
#include "Emu/NP/np_contexts.h"
@@ -61,22 +63,65 @@ void fmt_class_string<SceNpCommerce2Error>::format(std::string& out, u64 arg)
STR_CASE(SCE_NP_COMMERCE2_SERVER_ERROR_VOUCHER_ALREADY_CONSUMED);
STR_CASE(SCE_NP_COMMERCE2_SERVER_ERROR_EXCEEDS_AGE_LIMIT_IN_BROWSING);
STR_CASE(SCE_NP_COMMERCE2_SYSTEM_UTIL_ERROR_INVALID_VOUCHER);
STR_CASE(SCE_NP_COMMERCE_ERROR_REQ_BUSY);
}
return unknown;
});
}
void initialize_common_data(SceNpCommerce2CommonData* common_data, vm::ptr<void> data, u32 internal_data_addr, u32 internal_data_size)
{
std::memset(common_data, 0, sizeof(SceNpCommerce2CommonData));
common_data->version = SCE_NP_COMMERCE2_VERSION;
common_data->buf_head = data.addr();
common_data->data = internal_data_addr;
common_data->data_size = internal_data_size;
}
error_code check_and_get_data(vm::ptr<void> /*data*/, u32 data_size, u32& /*internal_data_addr*/, u32& /*internal_data_size*/)
{
if (data_size < 60)
return SCE_NP_COMMERCE2_ERROR_INVALID_RESULT_DATA;
// TODO: check and extract data
if (true)
return SCE_NP_COMMERCE2_ERROR_INVALID_RESULT_DATA;
return not_an_error(60);
}
error_code sceNpCommerce2ExecuteStoreBrowse(s32 targetType, vm::cptr<char> targetId, s32 userdata)
{
sceNpCommerce2.todo("sceNpCommerce2ExecuteStoreBrowse(targetType=%d, targetId=%s, userdata=%d)", targetType, targetId, userdata);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (targetType < SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_CATEGORY || targetType > SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT_CODE)
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_TYPE;
if (targetType < SCE_NP_COMMERCE2_STORE_BROWSE_TYPE_PRODUCT_CODE && (!targetId || !targetId[0]))
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
return CELL_OK;
}
error_code sceNpCommerce2GetStoreBrowseUserdata(vm::ptr<s32> userdata)
{
sceNpCommerce2.todo("sceNpCommerce2GetStoreBrowseUserdata(userdata=*0x%x)", userdata);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!userdata)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
*userdata = 0; // TODO
return CELL_OK;
}
@@ -84,6 +129,16 @@ error_code sceNpCommerce2Init()
{
sceNpCommerce2.warning("sceNpCommerce2Init()");
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_ALREADY_INITIALIZED;
if (!nph.is_NP_init)
return SCE_NP_ERROR_NOT_INITIALIZED;
nph.is_NP_Com2_init = true;
return CELL_OK;
}
@@ -91,6 +146,10 @@ error_code sceNpCommerce2Term()
{
sceNpCommerce2.warning("sceNpCommerce2Term()");
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
nph.is_NP_Com2_init = false;
return CELL_OK;
}
@@ -98,7 +157,24 @@ error_code sceNpCommerce2CreateCtx(u32 version, vm::cptr<SceNpId> npId, vm::ptr<
{
sceNpCommerce2.warning("sceNpCommerce2CreateCtx(version=%d, npId=*0x%x, handler=*0x%x, arg=*0x%x, ctx_id=*0x%x)", version, npId, handler, arg, ctx_id);
*ctx_id = create_commerce2_context(version, npId, handler, arg);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (version > SCE_NP_COMMERCE2_VERSION)
return SCE_NP_COMMERCE2_ERROR_UNSUPPORTED_VERSION;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_CTX_MAX;
if (!npId)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
if (ctx_id)
*ctx_id = create_commerce2_context(version, npId, handler, arg);
else
sceNpCommerce2.warning("sceNpCommerce2CreateCtx: ctx_id is null");
return CELL_OK;
}
@@ -107,18 +183,34 @@ s32 sceNpCommerce2DestroyCtx(u32 ctx_id)
{
sceNpCommerce2.warning("sceNpCommerce2DestroyCtx(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!destroy_commerce2_context(ctx_id))
{
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND; // TODO: verify
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
}
return CELL_OK;
}
s32 sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr<char> target_id)
error_code sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cptr<char> target_id)
{
sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckStart(ctx_id=%d, store_check_type=%d, target_id=*0x%x(%s))", ctx_id, store_check_type, target_id, target_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!target_id || !target_id[0])
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
if (store_check_type != SCE_NP_COMMERCE2_STORE_CHECK_TYPE_CATEGORY)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
@@ -141,25 +233,45 @@ s32 sceNpCommerce2EmptyStoreCheckStart(u32 ctx_id, s32 store_check_type, vm::cpt
error_code sceNpCommerce2EmptyStoreCheckAbort(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2EmptyStoreCheckAbort(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
return CELL_OK;
}
s32 sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr<s32> is_empty)
error_code sceNpCommerce2EmptyStoreCheckFinish(u32 ctx_id, vm::ptr<s32> is_empty)
{
sceNpCommerce2.warning("sceNpCommerce2EmptyStoreCheckFinish(ctx_id=%d, is_empty=*0x%x)", ctx_id, is_empty);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!is_empty)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
*is_empty = SCE_NP_COMMERCE2_STORE_IS_NOT_EMPTY;
return CELL_OK;
}
s32 sceNpCommerce2CreateSessionStart(u32 ctx_id)
error_code sceNpCommerce2CreateSessionStart(u32 ctx_id)
{
sceNpCommerce2.warning("sceNpCommerce2CreateSessionStart(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
{
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND; // TODO: verify
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
}
if (ctx->context_callback)
@@ -177,61 +289,208 @@ s32 sceNpCommerce2CreateSessionStart(u32 ctx_id)
error_code sceNpCommerce2CreateSessionAbort(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2CreateSessionAbort(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
s32 sceNpCommerce2CreateSessionFinish(u32 ctx_id, vm::ptr<SceNpCommerce2SessionInfo> sessionInfo)
{
sceNpCommerce2.warning("sceNpCommerce2CreateSessionFinish(ctx_id=%d, sessionInfo=*0x%x)", ctx_id, sessionInfo);
memset(sessionInfo.get_ptr(), 0, sizeof(sessionInfo));
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
s32 sdk_ver{};
[[maybe_unused]] s32 ret = process_get_sdk_version(process_getpid(), sdk_ver);
if (sdk_ver < 0x300000)
{
std::memset(sessionInfo.get_ptr(), 0, 8); // currencyCode, decimals
}
else
{
std::memset(sessionInfo.get_ptr(), 0, 44); // sizeof(SceNpCommerce2SessionInfo) - reserved
}
// TODO
if (false)
{
sessionInfo->currencyCode[0] = 'U';
sessionInfo->currencyCode[1] = 'S';
sessionInfo->currencyCode[2] = 'D';
sessionInfo->currencyCode[3] = '\0';
sessionInfo->decimals = 2;
sessionInfo->currencySymbol[0] = '$';
sessionInfo->currencySymbol[1] = '\0';
sessionInfo->currencySymbol[2] = '\0';
sessionInfo->currencySymbol[3] = '\0';
sessionInfo->symbolPosition = SCE_NP_COMMERCE2_SYM_POS_PRE;
sessionInfo->symbolWithSpace = false;
sessionInfo->padding1[3];
sessionInfo->thousandSeparator[0] = ',';
sessionInfo->thousandSeparator[1] = '\0';
sessionInfo->thousandSeparator[2] = '\0';
sessionInfo->thousandSeparator[3] = '\0';
sessionInfo->decimalLetter[0] = '.';
sessionInfo->decimalLetter[1] = '\0';
sessionInfo->decimalLetter[2] = '\0';
sessionInfo->decimalLetter[3] = '\0';
}
return CELL_OK;
}
error_code sceNpCommerce2GetCategoryContentsCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
{
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!req_id)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetCategoryContentsStart(u32 req_id, vm::cptr<char> categoryId, u32 startPosition, u32 maxCountOfResults)
{
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsStart(req_id=%d, categoryId=%s, startPosition=%d, maxCountOfResults=%d)", req_id, categoryId, startPosition, maxCountOfResults);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!categoryId || maxCountOfResults > SCE_NP_COMMERCE2_GETCAT_MAX_COUNT)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code get_result(u32 /* req_id */, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
{
if (!buf || !buf_size || ! fill_size)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
// TODO
return CELL_OK;
}
error_code sceNpCommerce2GetCategoryContentsGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
{
sceNpCommerce2.todo("sceNpCommerce2GetCategoryContentsGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
return CELL_OK;
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
return get_result(req_id, buf, buf_size, fill_size);
}
error_code sceNpCommerce2InitGetCategoryContentsResult(vm::ptr<SceNpCommerce2GetCategoryContentsResult> result, vm::ptr<void> data, u32 data_size)
{
sceNpCommerce2.todo("sceNpCommerce2InitGetCategoryContentsResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !data || !data_size)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
u32 internal_data_addr = 0, internal_data_size = 0;
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
if (error < 0)
return error;
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetCategoryContentsResult));
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
return CELL_OK;
}
error_code sceNpCommerce2GetCategoryInfo(vm::cptr<SceNpCommerce2GetCategoryContentsResult> result, vm::ptr<SceNpCommerce2CategoryInfo> categoryInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetCategoryInfo(result=*0x%x, categoryInfo=*0x%x)", result, categoryInfo);
return CELL_OK;
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !categoryInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
// Hack to stop crashes in some games
return SCE_NP_COMMERCE2_ERROR_SERVER_MAINTENANCE;
}
error_code sceNpCommerce2GetContentInfo(vm::cptr<SceNpCommerce2GetCategoryContentsResult> result, u32 index, vm::ptr<SceNpCommerce2ContentInfo> contentInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetContentInfo(result=*0x%x, index=%d, contentInfo=*0x%x)", result, index, contentInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !contentInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetCategoryInfoFromContentInfo(vm::cptr<SceNpCommerce2ContentInfo> contentInfo, vm::ptr<SceNpCommerce2CategoryInfo> categoryInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetCategoryInfoFromContentInfo(contentInfo=*0x%x, categoryInfo=*0x%x)", contentInfo, categoryInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!contentInfo || !categoryInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetGameProductInfoFromContentInfo(vm::cptr<SceNpCommerce2ContentInfo> contentInfo, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfoFromContentInfo(contentInfo=*0x%x, gameProductInfo=*0x%x)", contentInfo, gameProductInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!contentInfo || !gameProductInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
@@ -244,30 +503,87 @@ error_code sceNpCommerce2DestroyGetCategoryContentsResult(vm::ptr<SceNpCommerce2
error_code sceNpCommerce2GetProductInfoCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!req_id)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2GetProductInfoStart(u32 req_id, vm::cptr<char> categoryId, vm::cptr<char> productId)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoStart(req_id=%d, categoryId=%s, productId=%s)", req_id, categoryId, productId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!productId)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetProductInfoGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
return CELL_OK;
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
return get_result(req_id, buf, buf_size, fill_size);
}
error_code sceNpCommerce2InitGetProductInfoResult(vm::ptr<SceNpCommerce2GetProductInfoResult> result, vm::ptr<void> data, u32 data_size)
{
sceNpCommerce2.todo("sceNpCommerce2InitGetProductInfoResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !data || !data_size)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
u32 internal_data_addr = 0, internal_data_size = 0;
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
if (error < 0)
return error;
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetProductInfoResult));
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
return CELL_OK;
}
error_code sceNpCommerce2GetGameProductInfo(vm::cptr<SceNpCommerce2GetProductInfoResult> result, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfo(result=*0x%x, gameProductInfo=*0x%x)", result, gameProductInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !gameProductInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
@@ -280,30 +596,87 @@ error_code sceNpCommerce2DestroyGetProductInfoResult(vm::ptr<SceNpCommerce2GetPr
error_code sceNpCommerce2GetProductInfoListCreateReq(u32 ctx_id, vm::ptr<u32> req_id)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListCreateReq(ctx_id=%d, req_id=*0x%x)", ctx_id, req_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!req_id)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2GetProductInfoListStart(u32 req_id, vm::cptr<char[]> productIds, u32 productNum)
error_code sceNpCommerce2GetProductInfoListStart(u32 req_id, vm::cpptr<char> productIds, u32 productNum)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListStart(req_id=%d, productIds=*0x%x, productNum=%d)", req_id, productIds, productNum);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!productIds || !productNum || productNum > SCE_NP_COMMERCE2_GETPRODLIST_MAX_COUNT)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetProductInfoListGetResult(u32 req_id, vm::ptr<void> buf, u32 buf_size, vm::ptr<u32> fill_size)
{
sceNpCommerce2.todo("sceNpCommerce2GetProductInfoListGetResult(req_id=%d, buf=*0x%x, buf_size=%d, fill_size=*0x%x)", req_id, buf, buf_size, fill_size);
return CELL_OK;
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (buf_size < SCE_NP_COMMERCE2_RECV_BUF_SIZE)
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_BUF_SIZE;
return get_result(req_id, buf, buf_size, fill_size);
}
error_code sceNpCommerce2InitGetProductInfoListResult(vm::ptr<SceNpCommerce2GetProductInfoListResult> result, vm::ptr<void> data, u32 data_size)
{
sceNpCommerce2.todo("sceNpCommerce2InitGetProductInfoListResult(result=*0x%x, data=*0x%x, data_size=%d)", result, data, data_size);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !data || !data_size)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
u32 internal_data_addr = 0, internal_data_size = 0;
error_code error = check_and_get_data(data, data_size, internal_data_addr, internal_data_size);
if (error < 0)
return error;
std::memset(result.get_ptr(), 0, sizeof(SceNpCommerce2GetProductInfoListResult));
initialize_common_data(&result->commonData, data, internal_data_addr, internal_data_size);
return CELL_OK;
}
error_code sceNpCommerce2GetGameProductInfoFromGetProductInfoListResult(vm::cptr<SceNpCommerce2GetProductInfoListResult> result, u32 index, vm::ptr<SceNpCommerce2GameProductInfo> gameProductInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetGameProductInfoFromGetProductInfoListResult(result=*0x%x, index=%d, data=*0x%x)", result, index, gameProductInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!result || !gameProductInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
@@ -316,114 +689,381 @@ error_code sceNpCommerce2DestroyGetProductInfoListResult(vm::ptr<SceNpCommerce2G
error_code sceNpCommerce2GetContentRatingInfoFromGameProductInfo(vm::cptr<SceNpCommerce2GameProductInfo> gameProductInfo, vm::ptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingInfoFromGameProductInfo(gameProductInfo=*0x%x, contentRatingInfo=*0x%x)", gameProductInfo, contentRatingInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!gameProductInfo || !contentRatingInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetContentRatingInfoFromCategoryInfo(vm::cptr<SceNpCommerce2CategoryInfo> categoryInfo, vm::ptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingInfoFromCategoryInfo(categoryInfo=*0x%x, contentRatingInfo=*0x%x)", categoryInfo, contentRatingInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!categoryInfo || !contentRatingInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetContentRatingDescriptor(vm::cptr<SceNpCommerce2ContentRatingInfo> contentRatingInfo, u32 index, vm::ptr<SceNpCommerce2ContentRatingDescriptor> contentRatingDescriptor)
{
sceNpCommerce2.todo("sceNpCommerce2GetContentRatingDescriptor(contentRatingInfo=*0x%x, index=%d, contentRatingDescriptor=*0x%x)", contentRatingInfo, index, contentRatingDescriptor);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!contentRatingInfo || !contentRatingDescriptor)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetGameSkuInfoFromGameProductInfo(vm::cptr<SceNpCommerce2GameProductInfo> gameProductInfo, u32 index, vm::ptr<SceNpCommerce2GameSkuInfo> gameSkuInfo)
{
sceNpCommerce2.todo("sceNpCommerce2GetGameSkuInfoFromGameProductInfo(gameProductInfo=*0x%x, index=%d, gameSkuInfo=*0x%x)", gameProductInfo, index, gameSkuInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!gameProductInfo || !gameSkuInfo)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
return CELL_OK;
}
error_code sceNpCommerce2GetPrice(u32 ctx_id, vm::ptr<char> buf, u32 buflen, u32 price)
{
sceNpCommerce2.todo("sceNpCommerce2GetPrice(ctx_id=%d, buf=*0x%x, buflen=%d, price=%d)", ctx_id, buf, buflen, price);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoCheckoutStartAsync(u32 ctx_id, vm::cptr<char[]> sku_ids, u32 sku_num, u32 container)
error_code sceNpCommerce2DoCheckoutStartAsync(u32 ctx_id, vm::cpptr<char> sku_ids, u32 sku_num, u32 container)
{
sceNpCommerce2.todo("sceNpCommerce2DoCheckoutStartAsync(ctx_id=%d, sku_ids=*0x%x, sku_num=%d, container=%d)", ctx_id, sku_ids, sku_num, container);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (sku_num > SCE_NP_COMMERCE2_SKU_CHECKOUT_MAX)
return SCE_NP_COMMERCE2_ERROR_INVALID_SKU_NUM;
for (u32 i = 0; i < sku_num; i++)
{
if (!sku_ids[i] || !sku_ids[i][0])
return SCE_NP_COMMERCE2_ERROR_INVALID_SKUID;
}
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoCheckoutFinishAsync(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2DoCheckoutFinishAsync(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoProductBrowseStartAsync(u32 ctx_id, vm::cptr<char> product_id, u32 container, vm::cptr<SceNpCommerce2ProductBrowseParam> param)
{
sceNpCommerce2.todo("sceNpCommerce2DoProductBrowseStartAsync(ctx_id=%d, product_id=%s, container=%d, param=*0x%x)", ctx_id, product_id, container, param);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!product_id || !product_id[0])
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
if (param && param->size != sizeof(SceNpCommerce2ProductBrowseParam))
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoProductBrowseFinishAsync(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2DoProductBrowseFinishAsync(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoDlListStartAsync(u32 ctx_id, vm::cptr<char> service_id, vm::cptr<char[]> sku_ids, u32 sku_num, u32 container)
error_code sceNpCommerce2DoDlListStartAsync(u32 ctx_id, vm::cptr<char> service_id, vm::cpptr<char> sku_ids, u32 sku_num, u32 container)
{
sceNpCommerce2.todo("sceNpCommerce2DoDlListStartAsync(ctx_id=%d, service_id=%s, sku_ids=*0x%x, sku_num=%d, container=%d)", ctx_id, service_id, sku_ids, sku_num, container);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (sku_num > SCE_NP_COMMERCE2_SKU_CHECKOUT_MAX)
return SCE_NP_COMMERCE2_ERROR_INVALID_SKU_NUM;
if (sku_num == 0)
{
if (!service_id)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
}
else
{
for (u32 i = 0; i < sku_num; i++)
{
if (!sku_ids[i] || !sku_ids[i][0])
return SCE_NP_COMMERCE2_ERROR_INVALID_SKUID;
}
}
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoDlListFinishAsync(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2DoDlListFinishAsync(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoProductCodeStartAsync(u32 ctx_id, u32 container, vm::cptr<SceNpCommerce2ProductCodeParam> param)
{
sceNpCommerce2.todo("sceNpCommerce2DoProductCodeStartAsync(ctx_id=%d, container=%d, param=*0x%x)", ctx_id, container, param);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!param)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
if (param->size != sizeof(SceNpCommerce2ProductCodeParam))
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
if (param->inputMode != SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_USER_INPUT)
{
if (param->inputMode != SCE_NP_COMMERCE2_PRODUCT_CODE_INPUT_MODE_CODE_SPECIFIED)
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_TYPE;
if (!param->code1[0] || !param->code2[0] || !param->code3[0])
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
for (u32 i = 0; i < SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN; i++)
{
if (!isalnum(param->code1[i]) || !isalnum(param->code2[i]) || !isalnum(param->code3[i]))
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
}
}
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoProductCodeFinishAsync(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2DoProductCodeFinishAsync(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2GetBGDLAvailability(vm::ptr<b8> bgdlAvailability)
{
sceNpCommerce2.todo("sceNpCommerce2GetBGDLAvailability(bgdlAvailability=*0x%x)", bgdlAvailability);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!bgdlAvailability)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
*bgdlAvailability = false; // TODO
return CELL_OK;
}
error_code sceNpCommerce2SetBGDLAvailability(b8 bgdlAvailability)
{
sceNpCommerce2.todo("sceNpCommerce2SetBGDLAvailability(bgdlAvailability=%d)", bgdlAvailability);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
return CELL_OK;
}
error_code sceNpCommerce2AbortReq(u32 req_id)
{
sceNpCommerce2.todo("sceNpCommerce2AbortReq(req_id=%d)", req_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
return CELL_OK;
}
error_code sceNpCommerce2DestroyReq(u32 req_id)
{
sceNpCommerce2.todo("sceNpCommerce2DestroyReq(req_id=%d)", req_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
return CELL_OK;
}
error_code sceNpCommerce2DoServiceListStartAsync()
error_code sceNpCommerce2DoServiceListStartAsync(u32 ctx_id, vm::ptr<char> serviceId, u32 container, vm::ptr<SceNpCommerce2ProductBrowseParam> param)
{
sceNpCommerce2.todo("sceNpCommerce2DoServiceListStartAsync()");
sceNpCommerce2.todo("sceNpCommerce2DoServiceListStartAsync(ctx_id=%d, serviceId=*0x%x, container=%d, param=*0x%x)", ctx_id, serviceId, container, param);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
if (!param || !serviceId)
return SCE_NP_COMMERCE2_ERROR_INVALID_ARGUMENT;
if (param->size != sizeof(SceNpCommerce2ProductBrowseParam))
return SCE_NP_COMMERCE2_ERROR_INVALID_SIZE;
if (!serviceId[0])
return SCE_NP_COMMERCE2_ERROR_INVALID_TARGET_ID;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INVALID_MEMORY_CONTAINER;
if (false) // TODO
return SCE_NP_COMMERCE2_ERROR_INSUFFICIENT_MEMORY_CONTAINER;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
error_code sceNpCommerce2DoServiceListFinishAsync()
error_code sceNpCommerce2DoServiceListFinishAsync(u32 ctx_id)
{
sceNpCommerce2.todo("sceNpCommerce2DoServiceListFinishAsync()");
sceNpCommerce2.todo("sceNpCommerce2DoServiceListFinishAsync(ctx_id=%d)", ctx_id);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_Com2_init)
return SCE_NP_COMMERCE2_ERROR_NOT_INITIALIZED;
const auto ctx = get_commerce2_context(ctx_id);
if (!ctx)
return SCE_NP_COMMERCE2_ERROR_CTX_NOT_FOUND;
return CELL_OK;
}
+2 -2
View File
@@ -48,7 +48,6 @@ enum SceNpCommerce2Error
SCE_NP_COMMERCE2_SERVER_ERROR_VOUCHER_ALREADY_CONSUMED = 0x80023130,
SCE_NP_COMMERCE2_SERVER_ERROR_EXCEEDS_AGE_LIMIT_IN_BROWSING = 0x80023139,
SCE_NP_COMMERCE2_SYSTEM_UTIL_ERROR_INVALID_VOUCHER = 0x80024002,
SCE_NP_COMMERCE_ERROR_REQ_BUSY = 0x8002940d,
};
// Event types
@@ -266,6 +265,7 @@ struct SceNpCommerce2GameProductInfo
struct SceNpCommerce2GetProductInfoListResult
{
SceNpCommerce2CommonData commonData;
be_t<u32> countOfProductInfo;
};
// Structure for rating information
@@ -317,7 +317,7 @@ struct SceNpCommerce2ProductBrowseParam
struct SceNpCommerce2ProductCodeParam
{
be_t<u32> size;
be_t<u32> inputMode; // Unsigned ints go into be_t<u32>, right?
be_t<u32> inputMode;
s8 code1[SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN + 1];
s8 padding1[3];
s8 code2[SCE_NP_COMMERCE2_PRODUCT_CODE_BLOCK_LEN + 1];
+1 -1
View File
@@ -8,7 +8,7 @@ error_code sceNpManagerIsSP()
{
sceNpPlus.warning("sceNpManagerIsSP()");
// TODO seems to be cut to 1 byte by pshome likely a bool but may be more.
return CELL_OK;
return not_an_error(1);
}
DECLARE(ppu_module_manager::sceNpPlus)("sceNpPlus", []()
+326 -178
View File
@@ -288,33 +288,44 @@ error_code sceNpTusPollAsync(s32 transId, vm::ptr<s32> result)
template<typename T>
error_code scenp_tus_set_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::cptr<s64> variableArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !slotIdArray || !variableArray || arrayNum <= 0)
if (!slotIdArray || !variableArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
if (!is_slot_array_valid(slotIdArray, arrayNum))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -359,29 +370,12 @@ error_code sceNpTusSetMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
template<typename T>
error_code scenp_tus_get_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !slotIdArray || !variableArray || arrayNum <= 0)
if (!slotIdArray || !variableArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
@@ -391,6 +385,34 @@ error_code scenp_tus_get_multislot_variable(s32 transId, T targetNpId, vm::cptr<
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (!is_slot_array_valid(slotIdArray, arrayNum))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -435,6 +457,31 @@ error_code sceNpTusGetMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
template<typename T>
error_code scenp_tus_get_multiuser_variable(s32 transId, T targetNpIdArray, SceNpTusSlotId slotId, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
if (!variableArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_SCORE_MAX_NPID_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -442,29 +489,15 @@ error_code scenp_tus_get_multiuser_variable(s32 transId, T targetNpIdArray, SceN
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpIdArray || !variableArray || arrayNum <= 0)
if (!targetNpIdArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (option || slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
@@ -516,6 +549,43 @@ error_code sceNpTusGetMultiUserVariableVUserAsync(s32 transId, vm::cptr<SceNpTus
error_code scenp_tus_get_friends_variable(s32 transId, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr<SceNpTusVariable> variableArray, u32 variableArraySize, s32 arrayNum, vm::ptr<void> option, bool async)
{
if (!variableArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
// Undocumented behaviour and structure unknown
// Also checks a u32* at offset 4 of the struct for nullptr in which case it behaves like option == nullptr
if (option && *static_cast<u32*>(option.get_ptr()) != 0xC)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (sortType < SCE_NP_TUS_VARIABLE_SORTTYPE_DESCENDING_DATE || sortType > SCE_NP_TUS_VARIABLE_SORTTYPE_ASCENDING_VALUE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_SCORE_MAX_SELECTED_FRIENDS_NUM)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -523,31 +593,12 @@ error_code scenp_tus_get_friends_variable(s32 transId, SceNpTusSlotId slotId, s3
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!variableArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (option || arrayNum <= 0 || slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (variableArraySize != arrayNum * sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -587,9 +638,14 @@ error_code scenp_tus_add_and_get_variable(s32 transId, T targetNpId, SceNpTusSlo
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
if (slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if ((option && option->size != sizeof(SceNpTusAddAndGetVariableOptParam)) || outVariableSize != sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (!targetNpId)
@@ -597,14 +653,10 @@ error_code scenp_tus_add_and_get_variable(s32 transId, T targetNpId, SceNpTusSlo
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (slotId < 0)
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (outVariableSize != sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
@@ -658,12 +710,7 @@ error_code scenp_tus_try_and_set_variable(s32 transId, T targetNpId, SceNpTusSlo
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !resultVariable)
if (!resultVariable)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
@@ -673,11 +720,22 @@ error_code scenp_tus_try_and_set_variable(s32 transId, T targetNpId, SceNpTusSlo
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (resultVariableSize != sizeof(SceNpTusVariable))
if ((option && option->size != sizeof(SceNpTusTryAndSetVariableOptParam)) || resultVariableSize != sizeof(SceNpTusVariable))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -722,33 +780,39 @@ error_code sceNpTusTryAndSetVariableVUserAsync(s32 transId, vm::cptr<SceNpTusVir
template<typename T>
error_code scenp_tus_delete_multislot_variable(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !slotIdArray || arrayNum <= 0)
if (!slotIdArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
if (!is_slot_array_valid(slotIdArray, arrayNum))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -793,6 +857,16 @@ error_code sceNpTusDeleteMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNp
template<typename T>
error_code scenp_tus_set_data(s32 transId, T targetNpId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u32 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option, bool vuser, bool async)
{
if (slotId < 0 || !data || !totalSize)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if ((option && option->size != sizeof(SceNpTusSetDataOptParam)) || (info && infoStructSize != sizeof(SceNpTusDataInfo)))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -800,19 +874,15 @@ error_code scenp_tus_set_data(s32 transId, T targetNpId, SceNpTusSlotId slotId,
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !data || totalSize == 0)
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (slotId < 0)
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
@@ -859,18 +929,6 @@ error_code sceNpTusSetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId
template<typename T>
error_code scenp_tus_get_data(s32 transId, T targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option, bool vuser, bool async)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
@@ -881,6 +939,24 @@ error_code scenp_tus_get_data(s32 transId, T targetNpId, SceNpTusSlotId slotId,
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (dataStatusSize != sizeof(SceNpTusDataStatus))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -925,6 +1001,31 @@ error_code sceNpTusGetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId
template<typename T>
error_code scenp_tus_get_multislot_data_status(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
if (!slotIdArray || !statusArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (!is_slot_array_valid(slotIdArray, arrayNum))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -932,24 +1033,15 @@ error_code scenp_tus_get_multislot_data_status(s32 transId, T targetNpId, vm::cp
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !statusArray || arrayNum <= 0)
if (!targetNpId)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SLOT_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
@@ -996,6 +1088,31 @@ error_code sceNpTusGetMultiSlotDataStatusVUserAsync(s32 transId, vm::cptr<SceNpT
template<typename T>
error_code scenp_tus_get_multiuser_data_status(s32 transId, T targetNpIdArray, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
if (!statusArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_SCORE_MAX_NPID_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -1003,24 +1120,15 @@ error_code scenp_tus_get_multiuser_data_status(s32 transId, T targetNpIdArray, S
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpIdArray || !statusArray || arrayNum <= 0)
if (!targetNpIdArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (option || slotId < 0)
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_USER_NUM_PER_TRANS)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
@@ -1072,6 +1180,43 @@ error_code sceNpTusGetMultiUserDataStatusVUserAsync(s32 transId, vm::cptr<SceNpT
error_code scenp_tus_get_friends_data_status(s32 transId, SceNpTusSlotId slotId, s32 includeSelf, s32 sortType, vm::ptr<SceNpTusDataStatus> statusArray, u32 statusArraySize, s32 arrayNum, vm::ptr<void> option, bool async)
{
if (!statusArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (arrayNum == 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
// Undocumented behaviour and structure unknown
// Also checks a u32* at offset 4 of the struct for nullptr in which case it behaves like option == nullptr
if (option && *static_cast<u32*>(option.get_ptr()) != 0xC)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (sortType != SCE_NP_TUS_DATASTATUS_SORTTYPE_DESCENDING_DATE && sortType != SCE_NP_TUS_DATASTATUS_SORTTYPE_ASCENDING_DATE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_SCORE_MAX_SELECTED_FRIENDS_NUM)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
if (statusArraySize != arrayNum * sizeof(SceNpTusDataStatus))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ALIGNMENT;
}
if (slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
@@ -1079,26 +1224,12 @@ error_code scenp_tus_get_friends_data_status(s32 transId, SceNpTusSlotId slotId,
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!statusArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (option || arrayNum < 0 || slotId < 0)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
if (arrayNum > SCE_NP_TUS_MAX_SELECTED_FRIENDS_NUM)
{
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_NPID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -1131,24 +1262,12 @@ error_code sceNpTusGetFriendsDataStatusAsync(s32 transId, SceNpTusSlotId slotId,
template<typename T>
error_code scenp_tus_delete_multislot_data(s32 transId, T targetNpId, vm::cptr<SceNpTusSlotId> slotIdArray, s32 arrayNum, vm::ptr<void> option, bool vuser, bool async)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
if (!targetNpId || !slotIdArray || arrayNum < 0)
if (!slotIdArray)
{
return SCE_NP_COMMUNITY_ERROR_INSUFFICIENT_ARGUMENT;
}
if (option || !is_slot_array_valid(slotIdArray, arrayNum))
if (arrayNum == 0 || option)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
@@ -1158,6 +1277,24 @@ error_code scenp_tus_delete_multislot_data(s32 transId, T targetNpId, vm::cptr<S
return SCE_NP_COMMUNITY_ERROR_TOO_MANY_SLOTID;
}
if (!is_slot_array_valid(slotIdArray, arrayNum))
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ARGUMENT;
}
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_TUS_init)
{
return SCE_NP_COMMUNITY_ERROR_NOT_INITIALIZED;
}
// Probable vsh behaviour
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ONLINE_ID;
}
auto trans_ctx = idm::get<tus_transaction_ctx>(transId);
if (!trans_ctx)
@@ -1200,9 +1337,16 @@ error_code sceNpTusDeleteMultiSlotDataVUserAsync(s32 transId, vm::cptr<SceNpTusV
return scenp_tus_delete_multislot_data(transId, targetVirtualUserId, slotIdArray, arrayNum, option, true, true);
}
void scenp_tss_no_file(const std::shared_ptr<tus_transaction_ctx>& trans, vm::ptr<SceNpTssDataStatus> dataStatus)
{
// TSS are files stored on PSN by developers, no dumps available atm
std::memset(dataStatus.get_ptr(), 0, sizeof(SceNpTssDataStatus));
trans->result = not_an_error(0);
}
error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
{
sceNpTus.todo("sceNpTssGetData(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
sceNpTus.warning("sceNpTssGetData(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@@ -1228,12 +1372,14 @@ error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssD
return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
}
scenp_tss_no_file(trans_ctx, dataStatus);
return CELL_OK;
}
error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
{
sceNpTus.todo("sceNpTssGetDataAsync(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
sceNpTus.warning("sceNpTssGetDataAsync(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@@ -1259,6 +1405,8 @@ error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceN
return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
}
scenp_tss_no_file(trans_ctx, dataStatus);
return CELL_OK;
}
+9 -3
View File
@@ -279,6 +279,7 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n
&ppu_module_manager::sceNpClans,
&ppu_module_manager::sceNpCommerce2,
&ppu_module_manager::sceNpMatchingInt,
&ppu_module_manager::sceNpPlus,
&ppu_module_manager::sceNpSns,
&ppu_module_manager::sceNpTrophy,
&ppu_module_manager::sceNpTus,
@@ -1339,6 +1340,11 @@ static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment&
// Try to patch each segment, will only succeed if the address exists in SPU local storage
for (const auto& prog : obj.progs)
{
if (Emu.DeserialManager())
{
break;
}
// Apply the patch
applied += g_fxo->get<patch_engine>().apply(hash, [&](u32 addr, u32 /*size*/) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
@@ -1807,7 +1813,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
std::basic_string<u32> applied;
for (usz i = 0; i < prx->segs.size(); i++)
for (usz i = Emu.DeserialManager() ? prx->segs.size() : 0; i < prx->segs.size(); i++)
{
const auto& seg = prx->segs[i];
@@ -2856,9 +2862,9 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
}
// Apply the patch
auto applied = g_fxo->get<patch_engine>().apply(hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
auto applied = g_fxo->get<patch_engine>().apply(!Emu.DeserialManager() ? std::string{} : hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
if (!Emu.GetTitleID().empty())
if (!Emu.DeserialManager() && !Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr, u32 size) { return ovlm->get_ptr<u8>(addr, size); });
+21 -4
View File
@@ -3798,20 +3798,37 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
{
mself_record rec{};
std::set<u64> offs;
if (mself.read(rec) && rec.get_pos(mself.size()))
{
std::string name = rec.name;
if (rec.size <= 0x20)
{
continue;
}
if (!offs.emplace(rec.off).second)
{
// Duplicate
continue;
}
// Read characters safely
std::string name(sizeof(rec.name), '\0');
std::memcpy(name.data(), rec.name, name.size());
name = std::string(name.c_str());
upper = fmt::to_upper(name);
if (upper.ends_with(".SPRX"))
if (upper.find(".SPRX") != umax || upper.find(".PRX") != umax)
{
// .sprx inside .mself found
file_queue.emplace_back(dir_queue[i] + entry.name, rec.off, rec.size);
continue;
}
if (upper.ends_with(".SELF"))
if (upper.find(".SELF") != umax || upper.find(".ELF") != umax)
{
// .self inside .mself found
file_queue.emplace_back(dir_queue[i] + entry.name, rec.off, rec.size);
@@ -3881,7 +3898,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
if (u64 off = offset)
{
// Adjust offset for MSELF
src = make_file_view(std::move(src), offset);
src = make_file_view(std::move(src), offset, file_size);
// Adjust path for MSELF too
fmt::append(path, "_x%x", off);
+1
View File
@@ -276,6 +276,7 @@ public:
const u32 stack_addr; // Stack address
atomic_t<ppu_join_status> joiner; // Joining thread or status
u32 hw_sleep_time = 0; // Very specific delay for hardware threads switching, see lv2_obj::awake_unlocked for more details
lf_fifo<atomic_t<cmd64>, 127> cmd_queue; // Command queue for asynchronous operations.
+108 -5
View File
@@ -2756,16 +2756,77 @@ void spu_recompiler::FSMB(spu_opcode_t op)
void spu_recompiler::FREST(spu_opcode_t op)
{
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
c->rcpps(va, va);
c->movaps(SPU_OFF_128(gpr, op.rt), va);
const XmmLink& v_fraction = XmmAlloc();
const XmmLink& v_exponent = XmmAlloc();
const XmmLink& v_sign = XmmAlloc();
c->movdqa(v_fraction, va);
c->movdqa(v_exponent, va);
c->movdqa(v_sign, va);
c->psrld(v_fraction, 18);
c->psrld(v_exponent, 23);
c->andps(v_fraction, XmmConst(v128::from32p(0x1F)));
c->andps(v_exponent, XmmConst(v128::from32p(0xFF)));
c->andps(v_sign, XmmConst(v128::from32p(0x80000000)));
const u64 fraction_lut_addr = reinterpret_cast<u64>(spu_frest_fraction_lut);
const u64 exponent_lut_addr = reinterpret_cast<u64>(spu_frest_exponent_lut);
for (u32 index = 0; index < 4; index++)
{
c->pextrd(*qw0, v_fraction, index);
c->mov(*qw1, asmjit::x86::dword_ptr(fraction_lut_addr, *qw0, 2));
c->pinsrd(v_fraction, *qw1, index);
c->pextrd(*qw0, v_exponent, index);
c->mov(*qw1, asmjit::x86::dword_ptr(exponent_lut_addr, *qw0, 2));
c->pinsrd(v_exponent, *qw1, index);
}
// AVX2(not working?)
// c->mov(qw1->r64(),spu_frest_fraction_lut);
// c->vpgatherdd(v_fraction, asmjit::x86::dword_ptr(*qw1));
// c->mov(qw0->r64(),spu_frest_exponent_lut);
// c->vpgatherdd(v_exponent, asmjit::x86::dword_ptr(*qw0));
c->orps(v_fraction, v_exponent);
c->orps(v_sign, v_fraction);
c->movaps(SPU_OFF_128(gpr, op.rt), v_sign);
}
void spu_recompiler::FRSQEST(spu_opcode_t op)
{
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
c->andps(va, XmmConst(v128::from32p(0x7fffffff))); // abs
c->rsqrtps(va, va);
c->movaps(SPU_OFF_128(gpr, op.rt), va);
const XmmLink& v_fraction = XmmAlloc();
const XmmLink& v_exponent = XmmAlloc();
c->movdqa(v_fraction, va);
c->movdqa(v_exponent, va);
c->psrld(v_fraction, 18);
c->psrld(v_exponent, 23);
c->andps(v_fraction, XmmConst(v128::from32p(0x3F)));
c->andps(v_exponent, XmmConst(v128::from32p(0xFF)));
const u64 fraction_lut_addr = reinterpret_cast<u64>(spu_frsqest_fraction_lut);
const u64 exponent_lut_addr = reinterpret_cast<u64>(spu_frsqest_exponent_lut);
for (u32 index = 0; index < 4; index++)
{
c->pextrd(*qw0, v_fraction, index);
c->mov(*qw1, asmjit::x86::dword_ptr(fraction_lut_addr, *qw0, 2));
c->pinsrd(v_fraction, *qw1, index);
c->pextrd(*qw0, v_exponent, index);
c->mov(*qw1, asmjit::x86::dword_ptr(exponent_lut_addr, *qw0, 2));
c->pinsrd(v_exponent, *qw1, index);
}
c->orps(v_fraction, v_exponent);
c->movaps(SPU_OFF_128(gpr, op.rt), v_fraction);
}
void spu_recompiler::LQX(spu_opcode_t op)
@@ -3922,7 +3983,49 @@ void spu_recompiler::CEQB(spu_opcode_t op)
void spu_recompiler::FI(spu_opcode_t op)
{
// Floating Interpolate
const XmmLink& va = XmmGet(op.ra, XmmType::Float);
const XmmLink& vb = XmmGet(op.rb, XmmType::Float);
const XmmLink& vb_base = XmmAlloc();
const XmmLink& ymul = XmmAlloc();
const XmmLink& temp_reg = XmmAlloc();
const XmmLink& temp_reg2 = XmmAlloc();
c->movdqa(vb_base, vb);
c->movdqa(ymul, vb);
c->movdqa(temp_reg, va);
c->pand(vb_base, XmmConst(v128::from32p(0x007ffc00u)));
c->pslld(vb_base, 9);
c->pand(temp_reg, XmmConst(v128::from32p(0x7ffff))); // va_fraction
c->pand(ymul, XmmConst(v128::from32p(0x3ff)));
c->pmulld(ymul, temp_reg);
c->movdqa(temp_reg, vb_base);
c->psubd(temp_reg, ymul);
// Makes signed comparison unsigned and determines if we need to adjust exponent
auto xor_const = XmmConst(v128::from32p(0x80000000));
c->pxor(ymul, xor_const);
c->pxor(vb_base, xor_const);
c->pcmpgtd(ymul, vb_base);
c->movdqa(vb_base, ymul);
c->movdqa(temp_reg2, temp_reg);
c->pand(temp_reg2, vb_base);
c->psrld(temp_reg2, 8); // only shift right by 8 if exponent is adjusted
c->xorps(vb_base, XmmConst(v128::from32p(0xFFFFFFFF))); // Invert the mask
c->pand(temp_reg, vb_base);
c->psrld(temp_reg, 9); // shift right by 9 if not adjusted
c->por(temp_reg, temp_reg2);
c->pand(vb, XmmConst(v128::from32p(0xff800000u)));
c->pand(temp_reg, XmmConst(v128::from32p(~0xff800000u)));
c->por(vb, temp_reg);
c->pand(ymul, XmmConst(v128::from32p(1 << 23)));
c->psubd(vb, ymul);
c->movaps(SPU_OFF_128(gpr, op.rt), vb);
}
File diff suppressed because it is too large Load Diff
+36 -3
View File
@@ -689,15 +689,48 @@ bool FSMB(spu_thread& spu, spu_opcode_t op)
template <spu_exec_bit... Flags>
bool FREST(spu_thread& spu, spu_opcode_t op)
{
spu.gpr[op.rt] = _mm_rcp_ps(spu.gpr[op.ra]);
v128 fraction_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 18)) & v128(_mm_set1_epi32(0x1F));
v128 exponent_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 23)) & v128(_mm_set1_epi32(0xFF));
v128 sign = spu.gpr[op.ra] & _mm_set1_epi32(0x80000000);
// AVX2
// v128 fraction = _mm_i32gather_epi32(spu_frest_fraction_lut, fraction_index, 4);
// v128 exponent = _mm_i32gather_epi32(spu_frest_exponent_lut, exponent_index, 4);
v128 result;
for (u32 index = 0; index < 4; index++)
{
u32 r = spu_frest_fraction_lut[fraction_index._u32[index]];
r |= spu_frest_exponent_lut[exponent_index._u32[index]];
r |= sign._u32[index];
result._u32[index] = r;
}
spu.gpr[op.rt] = result;
return true;
}
template <spu_exec_bit... Flags>
bool FRSQEST(spu_thread& spu, spu_opcode_t op)
{
const auto mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff));
spu.gpr[op.rt] = _mm_rsqrt_ps(_mm_and_ps(spu.gpr[op.ra], mask));
v128 fraction_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 18)) & v128(_mm_set1_epi32(0x3F));
v128 exponent_index = v128(_mm_srli_epi32(spu.gpr[op.ra], 23)) & v128(_mm_set1_epi32(0xFF));
// AVX2
// v128 fraction = _mm_i32gather_epi32(spu_frsqest_fraction_lut, fraction_index, 4);
// v128 exponent = _mm_i32gather_epi32(spu_frsqest_exponent_lut, exponent_index, 4);
v128 result;
for (u32 index = 0; index < 4; index++)
{
u32 r = spu_frsqest_fraction_lut[fraction_index._u32[index]];
r |= spu_frsqest_exponent_lut[exponent_index._u32[index]];
result._u32[index] = r;
}
spu.gpr[op.rt] = result;
return true;
}
File diff suppressed because it is too large Load Diff
+60 -2
View File
@@ -46,6 +46,64 @@
#endif
#endif
// LUTs for SPU instructions
const u32 spu_frest_fraction_lut[32] =
{
0x7FFBE0, 0x7F87A6, 0x70EF72, 0x708B40, 0x638B12, 0x633AEA, 0x5792C4, 0x574AA0,
0x4CCA7E, 0x4C9262, 0x430A44, 0x42D62A, 0x3A2E12, 0x39FDFA, 0x3215E4, 0x31F1D2,
0x2AA9BE, 0x2A85AC, 0x23D59A, 0x23BD8E, 0x1D8576, 0x1D8576, 0x17AD5A, 0x17AD5A,
0x124543, 0x124543, 0x0D392D, 0x0D392D, 0x08851A, 0x08851A, 0x041D07, 0x041D07
};
const u32 spu_frest_exponent_lut[256] =
{
0x7F800000, 0x7E000000, 0x7D800000, 0x7D000000, 0x7C800000, 0x7C000000, 0x7B800000, 0x7B000000, 0x7A800000, 0x7A000000, 0x79800000, 0x79000000, 0x78800000, 0x78000000, 0x77800000, 0x77000000,
0x76800000, 0x76000000, 0x75800000, 0x75000000, 0x74800000, 0x74000000, 0x73800000, 0x73000000, 0x72800000, 0x72000000, 0x71800000, 0x71000000, 0x70800000, 0x70000000, 0x6F800000, 0x6F000000,
0x6E800000, 0x6E000000, 0x6D800000, 0x6D000000, 0x6C800000, 0x6C000000, 0x6B800000, 0x6B000000, 0x6A800000, 0x6A000000, 0x69800000, 0x69000000, 0x68800000, 0x68000000, 0x67800000, 0x67000000,
0x66800000, 0x66000000, 0x65800000, 0x65000000, 0x64800000, 0x64000000, 0x63800000, 0x63000000, 0x62800000, 0x62000000, 0x61800000, 0x61000000, 0x60800000, 0x60000000, 0x5F800000, 0x5F000000,
0x5E800000, 0x5E000000, 0x5D800000, 0x5D000000, 0x5C800000, 0x5C000000, 0x5B800000, 0x5B000000, 0x5A800000, 0x5A000000, 0x59800000, 0x59000000, 0x58800000, 0x58000000, 0x57800000, 0x57000000,
0x56800000, 0x56000000, 0x55800000, 0x55000000, 0x54800000, 0x54000000, 0x53800000, 0x53000000, 0x52800000, 0x52000000, 0x51800000, 0x51000000, 0x50800000, 0x50000000, 0x4F800000, 0x4F000000,
0x4E800000, 0x4E000000, 0x4D800000, 0x4D000000, 0x4C800000, 0x4C000000, 0x4B800000, 0x4B000000, 0x4A800000, 0x4A000000, 0x49800000, 0x49000000, 0x48800000, 0x48000000, 0x47800000, 0x47000000,
0x46800000, 0x46000000, 0x45800000, 0x45000000, 0x44800000, 0x44000000, 0x43800000, 0x43000000, 0x42800000, 0x42000000, 0x41800000, 0x41000000, 0x40800000, 0x40000000, 0x3F800000, 0x3F000000,
0x3E800000, 0x3E000000, 0x3D800000, 0x3D000000, 0x3C800000, 0x3C000000, 0x3B800000, 0x3B000000, 0x3A800000, 0x3A000000, 0x39800000, 0x39000000, 0x38800000, 0x38000000, 0x37800000, 0x37000000,
0x36800000, 0x36000000, 0x35800000, 0x35000000, 0x34800000, 0x34000000, 0x33800000, 0x33000000, 0x32800000, 0x32000000, 0x31800000, 0x31000000, 0x30800000, 0x30000000, 0x2F800000, 0x2F000000,
0x2E800000, 0x2E000000, 0x2D800000, 0x2D000000, 0x2C800000, 0x2C000000, 0x2B800000, 0x2B000000, 0x2A800000, 0x2A000000, 0x29800000, 0x29000000, 0x28800000, 0x28000000, 0x27800000, 0x27000000,
0x26800000, 0x26000000, 0x25800000, 0x25000000, 0x24800000, 0x24000000, 0x23800000, 0x23000000, 0x22800000, 0x22000000, 0x21800000, 0x21000000, 0x20800000, 0x20000000, 0x1F800000, 0x1F000000,
0x1E800000, 0x1E000000, 0x1D800000, 0x1D000000, 0x1C800000, 0x1C000000, 0x1B800000, 0x1B000000, 0x1A800000, 0x1A000000, 0x19800000, 0x19000000, 0x18800000, 0x18000000, 0x17800000, 0x17000000,
0x16800000, 0x16000000, 0x15800000, 0x15000000, 0x14800000, 0x14000000, 0x13800000, 0x13000000, 0x12800000, 0x12000000, 0x11800000, 0x11000000, 0x10800000, 0x10000000, 0x0F800000, 0x0F000000,
0x0E800000, 0x0E000000, 0x0D800000, 0x0D000000, 0x0C800000, 0x0C000000, 0x0B800000, 0x0B000000, 0x0A800000, 0x0A000000, 0x09800000, 0x09000000, 0x08800000, 0x08000000, 0x07800000, 0x07000000,
0x06800000, 0x06000000, 0x05800000, 0x05000000, 0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000, 0x02000000, 0x01800000, 0x01000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000
};
const u32 spu_frsqest_fraction_lut[64] =
{
0x350160, 0x34E954, 0x2F993D, 0x2F993D, 0x2AA523, 0x2AA523, 0x26190D, 0x26190D, 0x21E4F9, 0x21E4F9, 0x1E00E9, 0x1E00E9, 0x1A5CD9, 0x1A5CD9, 0x16F8CB, 0x16F8CB,
0x13CCC0, 0x13CCC0, 0x10CCB3, 0x10CCB3, 0x0E00AA, 0x0E00AA, 0x0B58A1, 0x0B58A1, 0x08D498, 0x08D498, 0x067491, 0x067491, 0x043089, 0x043089, 0x020C83, 0x020C83,
0x7FFDF4, 0x7FD1DE, 0x7859C8, 0x783DBA, 0x71559C, 0x71559C, 0x6AE57C, 0x6AE57C, 0x64F561, 0x64F561, 0x5F7149, 0x5F7149, 0x5A4D33, 0x5A4D33, 0x55811F, 0x55811F,
0x51050F, 0x51050F, 0x4CC8FE, 0x4CC8FE, 0x48D0F0, 0x48D0F0, 0x4510E4, 0x4510E4, 0x4180D7, 0x4180D7, 0x3E24CC, 0x3E24CC, 0x3AF4C3, 0x3AF4C3, 0x37E8BA, 0x37E8BA
};
const u32 spu_frsqest_exponent_lut[256] =
{
0x7F800000, 0x5E800000, 0x5E800000, 0x5E000000, 0x5E000000, 0x5D800000, 0x5D800000, 0x5D000000, 0x5D000000, 0x5C800000, 0x5C800000, 0x5C000000, 0x5C000000, 0x5B800000, 0x5B800000, 0x5B000000,
0x5B000000, 0x5A800000, 0x5A800000, 0x5A000000, 0x5A000000, 0x59800000, 0x59800000, 0x59000000, 0x59000000, 0x58800000, 0x58800000, 0x58000000, 0x58000000, 0x57800000, 0x57800000, 0x57000000,
0x57000000, 0x56800000, 0x56800000, 0x56000000, 0x56000000, 0x55800000, 0x55800000, 0x55000000, 0x55000000, 0x54800000, 0x54800000, 0x54000000, 0x54000000, 0x53800000, 0x53800000, 0x53000000,
0x53000000, 0x52800000, 0x52800000, 0x52000000, 0x52000000, 0x51800000, 0x51800000, 0x51000000, 0x51000000, 0x50800000, 0x50800000, 0x50000000, 0x50000000, 0x4F800000, 0x4F800000, 0x4F000000,
0x4F000000, 0x4E800000, 0x4E800000, 0x4E000000, 0x4E000000, 0x4D800000, 0x4D800000, 0x4D000000, 0x4D000000, 0x4C800000, 0x4C800000, 0x4C000000, 0x4C000000, 0x4B800000, 0x4B800000, 0x4B000000,
0x4B000000, 0x4A800000, 0x4A800000, 0x4A000000, 0x4A000000, 0x49800000, 0x49800000, 0x49000000, 0x49000000, 0x48800000, 0x48800000, 0x48000000, 0x48000000, 0x47800000, 0x47800000, 0x47000000,
0x47000000, 0x46800000, 0x46800000, 0x46000000, 0x46000000, 0x45800000, 0x45800000, 0x45000000, 0x45000000, 0x44800000, 0x44800000, 0x44000000, 0x44000000, 0x43800000, 0x43800000, 0x43000000,
0x43000000, 0x42800000, 0x42800000, 0x42000000, 0x42000000, 0x41800000, 0x41800000, 0x41000000, 0x41000000, 0x40800000, 0x40800000, 0x40000000, 0x40000000, 0x3F800000, 0x3F800000, 0x3F000000,
0x3F000000, 0x3E800000, 0x3E800000, 0x3E000000, 0x3E000000, 0x3D800000, 0x3D800000, 0x3D000000, 0x3D000000, 0x3C800000, 0x3C800000, 0x3C000000, 0x3C000000, 0x3B800000, 0x3B800000, 0x3B000000,
0x3B000000, 0x3A800000, 0x3A800000, 0x3A000000, 0x3A000000, 0x39800000, 0x39800000, 0x39000000, 0x39000000, 0x38800000, 0x38800000, 0x38000000, 0x38000000, 0x37800000, 0x37800000, 0x37000000,
0x37000000, 0x36800000, 0x36800000, 0x36000000, 0x36000000, 0x35800000, 0x35800000, 0x35000000, 0x35000000, 0x34800000, 0x34800000, 0x34000000, 0x34000000, 0x33800000, 0x33800000, 0x33000000,
0x33000000, 0x32800000, 0x32800000, 0x32000000, 0x32000000, 0x31800000, 0x31800000, 0x31000000, 0x31000000, 0x30800000, 0x30800000, 0x30000000, 0x30000000, 0x2F800000, 0x2F800000, 0x2F000000,
0x2F000000, 0x2E800000, 0x2E800000, 0x2E000000, 0x2E000000, 0x2D800000, 0x2D800000, 0x2D000000, 0x2D000000, 0x2C800000, 0x2C800000, 0x2C000000, 0x2C000000, 0x2B800000, 0x2B800000, 0x2B000000,
0x2B000000, 0x2A800000, 0x2A800000, 0x2A000000, 0x2A000000, 0x29800000, 0x29800000, 0x29000000, 0x29000000, 0x28800000, 0x28800000, 0x28000000, 0x28000000, 0x27800000, 0x27800000, 0x27000000,
0x27000000, 0x26800000, 0x26800000, 0x26000000, 0x26000000, 0x25800000, 0x25800000, 0x25000000, 0x25000000, 0x24800000, 0x24800000, 0x24000000, 0x24000000, 0x23800000, 0x23800000, 0x23000000,
0x23000000, 0x22800000, 0x22800000, 0x22000000, 0x22000000, 0x21800000, 0x21800000, 0x21000000, 0x21000000, 0x20800000, 0x20800000, 0x20000000, 0x20000000, 0x1F800000, 0x1F800000, 0x1F000000
};
using spu_rdata_t = decltype(spu_thread::rdata);
template <>
@@ -3721,7 +3779,7 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
if (!vm::check_addr(addr, vm::page_writable))
{
vm::_ref<atomic_t<u8>>(addr) += 0; // Access violate
utils::trigger_write_page_fault(vm::base(addr));
}
raddr = 0;
@@ -3823,7 +3881,7 @@ void do_cell_atomic_128_store(u32 addr, const void* to_write)
else if (!g_use_rtm)
{
// Provoke page fault
vm::_ref<atomic_t<u32>>(addr) += 0;
utils::trigger_write_page_fault(vm::base(addr));
// Hard lock
auto spu = cpu ? cpu->try_get<spu_thread>() : nullptr;
+6
View File
@@ -24,6 +24,12 @@ namespace utils
class shm;
}
// LUTs for SPU
extern const u32 spu_frest_fraction_lut[32];
extern const u32 spu_frest_exponent_lut[256];
extern const u32 spu_frsqest_fraction_lut[64];
extern const u32 spu_frsqest_exponent_lut[256];
// JIT Block
using spu_function_t = void(*)(spu_thread&, void*, u8*);
+47 -7
View File
@@ -1662,12 +1662,16 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
// Yield changed the queue before
bool changed_queue = prio == yield_cmd;
s32 lowest_new_priority = smax;
const bool has_free_hw_thread_space = count_non_sleeping_threads().onproc_count < g_cfg.core.ppu_threads + 0u;
if (cpu && prio != yield_cmd)
{
// Emplace current thread
if (emplace_thread(cpu))
{
changed_queue = true;
lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(cpu)->prio.load().prio, lowest_new_priority);
}
}
else for (const auto _cpu : g_to_awake)
@@ -1676,13 +1680,15 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
if (emplace_thread(_cpu))
{
changed_queue = true;
lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(_cpu)->prio.load().prio, lowest_new_priority);
}
}
auto target = +g_ppu;
usz i = 0;
// Suspend threads if necessary
for (usz i = 0, thread_count = g_cfg.core.ppu_threads; target; target = target->next_ppu, i++)
for (usz thread_count = g_cfg.core.ppu_threads; target; target = target->next_ppu, i++)
{
if (i >= thread_count && cpu_flag::suspend - target->state)
{
@@ -1709,6 +1715,31 @@ bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
}
}
// In real PS3 (it seems), when a thread with a higher priority than the caller is signaled and -
// - that there is available space on the running queue for the other hardware thread to start
// It prioritizes signaled thread - caller's hardware thread switches instantly to the new thread code
// While signaling to the other hardware thread to execute the caller's code.
// Resulting in a delay to the caller after such thread is signaled
if (current_ppu && changed_queue && has_free_hw_thread_space)
{
if (current_ppu->prio.load().prio > lowest_new_priority)
{
// When not being set to All timers - activate only for sys_ppu_thread_start
if (current_ppu->gpr[11] == 0x35 || g_cfg.core.sleep_timers_accuracy == sleep_timers_accuracy_level::_all_timers)
{
if (!current_ppu->state.test_and_set(cpu_flag::yield) || current_ppu->hw_sleep_time != 0)
{
current_ppu->hw_sleep_time += 35; // Seems like 35us after extensive testing
}
else
{
current_ppu->hw_sleep_time = 30000; // In addition to another flag's use (TODO: Refactor and clean this)
}
}
}
}
return changed_queue;
}
@@ -1736,9 +1767,13 @@ void lv2_obj::schedule_all(u64 current_time)
if (target->state & cpu_flag::suspend)
{
ppu_log.trace("schedule(): %s", target->id);
// Remove yield if it was sleeping until now
const bs_t<cpu_flag> remove_yield = target->start_time == 0 ? +cpu_flag::suspend : (cpu_flag::yield + cpu_flag::preempt);
target->start_time = 0;
if ((target->state.fetch_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
if ((target->state.fetch_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend, x-= remove_yield, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
{
continue;
}
@@ -1920,19 +1955,24 @@ bool lv2_obj::is_scheduler_ready()
return g_to_sleep.empty();
}
bool lv2_obj::has_ppus_in_running_state()
ppu_non_sleeping_count_t lv2_obj::count_non_sleeping_threads()
{
ppu_non_sleeping_count_t total{};
auto target = atomic_storage<ppu_thread*>::load(g_ppu);
for (usz i = 0, thread_count = g_cfg.core.ppu_threads; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu), i++)
for (usz thread_count = g_cfg.core.ppu_threads; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu))
{
if (i >= thread_count)
if (total.onproc_count == thread_count)
{
return true;
total.has_running = true;
break;
}
total.onproc_count++;
}
return false;
return total;
}
void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc)
+137 -58
View File
@@ -90,6 +90,23 @@ void fmt_class_string<lv2_dir>::format(std::string& out, u64 arg)
fmt::append(out, u8"Directory, “%s”, Entries: %u/%u", dir.name.data(), std::min<u64>(dir.pos, dir.entries.size()), dir.entries.size());
}
bool has_fs_write_rights(std::string_view vpath)
{
// VSH has access to everything
if (g_ps3_process_info.has_root_perm())
return true;
const auto norm_vpath = lv2_fs_object::get_normalized_path(vpath);
const auto parent_dir = fs::get_parent_dir_view(norm_vpath);
// This is not exhaustive, PS3 has a unix filesystem with rights for each directory and files
// This is mostly meant to protect against games doing insane things(ie NPUB30003 => NPUB30008)
if (parent_dir == "/dev_hdd0" || parent_dir == "/dev_hdd0/game")
return false;
return true;
}
bool verify_mself(const fs::file& mself_file)
{
FsMselfHeader mself_header;
@@ -160,7 +177,7 @@ bool lv2_fs_mount_info_map::remove(std::string_view path)
return false;
}
const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bool no_cell_fs_path) const
const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bool no_cell_fs_path, std::string* mount_path) const
{
if (path.starts_with("/"sv))
{
@@ -177,7 +194,9 @@ const lv2_fs_mount_info& lv2_fs_mount_info_map::lookup(std::string_view path, bo
if (iterator->second == &g_mp_sys_dev_root && parent_level > 1)
break;
if (no_cell_fs_path && iterator->second.device.starts_with(cell_fs_path))
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path); // Recursively look up the parent mount info
return lookup(iterator->second.device.substr(cell_fs_path.size()), no_cell_fs_path, mount_path); // Recursively look up the parent mount info
if (mount_path)
*mount_path = iterator->first;
return iterator->second;
}
} while (parent_dir.length() > 1); // Exit the loop when parent_dir == "/" or empty
@@ -191,38 +210,30 @@ u64 lv2_fs_mount_info_map::get_all(CellFsMountInfo* info, u64 len) const
if (!info)
return map.size();
struct mount_info
{
const std::string_view path, filesystem, dev_name;
const be_t<u32> unk1 = 0, unk2 = 0, unk3 = 0, unk4 = 0, unk5 = 0;
};
u64 count = 0;
auto push_info = [&](mount_info&& data)
for (const auto& [path, mount_info] : map)
{
if (count >= len)
return;
break;
strcpy_trunc(info->mount_path, data.path);
strcpy_trunc(info->filesystem, data.filesystem);
strcpy_trunc(info->dev_name, data.dev_name);
std::memcpy(&info->unk1, &data.unk1, sizeof(be_t<u32>) * 5);
strcpy_trunc(info[count].mount_path, path);
strcpy_trunc(info[count].filesystem, mount_info.file_system);
strcpy_trunc(info[count].dev_name, mount_info.device);
if (mount_info.read_only)
info[count].unk[4] |= 0x10000000;
info++, count++;
};
for (const auto& [path, mi] : map)
{
if (mi == &g_mp_sys_dev_root || mi == &g_mp_sys_dev_flash)
push_info(mount_info{.path = path, .filesystem = mi.file_system, .dev_name = mi.device, .unk5 = 0x10000000});
else
push_info(mount_info{.path = path, .filesystem = mi.file_system, .dev_name = mi.device});
count++;
}
return count;
}
bool lv2_fs_mount_info_map::is_device_mounted(std::string_view device_name) const
{
return std::any_of(map.begin(), map.end(), [&](const decltype(map)::value_type& info) { return info.second.device == device_name; });
}
bool lv2_fs_mount_info_map::vfs_unmount(std::string_view vpath, bool remove_from_map)
{
const std::string local_path = vfs::get(vpath);
@@ -264,13 +275,15 @@ std::string lv2_fs_object::get_normalized_path(std::string_view path)
return normalized_path.empty() ? "/" : normalized_path;
}
std::string_view lv2_fs_object::get_device_root(std::string_view path)
std::string lv2_fs_object::get_device_root(std::string_view filename)
{
std::string path = get_normalized_path(filename); // Prevent getting fooled by ".." trick such as "/dev_usb000/../dev_flash"
if (const auto first = path.find_first_not_of("/"sv); first != umax)
{
if (const auto pos = path.substr(first).find_first_of("/"sv); pos != umax)
path = path.substr(0, first + pos);
path.remove_prefix(std::max<usz>(0, first - 1)); // Remove duplicate leading '/' while keeping only one
path = path.substr(std::max<std::make_signed_t<usz>>(0, first - 1)); // Remove duplicate leading '/' while keeping only one
}
else
{
@@ -289,14 +302,12 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
filename.remove_prefix(cell_fs_path.size());
const bool is_path = filename.starts_with("/"sv);
std::string mp_name = std::string(is_path ? get_device_root(filename) : filename);
std::string mp_name = is_path ? get_device_root(filename) : std::string(filename);
const auto check_mp = [&]()
{
for (auto mp = &g_mp_sys_dev_root; mp; mp = mp->next)
{
const auto pos = mp->root.find_first_not_of('/');
const auto mp_root = pos != umax ? mp->root.substr(pos) : mp->root;
const auto& device_alias_check = !is_path && (
(mp == &g_mp_sys_dev_hdd0 && mp_name == "CELL_FS_IOS:PATA0_HDD_DRIVE"sv) ||
(mp == &g_mp_sys_dev_hdd1 && mp_name == "CELL_FS_IOS:PATA1_HDD_DRIVE"sv) ||
@@ -304,20 +315,17 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
if (mp == &g_mp_sys_dev_usb)
{
for (int i = 0; i < 8; i++)
if (mp_name.starts_with(is_path ? mp->root : mp->device))
{
if (fmt::format("%s%03d", is_path ? mp_root : mp->device, i) == mp_name)
{
if (!is_path)
mp_name = fmt::format("%s%03d", mp_root, i);
return mp;
}
if (!is_path)
mp_name = fmt::format("%s%s", mp->root, mp_name.substr(mp->device.size()));
return mp;
}
}
else if ((is_path ? mp_root : mp->device) == mp_name || device_alias_check)
else if ((is_path ? mp->root : mp->device) == mp_name || device_alias_check)
{
if (!is_path)
mp_name = mp_root;
mp_name = mp->root;
return mp;
}
}
@@ -335,7 +343,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
else if (result == &g_mp_sys_dev_hdd1)
*vfs_path = g_cfg_vfs.get(g_cfg_vfs.dev_hdd1, rpcs3::utils::get_emu_dir());
else if (result == &g_mp_sys_dev_usb)
*vfs_path = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("/%s", mp_name), rpcs3::utils::get_emu_dir()).path;
*vfs_path = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, mp_name, rpcs3::utils::get_emu_dir()).path;
else if (result == &g_mp_sys_dev_bdvd)
*vfs_path = g_cfg_vfs.get(g_cfg_vfs.dev_bdvd, rpcs3::utils::get_emu_dir());
else if (result == &g_mp_sys_dev_dvd)
@@ -354,7 +362,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename, std::string
*vfs_path = {};
if (is_path && !is_cell_fs_path && !vfs_path->empty())
vfs_path->append(filename.substr(mp_name.size() + 1)); // substr: remove leading "/mp_name"
vfs_path->append(filename.substr(mp_name.size()));
}
return result;
@@ -374,6 +382,12 @@ lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos)
{
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() & 0xfff'ffff) + (size & 0xfff'ffff); region == region_end && ((region >> 28) == 0 || region >= 0xC))
{
// Optimize reads from safe memory
return (opt_pos == umax ? file.read(buf.get_ptr(), size) : file.read_at(opt_pos, buf.get_ptr(), size));
}
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
std::vector<uchar> local_buf(std::min<u64>(size, 65536));
@@ -425,6 +439,8 @@ lv2_file::lv2_file(utils::serial& ar)
, flags(ar)
, type(ar)
{
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_fs);
ar(lock);
be_t<u64> arg = 0;
@@ -437,13 +453,19 @@ lv2_file::lv2_file(utils::serial& ar)
case lv2_file_type::edata: arg = 0x2, size = 8; break;
}
const std::string retrieve_real = ar;
const std::string retrieve_real = ar.pop<std::string>();
if (type == lv2_file_type::edata && version >= 2)
{
ar(g_fxo->get<loaded_npdrm_keys>().one_time_key);
}
open_result_t res = lv2_file::open(retrieve_real, flags & CELL_FS_O_ACCMODE, mode, size ? &arg : nullptr, size);
file = std::move(res.file);
real_path = std::move(res.real_path);
g_fxo->get<loaded_npdrm_keys>().npdrm_fds.raw() += type != lv2_file_type::regular;
g_fxo->get<loaded_npdrm_keys>().one_time_key = {};
if (ar.pop<bool>()) // see lv2_file::save in_mem
{
@@ -475,6 +497,13 @@ void lv2_file::save(utils::serial& ar)
USING_SERIALIZATION_VERSION(lv2_fs);
ar(name, mode, flags, type, lock, ensure(vfs::retrieve(real_path), FN(!x.empty())));
if (type == lv2_file_type::edata)
{
auto file_ptr = file.release();
ar(static_cast<EDATADecrypter*>(file_ptr.get())->get_key());
file.reset(std::move(file_ptr));
}
if (!mp.read_only && flags & CELL_FS_O_ACCMODE)
{
// Ensure accurate timestamps and content on disk
@@ -603,7 +632,13 @@ struct lv2_file::file_view : fs::file_base
fs::stat_t get_stat() override
{
return m_file->file.get_stat();
fs::stat_t stat = m_file->file.get_stat();
// TODO: Check this on realhw
//stat.size = utils::sub_saturate<u64>(stat.size, m_off);
stat.is_writable = false;
return stat;
}
bool trunc(u64) override
@@ -613,7 +648,7 @@ struct lv2_file::file_view : fs::file_base
u64 read(void* buffer, u64 size) override
{
const u64 result = m_file->file.read_at(m_off + m_pos, buffer, size);
const u64 result = file_view::read_at(m_pos, buffer, size);
m_pos += result;
return result;
@@ -621,7 +656,7 @@ struct lv2_file::file_view : fs::file_base
u64 read_at(u64 offset, void* buffer, u64 size) override
{
return m_file->file.read_at(offset, buffer, size);
return m_file->file.read_at(m_off + offset, buffer, size);
}
u64 write(const void*, u64) override
@@ -648,7 +683,7 @@ struct lv2_file::file_view : fs::file_base
u64 size() override
{
return m_file->file.size();
return utils::sub_saturate<u64>(m_file->file.size(), m_off);
}
fs::file_id get_id() override
@@ -905,6 +940,21 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
const auto& dec_keys = edatkeys.dec_keys;
const u64 max_i = std::min<u64>(std::size(dec_keys), init_pos);
if (edatkeys.one_time_key)
{
auto edata_file = std::make_unique<EDATADecrypter>(std::move(file), edatkeys.one_time_key);
edatkeys.one_time_key = {};
if (!edata_file->ReadHeader())
{
// Read failure
return {CELL_EFSSPECIFIC};
}
file.reset(std::move(edata_file));
break;
}
for (u64 i = 0;; i++)
{
if (i == max_i)
@@ -918,7 +968,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
if (!edata_file->ReadHeader())
{
// Prepare file for the next iteration
file = std::move(edata_file->edata_file);
file = std::move(edata_file->m_edata_file);
continue;
}
@@ -959,6 +1009,11 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
return {CELL_ENOTMOUNTED, path};
}
if (flags & CELL_FS_O_CREAT && !has_fs_write_rights(vpath) && !fs::is_dir(local_path))
{
return {CELL_EACCES};
}
lv2_file_type type = lv2_file_type::regular;
if (size == 8)
@@ -1481,7 +1536,9 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
if (local_path.empty())
{
return {CELL_ENOTMOUNTED, path};
// This syscall can be used by games and VSH to test the presence of dev_usb000 ~ dev_usb127
// Thus there is no need to fuss about CELL_ENOTMOUNTED in this case
return {sys_fs.warning, CELL_ENOTMOUNTED, path};
}
std::unique_lock lock(mp->mutex);
@@ -1639,6 +1696,11 @@ error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr<char> path, s32 mode)
return {CELL_EROFS, path};
}
if (!fs::exists(local_path) && !has_fs_write_rights(path.get_ptr()))
{
return {CELL_EACCES, path};
}
std::lock_guard lock(mp->mutex);
if (!fs::create_dir(local_path))
@@ -1762,6 +1824,11 @@ error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr<char> path)
return {CELL_EROFS, path};
}
if (fs::is_dir(local_path) && !has_fs_write_rights(path.get_ptr()))
{
return {CELL_EACCES};
}
std::lock_guard lock(mp->mutex);
if (!fs::remove_dir(local_path))
@@ -1796,7 +1863,8 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
const std::string local_path = vfs::get(vpath);
const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath);
std::string mount_path = fs::get_parent_dir(vpath); // Use its parent directory as fallback
const auto& mp = g_fxo->get<lv2_fs_mount_info_map>().lookup(vpath, true, &mount_path);
if (mp == &g_mp_sys_dev_root)
{
@@ -1820,8 +1888,7 @@ error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr<char> path)
std::lock_guard lock(mp->mutex);
// Provide default mp root or use parent directory if not available (such as host_root)
if (!vfs::host::unlink(local_path, vfs::get(mp->root.empty() ? vpath.substr(0, vpath.find_last_of('/')) : mp->root)))
if (!vfs::host::unlink(local_path, vfs::get(mount_path)))
{
switch (auto error = fs::g_tls_error)
{
@@ -1993,21 +2060,23 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
if (!file)
{
return CELL_EBADF;
return {CELL_EBADF, "fd=%u", fd};
}
sys_fs.warning("sys_fs_fcntl(0x80000009): fd=%d, arg->offset=0x%x, size=0x%x (file: %s)", fd, arg->offset, _size, *file);
std::lock_guard lock(file->mp->mutex);
if (!file->file)
{
return CELL_EBADF;
return {CELL_EBADF, "fd=%u", fd};
}
auto sdata_file = std::make_unique<EDATADecrypter>(lv2_file::make_view(file, arg->offset));
if (!sdata_file->ReadHeader())
{
return CELL_EFSSPECIFIC;
return {CELL_EFSSPECIFIC, "fd=%u", fd};
}
fs::file stream;
@@ -2044,6 +2113,16 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
return CELL_OK;
}
case 0xc0000003: // cellFsUtilitySetFakeSize
{
break;
}
case 0xc0000004: // cellFsUtilityGetFakeSize
{
break;
}
case 0xc0000006: // Unknown
{
const auto arg = vm::static_ptr_cast<lv2_file_c0000006>(_arg);
@@ -2543,7 +2622,7 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
{
switch (auto error = fs::g_tls_error)
{
case fs::error::inval: return CELL_EINVAL;
case fs::error::inval: return {CELL_EINVAL, "fd=%u, offset=0x%x, whence=%d", fd, offset, whence};
default: sys_fs.error("sys_fs_lseek(): unknown error %s", error);
}
@@ -3152,7 +3231,7 @@ error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
if (mp == &g_mp_sys_no_device)
return {CELL_ENXIO, device_name};
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
if (g_fxo->get<lv2_fs_mount_info_map>().is_device_mounted(device_name) || !lock.try_lock())
return {CELL_EBUSY, device_name};
if (vfs_path.empty())
@@ -3216,7 +3295,7 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
if (mp == &g_mp_sys_no_device)
return {CELL_ENXIO, device_name};
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
if (g_fxo->get<lv2_fs_mount_info_map>().is_device_mounted(device_name) || !lock.try_lock())
return {CELL_EBUSY, device_name};
if (vfs_path.empty())
@@ -3280,11 +3359,11 @@ error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char>
return CELL_OK;
}
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 unk2)
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 force)
{
ppu.state += cpu_flag::wait;
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, unk2=0x%x)", path, unk1, unk2);
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, force=%d)", path, unk1, force);
const auto [path_error, vpath] = translate_to_str(path);
@@ -3302,7 +3381,7 @@ error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 un
if (mp == &g_mp_sys_no_device)
return {CELL_EINVAL, vpath};
if (mp == &g_mp_sys_dev_root || !lock.try_lock())
if (mp == &g_mp_sys_dev_root || (!lock.try_lock() && !force))
return {CELL_EBUSY, vpath};
if (!lv2_fs_mount_info_map::vfs_unmount(vpath))
+5 -8
View File
@@ -213,8 +213,9 @@ public:
return map.try_emplace(std::forward<Args>(args)...).second;
}
bool remove(std::string_view path);
const lv2_fs_mount_info& lookup(std::string_view path, bool no_cell_fs_path = false) const;
const lv2_fs_mount_info& lookup(std::string_view path, bool no_cell_fs_path = false, std::string* mount_path = nullptr) const;
u64 get_all(CellFsMountInfo* info = nullptr, u64 len = 0) const;
bool is_device_mounted(std::string_view device_name) const;
static bool vfs_unmount(std::string_view vpath, bool remove_from_map = true);
@@ -249,7 +250,7 @@ public:
static std::string get_normalized_path(std::string_view path);
// Get the device's root path (e.g. "/dev_hdd0") from a given path
static std::string_view get_device_root(std::string_view path);
static std::string get_device_root(std::string_view filename);
// Filename can be either a path starting with '/' or a CELL_FS device name
// This should be used only when handling devices that are not mounted
@@ -620,11 +621,7 @@ struct CellFsMountInfo
char mount_path[0x20]; // 0x0
char filesystem[0x20]; // 0x20
char dev_name[0x40]; // 0x40
be_t<u32> unk1; // 0x80
be_t<u32> unk2; // 0x84
be_t<u32> unk3; // 0x88
be_t<u32> unk4; // 0x8C
be_t<u32> unk5; // 0x90
be_t<u32> unk[5]; // 0x80, probably attributes
};
CHECK_SIZE(CellFsMountInfo, 0x94);
@@ -682,6 +679,6 @@ error_code sys_fs_mapped_free(ppu_thread& ppu, u32 fd, vm::ptr<void> ptr);
error_code sys_fs_truncate2(ppu_thread& ppu, u32 fd, u64 size);
error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, s32 unk1, vm::cptr<char> str1);
error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk3, vm::cptr<char> str1, u32 str_len);
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 unk2);
error_code sys_fs_unmount(ppu_thread& ppu, vm::cptr<char> path, s32 unk1, s32 force);
error_code sys_fs_get_mount_info_size(ppu_thread& ppu, vm::ptr<u64> len);
error_code sys_fs_get_mount_info(ppu_thread& ppu, vm::ptr<CellFsMountInfo> info, u64 len, vm::ptr<u64> out_len);
+2 -2
View File
@@ -126,7 +126,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr<u32
// Try to get "physical memory"
if (!dct.take(size))
{
return CELL_ENOMEM;
return {CELL_ENOMEM, dct.size - dct.used};
}
if (const auto area = reserve_map(size, align))
@@ -200,7 +200,7 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u32 size, u32 cid
if (ct.ret)
{
return ct.ret;
return {ct.ret, ct->size - ct->used};
}
if (const auto area = reserve_map(size, align))
+1
View File
@@ -74,6 +74,7 @@ CellError lv2_memory::on_id_create()
{
if (!exists && !ct->take(size))
{
sys_mmapper.error("lv2_memory::on_id_create(): Cannot allocate 0x%x bytes (0x%x available)", size, ct->size - ct->used);
return CELL_ENOMEM;
}
+21 -16
View File
@@ -329,9 +329,14 @@ void lv2_socket::save(utils::serial& ar, bool save_only_this_class)
}
}
void sys_net_dump_data(std::string_view desc, const u8* data, s32 len)
void sys_net_dump_data(std::string_view desc, const u8* data, s32 len, const void* addr)
{
sys_net_dump.trace("%s:%s", desc, fmt::buf_to_hexstring(data, len));
const sys_net_sockaddr_in_p2p* p2p_addr = reinterpret_cast<const sys_net_sockaddr_in_p2p*>(addr);
if (p2p_addr)
sys_net_dump.trace("%s(%s:%d:%d): %s", desc, np::ip_to_string(std::bit_cast<u32>(p2p_addr->sin_addr)), p2p_addr->sin_port, p2p_addr->sin_vport, fmt::buf_to_hexstring(data, len));
else
sys_net_dump.trace("%s: %s", desc, fmt::buf_to_hexstring(data, len));
}
error_code sys_net_bnet_accept(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr> addr, vm::ptr<u32> paddrlen)
@@ -770,7 +775,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
{
ppu.state += cpu_flag::wait;
sys_net.warning("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
sys_net.trace("sys_net_bnet_recvfrom(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, paddrlen=*0x%x)", s, buf, len, flags, addr, paddrlen);
// If addr is null, paddrlen must be null as well
if (!buf || !len || addr.operator bool() != paddrlen.operator bool())
@@ -797,7 +802,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
{
sn_addr = res_addr;
std::memcpy(buf.get_ptr(), vec.data(), res);
sys_net_dump_data("recvfrom", vec.data(), res);
sys_net_dump_data("recvfrom", vec.data(), res, &res_addr);
}
result = res;
@@ -819,7 +824,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread& ppu, s32 s, vm::ptr<void> buf, u32
{
sn_addr = res_addr;
std::memcpy(buf.get_ptr(), vec.data(), res);
sys_net_dump_data("recvfrom", vec.data(), res);
sys_net_dump_data("recvfrom", vec.data(), res, &res_addr);
}
result = res;
lv2_obj::awake(&ppu);
@@ -984,7 +989,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
{
ppu.state += cpu_flag::wait;
sys_net.warning("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
sys_net.trace("sys_net_bnet_sendto(s=%d, buf=*0x%x, len=%u, flags=0x%x, addr=*0x%x, addrlen=%u)", s, buf, len, flags, addr, addrlen);
if (flags & ~(SYS_NET_MSG_DONTWAIT | SYS_NET_MSG_WAITALL | SYS_NET_MSG_USECRYPTO | SYS_NET_MSG_USESIGNATURE))
{
@@ -1003,7 +1008,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
return -SYS_NET_EAFNOSUPPORT;
}
sys_net_dump_data("sendto", static_cast<const u8 *>(buf.get_ptr()), len);
sys_net_dump_data("sendto", static_cast<const u8*>(buf.get_ptr()), len, addr ? addr.get_ptr() : nullptr);
const std::optional<sys_net_sockaddr> sn_addr = addr ? std::optional<sys_net_sockaddr>(*addr) : std::nullopt;
const std::vector<u8> buf_copy(vm::_ptr<const char>(buf.addr()), vm::_ptr<const char>(buf.addr()) + len);
@@ -1256,7 +1261,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
{
ppu.state += cpu_flag::wait;
sys_net.warning("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
sys_net.trace("sys_net_bnet_poll(fds=*0x%x, nfds=%d, ms=%d)", fds, nfds, ms);
if (nfds <= 0)
{
@@ -1424,13 +1429,13 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
}
}
std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0]));
if (!has_timedout && !signaled)
{
return -SYS_NET_EINTR;
}
std::memcpy(fds.get_ptr(), fds_buf.data(), nfds * sizeof(fds[0]));
return not_an_error(signaled);
}
@@ -1438,7 +1443,7 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
{
ppu.state += cpu_flag::wait;
sys_net.warning("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
sys_net.trace("sys_net_bnet_select(nfds=%d, readfds=*0x%x, writefds=*0x%x, exceptfds=*0x%x, timeout=*0x%x)", nfds, readfds, writefds, exceptfds, _timeout);
atomic_t<s32> signaled{0};
@@ -1664,6 +1669,11 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
}
}
if (!has_timedout && !signaled)
{
return -SYS_NET_EINTR;
}
if (readfds)
*readfds = rread;
if (writefds)
@@ -1671,11 +1681,6 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
if (exceptfds)
*exceptfds = rexcept;
if (!has_timedout && !signaled)
{
return -SYS_NET_EINTR;
}
return not_an_error(signaled);
}
@@ -20,7 +20,7 @@ lv2_socket_native::lv2_socket_native(lv2_socket_family family, lv2_socket_type t
}
lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type)
: lv2_socket(ar, type)
: lv2_socket(stx::make_exact(ar), type)
{
#ifdef _WIN32
ar(so_reuseaddr, so_reuseport);
@@ -37,7 +37,7 @@ lv2_socket_native::lv2_socket_native(utils::serial& ar, lv2_socket_type type)
void lv2_socket_native::save(utils::serial& ar)
{
static_cast<lv2_socket*>(this)->save(ar, true);
lv2_socket::save(ar, true);
#ifdef _WIN32
ar(so_reuseaddr, so_reuseport);
#else
+17 -13
View File
@@ -9,33 +9,36 @@ LOG_CHANNEL(sys_net);
lv2_socket_p2p::lv2_socket_p2p(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
: lv2_socket(family, type, protocol)
{
sockopt_cache cache_type;
cache_type.data._int = SYS_NET_SOCK_DGRAM_P2P;
cache_type.len = 4;
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
}
lv2_socket_p2p::lv2_socket_p2p(utils::serial& ar, lv2_socket_type type)
: lv2_socket(ar, type)
: lv2_socket(stx::make_exact(ar), type)
{
ar(port, vport, bound_addr);
std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data_dequeue{ar};
auto data_dequeue = ar.pop<std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>>>();
for (; !data_dequeue.empty(); data_dequeue.pop_front())
{
data.push(data_dequeue.front());
data.push(std::move(data_dequeue.front()));
}
ar(data_dequeue);
}
void lv2_socket_p2p::save(utils::serial& ar)
{
static_cast<lv2_socket*>(this)->save(ar, true);
lv2_socket::save(ar, true);
ar(port, vport, bound_addr);
std::deque<std::pair<sys_net_sockaddr_in_p2p, std::vector<u8>>> data_dequeue;
for (; !data.empty(); data.pop())
for (auto save_data = ::as_rvalue(data); !save_data.empty(); save_data.pop())
{
data_dequeue.push_back(data.front());
data_dequeue.push_back(std::move(save_data.front()));
}
ar(data_dequeue);
@@ -238,8 +241,6 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
lock.lock();
}
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
if (data.empty())
{
if (so_nbio || (flags & SYS_NET_MSG_DONTWAIT))
@@ -248,6 +249,8 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_p2p
return std::nullopt;
}
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
std::vector<u8> res_buf(len);
const auto& p2p_data = data.front();
@@ -285,9 +288,11 @@ std::optional<s32> lv2_socket_p2p::sendto(s32 flags, const std::vector<u8>& buf,
std::vector<u8> p2p_data(buf.size() + VPORT_P2P_HEADER_SIZE);
const le_t<u16> p2p_vport_le = p2p_vport;
const le_t<u16> src_vport_le = vport;
const le_t<u16> p2p_flags_le = P2P_FLAG_P2P;
memcpy(p2p_data.data(), &p2p_vport_le, sizeof(u16));
memcpy(p2p_data.data() + sizeof(u16), &p2p_flags_le, sizeof(u16));
memcpy(p2p_data.data() + sizeof(u16), &src_vport_le, sizeof(u16));
memcpy(p2p_data.data() + sizeof(u16) + sizeof(u16), &p2p_flags_le, sizeof(u16));
memcpy(p2p_data.data() + VPORT_P2P_HEADER_SIZE, buf.data(), buf.size());
int native_flags = 0;
@@ -360,7 +365,7 @@ s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native
{
std::lock_guard lock(mutex);
ensure(vport);
sys_net.trace("[P2P] poll checking for 0x%X", sn_pfd.events);
// Check if it's a bound P2P socket
if ((sn_pfd.events & SYS_NET_POLLIN) && !data.empty())
{
@@ -393,7 +398,6 @@ std::tuple<bool, bool, bool> lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> sel
if (selected & lv2_socket::poll_t::write)
{
sys_net.trace("[P2P] p2p_data for vport %d contains %d elements", vport, data.size());
write_set = true;
}
@@ -225,10 +225,12 @@ std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u
std::vector<u8> packet(packet_size);
u8* packet_data = packet.data();
le_t<u16> dst_port_le = +header.dst_port;
le_t<u16> src_port_le = +header.src_port;
le_t<u16> p2p_flags_le = P2P_FLAG_P2PS;
memcpy(packet_data, &dst_port_le, sizeof(u16));
memcpy(packet_data + sizeof(u16), &p2p_flags_le, sizeof(u16));
memcpy(packet_data + sizeof(u16), &src_port_le, sizeof(u16));
memcpy(packet_data + sizeof(u16) + sizeof(u16), &p2p_flags_le, sizeof(u16));
memcpy(packet_data + VPORT_P2P_HEADER_SIZE, &header, sizeof(p2ps_encapsulated_tcp));
if (datasize)
memcpy(packet_data + VPORT_P2P_HEADER_SIZE + sizeof(p2ps_encapsulated_tcp), data, datasize);
@@ -243,6 +245,11 @@ std::vector<u8> generate_u2s_packet(const p2ps_encapsulated_tcp& header, const u
lv2_socket_p2ps::lv2_socket_p2ps(lv2_socket_family family, lv2_socket_type type, lv2_ip_protocol protocol)
: lv2_socket_p2p(family, type, protocol)
{
sockopt_cache cache_type;
cache_type.data._int = SYS_NET_SOCK_STREAM_P2P;
cache_type.len = 4;
sockopts[(static_cast<u64>(SYS_NET_SOL_SOCKET) << 32ull) | SYS_NET_SO_TYPE] = cache_type;
}
lv2_socket_p2ps::lv2_socket_p2ps(socket_type socket, u16 port, u16 vport, u32 op_addr, u16 op_port, u16 op_vport, u64 cur_seq, u64 data_beg_seq, s32 so_nbio)
@@ -10,13 +10,13 @@ lv2_socket_raw::lv2_socket_raw(lv2_socket_family family, lv2_socket_type type, l
}
lv2_socket_raw::lv2_socket_raw(utils::serial& ar, lv2_socket_type type)
: lv2_socket(ar, type)
: lv2_socket(stx::make_exact(ar), type)
{
}
void lv2_socket_raw::save(utils::serial& ar)
{
static_cast<lv2_socket*>(this)->save(ar, true);
lv2_socket::save(ar, true);
}
std::tuple<bool, s32, std::shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_raw::accept([[maybe_unused]] bool is_lock)
@@ -19,6 +19,9 @@ s32 send_packet_from_p2p_port(const std::vector<u8>& data, const sockaddr_in& ad
{
auto& def_port = ::at32(nc.list_p2p_ports, SCE_NP_PORT);
res = ::sendto(def_port.p2p_socket, reinterpret_cast<const char*>(data.data()), ::size32(data), 0, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in));
if (res == -1)
sys_net.error("Failed to send signaling packet: %s", get_last_error(false, false));
}
else
{
+4 -3
View File
@@ -204,7 +204,8 @@ bool nt_p2p_port::recv_data()
return true;
}
const u16 vport_flags = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16));
const u16 src_vport = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16));
const u16 vport_flags = *reinterpret_cast<le_t<u16>*>(p2p_recv_data.data() + sizeof(u16) + sizeof(u16));
std::vector<u8> p2p_data(recv_res - VPORT_P2P_HEADER_SIZE);
memcpy(p2p_data.data(), p2p_recv_data.data() + VPORT_P2P_HEADER_SIZE, p2p_data.size());
@@ -218,7 +219,7 @@ bool nt_p2p_port::recv_data()
p2p_addr.sin_len = sizeof(sys_net_sockaddr_in);
p2p_addr.sin_family = SYS_NET_AF_INET;
p2p_addr.sin_addr = std::bit_cast<be_t<u32>, u32>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_addr.s_addr);
p2p_addr.sin_vport = dst_vport;
p2p_addr.sin_vport = src_vport;
p2p_addr.sin_port = std::bit_cast<be_t<u16>, u16>(reinterpret_cast<struct sockaddr_in*>(&native_addr)->sin_port);
auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport);
@@ -313,6 +314,6 @@ bool nt_p2p_port::recv_data()
}
}
sys_net.notice("Received a STREAM-P2P packet with no bound target");
sys_net.notice("Received a P2P packet with no bound target(dst_vport = %d)", dst_vport);
return true;
}
+2 -1
View File
@@ -20,7 +20,8 @@
#endif
#endif
constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16);
// dst_vport src_vport flags
constexpr s32 VPORT_P2P_HEADER_SIZE = sizeof(u16) + sizeof(u16) + sizeof(u16);
enum VPORT_P2P_FLAGS
{
+29 -6
View File
@@ -36,7 +36,15 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
ppu_exec_object obj = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
if (!src)
{
return {CELL_ENOEXEC, +"Failed to decrypt file"};
}
ppu_exec_object obj = std::move(src);
src.close();
if (obj != elf_error::ok)
{
@@ -72,8 +80,11 @@ fs::file make_file_view(fs::file&& file, u64 offset, u64 size);
std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
{
const std::string path = vfs::get(ar.pop<std::string>());
const s64 offset = ar;
const std::string vpath = ar.pop<std::string>();
const std::string path = vfs::get(vpath);
const s64 offset = ar.pop<s64>();
sys_overlay.success("lv2_overlay::load(): vpath='%s', path='%s', offset=0x%x", vpath, path, offset);
std::shared_ptr<lv2_overlay> ovlm;
@@ -84,11 +95,19 @@ std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
file = make_file_view(std::move(file), offset, umax);
ovlm = ppu_load_overlay(ppu_exec_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, false, path, 0, &ar).first;
ensure(ovlm);
if (!ovlm)
{
fmt::throw_exception("lv2_overlay::load(): ppu_load_overlay() failed. (vpath='%s', offset=0x%x)", vpath, offset);
}
}
else if (!g_cfg.savestate.state_inspection_mode.get())
{
fmt::throw_exception("lv2_overlay::load(): Failed to find file. (vpath='%s', offset=0x%x)", vpath, offset);
}
else
{
ensure(g_cfg.savestate.state_inspection_mode.get());
sys_overlay.error("lv2_overlay::load(): Failed to find file. (vpath='%s', offset=0x%x)", vpath, offset);
}
return ovlm;
@@ -97,7 +116,11 @@ std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
void lv2_overlay::save(utils::serial& ar)
{
USING_SERIALIZATION_VERSION(lv2_prx_overlay);
ar(vfs::retrieve(path), offset);
const std::string vpath = vfs::retrieve(path);
(vpath.empty() ? sys_overlay.error : sys_overlay.success)("lv2_overlay::save(): vpath='%s', offset=0x%x", vpath, offset);
ar(vpath, offset);
}
error_code sys_overlay_load_module(vm::ptr<u32> ovlmid, vm::cptr<char> path, u64 flags, vm::ptr<u32> entry)
+1 -1
View File
@@ -507,7 +507,7 @@ error_code _sys_ppu_thread_create(ppu_thread& ppu, vm::ptr<u64> thread_id, vm::p
// Try to obtain "physical memory" from the default container
if (!dct.take(stack_size))
{
return CELL_ENOMEM;
return {CELL_ENOMEM, dct.size - dct.used};
}
const vm::addr_t stack_base{vm::alloc(stack_size, vm::stack, 4096)};
+4
View File
@@ -340,6 +340,8 @@ error_code sys_process_detach_child(u64 unk)
return CELL_OK;
}
extern void signal_system_cache_can_stay();
void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
{
ppu.state += cpu_flag::wait;
@@ -349,6 +351,7 @@ void _sys_process_exit(ppu_thread& ppu, s32 status, u32 arg2, u32 arg3)
Emu.CallFromMainThread([]()
{
sys_process.success("Process finished");
signal_system_cache_can_stay();
Emu.Kill();
});
@@ -481,6 +484,7 @@ void lv2_exitspawn(ppu_thread& ppu, std::vector<std::string>& argv, std::vector<
}
};
signal_system_cache_can_stay();
Emu.Kill(false);
});
+11 -3
View File
@@ -96,7 +96,7 @@ extern const std::map<std::string_view, int> g_prx_list
{ "libmvcdec.sprx", 0 },
{ "libnet.sprx", 0 },
{ "libnetctl.sprx", 1 },
{ "libpamf.sprx", 0 },
{ "libpamf.sprx", 1 },
{ "libpngdec.sprx", 0 },
{ "libpngenc.sprx", 0 },
{ "libresc.sprx", 0 },
@@ -263,11 +263,19 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
ppu_prx_object obj = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
src = decrypt_self(std::move(src), reinterpret_cast<u8*>(&klic), nullptr, true);
if (!src)
{
return {CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE, +"Failed to decrypt file"};
}
ppu_prx_object obj = std::move(src);
src.close();
if (obj != elf_error::ok)
{
return CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE;
return {CELL_PRX_ERROR_UNSUPPORTED_PRX_TYPE, obj.get_error()};
}
const auto prx = ppu_load_prx(obj, false, path, file_offset);
+2 -1
View File
@@ -266,6 +266,7 @@ error_code sys_ss_appliance_info_manager(u32 code, vm::ptr<u8> buffer)
case 0x19006:
{
// qa values (dex only) ??
[[fallthrough]];
}
default: sys_ss.todo("sys_ss_appliance_info_manager(code=0x%x, buffer=*0x%x)", code, buffer);
}
@@ -497,7 +498,7 @@ error_code sys_ss_update_manager(u64 pkg_id, u64 a1, u64 a2, u64 a3, u64 a4, u64
if (!addr_ptr)
return CELL_EFAULT;
vm::write64(addr_ptr, 0x340ULL << 40); // 3.40
vm::write64(addr_ptr, 0x30040ULL << 32); // 3.40
break;
}

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