Compare commits

..

258 Commits

Author SHA1 Message Date
Ani 0178b20983 rpcs3_version: Bump to 0.0.27 2023-03-01 21:56:02 +00:00
JoãoPaulo f9142a1c56 Update FW 4.90 Latest Version
Updates the latest FW version according to: https://www.playstation.com/en-us/support/hardware/ps3/system-software/
List of changes:
This system software update improves system performance.
2023-03-01 20:25:24 +01:00
oltolm b19209c45c [3rdparty] update cubeb 2023-03-01 18:03:55 +01:00
Megamouse 73b5b9d7b7 cellRtc: fix out of bounds write
Turns out you should listen to warnings once in a while...
sprint actually wrote more than 3 characters into the microDigits buffer !!!
2023-03-01 10:15:21 +01:00
kd-11 0a9d646bd7 rsx: Workaround for clang build 2023-03-01 01:10:06 +03:00
kd-11 24edfe2190 rsx: Use simple_array for basic array of integers
- Some methods are getting spammed every draw call
2023-03-01 01:10:06 +03:00
kd-11 0bb8127372 rsx: Use emplace when constructing in-place.
- Unexpectedly high gains for msvc builds. GCC doesn't care, clang shits itself.
2023-03-01 01:10:06 +03:00
kd-11 1b8a69154f rsx: Use non-owning container to ellide a derpy allocation with msvc 2023-03-01 01:10:06 +03:00
illusion0001 f2155b49e2 Debugger: show CIA in register view (#13462) 2023-02-28 00:30:23 +01:00
Eladash 62c9b99e72 Update SPUThread.cpp 2023-02-27 14:17:05 +01:00
Elad Ashkenazi 2a344e1d87 cellMsgDialog: fix use-after-free 2023-02-27 14:17:05 +01:00
Megamouse ae14aa991d overlays: hotfix for return_code lifetime 2023-02-27 12:04:10 +01:00
Megamouse 2885c8a4ab cellSaveData: get rid of g_last_user_response 2023-02-27 10:24:06 +01:00
Megamouse 3d411431c6 input: disable ldd home menu for now 2023-02-26 22:17:55 +01:00
oltolm 1800faf0dd cellVdec.cpp: use lock_unlock instead of lock_guard 2023-02-26 22:20:07 +03:00
oltolm 19ded3809f fix out-of-bounds in VKGSRenderTypes.hpp 2023-02-26 22:20:07 +03:00
oltolm 40cd255051 fix: back() called an empty string in PPUThread.cpp 2023-02-26 22:20:07 +03:00
oltolm ad4677a67c fix use after move in cellVdec.cpp 2023-02-26 22:20:07 +03:00
RipleyTom d9e0d016c6 Fix for Windows ENOTCONN on connecting socket
Fixes Windows returns ENOTCONN when using recvfrom/sendto on connecting socket instead of the expected EAGAIN.
2023-02-26 10:35:20 +01:00
kd-11 c570fd03d1 rsx/overlays: Handle corner case where thread signal happens just before pop_all is called. 2023-02-26 02:30:16 +03:00
kd-11 1e0565f17b rsx/overlays: Fix emu close 2023-02-26 02:30:16 +03:00
Megamouse 3ff464641a Fix Visual Studio 17.5.0 compilation
For some reason there's an internal compiler error related to atomic wait.
I could work around this issue when I commented "mask = default_mask<X>;".
So after playing around a bit I had the suspicion that the VS parser can't handle some of the templatization.
Giving the decltype its own alias seems to fix this issue (and makes the code a bit more readable anyway in my opinion).
2023-02-25 19:20:55 +03:00
Megamouse 7cb3d305d4 main: set locale to C before doing anything else 2023-02-25 11:23:07 +01:00
Megamouse e46dba43ad config: try to fix float locale issues 2023-02-25 11:23:07 +01:00
Megamouse afad96a52a Qt: try to fix QString::toDouble locale nonsense 2023-02-25 11:23:07 +01:00
RipleyTom 1a00341e6b Forces local IP to bind address if set 2023-02-25 10:38:19 +01:00
kd-11 3388c8ed09 overlays/manager: Explicit ctor for the input context 2023-02-24 00:46:53 +03:00
kd-11 6d4967ffef [overlays] Implement input suspend+resume
- Tested with OSK + home menu. Requires hacks to enable this setup so it's mostly an academic achievement.
2023-02-24 00:46:53 +03:00
kd-11 b27366e1e6 rsx/overlays: Enable input hijacking 2023-02-24 00:46:53 +03:00
kd-11 95a29b5d1d rsx/overlays: Fix clang/msvc build 2023-02-24 00:46:53 +03:00
kd-11 d320867569 rsx/overlays: Add a name to the input thread context 2023-02-24 00:46:53 +03:00
kd-11 aabd388070 rsx/overlays: Remove some unused thread declarations 2023-02-24 00:46:53 +03:00
kd-11 7db6594358 rsx/overlays: Use lf_queue for a lock-free stack implementation 2023-02-24 00:46:53 +03:00
kd-11 388ca1c645 rsx/overlays: Fix build 2023-02-24 00:46:53 +03:00
kd-11 7c04b00e2b rsx/vk: Remove stale include 2023-02-24 00:46:53 +03:00
kd-11 dac0644f07 rsx/overlays: Fill in the user response when message dialog input loop exits and we're in blocking mode
- Avoids a race condition caused by the "blocking" call only updating results on cleanup/close
2023-02-24 00:46:53 +03:00
kd-11 7d103fe0f0 rsx/overlays: Fix crash 2023-02-24 00:46:53 +03:00
kd-11 901d9f3f6e rsx/overlays: Simplify attach-thread-input API
- Restructure the inputs to encourage shorter input signature.
2023-02-24 00:46:53 +03:00
kd-11 ddc9e74aa8 rsx/overlays: Putting it all together
- Migrate dialogs with input-only threads to use the unified input
  system
2023-02-24 00:46:53 +03:00
kd-11 12690de9de rsx/overlays: Refactor and implement callbacks for input token 2023-02-24 00:46:53 +03:00
Megamouse 6b30cdac09 Use u64 for system_time_t, as s64 doesn't make much sense in a microsecond context. 2023-02-22 23:57:20 +01:00
Megamouse ccb7528a59 fix some warnings 2023-02-22 23:57:20 +01:00
Ivan Chikish 01ed8a07af fixed_typemap.hpp: static allocation for m_init
This allows to safely call is_init<T>() at any time.
2023-02-23 00:54:35 +03:00
Megamouse a9f399d6cc patch_manager: fix focus loss during config value editing 2023-02-22 01:17:36 +01:00
Megamouse a527a485f8 patch_manager: add more decimal places to spinbox 2023-02-22 01:17:36 +01:00
Megamouse f0c8f6d47b patch_manager: check config values before applying them 2023-02-22 01:17:36 +01:00
Megamouse a29dc3893f patch_manager: change wording 2023-02-22 01:17:36 +01:00
Megamouse c6f392bf6c patch_manager: allow multiple dynamic values 2023-02-22 01:17:36 +01:00
Megamouse f2ded2fdb7 patch_manager: Add dropdown labels 2023-02-22 01:17:36 +01:00
Megamouse 080737fd1f patch_manager: add more dynamic to dynamic patches 2023-02-22 01:17:36 +01:00
Megamouse 1040757556 patch_manager: add ability to add dynamic patches 2023-02-22 01:17:36 +01:00
Megamouse 38d612e5ba fix some warnings 2023-02-22 00:15:11 +01:00
Megamouse c20aa2ad5f VS: fix miniupnp dependency 2023-02-22 00:15:11 +01:00
Megamouse a7c3753c5a cellMsgDialog: Improve some logging and localization 2023-02-21 23:35:13 +01:00
Megamouse ebf72eb126 overlays: add internal lock for progress bar texts 2023-02-21 23:35:13 +01:00
Megamouse be49a80bc7 overlays: use array for progress bars 2023-02-21 23:35:13 +01:00
brian218 23da770364 sys_game: LV2 board storage minor fixups 2023-02-21 22:57:37 +01:00
RipleyTom 6186ac0245 rpcn: signaling handler improvements & upnp
Simplify signaling by making Matching2 a layer over normal signaling.
Implements UPNP port forwarding
Implement sceNpMatching2AbortRequest
Fix reported bw in sceNpUtil
Hack for Fat Princess binding udp on 3658
Reenable CB for sceNpBasicAddPlayersHistoryAsync
Misc fixes
2023-02-21 15:41:41 +01:00
Megamouse 364c33060b Fix boot by RPCS3_GAMEID constants 2023-02-20 22:53:22 +01:00
Elad Ashkenazi 3922f76ab5 PINE: Fix crash on exit (#13409) 2023-02-20 09:59:40 +01:00
Megamouse bb2c9196f5 cellVdec: align RGBA/ARGB strides 2023-02-18 22:43:22 +01:00
Megamouse 1c8d3e0dca cellVdec: fix au_count race condition 2023-02-18 15:31:21 +01:00
Megamouse 6b556a5206 PINE: fix error format function 2023-02-17 19:33:58 +01:00
oltolm bbd308a908 fix order of static initialization: add comment 2023-02-17 11:58:40 +03:00
oltolm b5b8f7c35a fix order of static initialization 2023-02-17 11:58:40 +03:00
Megamouse de592267fe Create TEST12345/USRDIR for test elfs 2023-02-16 20:51:57 +01:00
Margen67 5bb89328d0 Remove whitespace 2023-02-15 08:58:02 +01:00
Megamouse 75fbb3d551 Qt: save last gs_frame screen and re-use it in no-gui mode 2023-02-14 08:03:26 +01:00
Megamouse eb0fb2fef7 Qt/CLI: Add option to select a screen for the game window 2023-02-14 08:03:26 +01:00
Megamouse b1b92e95ab Qt: move fullscreen cli arg from global to gs_frame 2023-02-14 08:03:26 +01:00
Megamouse 3a68b7ac0d Fix C00 boot from external HG games 2023-02-13 07:31:58 +01:00
Eladash 3976018980 Debugger: Print error codes stored in GPR 2023-02-12 23:10:20 +01:00
Megamouse 1dac708323 cellPad: move CellPadData to pad_types for convenience 2023-02-12 17:04:31 +01:00
Megamouse d4187242b9 cellPad/vsh: fix PS button recognition 2023-02-12 17:04:31 +01:00
Megamouse 61c0d81667 cellPad/vsh: fix sixaxis interaction
This allows you to influence the particles in the XMB
2023-02-12 17:04:31 +01:00
Megamouse c3e4b6da46 cellPad: allow opening the home menu with ldd pads
Speculative
2023-02-12 17:04:31 +01:00
Megamouse 38c7cf474d cellPad: add sanity check for indices in cellPadLdd functions 2023-02-12 17:04:31 +01:00
Megamouse ec4cbe5380 cellPad: simplify some code 2023-02-12 17:04:31 +01:00
Vestral 465ca97531 Fix displaying of audio device when it's disconnected 2023-02-10 04:19:20 +01:00
Megamouse 2f7a2d5134 Update hidapi from 0.13.0 to 0.13.1 2023-02-09 23:34:07 +01:00
Megamouse c1b5af21f0 Update SDL from 2.26.2 to 2.26.3 2023-02-09 23:34:07 +01:00
Megamouse cfb788941c overlays: review fixes 2023-02-09 20:36:35 +01:00
Megamouse 69435dd951 overlays: Fix linux compilation 2023-02-09 20:36:35 +01:00
Megamouse 3b09a39576 overlays: fix home menu date format 2023-02-09 20:36:35 +01:00
Megamouse 53f81ce9c7 overlays: fix home menu entry center alignment 2023-02-09 20:36:35 +01:00
Megamouse e064380a7d overlays: add message box to home menu 2023-02-09 20:36:35 +01:00
Megamouse 9375e255e1 overlays: add pages to home menu 2023-02-09 20:36:35 +01:00
Megamouse a56fc57a83 evdev: allow more buttons on sony guitar 2023-02-09 18:09:07 +01:00
Megamouse 4f75dc06b9 evdev: optimize get_next_button_press pad id check 2023-02-09 18:09:07 +01:00
Megamouse d3b7a5146c rsx_debugger: replace malloc with buffer cache
This also fixes a memory leak.
2023-02-09 00:02:43 +01:00
Megamouse bc487f43f7 cli: Add option to boot a game with a specific pad profile 2023-02-08 21:54:51 +01:00
Megamouse 088ac71930 cli: allow to start games in fullscreen
Technically this can be done through configs, but since it is easy to do, let's just add the cli option.
2023-02-08 21:54:51 +01:00
Eladash 27cad422b9 cellGame: Truncate excess of characters in TITLE_ID
In cellGameDataCheckCreate
2023-02-07 18:21:18 +03:00
Megamouse 5a63271f0e Fix openGl overlay colors 2023-02-07 13:40:47 +01:00
kd-11 9a35684507 rsx: Don't accept garbage shader input 2023-02-07 13:51:26 +03:00
RipleyTom 0d2714a1d9 FAudio cmake fix 2023-02-07 08:54:41 +01:00
Megamouse 553e4b28bf Disable 3rdparty FAudio if the SDL version is not available 2023-02-06 21:41:20 +01:00
Megamouse b579e45ed7 Update SDL download link 2023-02-06 21:41:20 +01:00
Megamouse 1662bcfae4 Qt: add copy context menu entry to log viewer 2023-02-06 20:43:53 +01:00
Elad Ashkenazi 8652b7d358 cellVdec: Fix AU decode count on callback (#13342) 2023-02-05 13:02:51 +01:00
Megamouse 1cf8aacc11 CLI: add option to boot rsx capture 2023-02-05 11:47:08 +01:00
Megamouse 13ef4c1236 Clarify unknown type log message 2023-02-05 09:36:56 +01:00
Megamouse 68dd25a4a5 Replace std::filesystem::u8path with TODO 2023-02-05 09:36:56 +01:00
Megamouse bcb1c6c080 Fix package installation exit condition after error occured 2023-02-05 09:36:56 +01:00
Megamouse 2372116acc Improve logging during package installations 2023-02-05 09:36:56 +01:00
kd-11 508dd2b509 rsx: Formatting change 2023-02-05 01:30:20 +03:00
kd-11 61b69eeed2 rsx/overlays: Force disable rounded rectangles on macOS 2023-02-05 01:30:20 +03:00
kd-11 dc8652806e rsx/overlays: Support disabling vertex-snap on a per-draw-call basis 2023-02-05 01:30:20 +03:00
kd-11 af6db7d895 rsx/overlays: Remove workaround no longer required 2023-02-05 01:30:20 +03:00
kd-11 64ec99be33 rsx: Unify UI rendering shaders 2023-02-05 01:30:20 +03:00
brian218 53e906d0f3 sys_game: Implemented sys_game_board_storage_write() & Made it modifiable 2023-02-04 22:19:59 +01:00
Elad Ashkenazi 780a6a7b40 sys_prx: Fix PRX module filename reading (#13339) 2023-02-04 21:24:24 +01:00
RipleyTom 737fbf661a Add more disconnected limitations in sys_net 2023-02-04 20:33:21 +01:00
kd-11 c0243a92d0 rsx/overlays: Re-export spinner
Fixes some incorrect black pixels
2023-02-04 17:36:09 +01:00
Megamouse 3a8fcd1b0b Update wolfssl from 5.5.0 to 5.5.4 2023-02-04 16:57:22 +01:00
Megamouse 04d9693b51 Update FAudio from 22.08 to 23.02 2023-02-04 16:57:22 +01:00
Nicolas Adenis-Lamarre 0023716053 - fix analog_t required for some games
- changing mapping to better fit to games (for example, select is rarely used)

Signed-off-by: Nicolas Adenis-Lamarre <nicolas.adenis.lamarre@gmail.com>
2023-02-04 16:10:40 +01:00
kd-11 37b46df738 rsx/overlays/message-queue: Do not fade albedo, only alpha 2023-02-04 16:08:20 +03:00
illusion0001 ba06198012 GUI: Remove ANSI color code from TTY 2023-02-04 10:51:19 +01:00
Elad Ashkenazi acf9caa4ae Game list: Use the latest game icon instead of 1.00's (#13340) 2023-02-04 07:41:30 +01:00
Megamouse d69e345bc0 SDL: Fix button presses
SDL currently always returns 1 if a button is pressed
2023-02-03 18:24:54 +01:00
Megamouse c5b25fa132 Remove obsolete shader compilation hint settings 2023-02-03 09:27:04 +01:00
kd-11 262ecda2bf rsx/overlays: Cache the loading animated icon
Smooths out some frametime spikes when showing the notification message
2023-02-03 09:13:27 +03:00
kd-11 f243539362 rsx/overlays: Make animation caching possible 2023-02-03 09:13:27 +03:00
kd-11 ec33891f6f rsx/overlays: Work around some commitee grandstanding shenanigans
ffs: https://bugs.launchpad.net/ubuntu/+source/gcc-8/+bug/1831385
2023-02-03 09:13:27 +03:00
kd-11 f007188fa0 rsx/overlays: Minor fixups 2023-02-03 09:13:27 +03:00
kd-11 170fd813f0 rsx/overlays: Ellide redundant clock read 2023-02-03 09:13:27 +03:00
kd-11 4c59359cf7 rsx/overlays: Add time unit to some measured variables 2023-02-03 09:13:27 +03:00
kd-11 ddf25b7869 rsx/overlays: Shorten notification fadeout 2023-02-03 09:13:27 +03:00
kd-11 4914f3b2c3 rsx/overlays: Fix msvc project files 2023-02-03 09:13:27 +03:00
kd-11 08faed7d86 rsx/overlays: Naming sense 2023-02-03 09:13:27 +03:00
kd-11 989e0f28d6 rsx/overlays: Remove redundant resize 2023-02-03 09:13:27 +03:00
kd-11 54f41095e9 rsx/overlay: Some cleanup in animated icon class 2023-02-03 09:13:27 +03:00
kd-11 faaa72d398 rsx: Reverse declaration order for animated icon 2023-02-03 09:13:27 +03:00
kd-11 97b36eb53c cmake: Add shader compile notification to build system 2023-02-03 09:13:27 +03:00
kd-11 2503525016 rsx/overlays: Move shader compiling notification to a separate cpp file 2023-02-03 09:13:27 +03:00
kd-11 e96fedfefe rsx/overlays: Darken background a bit 2023-02-03 09:13:27 +03:00
kd-11 f279d8d6d1 rsx: Lower shader notification duration to 5 seconds 2023-02-03 09:13:27 +03:00
kd-11 4bfba20877 rsx: Remove unnecessary debug pragma 2023-02-03 09:13:27 +03:00
kd-11 504bd93007 rsx: Implement overlay message queue stacking 2023-02-03 09:13:27 +03:00
kd-11 2f2fe72808 ui: Add 24x24 loading animation 2023-02-03 09:13:27 +03:00
kd-11 5babf47e70 build: Fix linux build 2023-02-03 09:13:27 +03:00
kd-11 78a588cacb rsx: Use animated icon and message queue for shader compiler notifications 2023-02-03 09:13:27 +03:00
kd-11 205f8a6479 rsx: Fix message queue order and positioning 2023-02-03 09:13:27 +03:00
kd-11 34c1e1a9ad rsx: Add support for rounded rectangles 2023-02-03 09:13:27 +03:00
kd-11 0885884839 rsx: Implement notification queue 2023-02-03 09:13:27 +03:00
Elad Ashkenazi c471120a80 System.cpp: Do not create VFS directories outside of RPCS3 dir (#13311) 2023-01-29 11:29:26 +01:00
Megamouse 3202cc7021 overlays/osk: fix y offset direction facepalm 2023-01-28 23:13:51 +01:00
Megamouse fc34b3f144 cellOskDialog: improve kb event hook callback 2023-01-28 21:51:36 +01:00
Megamouse fb974a4551 overlays/osk: clear on enter 2023-01-28 21:51:36 +01:00
Megamouse 3d99fea575 overlays/osk: Do not render preview background in separate windows mode 2023-01-28 21:51:36 +01:00
Megamouse feb04c64a8 overlays/osk: show dialog on key input 2023-01-28 21:51:36 +01:00
Megamouse d3dbf9e83f overlays/osk: Clear text properly in continuous mode 2023-01-28 21:51:36 +01:00
Megamouse 9b6d7a5275 cellOskDialog: don't wait for osk event_hook_callback 2023-01-28 21:51:36 +01:00
Megamouse 754fd03750 overlays/osk: hide title in separate window mode 2023-01-28 21:51:36 +01:00
Megamouse adfa5d63bb overlays/osk: fix dialog positioning 2023-01-28 21:51:36 +01:00
Megamouse 81ff956a48 cellOskDialog: use getter instead of weak_ptr 2023-01-28 21:51:36 +01:00
Megamouse daefe27fec overlays: allow keyboard input if pads are intercepted 2023-01-28 21:51:36 +01:00
Megamouse 6106ff8b31 overlays/osk: fix cellOskDialogExtInputDeviceLock 2023-01-28 21:51:36 +01:00
Megamouse 1e6c180690 overlays: don't flip if the overlay is invisible 2023-01-28 21:51:36 +01:00
Megamouse d1a950d59f overlays/osk: keep dialog open in continuous mode 2023-01-28 21:51:36 +01:00
Megamouse 641fadc1fb overlays/osk: handle input device change 2023-01-28 21:51:36 +01:00
Megamouse 5299061282 overlays/osk: visualize separate windows 2023-01-28 21:51:36 +01:00
kd-11 d0c029c83e rsx: Ignore bad face mode configuration 2023-01-28 21:11:32 +03:00
Eladash 5aced2e44b Print LV2 Syscall Usage only if increased since last print 2023-01-28 17:21:45 +01:00
Ani 18ab42dc83 readme: Remove Azure, rename Cirrus CI badges 2023-01-28 14:43:54 +01:00
Jan Beich a748508bc9 evdev_gun: enable udev code on BSDs
libudev is supported on DragonFly and FreeBSD, similar to libevdev.
NetBSD and OpenBSD can simply disable both via -DUSE_LIBEVDEV=OFF.

This reverts commit 337ee77a99.
2023-01-28 12:35:42 +00:00
shinra-electric 8b453e6a3b Update MoltenVK to 1.2.2 (VK 1.3.239) 2023-01-28 11:52:11 +00:00
Jan Beich 3196ada481 vk: Drop deprecated (239) and removed (240) header
In file included from rpcs3/Emu/System.cpp:55:
rpcs3/Emu/RSX/VK/VulkanAPI.h:17:10: fatal error: 'vulkan/vk_sdk_platform.h' file not found
 #include <vulkan/vk_sdk_platform.h>
          ^~~~~~~~~~~~~~~~~~~~~~~~~~
2023-01-28 01:26:12 +01:00
RipleyTom 7c4734c24d Fix SDL CMake warnings 2023-01-27 17:02:13 +01:00
Malcolm Jestadt 813f7b50c1 SPU LLVM: Minor SUMB AVX-512 path optimization
- Tweak shuffle to allow LLVM to emit a cheap blend instruction instead of the expensive VPERMI2W instruction
2023-01-27 13:06:48 +03:00
Megamouse 6532d2b650 input: fix dynamic pad handler mode reset 2023-01-27 00:13:20 +01:00
Megamouse e0baad417a perfoverlay: fix minimal graph min/max calculation 2023-01-27 00:13:20 +01:00
Megamouse f6759de1e9 Qt: fix vfs dialog usb tab cast 2023-01-26 21:33:39 +01:00
brian218 7648171fe2 sys_usbd: Improved usb_handler_thread timing 2023-01-26 15:52:45 +01:00
brian218 71c3264ea7 USIO: Always resize the response vector to the given size 2023-01-26 15:52:45 +01:00
brian218 49455965c0 sys_game: Implemented sys_game_get_rtc_status() & Updated sys_game_board_storage_read() 2023-01-26 15:52:45 +01:00
kd-11 5f0467b084 rsx: Remove framebuffer_status_valid flag and move to state 2023-01-26 11:42:39 +03:00
kd-11 6adcabda29 rsx: Fix graphics state foot-gun 2023-01-26 11:42:39 +03:00
Anton Kourganov 52b67b46fc Allow unbinding pad buttons by right clicking (#13283) 2023-01-23 22:00:08 +01:00
MSuih a700e3b39e Whitelist EA Active 2 accessories 2023-01-23 17:02:51 +01:00
Egor Romanovskiy 4fbbdf8732 sys_usbd: fix usb handler deinitialization 2023-01-23 12:32:09 +01:00
Elad Ashkenazi 9a91fef337 Deprecate HDD0/disc, make RPCS3/games movable (#13265) 2023-01-23 08:00:46 +01:00
nastys 53cc067c17 macOS CI: move to AArch64 VM (x64 builds) (#13155) 2023-01-21 21:21:41 +01:00
Megamouse d3183708e8 overlays: do not open home menu in VSH 2023-01-21 09:11:53 +01:00
Megamouse 44771150b7 overlays: add simple home menu 2023-01-21 09:11:53 +01:00
Megamouse ac2b2d82d2 overlays/osk: move pointer variables to fxo 2023-01-20 23:41:56 +01:00
Megamouse 11c42eb8d4 overlays/osk: add analog movement if CELL_OSKDIALOG_NO_INPUT_ANALOG is unset 2023-01-20 23:41:56 +01:00
Megamouse dc0230c476 overlays/osk: Fix layout and positioning 2023-01-20 23:41:56 +01:00
Megamouse 0a8c343e6e cellOskDialog: cellOskDialogSetSeparateWindowOption 2023-01-20 23:41:56 +01:00
Megamouse afdbef1a81 cellOskDialog: improve dictionary functions 2023-01-20 23:41:56 +01:00
Megamouse f659338e5e overlays/osk: implement first osk pointer 2023-01-20 23:41:56 +01:00
Megamouse 4a82d81efe overlays/osk: implement scaling 2023-01-20 23:41:56 +01:00
Megamouse 6b4208be9b overlays/osk: align osk position 2023-01-20 23:41:56 +01:00
Megamouse 3d78c7f43a cellOskDialog: fix error check and improve initialKeyLayout logic 2023-01-20 23:41:56 +01:00
Megamouse 34df4509af overlays/osk: implement "support languages"
Some languages/panels in the osk need to be activated by the developer.
They are not available otherwise.
So let's check if they were pre-configured and only add the panels if they are supported.
2023-01-20 23:41:56 +01:00
brian218 6c3b2b938a Fix emucore.vcxproj.filters 2023-01-20 13:05:33 +03:00
Egor Romanovskiy 59588b12c8 Fix BUILDING.md Vulkan SDK Version 2023-01-19 23:35:00 +01:00
Jan Beich 3eaa29e7c2 Thread: silence -Wsign-compare on FreeBSD aarch64
Utilities/Thread.cpp:1805:48: error: comparison of integers of different signs: 'uptr' (aka 'unsigned long') and '__register_t' (aka 'long') [-Werror,-Wsign-compare]
        const bool is_executing = uptr(info->si_addr) == RIP(context);
                                  ~~~~~~~~~~~~~~~~~~~ ^  ~~~~~~~~~~~~
2023-01-19 18:49:06 +03:00
brian218 c3c2bb7ad8 USIO: Do not crash simply due to invalid commands 2023-01-18 08:09:35 +01:00
brian218 0e64c74a78 sys_usbd: Added buffer null check for sys_usbd_transfer_data() 2023-01-18 08:09:35 +01:00
brian218 d2dc57585c sys_fs: Improved sys_fs_fcntl(0xc0000015&0xc000001c) 2023-01-18 08:09:35 +01:00
brian218 e0fe7989e9 USB: Added VFS USB mass storage devices to the USB device list 2023-01-18 08:09:35 +01:00
Megamouse 709305df0e overlays: fix indentation 2023-01-18 00:24:00 +01:00
Megamouse 24445123fc Recording: Fix possible encoder segfault 2023-01-18 00:24:00 +01:00
Megamouse 450a64b58b cellUserInfo: fix potentially locked user dialogs 2023-01-18 00:24:00 +01:00
Megamouse b3a9c8d83f input: move config getters to base class 2023-01-17 02:01:03 +01:00
Megamouse 35c92723c7 input: fix ds4 led/rumble in pad settings 2023-01-17 01:07:57 +01:00
kd-11 719e7a9d56 rsx: Fix inadvertent signal override for MSAA 2023-01-17 02:24:21 +03:00
kd-11 eed9e56bf4 rsx: Allow vertex fetch from uninitialized register 2023-01-17 02:24:21 +03:00
Megamouse 1d214ed26a Qt: add system cmd dialog 2023-01-16 17:26:05 +01:00
Megamouse 2d9d92b7d7 Qt: Remove CELL_SYSUTIL_SYSTEM_MENU_CLOSE menu option
This never made any sense
2023-01-16 17:26:05 +01:00
xperia64 240cb2d627 Add output scaling filtering options, migrate FSR checkbox to these options 2023-01-16 13:52:51 +01:00
kd-11 bd69466e94 rsx: Fix some pipe state signal propagation routines 2023-01-16 15:20:53 +03:00
Elad Ashkenazi e6a7597444 PRX/savestates/VSH: Associate PRX with library, fix VSH browser (#13221)
* PRX/savestate:s Associate PRX with library

* VFS/Savestates: Fix host_root resolving

* Savestates: Fix segfault in ppu_initialize

* SPU: Avoid some log spam

* VSH/sys_vm: Fix limit on root processes
2023-01-15 23:12:54 +03:00
Megamouse 1852b370d7 Qt: Allow users to create shortcuts for existing bootable entries even after canceling the installation 2023-01-11 19:26:13 +01:00
Megamouse 12fa7c41f6 Qt: more package install fixes
- Clean directories if fill_path fails
- Fix check_target_app_version when installing multiple packages (compromise: no more optimized singular file installs for now)
2023-01-11 19:26:13 +01:00
Megamouse fc85ed8730 Qt: fix some package install cancellation issues
- Abort installation if any thread has errors
- Only clean the directories of packages that actually had errors
- Additionally clean the directories of packages that were cancelled before they could finish
- Clear boot path in case of error or cancelation
- Propagate result to caller
- Skip success message if the installation was canceled
2023-01-11 19:26:13 +01:00
Megamouse 65f10ff840 package_reader: fix some warnings 2023-01-11 19:26:13 +01:00
kd-11 6809d84a00 vk: Bump max number of suppported inline draw calls to 32k
- Surprisingly some games actually exhaust the entire 16k pool causing slowdown
2023-01-11 16:48:53 +03:00
kd-11 2752cd1390 rsx/vk: Fix some problems with dynamic state updates 2023-01-11 16:48:53 +03:00
kd-11 10b56415e8 vk: Avoid loading the whole dynamic state properties if only the shader changed
- Handles a common case where a game engine switches materials but uses the same configuration
  e.g rendering two types of wall or ground may need different shaders but similar state properties
2023-01-11 16:48:53 +03:00
kd-11 bd87c80943 rsx: Simplify the debug overlay print text routines.
- Greatly simplifies adding text
2023-01-11 16:48:53 +03:00
kd-11 f71e7ef1cc vk: Switch programs if the primitive type changed
- This will change when EXT_dynamic_state is integrated
2023-01-11 16:48:53 +03:00
kd-11 756ad17c2c Fix GCC11 compilation 2023-01-11 16:48:53 +03:00
kd-11 29c1b20b41 Fix compilation 2023-01-11 16:48:53 +03:00
kd-11 aa5097e0d4 glsl: Update fog enums in shaders 2023-01-11 16:48:53 +03:00
kd-11 2ccfee2e45 rsx: Propagate decode failures up the chain.
- Dumping invalid data should not crash
2023-01-11 16:48:53 +03:00
kd-11 bf1311b902 Fix GCC compilation 2023-01-11 16:48:53 +03:00
kd-11 71efb3bc84 rsx: Use gcm cast to handle input enum validation 2023-01-11 16:48:53 +03:00
kd-11 439bdde849 rsx: Fix printing of expected values 2023-01-11 16:48:53 +03:00
kd-11 c7fed20f3c vk: Short-circuit program load if state did not change
- TODO: Incorporate VK_EXT_extended_dynamic_state
2023-01-11 16:48:53 +03:00
kd-11 3dd6e5664c rsx: Do not call a dynamic function to simply test-and-set. Do it inline. 2023-01-11 16:48:53 +03:00
kd-11 d4ee308ffd vk: Fix rare crash when handling mixed depth format types 2023-01-11 16:48:53 +03:00
kd-11 a272f3e3b9 rsx: Improve performance by using an integral type to indicate error 2023-01-11 16:48:53 +03:00
kd-11 f6027719d2 rsx: Fix vertex decode 2023-01-11 16:48:53 +03:00
kd-11 38402e78c0 rsx: Fixup vertex enums in shaders 2023-01-11 16:48:53 +03:00
kd-11 eae1ac6558 refactor: Fix build 2023-01-11 16:48:53 +03:00
kd-11 0b019401bd Refactor gcm enums 2023-01-11 16:48:53 +03:00
kd-11 73cda2324a rsx/lv2: Refactor DMA control stuff after VSH work 2023-01-11 16:48:53 +03:00
kd-11 3dba894369 rsx: Minor refactoring RSXThread
- Part 1 of many
2023-01-11 16:48:53 +03:00
brian218 659ee81e80 USIO: Backup file fixup 2023-01-11 09:10:09 +01:00
Megamouse 99c307a68e Qt: move progress indication to own handler 2023-01-11 05:43:34 +01:00
Megamouse d28eea342a Qt: use play_sound callback instead of QSound 2023-01-10 20:57:32 +01:00
Megamouse 29078bc428 cellMouse: decrease some log levels 2023-01-10 20:57:32 +01:00
Megamouse 6bcb5ae66b Fix some includes and warnings 2023-01-10 20:57:32 +01:00
brian218 cf1e68fb0e USIO: Emulation Performance Optimization 2023-01-10 21:35:12 +03:00
Megamouse 43288a6760 Qt: add shortcut manager 2023-01-10 19:28:26 +01:00
UltraHDR 9ee7d04560 Set LSApplicationCategoryType to games
https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype
This makes it show up in the macOS Launchpad Games folder
2023-01-10 20:53:43 +03:00
Megamouse 16bf215d8a pine: fix some warnings 2023-01-10 19:51:25 +03:00
Megamouse 99ed77da8c pine: simplify writing strings 2023-01-10 19:51:25 +03:00
Megamouse b9f733c401 pine: replace goto with lambda 2023-01-10 19:51:25 +03:00
Megamouse 18966d424c Input: ignore F11 in keyboard pad handler (starts/stops recordings) 2023-01-09 21:06:51 +01:00
Megamouse c725b886e1 Qt: Disable ESC to exit fullscreen if kb shortcuts are disabled 2023-01-09 21:06:51 +01:00
Megamouse 9b7eaf5117 Improve error logging 2023-01-09 21:06:51 +01:00
Elad Ashkenazi 36a55660bf Unbreak BSD 2023-01-09 20:20:13 +01:00
Elad Ashkenazi 0946e5945f VSH Improvements (#13172)
* sys_prx: Implement PRX LIB register syscall

* VSH: partial log spam fix

* sys_process reboot fix

* Implement sys_memory_container_destroy_parent_with_childs

* sys_net: Implement SO_RCVTIMEO/SO_SENDTIMEO

* VSH: Implement sys_rsx_context_free

* PPU LLVM: distinguish PPU cache exec also by address

Fixes referencing multiple PRX.

* UI: Do not report size of apps inside /dev_flash
2023-01-09 20:03:01 +03:00
Ani 8ec1a5627d AppImage: Do not bundle libgmodule-2.0 and libglib-2.0 2023-01-09 08:19:53 +01:00
369 changed files with 14150 additions and 7258 deletions
+41 -11
View File
@@ -1,15 +1,43 @@
#!/bin/sh -ex
brew update
brew install llvm@13 sdl2 nasm qt@5 ninja cmake glew git p7zip create-dmg ccache
brew install -f --overwrite llvm@14 nasm ninja git p7zip create-dmg ccache
export MACOSX_DEPLOYMENT_TARGET=11.6
#/usr/sbin/softwareupdate --install-rosetta --agree-to-license
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/homebrew/bin/brew install -f --overwrite llvm@14 sdl2 glew cmake
#export MACOSX_DEPLOYMENT_TARGET=12.0
export CXX=clang++
export CC=clang
export Qt5_DIR="/usr/local/opt/qt@5/lib/cmake/Qt5"
export PATH="/usr/local/opt/llvm@13/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin"
export LDFLAGS="-L/usr/local/opt/llvm@13/lib -Wl,-rpath,/usr/local/opt/llvm@13/lib"
export CPPFLAGS="-I/usr/local/opt/llvm@13/include -msse -msse2 -mcx16 -no-pie"
export BREW_PATH;
BREW_PATH="$(brew --prefix)"
export BREW_X64_PATH;
BREW_X64_PATH="$("/usr/local/homebrew/bin/brew" --prefix)"
export BREW_BIN="/usr/local/bin"
export BREW_SBIN="/usr/local/sbin"
export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=X86'
export WORKDIR;
WORKDIR="$(pwd)"
#get Qt
git clone https://github.com/engnr/qt-downloader.git
cd qt-downloader
git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597
pip3 install pipenv
pipenv run pip3 install py7zr requests semantic_version lxml
pipenv run ./qt-downloader macos desktop 5.15.2 clang_64 --opensource
cd ..
export Qt5_DIR="$WORKDIR/qt-downloader/5.15.2/clang_64/lib/cmake/Qt5"
export SDL2_DIR="$BREW_X64_PATH/opt/sdl2/lib/cmake/SDL2"
export PATH="$BREW_PATH/opt/llvm@14/bin:$WORKDIR/qt-downloader/5.15.2/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib"
export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie"
export LIBRARY_PATH="$BREW_X64_PATH/lib"
export LD_LIBRARY_PATH="$BREW_X64_PATH/lib"
git submodule update --init --recursive --depth 1
@@ -18,16 +46,18 @@ sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVer
mkdir build && cd build || exit 1
cmake .. \
"$BREW_X64_PATH/bin/cmake" .. \
-DUSE_SDL=ON -DUSE_DISCORD_RPC=OFF -DUSE_VULKAN=ON -DUSE_ALSA=OFF -DUSE_PULSE=OFF -DUSE_AUDIOUNIT=ON \
-DLLVM_CCACHE_BUILD=OFF -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF \
-DLLVM_CCACHE_BUILD=OFF -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF \
-DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF \
-DLLVM_INCLUDE_UTILS=OFF -DLLVM_USE_PERF=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF \
-DUSE_NATIVE_INSTRUCTIONS=OFF \
-DUSE_SYSTEM_MVK=OFF \
$CMAKE_EXTRA_OPTS \
-DLLVM_TARGET_ARCH=X86_64 -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \
-G Ninja
ninja; build_status=$?;
"$BREW_PATH/bin/ninja"; build_status=$?;
cd ..
@@ -36,4 +66,4 @@ cd ..
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
.ci/deploy-mac.sh
fi
fi
+8 -4
View File
@@ -18,10 +18,14 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
# Embed newer libstdc++ for distros that don't come with it (ubuntu 16.04)
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
rm -f ./appdir/usr/lib/libwayland-client.so*
# Remove libgmodule and libglib due to g_source_set_static_name symbol conflict with host libraries
rm -f ./appdir/usr/lib/libgmodule-2.0.so*
rm -f ./appdir/usr/lib/libglib-2.0.so*
# Install latest appimage runner
rm ./appdir/AppRun
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
@@ -39,7 +43,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
# Package it up and send it off
./squashfs-root/usr/bin/appimagetool "$APPDIR"
ls
COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp)
@@ -57,7 +61,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
FILESIZE=$(stat -c %s ./rpcs3*.AppImage)
SHA256SUM=$(sha256sum ./rpcs3*.AppImage | awk '{ print $1 }')
echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE"
fi
if [ "$DEPLOY_PPA" = "true" ]; then
+5 -3
View File
@@ -14,7 +14,9 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
cd bin
mkdir "rpcs3.app/Contents/lib/"
cp "/usr/local/opt/llvm@13/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
cp "/usr/local/Homebrew/opt/llvm@14/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
@@ -33,7 +35,7 @@ echo "IconIndex=0" >> Quickstart.url
DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg"
create-dmg --volname RPCS3 \
/opt/homebrew/bin/create-dmg --volname RPCS3 \
--window-size 800 400 \
--icon-size 100 \
--icon rpcs3.app 200 190 \
@@ -45,7 +47,7 @@ create-dmg --volname RPCS3 \
"$DMG_FILEPATH" \
RPCS3.app
7z a -mx9 rpcs3-v"${COMM_TAG}"-"${COMM_COUNT}"-"${COMM_HASH}"_macos.7z RPCS3.app
/opt/homebrew/bin/7z a -mx9 rpcs3-v"${COMM_TAG}"-"${COMM_COUNT}"-"${COMM_HASH}"_macos.7z RPCS3.app
FILESIZE=$(stat -f %z "$DMG_FILEPATH")
SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }')
+6 -4
View File
@@ -120,15 +120,17 @@ freebsd_task:
script: "./.ci/build-freebsd.sh"
macos_task:
timeout_in: 120m
timeout_in: 12000m
ccache_cache:
folder: /tmp/ccache_dir
matrix:
- name: Cirrus macOS
osx_instance:
image: monterey-xcode-13.3
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
mac_script:
- mkdir artifacts
- chmod +x ".ci/build-mac.sh"
- chmod +x ".ci/deploy-mac.sh"
- ".ci/build-mac.sh"
env:
BUILD_ARTIFACTSTAGINGDIRECTORY: ${CIRRUS_WORKING_DIR}/artifacts
@@ -141,7 +143,7 @@ macos_task:
RELEASE_MESSAGE: "../GitHubReleaseMessage.txt"
artifacts:
name: Artifact
path: "artifacts/rpcs3-*_macos.*"
path: "artifacts/*"
push_script: |
if [ "$CIRRUS_REPO_OWNER" = "RPCS3" ] && [ -z "$CIRRUS_PR" ] && [ "$CIRRUS_BRANCH" = "master" ]; then
source './.ci/export-cirrus-vars.sh'
@@ -29,8 +29,8 @@ body:
Please provide the _exact_ build (or commit) information that introduced the regression you're reporting.
* Please see [How to find the build that caused a regression](https://wiki.rpcs3.net/index.php?title=Help:Using_different_versions_of_RPCS3#How_to_find_the_build_that_caused_a_regression.3F) in our wiki.
* You can find [History of RPCS3 builds](https://rpcs3.net/compatibility?b) here.
Make sure you're running with settings as close to default as possible
Make sure you're running with settings as close to default as possible
* **Do NOT enable any emulator game patches when reporting issues**
* Only change settings that are required for the game to work
- type: input
@@ -48,7 +48,7 @@ body:
Obtaining the log file:
* Run the game until you find the regression.
* Completely close RPCS3 and locate the log file.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
@@ -70,21 +70,21 @@ body:
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
* Captures will be stored in RPCS3 folder → captures.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
- type: textarea
- type: textarea
id: captures
attributes:
label: Attach capture files for visual issues
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
validations:
required: false
- type: textarea
- type: textarea
id: system-info
attributes:
label: System configuration
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
validations:
required: false
- type: textarea
- type: textarea
id: other-details
attributes:
label: Other details
+6 -6
View File
@@ -28,13 +28,13 @@ body:
attributes:
value: |
# Log files
Provide a log file that includes the bug you're reporting.
Obtaining the log file:
* Run the game until you find the regression.
* Completely close RPCS3 and locate the log file.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
@@ -55,21 +55,21 @@ body:
* To create an RSX capture, use _Create_ _RSX_ _Capture_ under _Utilities_.
* Captures will be stored in RPCS3 folder → captures.
* To create a RenderDoc capture, please refer to [RenderDoc's documentation](https://renderdoc.org/docs/how/how_capture_frame.html).
- type: textarea
- type: textarea
id: captures
attributes:
label: Attach capture files for visual issues
description: Compress your capture with 7z, Rar etc. and attach it here, or upload it to the cloud (Dropbox, Mega etc) and add a link to it.
validations:
required: false
- type: textarea
- type: textarea
id: system-info
attributes:
label: System configuration
description: Provide information about your system, such as operating system, CPU and GPU model, GPU driver version and other information that describes your system configuration.
validations:
required: false
- type: textarea
- type: textarea
id: other-details
attributes:
label: Other details
+1 -1
View File
@@ -29,7 +29,7 @@ body:
* Why your feature is important to RPCS3.
* If the feature is implemented in other projects, attach screenshots.
* If this feature is something that a game is trying to use, upload a log file for it.
RPCS3's Log file will be ```RPCS3.log.gz``` (sometimes shows as RPCS3.log with zip icon) or ```RPCS3.log``` (sometimes shows as RPCS3 wtih notepad icon).
* On Windows it will be in the RPCS3 directory near the executable
* On Linux it will be in ```~/.cache/rpcs3/```
+3
View File
@@ -122,6 +122,9 @@ yaml-cpp.pc
/3rdparty/libusb_cmake/config.h
/3rdparty/libusb_cmake/libusb-1.0.pc
# miniupnp
/3rdparty/miniupnp/x64/*
# ssl certificate
cacert.pem
+3
View File
@@ -80,3 +80,6 @@
path = 3rdparty/libsdl-org/SDL
url = https://github.com/libsdl-org/SDL.git
ignore = dirty
[submodule "3rdparty/miniupnp/miniupnp"]
path = 3rdparty/miniupnp/miniupnp
url = https://github.com/miniupnp/miniupnp.git
+21 -10
View File
@@ -142,7 +142,7 @@ add_subdirectory(SoundTouch EXCLUDE_FROM_ALL)
# libevdev
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
if(USE_LIBEVDEV)
pkg_check_modules(LIBEVDEV libevdev)
pkg_check_modules(LIBEVDEV libevdev libudev)
if(LIBEVDEV_FOUND)
add_library(3rdparty_libevdev INTERFACE)
target_compile_definitions(3rdparty_libevdev INTERFACE -DHAVE_LIBEVDEV)
@@ -222,21 +222,28 @@ set(FAUDIO_TARGET 3rdparty_dummy_lib)
if(USE_FAUDIO)
# FAudio depends on SDL2
find_package(SDL2)
if (NOT SDL2_FOUND OR SDL2_VERSION VERSION_LESS 2.0.12)
message(WARNING
"-- RPCS3: FAudio requires SDL 2.0.9 or newer. Please note, this warning"
"can also be displayed with SDL2 versions between 2.0.9-2.0.12, as the"
"CMake config files are not correctly installed. Since a valid SDL2"
">=2.0.9 version cannot be found, building with FAudio will be skipped.")
set(USE_FAUDIO False)
else()
if (USE_SYSTEM_FAUDIO)
if (USE_SYSTEM_FAUDIO)
if (NOT SDL2_FOUND OR SDL2_VERSION VERSION_LESS 2.0.12)
message(WARNING
"-- RPCS3: System FAudio requires SDL 2.0.9 or newer. Please note, this warning"
"can also be displayed with SDL2 versions between 2.0.9-2.0.12, as the"
"CMake config files are not correctly installed. Since a valid SDL2"
">=2.0.9 version cannot be found, building with FAudio will be skipped.")
set(USE_FAUDIO OFF CACHE BOOL "Disabled using system FAudio with SDL < 2.0.12" FORCE)
else()
message(STATUS "RPCS3: Using system FAudio")
find_package(FAudio REQUIRED CONFIGS FAudioConfig.cmake FAudio-config.cmake)
add_library(3rdparty_FAudio INTERFACE)
target_link_libraries(3rdparty_FAudio INTERFACE FAudio)
target_compile_definitions(3rdparty_FAudio INTERFACE -DHAVE_FAUDIO)
set(FAUDIO_TARGET 3rdparty_FAudio)
endif()
else()
if (NOT SDL2_FOUND OR SDL2_VERSION VERSION_LESS 2.24.0)
message(WARNING
"-- RPCS3: 3rdparty FAudio requires SDL 2.24.0 or newer. Since a valid SDL2"
">=2.24.0 version cannot be found, building with FAudio will be skipped.")
set(USE_FAUDIO OFF CACHE BOOL "Disabled FAudio with SDL < 2.24.0" FORCE)
else()
message(STATUS "RPCS3: Using builtin FAudio")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared library")
@@ -351,6 +358,9 @@ if(USE_SDL)
endif()
endif()
# MINIUPNP
add_subdirectory(miniupnp EXCLUDE_FROM_ALL)
# add nice ALIAS targets for ease of use
if(USE_SYSTEM_LIBUSB)
add_library(3rdparty::libusb ALIAS usb-1.0-shared)
@@ -378,3 +388,4 @@ add_library(3rdparty::wolfssl ALIAS wolfssl)
add_library(3rdparty::libcurl ALIAS libcurl)
add_library(3rdparty::soundtouch ALIAS soundtouch)
add_library(3rdparty::sdl2 ALIAS ${SDL2_TARGET})
add_library(3rdparty::miniupnpc ALIAS libminiupnpc-static)
+1 -1
View File
@@ -4,7 +4,7 @@ include(ExternalProject)
ExternalProject_Add(moltenvk
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
GIT_TAG a307b24
GIT_TAG fb581e4
BUILD_IN_SOURCE 1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
+3 -3
View File
@@ -1,11 +1,11 @@
option(SDL2_DISABLE_SDL2MAIN "" ON)
option(SDL2_DISABLE_INSTALL "" ON)
option(SDL2_DISABLE_UNINSTALL "" ON)
set(SDL_SHARED OFF)
option(SDL_SHARED OFF)
set(SDL_SHARED_ENABLED_BY_DEFAULT OFF)
set(SDL_STATIC ON)
option(SDL_STATIC ON)
set(SDL_STATIC_ENABLED_BY_DEFAULT ON)
set(SDL_TEST OFF)
option(SDL_TEST OFF)
set(SDL_TEST_ENABLED_BY_DEFAULT OFF)
set(OPT_DEF_LIBC ON)
add_subdirectory(SDL EXCLUDE_FROM_ALL)
+8
View File
@@ -0,0 +1,8 @@
option (UPNPC_BUILD_STATIC "Build static library" TRUE)
option (UPNPC_BUILD_SHARED "Build shared library" FALSE)
option (UPNPC_BUILD_TESTS "Build test executables" FALSE)
option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE)
option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
option (UPNPC_NO_INSTALL "Disable installation" TRUE)
add_subdirectory(miniupnp/miniupnpc EXCLUDE_FROM_ALL)
Vendored Submodule
+1
+138
View File
@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{5228f863-e0dd-4de7-aa7b-5c52b14cd4d0}</ProjectGuid>
<RootNamespace>miniupnpcstatic</RootNamespace>
</PropertyGroup>
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(SolutionDir)\buildfiles\msvc\common_default_macros.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WINSOCK_DEPRECATED_NO_WARNINGS; _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
<PreBuildEvent>
<Command>cd $(ProjectDir)miniupnp\miniupnpc\msvc
cscript genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WINSOCK_DEPRECATED_NO_WARNINGS; _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>generated;miniupnp\miniupnpc;miniupnp\miniupnpc\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PreBuildEvent>
<Command>cd $(ProjectDir)miniupnp\miniupnpc\msvc
cscript genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="miniupnp\miniupnpc\src\addr_is_reserved.c" />
<ClCompile Include="miniupnp\miniupnpc\src\connecthostport.c" />
<ClCompile Include="miniupnp\miniupnpc\src\igd_desc_parse.c" />
<ClCompile Include="miniupnp\miniupnpc\src\minisoap.c" />
<ClCompile Include="miniupnp\miniupnpc\src\minissdpc.c" />
<ClCompile Include="miniupnp\miniupnpc\src\miniupnpc.c" />
<ClCompile Include="miniupnp\miniupnpc\src\miniwget.c" />
<ClCompile Include="miniupnp\miniupnpc\src\minixml.c" />
<ClCompile Include="miniupnp\miniupnpc\src\portlistingparse.c" />
<ClCompile Include="miniupnp\miniupnpc\src\receivedata.c" />
<ClCompile Include="miniupnp\miniupnpc\src\upnpcommands.c" />
<ClCompile Include="miniupnp\miniupnpc\src\upnpdev.c" />
<ClCompile Include="miniupnp\miniupnpc\src\upnperrors.c" />
<ClCompile Include="miniupnp\miniupnpc\src\upnpreplyparse.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="miniupnp\miniupnpc\src\addr_is_reserved.h" />
<ClInclude Include="miniupnp\miniupnpc\src\connecthostport.h" />
<ClInclude Include="miniupnp\miniupnpc\include\igd_desc_parse.h" />
<ClInclude Include="miniupnp\miniupnpc\src\minisoap.h" />
<ClInclude Include="miniupnp\miniupnpc\src\minissdpc.h" />
<ClInclude Include="miniupnp\miniupnpc\include\miniupnpc.h" />
<ClInclude Include="miniupnp\miniupnpc\miniupnpcstrings.h" />
<ClInclude Include="miniupnp\miniupnpc\include\miniupnpctypes.h" />
<ClInclude Include="miniupnp\miniupnpc\include\miniupnpc_declspec.h" />
<ClInclude Include="miniupnp\miniupnpc\include\miniwget.h" />
<ClInclude Include="miniupnp\miniupnpc\src\miniwget_private.h" />
<ClInclude Include="miniupnp\miniupnpc\src\minixml.h" />
<ClInclude Include="miniupnp\miniupnpc\include\portlistingparse.h" />
<ClInclude Include="miniupnp\miniupnpc\src\receivedata.h" />
<ClInclude Include="miniupnp\miniupnpc\include\upnpcommands.h" />
<ClInclude Include="miniupnp\miniupnpc\include\upnpdev.h" />
<ClInclude Include="miniupnp\miniupnpc\include\upnperrors.h" />
<ClInclude Include="miniupnp\miniupnpc\include\upnpreplyparse.h" />
<ClInclude Include="miniupnp\miniupnpc\src\win32_snprintf.h" />
<ClInclude Include="miniupnp\miniupnpc\rc_version.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
+94 -100
View File
@@ -24,6 +24,7 @@
#define close_portable(a) (close(a))
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <unistd.h>
#endif
@@ -79,13 +80,13 @@ namespace pine
* A preallocated buffer used to store all IPC replies.
* to the size of 50.000 MsgWrite64 IPC calls.
*/
char* m_ret_buffer;
std::vector<char> m_ret_buffer;
/**
* IPC messages buffer.
* A preallocated buffer used to store all IPC messages.
*/
char* m_ipc_buffer;
std::vector<char> m_ipc_buffer;
/**
* IPC Command messages opcodes.
@@ -118,8 +119,8 @@ namespace pine
*/
struct IPCBuffer
{
int size; /**< Size of the buffer. */
char* buffer; /**< Buffer. */
usz size{}; /**< Size of the buffer. */
char* buffer{}; /**< Buffer. */
};
/**
@@ -144,13 +145,30 @@ namespace pine
*/
IPCBuffer ParseCommand(char* buf, char* ret_buffer, u32 buf_size)
{
u32 ret_cnt = 5;
u32 buf_cnt = 0;
usz ret_cnt = 5;
usz buf_cnt = 0;
const auto error = [&]()
{
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
};
const auto write_string = [&](const std::string& str)
{
if (!SafetyChecks(buf_cnt, 0, ret_cnt, str.size() + 1 + sizeof(u32), buf_size))
return false;
ToArray(ret_buffer, ::narrow<u32>(str.size() + 1), ret_cnt);
ret_cnt += sizeof(u32);
memcpy(&ret_buffer[ret_cnt], str.data(), str.size());
ret_cnt += str.size();
ret_buffer[ret_cnt++] = '\0';
return true;
};
while (buf_cnt < buf_size)
{
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size))
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
return error();
buf_cnt++;
// example IPC messages: MsgRead/Write
// refer to the client doc for more info on the format
@@ -169,10 +187,10 @@ namespace pine
case MsgRead8:
{
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 1, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr(a))
goto error;
return error();
const u8 res = Impl::read8(a);
ToArray(ret_buffer, res, ret_cnt);
ret_cnt += 1;
@@ -182,10 +200,10 @@ namespace pine
case MsgRead16:
{
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 2, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<2>(a))
goto error;
return error();
const u16 res = Impl::read16(a);
ToArray(ret_buffer, res, ret_cnt);
ret_cnt += 2;
@@ -195,10 +213,10 @@ namespace pine
case MsgRead32:
{
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 4, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<4>(a))
goto error;
return error();
const u32 res = Impl::read32(a);
ToArray(ret_buffer, res, ret_cnt);
ret_cnt += 4;
@@ -208,10 +226,10 @@ namespace pine
case MsgRead64:
{
if (!SafetyChecks(buf_cnt, 4, ret_cnt, 8, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<8>(a))
goto error;
return error();
u64 res = Impl::read64(a);
ToArray(ret_buffer, res, ret_cnt);
ret_cnt += 8;
@@ -221,10 +239,10 @@ namespace pine
case MsgWrite8:
{
if (!SafetyChecks(buf_cnt, 1 + 4, ret_cnt, 0, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr(a, vm::page_writable))
goto error;
return error();
Impl::write8(a, FromArray<u8>(&buf[buf_cnt], 4));
buf_cnt += 5;
break;
@@ -232,10 +250,10 @@ namespace pine
case MsgWrite16:
{
if (!SafetyChecks(buf_cnt, 2 + 4, ret_cnt, 0, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<2>(a, vm::page_writable))
goto error;
return error();
Impl::write16(a, FromArray<u16>(&buf[buf_cnt], 4));
buf_cnt += 6;
break;
@@ -243,10 +261,10 @@ namespace pine
case MsgWrite32:
{
if (!SafetyChecks(buf_cnt, 4 + 4, ret_cnt, 0, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<4>(a, vm::page_writable))
goto error;
return error();
Impl::write32(a, FromArray<u32>(&buf[buf_cnt], 4));
buf_cnt += 8;
break;
@@ -254,31 +272,24 @@ namespace pine
case MsgWrite64:
{
if (!SafetyChecks(buf_cnt, 8 + 4, ret_cnt, 0, buf_size))
goto error;
return error();
const u32 a = FromArray<u32>(&buf[buf_cnt], 0);
if (!Impl::template check_addr<8>(a, vm::page_writable))
goto error;
return error();
Impl::write64(a, FromArray<u64>(&buf[buf_cnt], 4));
buf_cnt += 12;
break;
}
case MsgVersion:
{
char version[256] = {};
snprintf(version, sizeof(version), "RPCS3 %s", Impl::get_version_and_branch().c_str());
const u32 size = strlen(version) + 1;
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error;
ToArray(ret_buffer, size, ret_cnt);
ret_cnt += 4;
memcpy(&ret_buffer[ret_cnt], version, size);
ret_cnt += size;
if (!write_string("RPCS3 " + Impl::get_version_and_branch()))
return error();
break;
}
case MsgStatus:
{
if (!SafetyChecks(buf_cnt, 0, ret_cnt, 4, buf_size))
goto error;
return error();
EmuStatus status = Impl::get_status();
ToArray(ret_buffer, status, ret_cnt);
ret_cnt += 4;
@@ -287,72 +298,35 @@ namespace pine
}
case MsgTitle:
{
const auto title_string = Impl::get_title();
const auto size = title_string.size() + 1;
char* title = new char[size];
snprintf(title, size, "%s", title_string.c_str());
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error;
ToArray(ret_buffer, size, ret_cnt);
ret_cnt += 4;
memcpy(&ret_buffer[ret_cnt], title, size);
ret_cnt += size;
delete[] title;
if (!write_string(Impl::get_title()))
return error();
break;
}
case MsgID:
{
const auto title_id_string = Impl::get_title_ID();
const auto size = title_id_string.size() + 1;
char* title_id = new char[size];
snprintf(title_id, size, "%s", title_id_string.c_str());
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error;
ToArray(ret_buffer, size, ret_cnt);
ret_cnt += 4;
memcpy(&ret_buffer[ret_cnt], title_id, size);
ret_cnt += size;
delete[] title_id;
if (!write_string(Impl::get_title_ID()))
return error();
break;
}
case MsgUUID:
{
const auto hash_string = Impl::get_executable_hash();
const auto size = hash_string.size() + 1;
char* hash = new char[size];
snprintf(hash, size, "%s", hash_string.c_str());
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
goto error;
ToArray(ret_buffer, size, ret_cnt);
ret_cnt += 4;
memcpy(&ret_buffer[ret_cnt], hash, size);
ret_cnt += size;
delete[] hash;
if (!write_string(Impl::get_executable_hash()))
return error();
break;
}
case MsgGameVersion:
{
const auto game_version_string = Impl::get_app_version();
const auto size = game_version_string.size() + 1;
char* game_version = new char[size];
snprintf(game_version, size, "%s", game_version_string.c_str());
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size, buf_size))
goto error;
ToArray(ret_buffer, size, ret_cnt);
ret_cnt += 4;
memcpy(&ret_buffer[ret_cnt], game_version, size);
ret_cnt += size;
delete[] game_version;
if (!write_string(Impl::get_app_version()))
return error();
break;
}
default:
{
error:
return IPCBuffer{ 5, MakeFailIPC(ret_buffer) };
return error();
}
}
}
return IPCBuffer{ static_cast<int>(ret_cnt), MakeOkIPC(ret_buffer, ret_cnt) };
return IPCBuffer{ ret_cnt, MakeOkIPC(ret_buffer, ret_cnt) };
}
/**
@@ -361,26 +335,48 @@ namespace pine
* size: size of the IPC buffer.
* return value: buffer containing the status code allocated of size
*/
static inline char* MakeOkIPC(char* ret_buffer, uint32_t size = 5)
static inline char* MakeOkIPC(char* ret_buffer, usz size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ToArray(ret_buffer, ::narrow<u32>(size), 0);
ret_buffer[4] = IPC_OK;
return ret_buffer;
}
static inline char* MakeFailIPC(char* ret_buffer, uint32_t size = 5)
static inline char* MakeFailIPC(char* ret_buffer, usz size = 5)
{
ToArray<uint32_t>(ret_buffer, size, 0);
ToArray(ret_buffer, ::narrow<u32>(size), 0);
ret_buffer[4] = IPC_FAIL;
return ret_buffer;
}
/**
* Initializes an open socket for IPC communication.
* return value: -1 if a fatal failure happened, 0 otherwise.
* return value: false if a fatal failure happened, true otherwise.
*/
int StartSocket()
bool StartSocket()
{
::pollfd poll_fd{};
for (int pending_connection = 0; pending_connection != 1;)
{
if (thread_ctrl::state() == thread_state::aborting)
{
return false;
}
std::memset(&poll_fd, 0, sizeof(poll_fd));
poll_fd.events = POLLIN;
poll_fd.revents = 0;
poll_fd.fd = m_sock;
#ifdef _WIN32
// Try to wait for an incoming connection
pending_connection = ::WSAPoll(&poll_fd, 1, 10);
#else
pending_connection = ::poll(&poll_fd, 1, 10);
#endif
}
m_msgsock = accept(m_sock, 0, 0);
if (m_msgsock == -1)
@@ -398,10 +394,10 @@ namespace pine
{
#endif
Impl::error("IPC: An unrecoverable error happened! Shutting down...");
return -1;
return false;
}
}
return 0;
return true;
}
// Thread used to relay IPC commands.
@@ -409,10 +405,10 @@ namespace pine
{
// we allocate once buffers to not have to do mallocs for each IPC
// request, as malloc is expansive when we optimize for µs.
m_ret_buffer = new char[MAX_IPC_RETURN_SIZE];
m_ipc_buffer = new char[MAX_IPC_SIZE];
m_ret_buffer.resize(MAX_IPC_RETURN_SIZE);
m_ipc_buffer.resize(MAX_IPC_SIZE);
if (StartSocket() < 0)
if (!StartSocket())
return;
while (thread_ctrl::state() != thread_state::aborting)
@@ -432,7 +428,7 @@ namespace pine
if (tmp_length <= 0)
{
receive_length = 0;
if (StartSocket() < 0)
if (!StartSocket())
return;
break;
}
@@ -442,7 +438,7 @@ namespace pine
// if we got at least the final size then update
if (end_length == 4 && receive_length >= 4)
{
end_length = FromArray<u32>(m_ipc_buffer, 0);
end_length = FromArray<u32>(m_ipc_buffer.data(), 0);
// we'd like to avoid a client trying to do OOB
if (end_length > MAX_IPC_SIZE || end_length < 4)
{
@@ -459,12 +455,12 @@ namespace pine
// disconnects
if (receive_length != 0)
{
pine_server::IPCBuffer res = ParseCommand(&m_ipc_buffer[4], m_ret_buffer, static_cast<u32>(end_length) - 4);
pine_server::IPCBuffer res = ParseCommand(&m_ipc_buffer[4], m_ret_buffer.data(), static_cast<u32>(end_length) - 4);
// if we cannot send back our answer restart the socket
if (write_portable(m_msgsock, res.buffer, res.size) < 0)
{
if (StartSocket() < 0)
if (!StartSocket())
return;
}
}
@@ -480,9 +476,9 @@ namespace pine
* NB: implicitely inlined
*/
template <typename T>
static char* ToArray(char* res_array, T res, int i)
static char* ToArray(char* res_array, T res, usz i)
{
memcpy((res_array + i), reinterpret_cast<char*>(&res), sizeof(T));
memcpy(res_array + i, reinterpret_cast<char*>(&res), sizeof(T));
return res_array;
}
@@ -503,7 +499,7 @@ namespace pine
* Ensures an IPC message isn't too big.
* return value: false if checks failed, true otherwise.
*/
static inline bool SafetyChecks(u32 command_len, int command_size, u32 reply_len, int reply_size = 0, u32 buf_size = MAX_IPC_SIZE - 1)
static inline bool SafetyChecks(usz command_len, usz command_size, usz reply_len, usz reply_size = 0, usz buf_size = MAX_IPC_SIZE - 1)
{
bool res = ((command_len + command_size) > buf_size ||
(reply_len + reply_size) >= MAX_IPC_RETURN_SIZE);
@@ -609,8 +605,6 @@ namespace pine
~pine_server()
{
Cleanup();
delete[] m_ret_buffer;
delete[] m_ipc_buffer;
}
}; // class pine_server
+3 -3
View File
@@ -11,7 +11,7 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
* [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
* [Qt 5.15.2](https://www.qt.io/download-qt-installer)
* [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community)
* [Vulkan SDK 1.2.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.15.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
@@ -22,8 +22,8 @@ These are the essentials tools to build RPCS3 on Linux. Some of them can be inst
* Clang 12+ or GCC 11+
* [CMake 3.16.9+](https://www.cmake.org/download/)
* [Qt 5.15.2](https://www.qt.io/download-qt-installer)
* [Vulkan SDK 1.2.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
* [SDL2](https://www.libsdl.org/download-2.0.php) (for the FAudio backend)
* [Vulkan SDK 1.3.224+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
* [SDL2](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
+2 -3
View File
@@ -1,9 +1,8 @@
RPCS3
=====
[![Azure Build Status](https://dev.azure.com/nekotekina/nekotekina/_apis/build/status/RPCS3.rpcs3?branchName=master)](https://dev.azure.com/nekotekina/nekotekina/_build?definitionId=4&branchName=master)
[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/RPCS3/rpcs3?label=Cirrus%20CI%20(FreeBSD)&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
[![RPCS3 discord server](https://img.shields.io/discord/272035812277878785?color=5865F2&label=RPCS3%20Discord&logo=discord&logoColor=white)](https://discord.me/rpcs3)
[![Cirrus CI - Base Branch Build Status](https://img.shields.io/cirrus/github/RPCS3/rpcs3?label=Cirrus%20CI&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
[![RPCS3 Discord Server](https://img.shields.io/discord/272035812277878785?color=5865F2&label=RPCS3%20Discord&logo=discord&logoColor=white)](https://discord.me/rpcs3)
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows, Linux, macOS and FreeBSD.
+37 -5
View File
@@ -1,7 +1,6 @@
#include "stdafx.h"
#include "Config.h"
#include "util/types.hpp"
#include "util/yaml.hpp"
#include <charconv>
@@ -97,7 +96,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_int64('%s'): out of bounds (%d..%d)", value, min, max);
if (out) cfg_log.error("cfg::try_to_int64('%s'): out of bounds (val=%d, min=%d, max=%d)", value, result, min, max);
return false;
}
@@ -140,7 +139,7 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (%u..%u)", value, min, max);
if (out) cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (val=%u, min=%u, max=%u)", value, result, min, max);
return false;
}
@@ -176,7 +175,7 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_float('%s'): out of bounds (%f..%f)", value, min, max);
if (out) cfg_log.error("cfg::try_to_float('%s'): out of bounds (val=%f, min=%f, max=%f)", value, result, min, max);
return false;
}
@@ -184,6 +183,27 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
return true;
}
bool try_to_string(std::string* out, const f64& value)
{
#ifdef __APPLE__
if (out) *out = std::to_string(value);
return true;
#else
std::array<char, 32> str{};
if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value, std::chars_format::fixed); ec == std::errc())
{
if (out) *out = std::string(str.data(), ptr);
return true;
}
else
{
if (out) cfg_log.error("cfg::try_to_string(): could not convert value '%f' to string. error='%s'", value, std::make_error_code(ec).message());
return false;
}
#endif
}
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value)
{
u64 max = umax;
@@ -231,7 +251,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
if (result > max)
{
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): out of bounds(val=%u, min=0, max=%u)", value, result, max);
return false;
}
@@ -559,6 +579,18 @@ void cfg::log_entry::from_default()
set_map({});
}
std::pair<u16, u16> cfg::device_info::get_usb_ids() const
{
auto string_to_hex = [](const std::string& str) -> u16
{
u16 value = 0x0000;
if (!str.empty() && std::from_chars(str.data(), str.data() + str.size(), value, 16).ec != std::errc{})
cfg_log.error("Failed to parse hex from string \"%s\"", str);
return value;
};
return {string_to_hex(vid), string_to_hex(pid)};
}
void cfg::device_entry::set_map(map_of_type<device_info>&& map)
{
m_map = std::move(map);
+22 -6
View File
@@ -285,8 +285,8 @@ namespace cfg
int_type def;
// Expose range
static const s64 max = Max;
static const s64 min = Min;
static constexpr s64 max = Max;
static constexpr s64 min = Min;
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)), bool dynamic = false)
: _base(type::_int, owner, name, dynamic)
@@ -334,6 +334,7 @@ namespace cfg
void set(const s64& value)
{
ensure(value >= Min && value <= Max);
m_value = static_cast<int_type>(value);
}
@@ -383,12 +384,24 @@ namespace cfg
std::string to_string() const override
{
return std::to_string(m_value);
std::string result;
if (try_to_string(&result, m_value))
{
return result;
}
return "0.0";
}
std::string def_to_string() const override
{
return std::to_string(def);
std::string result;
if (try_to_string(&result, def))
{
return result;
}
return "0.0";
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
@@ -405,6 +418,7 @@ namespace cfg
void set(const f64& value)
{
ensure(value >= Min && value <= Max);
m_value = static_cast<float_type>(value);
}
@@ -435,8 +449,8 @@ namespace cfg
int_type def;
// Expose range
static const u64 max = Max;
static const u64 min = Min;
static constexpr u64 max = Max;
static constexpr u64 min = Min;
uint(node* owner, const std::string& name, int_type def = std::max<int_type>(Min, 0), bool dynamic = false)
: _base(type::uint, owner, name, dynamic)
@@ -484,6 +498,7 @@ namespace cfg
void set(const u64& value)
{
ensure(value >= Min && value <= Max);
m_value = static_cast<int_type>(value);
}
@@ -644,6 +659,7 @@ namespace cfg
std::string serial;
std::string vid;
std::string pid;
std::pair<u16, u16> get_usb_ids() const;
};
class device_entry final : public _base
+6
View File
@@ -37,6 +37,12 @@ std::string utf16_to_utf8(std::u16string_view src)
return converter.to_bytes(src.data());
}
std::u16string utf8_to_utf16(std::string_view src)
{
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter{};
return converter.from_bytes(src.data());
}
std::wstring utf8_to_wchar(std::string_view src)
{
#ifdef _WIN32
+23
View File
@@ -237,6 +237,29 @@ struct fmt_class_string<wchar_t*, void> : fmt_class_string<const wchar_t*>
{
};
namespace fmt
{
template <typename T>
concept StringConvertible = requires (T & t)
{
{ t.to_string() } -> std::convertible_to<std::string>;
};
}
template <fmt::StringConvertible T>
struct fmt_class_string<T, void>
{
static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg)
{
return *reinterpret_cast<const T*>(static_cast<uptr>(arg));
}
static void format(std::string& out, u64 arg)
{
out += get_object(arg).to_string();
}
};
namespace fmt
{
// Both uchar and std::byte are allowed
+4
View File
@@ -11,6 +11,7 @@
std::wstring utf8_to_wchar(std::string_view src);
std::string wchar_to_utf8(std::wstring_view src);
std::string utf16_to_utf8(std::u16string_view src);
std::u16string utf8_to_utf16(std::string_view src);
// Copy null-terminated string from a std::string or a char array to a char array with truncation
template <typename D, typename T>
@@ -30,6 +31,9 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
// Convert string to float
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max);
// Convert float to string locale independent
bool try_to_string(std::string* out, const f64& value);
// Get the file extension of a file path ("png", "jpg", etc.)
std::string get_file_extension(const std::string& file_path);
+1 -1
View File
@@ -1796,7 +1796,7 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
const bool is_executing = err & 0x10;
const bool is_writing = err & 0x2;
#elif defined(ARCH_ARM64)
const bool is_executing = uptr(info->si_addr) == RIP(context);
const bool is_executing = uptr(info->si_addr) == uptr(RIP(context));
const u32 insn = is_executing ? 0 : *reinterpret_cast<u32*>(RIP(context));
const bool is_writing = (insn & 0xbfff0000) == 0x0c000000
|| (insn & 0xbfe00000) == 0x0c800000
+467 -117
View File
@@ -31,6 +31,23 @@ void fmt_class_string<YAML::NodeType::value>::format(std::string& out, u64 arg)
});
}
template <>
void fmt_class_string<patch_configurable_type>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](patch_configurable_type value)
{
switch (value)
{
case patch_configurable_type::double_range: return "double_range";
case patch_configurable_type::double_enum: return "double_enum";
case patch_configurable_type::long_range: return "long_range";
case patch_configurable_type::long_enum: return "long_enum";
}
return unknown;
});
}
template <>
void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
{
@@ -65,6 +82,35 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
});
}
void patch_engine::patch_config_value::set_and_check_value(f64 new_value, const std::string& name)
{
switch (type)
{
case patch_configurable_type::double_enum:
case patch_configurable_type::long_enum:
{
if (std::none_of(allowed_values.begin(), allowed_values.end(), [&new_value](const patch_allowed_value& allowed_value){ return allowed_value.value == new_value; }))
{
patch_log.error("Can't set configurable enumerated value '%s' to %f. Using default value %f", name, new_value, value);
return;
}
break;
}
case patch_configurable_type::double_range:
case patch_configurable_type::long_range:
{
if (new_value < min || new_value > max)
{
patch_log.error("Can't set configurable range value '%s' to %f. Using default value %f", name, new_value, value);
return;
}
break;
}
}
value = new_value;
}
patch_engine::patch_engine()
{
}
@@ -96,10 +142,17 @@ std::string patch_engine::get_imported_patch_path()
return get_patches_path() + "imported_patch.yml";
}
static void append_log_message(std::stringstream* log_messages, const std::string& message)
static void append_log_message(std::stringstream* log_messages, const std::string& message, const logs::message* channel = nullptr)
{
if (log_messages)
if (channel)
{
channel->operator()("%s", message);
}
if (log_messages && !message.empty())
{
*log_messages << message << std::endl;
}
};
bool patch_engine::load(patch_map& patches_map, const std::string& path, std::string content, bool importing, std::stringstream* log_messages)
@@ -144,8 +197,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (version != patch_engine_version)
{
append_log_message(log_messages, fmt::format("Error: File version %s does not match patch engine target version %s (location: %s, file: %s)", version, patch_engine_version, get_yaml_node_location(version_node), path));
patch_log.error("File version %s does not match patch engine target version %s (location: %s, file: %s)", version, patch_engine_version, get_yaml_node_location(version_node), path);
append_log_message(log_messages, fmt::format("Error: File version %s does not match patch engine target version %s (location: %s, file: %s)", version, patch_engine_version, get_yaml_node_location(version_node), path), &patch_log.error);
return false;
}
@@ -154,8 +206,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
}
else
{
append_log_message(log_messages, fmt::format("Error: No '%s' entry found. Patch engine version = %s (file: %s)", patch_key::version, patch_engine_version, path));
patch_log.error("No '%s' entry found. Patch engine version = %s (file: %s)", patch_key::version, patch_engine_version, path);
append_log_message(log_messages, fmt::format("Error: No '%s' entry found. Patch engine version = %s (file: %s)", patch_key::version, patch_engine_version, path), &patch_log.error);
return false;
}
@@ -168,16 +219,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (const auto yml_type = pair.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping key %s: expected Map, found %s (location: %s)", main_key, yml_type, get_yaml_node_location(pair.second)));
patch_log.error("Skipping key %s: expected Map, found %s (location: %s, file: %s)", main_key, yml_type, get_yaml_node_location(pair.second), path);
append_log_message(log_messages, fmt::format("Error: Skipping key %s: expected Map, found %s (location: %s, file: %s)", main_key, yml_type, get_yaml_node_location(pair.second), path), &patch_log.error);
is_valid = false;
continue;
}
if (main_key.empty())
{
append_log_message(log_messages, fmt::format("Error: Skipping empty key (location: %s)", get_yaml_node_location(pair.second)));
patch_log.error("Skipping empty key (location: %s, file: %s)", get_yaml_node_location(pair.second), path);
append_log_message(log_messages, fmt::format("Error: Skipping empty key (location: %s, file: %s)", get_yaml_node_location(pair.second), path), &patch_log.error);
is_valid = false;
continue;
}
@@ -203,8 +252,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (const auto yml_type = patches_entry.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping Patch key %s: expected Map, found %s (key: %s, location: %s)", description, yml_type, main_key, get_yaml_node_location(patches_entry.second)));
patch_log.error("Skipping Patch key %s: expected Map, found %s (key: %s, location: %s, file: %s)", description, yml_type, main_key, get_yaml_node_location(patches_entry.second), path);
append_log_message(log_messages, fmt::format("Error: Skipping Patch key %s: expected Map, found %s (key: %s, location: %s, file: %s)", description, yml_type, main_key, get_yaml_node_location(patches_entry.second), path), &patch_log.error);
is_valid = false;
continue;
}
@@ -219,8 +267,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
{
if (const auto yml_type = games_node.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping Games key: expected Map, found %s (patch: %s, key: %s, location: %s)", yml_type, description, main_key, get_yaml_node_location(games_node)));
patch_log.error("Skipping Games key: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", yml_type, description, main_key, get_yaml_node_location(games_node), path);
append_log_message(log_messages, fmt::format("Error: Skipping Games key: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", yml_type, description, main_key, get_yaml_node_location(games_node), path), &patch_log.error);
is_valid = false;
continue;
}
@@ -231,16 +278,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (title.empty())
{
append_log_message(log_messages, fmt::format("Error: Empty game title (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(game_node), path));
patch_log.error("Empty game title (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(game_node), path);
append_log_message(log_messages, fmt::format("Error: Empty game title (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(game_node), path), &patch_log.error);
is_valid = false;
continue;
}
if (const auto yml_type = game_node.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping game %s: expected Map, found %s (patch: %s, key: %s, location: %s)", title, yml_type, description, main_key, get_yaml_node_location(game_node)));
patch_log.error("Skipping game %s: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", title, yml_type, description, main_key, get_yaml_node_location(game_node), path);
append_log_message(log_messages, fmt::format("Error: Skipping game %s: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", title, yml_type, description, main_key, get_yaml_node_location(game_node), path), &patch_log.error);
is_valid = false;
continue;
}
@@ -253,33 +298,30 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (serial.empty())
{
append_log_message(log_messages, fmt::format("Error: Using empty serial (title: %s, patch: %s, key: %s, location: %s)", title, description, main_key, get_yaml_node_location(serial_node)));
patch_log.error("Using empty serial (title: %s, patch: %s, key: %s, location: %s, file: %s)", title, description, main_key, get_yaml_node_location(serial_node), path);
append_log_message(log_messages, fmt::format("Error: Using empty serial (title: %s, patch: %s, key: %s, location: %s, file: %s)", title, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
continue;
}
else if (serial == patch_key::all)
if (serial == patch_key::all)
{
if (!title_is_all_key)
{
append_log_message(log_messages, fmt::format("Error: Using '%s' as serial is not allowed for titles other than '%s' (title: %s, patch: %s, key: %s, location: %s)", patch_key::all, patch_key::all, title, description, main_key, get_yaml_node_location(serial_node)));
patch_log.error("Error: Using '%s' as serial is not allowed for titles other than '%s' (title: %s, patch: %s, key: %s, location: %s, file: %s)", patch_key::all, patch_key::all, title, description, main_key, get_yaml_node_location(serial_node), path);
append_log_message(log_messages, fmt::format("Error: Using '%s' as serial is not allowed for titles other than '%s' (title: %s, patch: %s, key: %s, location: %s, file: %s)", patch_key::all, patch_key::all, title, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
continue;
}
}
else if (title_is_all_key)
{
append_log_message(log_messages, fmt::format("Error: Only '%s' is allowed as serial if the title is '%s' (serial: %s, patch: %s, key: %s, location: %s)", patch_key::all, patch_key::all, serial, description, main_key, get_yaml_node_location(serial_node)));
patch_log.error("Error: Only '%s' is allowed as serial if the title is '%s' (serial: %s, patch: %s, key: %s, location: %s, file: %s)", patch_key::all, patch_key::all, serial, description, main_key, get_yaml_node_location(serial_node), path);
append_log_message(log_messages, fmt::format("Error: Only '%s' is allowed as serial if the title is '%s' (serial: %s, patch: %s, key: %s, location: %s, file: %s)", patch_key::all, patch_key::all, serial, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
continue;
}
if (const auto yml_type = serial_node.second.Type(); yml_type != YAML::NodeType::Sequence)
{
append_log_message(log_messages, fmt::format("Error: Skipping %s: expected Sequence, found %s (title: %s, patch: %s, key: %s, location: %s)", serial, title, yml_type, description, main_key, get_yaml_node_location(serial_node)));
patch_log.error("Skipping %s: expected Sequence, found %s (title: %s, patch: %s, key: %s, location: %s, file: %s)", serial, title, yml_type, description, main_key, get_yaml_node_location(serial_node), path);
append_log_message(log_messages, fmt::format("Error: Skipping %s: expected Sequence, found %s (title: %s, patch: %s, key: %s, location: %s, file: %s)", serial, title, yml_type, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
continue;
}
@@ -288,18 +330,17 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
for (const auto version : serial_node.second)
{
const auto& app_version = version.Scalar();
const std::string& app_version = version.Scalar();
// Find out if this patch was enabled in the patch config
const bool enabled = patch_config[main_key].patch_info_map[description].titles[title][serial][app_version];
// Get this patch's config values
const patch_config_values& config_values = patch_config[main_key].patch_info_map[description].titles[title][serial][app_version];
app_versions.emplace(version.Scalar(), enabled);
app_versions[version.Scalar()] = config_values;
}
if (app_versions.empty())
{
append_log_message(log_messages, fmt::format("Error: Skipping %s: empty Sequence (title: %s, patch: %s, key: %s, location: %s)", serial, title, description, main_key, get_yaml_node_location(serial_node)));
patch_log.error("Skipping %s: empty Sequence (title: %s, patch: %s, key: %s, location: %s, file: %s)", serial, title, description, main_key, get_yaml_node_location(serial_node), path);
append_log_message(log_messages, fmt::format("Error: Skipping %s: empty Sequence (title: %s, patch: %s, key: %s, location: %s, file: %s)", serial, title, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
}
else
@@ -332,8 +373,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
}
else
{
append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s)", description, main_key, get_yaml_node_location(note)));
patch_log.error("Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path);
append_log_message(log_messages, fmt::format("Error: Skipping sequenced Note (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(note), path), &patch_log.error);
is_valid = false;
}
}
@@ -349,6 +389,188 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
info.patch_group = patch_group_node.Scalar();
}
if (const auto config_values_node = patches_entry.second[patch_key::config_values])
{
if (const auto yml_type = config_values_node.Type(); yml_type != YAML::NodeType::Map || config_values_node.size() == 0)
{
append_log_message(log_messages, fmt::format("Error: Skipping configurable values: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", yml_type, description, main_key, get_yaml_node_location(config_values_node), path), &patch_log.error);
is_valid = false;
}
else
{
for (const auto config_value_node : config_values_node)
{
const std::string& value_key = config_value_node.first.Scalar();
if (const auto yml_type = config_value_node.second.Type(); yml_type != YAML::NodeType::Map)
{
append_log_message(log_messages, fmt::format("Error: Skipping configurable value %s: expected Map, found %s (patch: %s, key: %s, location: %s, file: %s)", value_key, yml_type, description, main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
else
{
patch_config_value& config_value = info.default_config_values[value_key];
if (const auto config_value_type_node = config_value_node.second[patch_key::type]; config_value_type_node && config_value_type_node.IsScalar())
{
const std::string& str_type = config_value_type_node.Scalar();
bool is_valid_type = false;
for (patch_configurable_type type : { patch_configurable_type::double_range, patch_configurable_type::double_enum, patch_configurable_type::long_range, patch_configurable_type::long_enum })
{
if (str_type == fmt::format("%s", type))
{
config_value.type = type;
is_valid_type = true;
break;
}
}
if (is_valid_type)
{
const auto get_and_check_config_value = [&](const YAML::Node& node)
{
std::string err;
f64 val{};
switch (config_value.type)
{
case patch_configurable_type::double_range:
case patch_configurable_type::double_enum:
val = get_yaml_node_value<f64>(node, err);
break;
case patch_configurable_type::long_range:
case patch_configurable_type::long_enum:
val = static_cast<f64>(get_yaml_node_value<s64>(node, err));
break;
}
if (!err.empty())
{
append_log_message(log_messages, fmt::format("Error: Invalid data type found in configurable value: %s (key: %s, location: %s, file: %s)", err, main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
return val;
};
if (const auto config_value_value_node = config_value_node.second[patch_key::value]; config_value_value_node && config_value_value_node.IsScalar())
{
config_value.value = get_and_check_config_value(config_value_value_node);
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid or missing configurable value (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
switch (config_value.type)
{
case patch_configurable_type::double_range:
case patch_configurable_type::long_range:
{
if (const auto config_value_min_node = config_value_node.second[patch_key::min]; config_value_min_node && config_value_min_node.IsScalar())
{
config_value.min = get_and_check_config_value(config_value_min_node);
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid or missing configurable min (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
if (const auto config_value_max_node = config_value_node.second[patch_key::max]; config_value_max_node && config_value_max_node.IsScalar())
{
config_value.max = get_and_check_config_value(config_value_max_node);
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid or missing configurable max (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
if (config_value.min >= config_value.max)
{
append_log_message(log_messages, fmt::format("Error: Configurable max has to be larger than configurable min (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
if (config_value.value < config_value.min || config_value.value > config_value.max)
{
append_log_message(log_messages, fmt::format("Error: Configurable value out of range (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
break;
}
case patch_configurable_type::double_enum:
case patch_configurable_type::long_enum:
{
if (const auto config_value_allowed_values_node = config_value_node.second[patch_key::allowed_values]; config_value_allowed_values_node && config_value_allowed_values_node.IsMap())
{
config_value.allowed_values.clear();
for (const auto allowed_value : config_value_allowed_values_node)
{
if (allowed_value.second && allowed_value.second.IsScalar())
{
patch_allowed_value new_allowed_value{};
new_allowed_value.label = allowed_value.first.Scalar();
new_allowed_value.value = get_and_check_config_value(allowed_value.second);
if (std::any_of(config_value.allowed_values.begin(), config_value.allowed_values.end(), [&new_allowed_value](const patch_allowed_value& other){ return new_allowed_value.value == other.value || new_allowed_value.label == other.label; }))
{
append_log_message(log_messages, fmt::format("Error: Skipping configurable allowed value. Another entry with the same label or value already exists. (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(allowed_value), path), &patch_log.error);
is_valid = false;
}
else
{
config_value.allowed_values.push_back(new_allowed_value);
}
}
else
{
append_log_message(log_messages, fmt::format("Error: Skipping configurable allowed value (patch: %s, key: %s, location: %s, file: %s)", description, main_key, get_yaml_node_location(allowed_value), path), &patch_log.error);
is_valid = false;
}
}
if (config_value.allowed_values.size() < 2)
{
append_log_message(log_messages, fmt::format("Error: Configurable allowed values need at least 2 entries (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_allowed_values_node), path), &patch_log.error);
is_valid = false;
}
if (std::none_of(config_value.allowed_values.begin(), config_value.allowed_values.end(), [&config_value](const patch_allowed_value& other){ return other.value == config_value.value; }))
{
append_log_message(log_messages, fmt::format("Error: Configurable value was not found in allowed values (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_allowed_values_node), path), &patch_log.error);
is_valid = false;
}
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid or missing configurable allowed values (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
break;
}
}
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid configurable type (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_type_node), path), &patch_log.error);
is_valid = false;
}
}
else
{
append_log_message(log_messages, fmt::format("Error: Invalid or missing configurable type (key: %s, location: %s, file: %s)", main_key, get_yaml_node_location(config_value_node), path), &patch_log.error);
is_valid = false;
}
}
}
}
}
if (const auto patch_node = patches_entry.second[patch_key::patch])
{
if (!read_patch_node(info, patch_node, root, log_messages))
@@ -358,7 +580,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
}
// Skip this patch if a higher patch version already exists
if (container.patch_info_map.find(description) != container.patch_info_map.end())
if (container.patch_info_map.contains(description))
{
bool ok;
const std::string& existing_version = container.patch_info_map[description].patch_version;
@@ -366,11 +588,11 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
if (!ok || !version_is_bigger)
{
patch_log.warning("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s (in file %s)", info.patch_version, existing_version, main_key, description, path);
append_log_message(log_messages, fmt::format("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s (in file %s)", info.patch_version, existing_version, main_key, description, path));
append_log_message(log_messages, fmt::format("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s (in file %s)", info.patch_version, existing_version, main_key, description, path), &patch_log.warning);
continue;
}
else if (!importing)
if (!importing)
{
patch_log.warning("A lower patch version was found ('%s' vs '%s') for %s: %s (in file %s)", existing_version, info.patch_version, main_key, description, container.patch_info_map[description].source_path);
}
@@ -410,8 +632,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
{
if (!node || !node.IsSequence())
{
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
@@ -424,8 +645,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
if (type == patch_type::invalid)
{
const auto type_str = type_node && type_node.IsScalar() ? type_node.Scalar() : "";
append_log_message(log_messages, fmt::format("Skipping patch node %s: type '%s' is invalid. (key: %s, location: %s)", info.description, type_str, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping patch node %s: type '%s' is invalid. (key: %s, location: %s)", info.description, type_str, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping patch node %s: type '%s' is invalid. (key: %s, location: %s)", info.description, type_str, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
@@ -436,8 +656,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
// Check if the anchor was resolved.
if (const auto yml_type = addr_node.Type(); yml_type != YAML::NodeType::Sequence)
{
append_log_message(log_messages, fmt::format("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
@@ -459,15 +678,13 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
if (const auto yml_type = value_node.Type(); yml_type != YAML::NodeType::Scalar)
{
append_log_message(log_messages, fmt::format("Skipping patch node %s. Value element has wrong type %s. (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping patch node %s. Value element has wrong type %s. (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping patch node %s. Value element has wrong type %s. (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
if (!addr_node.Scalar().starts_with("0x"))
{
append_log_message(log_messages, fmt::format("Skipping patch node %s. Address element has wrong format %s. (key: %s, location: %s)", info.description, addr_node.Scalar(), info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping patch node %s. Address element has wrong format %s. (key: %s, location: %s)", info.description, addr_node.Scalar(), info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping patch node %s. Address element has wrong format %s. (key: %s, location: %s)", info.description, addr_node.Scalar(), info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
@@ -476,6 +693,9 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
p_data.offset = addr_node.as<u32>(0) + modifier;
p_data.original_value = value_node.Scalar();
const bool is_config_value = info.default_config_values.contains(p_data.original_value);
const patch_config_value config_value = is_config_value ? ::at32(info.default_config_values, p_data.original_value) : patch_config_value{};
std::string error_message;
switch (p_data.type)
@@ -490,15 +710,16 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
case patch_type::bef64:
case patch_type::lef64:
{
p_data.value.double_value = get_yaml_node_value<f64>(value_node, error_message);
p_data.value.double_value = is_config_value ? config_value.value : get_yaml_node_value<f64>(value_node, error_message);
break;
}
default:
{
p_data.value.long_value = get_yaml_node_value<u64>(value_node, error_message);
p_data.value.long_value = is_config_value ? static_cast<u64>(config_value.value) : get_yaml_node_value<u64>(value_node, error_message);
if (error_message.find("bad conversion") != std::string::npos)
{
error_message = "";
error_message.clear();
p_data.value.long_value = get_yaml_node_value<s64>(value_node, error_message);
}
break;
@@ -509,8 +730,7 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
{
error_message = fmt::format("Skipping patch data entry: [ %s, 0x%.8x, %s ] (key: %s, location: %s) %s",
p_data.type, p_data.offset, p_data.original_value.empty() ? "?" : p_data.original_value, info.hash, get_yaml_node_location(node), error_message);
append_log_message(log_messages, error_message);
patch_log.error("%s", error_message);
append_log_message(log_messages, error_message, &patch_log.error);
return false;
}
@@ -523,15 +743,13 @@ bool patch_engine::read_patch_node(patch_info& info, YAML::Node node, const YAML
{
if (!node)
{
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s, location: %s)", info.description, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
if (const auto yml_type = node.Type(); yml_type != YAML::NodeType::Sequence)
{
append_log_message(log_messages, fmt::format("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)));
patch_log.error("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node));
append_log_message(log_messages, fmt::format("Skipping patch node %s: expected Sequence, found %s (key: %s, location: %s)", info.description, yml_type, info.hash, get_yaml_node_location(node)), &patch_log.error);
return false;
}
@@ -581,11 +799,44 @@ void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
}
// Returns old 'applied' size
static usz apply_modification(std::basic_string<u32>& applied, const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
static usz apply_modification(std::basic_string<u32>& applied, patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
{
const usz old_applied_size = applied.size();
for (const auto& p : patch.data_list)
// Update configurable values
for (const auto& [key, config_value] : patch.actual_config_values)
{
for (usz i = 0; i < patch.data_list.size(); i++)
{
patch_engine::patch_data& p = ::at32(patch.data_list, i);
if (p.original_value == key)
{
switch (p.type)
{
case patch_type::bef32:
case patch_type::lef32:
case patch_type::bef64:
case patch_type::lef64:
{
p.value.double_value = config_value.value;
patch_log.notice("Using configurable value (key='%s', value=%f, index=%d, hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s')",
key, p.value.double_value, i, patch.hash, patch.description, patch.author, patch.patch_version, patch.version);
break;
}
default:
{
p.value.long_value = static_cast<u64>(config_value.value);
patch_log.notice("Using configurable value (key='%s', value=0x%x=%d, index=%d, hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s')",
key, p.value.long_value, p.value.long_value, i, patch.hash, patch.description, patch.author, patch.patch_version, patch.version);
break;
}
}
}
}
}
for (const patch_engine::patch_data& p : patch.data_list)
{
if (p.type != patch_type::alloc) continue;
@@ -659,7 +910,7 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
u32 relocate_instructions_at = 0;
for (const auto& p : patch.data_list)
for (const patch_engine::patch_data& p : patch.data_list)
{
u32 offset = p.offset;
@@ -756,7 +1007,7 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
// NOP filled
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
// Check if already registered by previous code allocation
// Check if already registered by previous code allocation
if (relocate_instructions_at != addr)
{
// Register code
@@ -951,7 +1202,7 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
{
if (m_map.find(name) == m_map.cend())
if (!m_map.contains(name))
{
return {};
}
@@ -962,10 +1213,10 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
const auto& app_version = Emu.GetAppVersion();
// Different containers in order to seperate the patches
std::vector<const patch_info*> patches_for_this_serial_and_this_version;
std::vector<const patch_info*> patches_for_this_serial_and_all_versions;
std::vector<const patch_info*> patches_for_all_serials_and_this_version;
std::vector<const patch_info*> patches_for_all_serials_and_all_versions;
std::vector<std::shared_ptr<patch_info>> patches_for_this_serial_and_this_version;
std::vector<std::shared_ptr<patch_info>> patches_for_this_serial_and_all_versions;
std::vector<std::shared_ptr<patch_info>> patches_for_all_serials_and_this_version;
std::vector<std::shared_ptr<patch_info>> patches_for_all_serials_and_all_versions;
// Sort patches into different vectors based on their serial and version
for (const auto& [description, patch] : container.patch_info_map)
@@ -978,11 +1229,11 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
std::string found_serial;
if (serials.find(serial) != serials.end())
if (serials.contains(serial))
{
found_serial = serial;
}
else if (serials.find(patch_key::all) != serials.end())
else if (serials.contains(patch_key::all))
{
found_serial = patch_key::all;
is_all_serials = true;
@@ -996,37 +1247,61 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
const auto& app_versions = ::at32(serials, found_serial);
std::string found_app_version;
if (app_versions.find(app_version) != app_versions.end())
if (app_versions.contains(app_version))
{
found_app_version = app_version;
}
else if (app_versions.find(patch_key::all) != app_versions.end())
else if (app_versions.contains(patch_key::all))
{
found_app_version = patch_key::all;
is_all_versions = true;
}
if (!found_app_version.empty() && ::at32(app_versions, found_app_version))
if (found_app_version.empty())
{
// This patch is enabled
continue;
}
const patch_config_values& config_values = ::at32(app_versions, found_app_version);
// Check if this patch is enabled
if (config_values.enabled)
{
// Make copy of this patch
std::shared_ptr<patch_info> p_ptr = std::make_shared<patch_info>(patch);
// Move configurable values to special container for readability
p_ptr->actual_config_values = p_ptr->default_config_values;
// Update configurable values
for (auto& [key, config_value] : config_values.config_values)
{
if (p_ptr->actual_config_values.contains(key))
{
patch_config_value& actual_config_value = ::at32(p_ptr->actual_config_values, key);
actual_config_value.set_and_check_value(config_value.value, key);
}
}
// Sort the patch by priority
if (is_all_serials)
{
if (is_all_versions)
{
patches_for_all_serials_and_all_versions.emplace_back(&patch);
patches_for_all_serials_and_all_versions.emplace_back(p_ptr);
}
else
{
patches_for_all_serials_and_this_version.emplace_back(&patch);
patches_for_all_serials_and_this_version.emplace_back(p_ptr);
}
}
else if (is_all_versions)
{
patches_for_this_serial_and_all_versions.emplace_back(&patch);
patches_for_this_serial_and_all_versions.emplace_back(p_ptr);
}
else
{
patches_for_this_serial_and_this_version.emplace_back(&patch);
patches_for_this_serial_and_this_version.emplace_back(p_ptr);
}
break;
@@ -1034,17 +1309,6 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
}
}
// Apply modifications sequentially
auto apply_func = [&](const patch_info& patch)
{
const usz old_size = apply_modification(applied_total, patch, dst, filesz, min_addr);
if (applied_total.size() != old_size)
{
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied_total.size() - old_size);
}
};
// Sort specific patches after global patches
// So they will determine the end results
const auto patch_super_list =
@@ -1064,19 +1328,26 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
{
if (!m_applied_groups.insert(patch->patch_group).second)
{
patch = nullptr;
patch.reset();
}
}
}
}
// Apply modifications sequentially
for (auto patch_list : patch_super_list)
{
for (const patch_info* patch : *patch_list)
for (const std::shared_ptr<patch_info>& patch : *patch_list)
{
if (patch)
{
apply_func(*patch);
const usz old_size = apply_modification(applied_total, *patch, dst, filesz, min_addr);
if (applied_total.size() != old_size)
{
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)",
patch->hash, patch->description, patch->author, patch->patch_version, patch->version, applied_total.size() - old_size);
}
}
}
}
@@ -1086,7 +1357,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
void patch_engine::unload(const std::string& name)
{
if (m_map.find(name) == m_map.cend())
if (!m_map.contains(name))
{
return;
}
@@ -1117,7 +1388,7 @@ void patch_engine::save_config(const patch_map& patches_map)
YAML::Emitter out;
out << YAML::BeginMap;
// Save 'enabled' state per hash, description, serial and app_version
// Save values per hash, description, serial and app_version
patch_map config_map;
for (const auto& [hash, container] : patches_map)
@@ -1128,28 +1399,28 @@ void patch_engine::save_config(const patch_map& patches_map)
{
for (const auto& [serial, app_versions] : serials)
{
for (const auto& [app_version, enabled] : app_versions)
for (const auto& [app_version, config_values] : app_versions)
{
if (enabled)
const bool config_values_dirty = !patch.default_config_values.empty() && !config_values.config_values.empty() && patch.default_config_values != config_values.config_values;
if (config_values.enabled || config_values_dirty)
{
config_map[hash].patch_info_map[description].titles[title][serial][app_version] = true;
config_map[hash].patch_info_map[description].titles[title][serial][app_version] = config_values;
}
}
}
}
}
if (const auto& enabled_patches = config_map[hash].patch_info_map; !enabled_patches.empty())
if (const auto& patches_to_save = config_map[hash].patch_info_map; !patches_to_save.empty())
{
out << hash << YAML::BeginMap;
for (const auto& [description, patch] : enabled_patches)
for (const auto& [description, patch] : patches_to_save)
{
const auto& titles = patch.titles;
out << description << YAML::BeginMap;
for (const auto& [title, serials] : titles)
for (const auto& [title, serials] : patch.titles)
{
out << title << YAML::BeginMap;
@@ -1157,9 +1428,34 @@ void patch_engine::save_config(const patch_map& patches_map)
{
out << serial << YAML::BeginMap;
for (const auto& [app_version, enabled] : app_versions)
for (const auto& [app_version, config_values] : app_versions)
{
out << app_version << enabled;
const auto& default_config_values = ::at32(container.patch_info_map, description).default_config_values;
const bool config_values_dirty = !default_config_values.empty() && !config_values.config_values.empty() && default_config_values != config_values.config_values;
if (config_values.enabled || config_values_dirty)
{
out << app_version << YAML::BeginMap;
if (config_values.enabled)
{
out << patch_key::enabled << config_values.enabled;
}
if (config_values_dirty)
{
out << patch_key::config_values << YAML::BeginMap;
for (const auto& [name, config_value] : config_values.config_values)
{
out << name << config_value.value;
}
out << YAML::EndMap;
}
out << YAML::EndMap;
}
}
out << YAML::EndMap;
@@ -1181,7 +1477,7 @@ void patch_engine::save_config(const patch_map& patches_map)
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
{
patch_log.error("Failed to create patch config file %s (%s)", path, fs::g_tls_error);
patch_log.error("Failed to create patch config file %s (error=%s)", path, fs::g_tls_error);
}
}
@@ -1191,7 +1487,7 @@ static void append_patches(patch_engine::patch_map& existing_patches, const patc
{
total += new_container.patch_info_map.size();
if (existing_patches.find(hash) == existing_patches.end())
if (!existing_patches.contains(hash))
{
existing_patches[hash] = new_container;
count += new_container.patch_info_map.size();
@@ -1202,7 +1498,7 @@ static void append_patches(patch_engine::patch_map& existing_patches, const patc
for (const auto& [description, new_info] : new_container.patch_info_map)
{
if (container.patch_info_map.find(description) == container.patch_info_map.end())
if (!container.patch_info_map.contains(description))
{
container.patch_info_map[description] = new_info;
count++;
@@ -1216,15 +1512,13 @@ static void append_patches(patch_engine::patch_map& existing_patches, const patc
if (!ok)
{
patch_log.error("Failed to compare patch versions ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description);
append_log_message(log_messages, fmt::format("Failed to compare patch versions ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description));
append_log_message(log_messages, fmt::format("Failed to compare patch versions ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description), &patch_log.error);
continue;
}
if (!version_is_bigger)
{
patch_log.error("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description);
append_log_message(log_messages, fmt::format("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description));
append_log_message(log_messages, fmt::format("A higher or equal patch version already exists ('%s' vs '%s') for %s: %s", new_info.patch_version, info.patch_version, hash, description), &patch_log.error);
continue;
}
@@ -1255,8 +1549,7 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
fs::file file(path, fs::rewrite);
if (!file)
{
patch_log.fatal("save_patches: Failed to open patch file %s (%s)", path, fs::g_tls_error);
append_log_message(log_messages, fmt::format("Failed to open patch file %s (%s)", path, fs::g_tls_error));
append_log_message(log_messages, fmt::format("Failed to open patch file %s (%s)", path, fs::g_tls_error), &patch_log.fatal);
return false;
}
@@ -1305,6 +1598,42 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
if (!info.patch_group.empty()) out << patch_key::group << info.patch_group;
if (!info.notes.empty()) out << patch_key::notes << info.notes;
if (!info.default_config_values.empty())
{
out << patch_key::config_values << YAML::BeginMap;
for (const auto& [key, config_value] : info.default_config_values)
{
out << key << YAML::BeginMap;
out << patch_key::type << fmt::format("%s", config_value.type);
out << patch_key::value << config_value.value;
switch (config_value.type)
{
case patch_configurable_type::double_range:
case patch_configurable_type::long_range:
out << patch_key::min << config_value.min;
out << patch_key::max << config_value.max;
break;
case patch_configurable_type::double_enum:
case patch_configurable_type::long_enum:
out << patch_key::allowed_values << YAML::BeginMap;
for (const auto& allowed_value : config_value.allowed_values)
{
out << allowed_value.label << allowed_value.value;
}
out << YAML::EndMap;
break;
}
out << YAML::EndMap;
}
out << YAML::EndMap;
}
out << patch_key::patch << YAML::BeginSeq;
for (const auto& data : info.data_list)
@@ -1356,11 +1685,11 @@ bool patch_engine::remove_patch(const patch_info& info)
if (load(patches, info.source_path))
{
if (patches.find(info.hash) != patches.end())
if (patches.contains(info.hash))
{
auto& container = patches[info.hash];
if (container.patch_info_map.find(info.description) != container.patch_info_map.end())
if (container.patch_info_map.contains(info.description))
{
container.patch_info_map.erase(info.description);
return save_patches(patches, info.source_path);
@@ -1373,7 +1702,7 @@ bool patch_engine::remove_patch(const patch_info& info)
patch_engine::patch_map patch_engine::load_config()
{
patch_map config_map;
patch_map config_map{};
const std::string path = get_patch_config_path();
patch_log.notice("Loading patch config file %s", path);
@@ -1431,8 +1760,29 @@ patch_engine::patch_map patch_engine::load_config()
for (const auto app_version_node : serial_node.second)
{
const auto& app_version = app_version_node.first.Scalar();
const bool enabled = app_version_node.second.as<bool>(false);
config_map[hash].patch_info_map[description].titles[title][serial][app_version] = enabled;
auto& config_values = config_map[hash].patch_info_map[description].titles[title][serial][app_version];
if (app_version_node.second.IsMap())
{
if (const auto enable_node = app_version_node.second[patch_key::enabled])
{
config_values.enabled = enable_node.as<bool>(false);
}
if (const auto config_values_node = app_version_node.second[patch_key::config_values])
{
for (const auto config_value_node : config_values_node)
{
patch_config_value& config_value = config_values.config_values[config_value_node.first.Scalar()];
config_value.value = config_value_node.second.as<f64>(0.0);
}
}
}
else
{
// Legacy
config_values.enabled = app_version_node.second.as<bool>(false);
}
}
}
}
+51 -1
View File
@@ -18,6 +18,13 @@ namespace patch_key
static const std::string patch = "Patch";
static const std::string patch_version = "Patch Version";
static const std::string version = "Version";
static const std::string enabled = "Enabled";
static const std::string config_values = "Configurable Values";
static const std::string value = "Value";
static const std::string type = "Type";
static const std::string min = "Min";
static const std::string max = "Max";
static const std::string allowed_values = "Allowed Values";
}
inline static const std::string patch_engine_version = "1.2";
@@ -47,6 +54,14 @@ enum class patch_type
utf8, // Text of string (not null-terminated automatically)
};
enum class patch_configurable_type
{
double_range,
double_enum,
long_range,
long_enum
};
class patch_engine
{
public:
@@ -63,7 +78,40 @@ public:
mutable u32 alloc_addr = 0; // Used to save optional allocation address (if occured)
};
using patch_app_versions = std::unordered_map<std::string /*app_version*/, bool /*enabled*/>;
struct patch_allowed_value
{
std::string label;
f64 value{};
bool operator==(const patch_allowed_value& other) const
{
return value == other.value && label == other.label;
}
};
struct patch_config_value
{
f64 value{};
f64 min{};
f64 max{};
patch_configurable_type type{};
std::vector<patch_allowed_value> allowed_values;
bool operator==(const patch_config_value& other) const
{
return value == other.value && min == other.min && max == other.max && type == other.type && allowed_values == other.allowed_values;
}
void set_and_check_value(f64 new_value, const std::string& name);
};
struct patch_config_values
{
bool enabled{};
std::map<std::string, patch_config_value> config_values;
};
using patch_app_versions = std::unordered_map<std::string /*app_version*/, patch_config_values>;
using patch_serials = std::unordered_map<std::string /*serial*/, patch_app_versions>;
using patch_titles = std::unordered_map<std::string /*serial*/, patch_serials>;
@@ -78,10 +126,12 @@ public:
std::string author{};
std::string notes{};
std::string source_path{};
std::map<std::string, patch_config_value> default_config_values;
// Redundant information for accessibility (see patch_container)
std::string hash{};
std::string version{};
std::map<std::string, patch_config_value> actual_config_values;
};
struct patch_container
+3 -3
View File
@@ -777,9 +777,9 @@ struct color4_base
};
constexpr color4_base()
: x{}
, y{}
, z{}
: x{ T{0} }
, y{ T{0} }
, z{ T{0} }
, w{ T(1) }
{
}
+8
View File
@@ -359,6 +359,14 @@ public:
return result;
}
// Withdraw the list in reverse order (LIFO/FILO)
lf_queue_slice<T> pop_all_reversed()
{
lf_queue_slice<T> result;
result.m_head = m_head.exchange(nullptr);
return result;
}
// Apply func(data) to each element, return the total length
template <typename F>
usz apply(F func)
@@ -1,5 +1,5 @@
/*
TheMitoSan Dark-Theme for RPCS3
/*
TheMitoSan Dark-Theme for RPCS3
Based on Kuroi (Dark) by Ani
by themitosan @ twitter.com/themitosan
Ani @ https://github.com/AniLeo
@@ -135,7 +135,7 @@ QPushButton::disabled {
}
QPushButton::pressed {
background-color: #333;
background-color: #333;
}
/* Log and Debugger borders */
@@ -193,7 +193,7 @@ QListWidget::item:hover {
}
/* Pad Settings: Controller Image */
QLabel#l_controller {
QLabel#l_controller {
color: #FFF;
}
@@ -230,8 +230,8 @@ QLabel#color_button {
}
/* Search bar on main toolbar */
QLineEdit#mw_searchbar {
margin-left: 0.7em;
QLineEdit#mw_searchbar {
margin-left: 0.7em;
color: #ecf0f1;
}
+10 -10
View File
@@ -1,4 +1,4 @@
/*
/*
Envy theme for RPCS3 by drysalter (https://github.com/drysalter)
03/04/2019
*/
@@ -19,7 +19,7 @@ Candy Red: #ff007a
Yellow: #f2d571
Fuchsia: #ff00ff
Orange: #ff6000
Cyan: #3498d8
Cyan: #3498d8
*/
@@ -209,7 +209,7 @@ QRadioButton::indicator:unchecked {
}
QRadioButton::indicator:disabled {
background-color: #4d5058;
background-color: #4d5058;
}
/* Combo Boxes */
@@ -272,7 +272,7 @@ QSlider::handle:horizontal {
border: 1px solid #5c5c5c;
border-radius: 0.1em;
width: 18px;
margin: -2px 2;
margin: -2px 2;
}
QSlider::handle:horizontal:hover {
@@ -335,7 +335,7 @@ QPushButton:disabled {
}
QPushButton::pressed {
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d3038, stop: 1 #2d3038);
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #2d3038, stop: 1 #2d3038);
}
/* Spinboxes */
@@ -479,7 +479,7 @@ QListWidget::indicator:disabled {
}
/* Pad Settings: Controller Image */
QLabel#l_controller {
QLabel#l_controller {
color: #8cf944;
}
@@ -505,7 +505,7 @@ QLabel#color_button {
}
/* Search bar on main toolbar */
QLineEdit#mw_searchbar {
QLineEdit#mw_searchbar {
color: #ecf0f1;
background-color: #2d3038;
border: 1px solid #f8f8f8;
@@ -513,14 +513,14 @@ QLineEdit#mw_searchbar {
padding-left: 5px;
}
QLineEdit#mw_searchbar:hover {
QLineEdit#mw_searchbar:hover {
color: #ecf0f1;
background-color: #2d3038;
border: 1px solid #8cf944;
margin: 15px;
}
QLineEdit#mw_searchbar:focus {
QLineEdit#mw_searchbar:focus {
border: 1px solid #8cf944;
}
@@ -566,7 +566,7 @@ QTableView {
QTableView::item:hover {
color: #8cf944;
}
}
/* Game Icon Background */
QLabel#gamelist_icon_background_color {
@@ -1,5 +1,5 @@
/*
ModernBlue Theme for RPCS3
/*
ModernBlue Theme for RPCS3
by themitosan @ twitter.com/themitosan
(2017.12.12)
*/
@@ -124,7 +124,7 @@ QGroupBox::title {
/* Buttons */
QPushButton {
background-color: #46598b;
color: #fff;
color: #fff;
}
QPushButton::disabled {
@@ -133,7 +133,7 @@ QPushButton::disabled {
}
QPushButton::pressed {
background-color: #172038;
background-color: #172038;
}
/* Log and Debugger borders */
@@ -190,7 +190,7 @@ QListWidget::item:hover {
}
/* Pad Settings: Controller Image */
QLabel#l_controller {
QLabel#l_controller {
color: #fff;
}
@@ -227,8 +227,8 @@ QLabel#color_button {
}
/* Search bar on main toolbar */
QLineEdit#mw_searchbar {
margin-left: 0.7em;
QLineEdit#mw_searchbar {
margin-left: 0.7em;
color: #ecf0f1;
}
+8 -8
View File
@@ -105,7 +105,7 @@ QToolButton:hover {
}
/* Search bar in Main Toolbar */
QLineEdit#mw_searchbar {
QLineEdit#mw_searchbar {
color: #455971;
background-color: #FFFFFF;
border: 1px solid #999999;
@@ -113,15 +113,15 @@ QLineEdit#mw_searchbar {
padding-left: 5px;
}
QLineEdit#mw_searchbar:hover {
margin-left: 0.7em;
QLineEdit#mw_searchbar:hover {
margin-left: 0.7em;
color: #0074e7;
background-color: #FFFFFF;
border: 1px solid #0071ec;
margin: 15px;
}
QLineEdit#mw_searchbar:focus {
QLineEdit#mw_searchbar:focus {
border: 1px solid #0074e7;
}
@@ -148,7 +148,7 @@ QSlider::handle:horizontal {
border: 1px solid #5c5c5c;
border-radius: 0.1em;
width: 18px;
margin: -2px 2;
margin: -2px 2;
}
QSlider::handle:horizontal:hover {
@@ -242,7 +242,7 @@ QRadioButton::indicator:unchecked {
}
QRadioButton::indicator:disabled {
background-color: #455971;
background-color: #455971;
}
/* Checkboxes */
@@ -533,7 +533,7 @@ QProgressBar::chunk {
}
/* Pad Settings: Controller Image */
QLabel#l_controller {
QLabel#l_controller {
color: #0074e7;
}
@@ -554,7 +554,7 @@ QHeaderView::section {
background-color: #111525;
color: #FFFFFF;
padding-top: 3px;
padding-left: 3px;
padding-left: 3px;
height: 20px;
border: none;
}
+8 -8
View File
@@ -105,7 +105,7 @@ QToolButton:hover {
}
/* Search bar in Main Toolbar */
QLineEdit#mw_searchbar {
QLineEdit#mw_searchbar {
color: #455971;
background-color: #FFFFFF;
border: 1px solid #999999;
@@ -113,15 +113,15 @@ QLineEdit#mw_searchbar {
padding-left: 5px;
}
QLineEdit#mw_searchbar:hover {
margin-left: 0.7em;
QLineEdit#mw_searchbar:hover {
margin-left: 0.7em;
color: #455971;
background-color: #FFFFFF;
border: 1px solid #0071ec;
margin: 15px;
}
QLineEdit#mw_searchbar:focus {
QLineEdit#mw_searchbar:focus {
border: 1px solid #0071ec;
}
@@ -148,7 +148,7 @@ QSlider::handle:horizontal {
border: 1px solid #5c5c5c;
border-radius: 0.1em;
width: 18px;
margin: -2px 2;
margin: -2px 2;
}
QSlider::handle:horizontal:disabled {
@@ -254,7 +254,7 @@ QRadioButton::indicator:unchecked {
}
QRadioButton::indicator:disabled {
background-color: #455971;
background-color: #455971;
}
/* Checkboxes */
@@ -552,7 +552,7 @@ QProgressBar::chunk {
}
/* Pad Settings: Controller Image */
QLabel#l_controller {
QLabel#l_controller {
color: #4343c1;
}
@@ -573,7 +573,7 @@ QHeaderView::section {
background-color: #FFFFFF;
color: #455971;
padding-top: 3px;
padding-left: 3px;
padding-left: 3px;
height: 20px;
border: none;
}
+2 -2
View File
@@ -456,10 +456,10 @@ QSlider#sizeSlider::groove:horizontal {
height: 1.5em;
}
/*
/*
On Patch Creator, it's the patch validation label
since we can't avoid repainting color, let's bold it instead
*/
QLabel#validLabel {
font-weight: 600;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

+2 -2
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Common default props for defining properties with macros. Import right after Microsoft.Cpp.Default.props -->
<ImportGroup Label="PropertySheets" />
@@ -8,4 +8,4 @@
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup />
</Project>
</Project>
+9 -1
View File
@@ -6,6 +6,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asmjit", "3rdparty\asmjit\a
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "emucore", "rpcs3\emucore.vcxproj", "{C4A10229-4712-4BD2-B63E-50D93C67A038}"
ProjectSection(ProjectDependencies) = postProject
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}
{939FE206-1182-ABC3-1234-FEAB88E98404} = {939FE206-1182-ABC3-1234-FEAB88E98404}
EndProjectSection
EndProject
@@ -74,7 +75,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcurl", "3rdparty\curl\li
{73973223-5EE8-41CA-8E88-1D60E89A237B} = {73973223-5EE8-41CA-8E88-1D60E89A237B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SPIRV", "3rdparty\SPIRV\spirv.vcxproj", "{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spirv", "3rdparty\SPIRV\spirv.vcxproj", "{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3rdParty", "3rdParty", "{6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}"
EndProject
@@ -93,6 +94,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pine", "pine", "{A55DA1B5-C
3rdparty\pine\pine_server.h = 3rdparty\pine\pine_server.h
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc_static", "3rdparty\miniupnp\miniupnpc_static.vcxproj", "{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -187,6 +190,10 @@ Global
{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Debug|x64.Build.0 = Debug|x64
{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Release|x64.ActiveCfg = Release|x64
{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA}.Release|x64.Build.0 = Release|x64
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Debug|x64.ActiveCfg = Debug|x64
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Debug|x64.Build.0 = Debug|x64
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.ActiveCfg = Release|x64
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -216,6 +223,7 @@ Global
{508C291A-3D18-49F5-B25D-F7C8DB92CB21} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
{8DC244EE-A0BD-4038-BAF7-CFAFA5EB2BAA} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
{A55DA1B5-CC17-4525-BE7F-1659CE17BB56} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
{5228F863-E0DD-4DE7-AA7B-5C52B14CD4D0} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
+2 -2
View File
@@ -187,13 +187,13 @@ elseif(WIN32)
# If Qt5 is installed from vcpkg
list(APPEND _QT5_TOOLS_PATHS "${Qt5_DIR}/../../../tools/qt5$<$<CONFIG:Debug>:/debug>/bin/")
add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND set PATH=${_QT5_TOOLS_PATHS}$<SEMICOLON>%PATH%
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
# If Qt5 is installed from vcpkg, add binary path to PATH
# otherwise windeployqt tool won't be able to locate necessary dlls
COMMAND set PATH=${Qt5_DIR}/../../../$<$<CONFIG:Debug>:debug/>bin/$<SEMICOLON>%PATH%
COMMAND set PATH=${Qt5_DIR}/../../../$<$<CONFIG:Debug>:debug/>bin/$<SEMICOLON>%PATH%
COMMAND "${_WINDEPLOYQT}" --no-angle --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --plugindir "$<TARGET_FILE_DIR:rpcs3>/qt/plugins" $<$<CONFIG:Debug>:--debug> $<$<CONFIG:Release>:--release> "$<TARGET_FILE:rpcs3>")
endif()
+1 -1
View File
@@ -124,7 +124,7 @@ int aes_crypt_cbc( aes_context *ctx,
* both encryption and decryption. So a context initialized with
* aes_setkey_enc() for both AES_ENCRYPT and AES_DECRYPT.
*
* both
* both
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
+1 -1
View File
@@ -245,7 +245,7 @@ constexpr s64 PAID_30 = 0x1070000023000001L; // spu_token_processor.self; CoreOS
constexpr s64 PAID_31 = 0x1070000024000001L; // sv_iso_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_32 = 0x1070000025000001L; // aim_spu_module.self; CoreOS; same for CEX DEX DECR
constexpr s64 PAID_33 = 0x1070000026000001L; // ss_sc_init.self; [2.43 JIG PUP]
constexpr s64 PAID_34 = 0x1070000027000001L; // dispatcher.fself; lv1;
constexpr s64 PAID_34 = 0x1070000027000001L; // dispatcher.fself; lv1;
constexpr s64 PAID_35 = 0x1070000028000001L; // factory_data_mngr_server.fself; JIG lv1 proc
constexpr s64 PAID_36 = 0x1070000029000001L; // fdm_spu_module.self; [2.43 JIG PUP]
constexpr s64 PAID_37 = 0x107000002A000001L;
+173 -61
View File
@@ -66,6 +66,7 @@ bool package_reader::read_header()
return false;
}
pkg_log.notice("Path: '%s'", m_path);
pkg_log.notice("Header: pkg_magic = 0x%x = \"%s\"", +m_header.pkg_magic, std::string(reinterpret_cast<const char*>(&m_header.pkg_magic), 4));
pkg_log.notice("Header: pkg_type = 0x%x = %d", m_header.pkg_type, m_header.pkg_type);
pkg_log.notice("Header: pkg_platform = 0x%x = %d", m_header.pkg_platform, m_header.pkg_platform);
@@ -515,7 +516,7 @@ bool package_reader::read_param_sfo()
std::memcpy(entries.data(), m_bufs.back().get(), entries.size() * sizeof(PKGEntry));
for (const auto& entry : entries)
for (const PKGEntry& entry : entries)
{
if (entry.name_size > 256)
{
@@ -567,11 +568,9 @@ bool package_reader::read_param_sfo()
return true;
}
else
{
pkg_log.error("Failed to create temporary PARAM.SFO file");
return false;
}
pkg_log.error("Failed to create temporary PARAM.SFO file");
return false;
}
return false;
@@ -621,7 +620,7 @@ package_error package_reader::check_target_app_version() const
if (!target_app_ver.empty())
{
// We are unable to compare anything with the target app version
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s", target_app_ver, title_id);
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s. (path='%s', error=%s)", target_app_ver, title_id, sfo_path, fs::g_tls_error);
return package_error::app_version;
}
@@ -691,20 +690,16 @@ package_error package_reader::check_target_app_version() const
return package_error::app_version;
}
bool package_reader::fill_data(std::map<std::string, install_entry*>& all_install_entries)
bool package_reader::set_install_path()
{
if (!m_is_valid)
{
return false;
}
m_install_entries.clear();
m_install_path.clear();
m_bootable_file_path.clear();
m_entry_indexer = 0;
m_written_bytes = 0;
// Get full path and create the directory
// Get full path
std::string dir = rpcs3::utils::get_hdd0_dir();
// Based on https://www.psdevwiki.com/ps3/PKG_files#ContentType
@@ -741,13 +736,27 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
// If false, an existing directory is being overwritten: cannot cancel the operation
m_was_null = !fs::is_dir(dir);
if (!fs::create_path(dir))
m_install_path = dir;
return true;
}
bool package_reader::fill_data(std::map<std::string, install_entry*>& all_install_entries)
{
if (!m_is_valid)
{
pkg_log.error("Could not create the installation directory %s", dir);
return false;
}
m_install_path = dir;
if (!fs::create_path(m_install_path))
{
pkg_log.error("Could not create the installation directory %s (error=%s)", m_install_path, fs::g_tls_error);
return false;
}
m_install_entries.clear();
m_bootable_file_path.clear();
m_entry_indexer = 0;
m_written_bytes = 0;
if (!decrypt_data())
{
@@ -774,13 +783,20 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
const std::string name{reinterpret_cast<char*>(m_bufs.back().get()), entry.name_size};
std::string path = dir + vfs::escape(name);
std::string path = m_install_path + vfs::escape(name);
const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS);
if (entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS))
{
pkg_log.todo("Entry with unknown type or padding: type=0x%08x, pad=0x%x, name='%s'", entry.type, entry.pad, name);
}
else
{
pkg_log.notice("Entry: type=0x%08x, name='%s'", entry.type, name);
}
(log_error ? pkg_log.error : pkg_log.notice)("Entry 0x%08x: %s (pad=0x%x)", entry.type, name, entry.pad);
const u8 entry_type = entry.type & 0xff;
switch (const u8 entry_type = entry.type & 0xff)
switch (entry_type)
{
case PKG_FILE_ENTRY_FOLDER:
case 0x12:
@@ -804,10 +820,25 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
}
default:
{
const std::string true_path = std::filesystem::weakly_canonical(std::filesystem::u8path(path)).string();
// TODO: check for valid utf8 characters
const std::string true_path = std::filesystem::weakly_canonical(path).string();
if (true_path.empty())
{
num_failures++;
pkg_log.error("Failed to get weakly_canonical path for '%s'", path);
break;
}
auto map_ptr = &*all_install_entries.try_emplace(true_path).first;
m_install_entries.push_back({ map_ptr, name, entry.file_offset, entry.file_size, entry.type, entry.pad });
m_install_entries.push_back({
.weak_reference = map_ptr,
.name = name,
.file_offset = entry.file_offset,
.file_size = entry.file_size,
.type = entry.type,
.pad = entry.pad
});
if (map_ptr->second && !(entry.type & PKG_FILE_ENTRY_OVERWRITE))
{
@@ -824,7 +855,7 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
if (num_failures != 0)
{
pkg_log.error("Package installation failed: %s", dir);
pkg_log.error("Package installation failed: %s", m_install_path);
return false;
}
@@ -833,20 +864,28 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic, bool verbose = false);
usz package_reader::extract_worker(thread_key thread_data_key, std::map<std::string, install_entry*>& all_install_entries)
void package_reader::extract_worker(thread_key thread_data_key)
{
usz num_failures = 0;
while (true)
while (m_num_failures == 0 && !m_aborted)
{
const usz maybe_index = m_entry_indexer++;
// Make sure m_entry_indexer does not exceed m_install_entries
const usz index = m_entry_indexer.fetch_op([this](usz& v)
{
if (v < m_install_entries.size())
{
v++;
return true;
}
if (maybe_index >= m_install_entries.size())
return false;
}).first;
if (index >= m_install_entries.size())
{
break;
}
const install_entry& entry = ::at32(m_install_entries, maybe_index);
const install_entry& entry = ::at32(m_install_entries, index);
if (!entry.is_dominating())
{
@@ -860,8 +899,14 @@ usz package_reader::extract_worker(thread_key thread_data_key, std::map<std::str
const std::string& path = entry.weak_reference->first;
const std::string& name = entry.name;
const bool log_error = entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS);
(log_error ? pkg_log.error : pkg_log.notice)("Entry 0x%08x: %s (pad=0x%x)", entry.type, name, entry.pad);
if (entry.pad || (entry.type & ~PKG_FILE_ENTRY_KNOWN_BITS))
{
pkg_log.todo("Entry with unknown type or padding: type=0x%08x, pad=0x%x, name='%s'", entry.type, entry.pad, name);
}
else
{
pkg_log.notice("Entry: type=0x%08x, name='%s'", entry.type, name);
}
switch (const u8 entry_type = entry.type & 0xff)
{
@@ -908,14 +953,14 @@ usz package_reader::extract_worker(thread_key thread_data_key, std::map<std::str
if (data_span.size() != block_size)
{
extract_success = false;
pkg_log.error("Failed to extract file %s", path);
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)
{
extract_success = false;
pkg_log.error("Failed to write file %s", path);
pkg_log.error("Failed to write file %s (error=%s)", path, fs::g_tls_error);
break;
}
@@ -927,8 +972,8 @@ usz package_reader::extract_worker(thread_key thread_data_key, std::map<std::str
out = DecryptEDAT(out, name, 1, reinterpret_cast<u8*>(&m_header.klicensee), true);
if (!out || !fs::write_file(path, fs::rewrite, static_cast<fs::container_stream<std::vector<u8>>*>(out.release().get())->obj))
{
num_failures++;
pkg_log.error("Failed to create file %s", path);
m_num_failures++;
pkg_log.error("Failed to create file %s (error=%s)", path, fs::g_tls_error);
break;
}
}
@@ -952,71 +997,121 @@ usz package_reader::extract_worker(thread_key thread_data_key, std::map<std::str
}
else
{
num_failures++;
m_num_failures++;
}
}
else
{
num_failures++;
pkg_log.error("Failed to create file %s", path);
m_num_failures++;
pkg_log.error("Failed to create file %s (is_buffered=%d, did_overwrite=%d, error=%s)", path, is_buffered, did_overwrite, fs::g_tls_error);
}
break;
}
default:
{
num_failures++;
m_num_failures++;
pkg_log.error("Unknown PKG entry type (0x%x) %s", entry.type, name);
break;
}
}
}
return num_failures;
}
bool package_reader::extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths)
package_error package_reader::extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths)
{
std::map<std::string, install_entry*> all_install_entries;
package_error error = package_error::no_error;
usz num_failures = 0;
for (auto& reader : readers)
// Set paths first in order to know if the install dir was empty before starting any installations.
// This will also allow us to remove all the new packages in one path at once if any of them fail.
for (package_reader& reader : readers)
{
if (!reader.fill_data(all_install_entries))
reader.m_result = result::not_started;
if (!reader.set_install_path())
{
return false;
error = package_error::other;
reader.m_result = result::error;
break;
}
}
usz num_failures = 0;
for (auto& reader : readers)
for (package_reader& reader : readers)
{
// Use a seperate map for each reader. We need to check if the target app version exists for each package in sequence.
std::map<std::string, install_entry*> all_install_entries;
if (error == package_error::no_error)
{
// Check if this package is allowed to be installed on top of the existing data
error = reader.check_target_app_version();
}
if (error == package_error::no_error)
{
reader.m_result = result::started;
// Parse the files to be installed and create all paths.
if (!reader.fill_data(all_install_entries))
{
error = package_error::other;
}
}
reader.m_bufs.resize(std::min<usz>(utils::get_thread_count(), reader.m_install_entries.size()));
reader.m_num_failures = error == package_error::no_error ? 0 : 1;
atomic_t<usz> thread_indexer = 0;
named_thread_group workers("PKG Installer "sv, std::max<usz>(reader.m_bufs.size(), 1) - 1, [&]()
named_thread_group workers("PKG Installer "sv, std::max<u32>(::narrow<u32>(reader.m_bufs.size()), 1) - 1, [&]()
{
num_failures += reader.extract_worker(thread_key{thread_indexer++}, all_install_entries);
reader.extract_worker(thread_key{thread_indexer++});
});
num_failures += reader.extract_worker(thread_key{thread_indexer++}, all_install_entries);
reader.extract_worker(thread_key{thread_indexer++});
workers.join();
num_failures += reader.m_num_failures;
reader.m_bufs.clear();
reader.m_bufs.shrink_to_fit();
if (num_failures)
// We don't count this package as aborted if all entries were processed.
if (reader.m_num_failures || (reader.m_aborted && reader.m_entry_indexer < reader.m_install_entries.size()))
{
if (reader.m_was_null)
// Clear boot path. We don't want to propagate potentially broken paths to the caller.
reader.m_bootable_file_path.clear();
bool cleaned = reader.m_was_null;
if (reader.m_was_null && fs::is_dir(reader.m_install_path))
{
fs::remove_all(reader.m_install_path, true);
pkg_log.notice("Removing partial installation ('%s')", reader.m_install_path);
if (!fs::remove_all(reader.m_install_path, true))
{
pkg_log.notice("Failed to remove partial installation ('%s') (error=%s)", reader.m_install_path, fs::g_tls_error);
cleaned = false;
}
}
pkg_log.success("Package failed to install ('%s')", reader.m_install_path);
if (reader.m_num_failures)
{
pkg_log.error("Package failed to install ('%s')", reader.m_install_path);
reader.m_result = cleaned ? result::error_cleaned : result::error;
}
else
{
pkg_log.warning("Package installation aborted ('%s')", reader.m_install_path);
reader.m_result = cleaned ? result::aborted_cleaned : result::aborted;
}
break;
}
else if (reader.get_progress(1) != 1)
reader.m_result = result::success;
if (reader.get_progress(1) != 1)
{
pkg_log.warning("Missing %d bytes from PKG total files size.", reader.m_header.data_size - reader.m_written_bytes);
reader.m_written_bytes = reader.m_header.data_size; // Mark as completed anyway
@@ -1026,13 +1121,18 @@ bool package_reader::extract_data(std::deque<package_reader>& readers, std::dequ
bootable_paths.emplace_back(std::move(reader.m_bootable_file_path));
}
return num_failures == 0;
if (num_failures > 0)
{
error = package_error::other;
}
return error;
}
void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode)
{
if (m_file) m_file.seek(new_offset, damode);
};
}
u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
{
@@ -1128,4 +1228,16 @@ std::span<const char> package_reader::decrypt(u64 offset, u64 size, const uchar*
// Return the amount of data written in buf
return data_span;
};
}
int package_reader::get_progress(int maximum) const
{
const usz wr = m_written_bytes;
return wr >= m_header.data_size ? maximum : ::narrow<int>(wr * maximum / m_header.data_size);
}
void package_reader::abort_extract()
{
m_aborted = true;
}
+20 -21
View File
@@ -332,31 +332,26 @@ public:
package_reader(const std::string& path);
~package_reader();
enum result
{
not_started,
started,
success,
aborted,
aborted_cleaned,
error,
error_cleaned
};
bool is_valid() const { return m_is_valid; }
package_error check_target_app_version() const;
static bool extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
static package_error extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
psf::registry get_psf() const { return m_psf; }
result get_result() const { return m_result; };
int get_progress(int maximum = 100) const
{
const usz wr = m_written_bytes;
int get_progress(int maximum = 100) const;
return wr >= m_header.data_size ? maximum : ::narrow<int>(wr * maximum / m_header.data_size);
}
void abort_extract()
{
m_entry_indexer.fetch_op([this](usz& v)
{
if (v < m_install_entries.size())
{
v = m_install_entries.size();
return true;
}
return false;
});
}
void abort_extract();
private:
bool read_header();
@@ -365,13 +360,16 @@ private:
bool decrypt_data();
void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set);
u64 archive_read(void* data_ptr, u64 num_bytes);
bool set_install_path();
bool fill_data(std::map<std::string, install_entry*>& all_install_entries);
std::span<const char> archive_read_block(u64 offset, void* data_ptr, u64 num_bytes);
std::span<const char> decrypt(u64 offset, u64 size, const uchar* key, thread_key thread_data_key = {0});
usz extract_worker(thread_key thread_data_key, std::map<std::string, install_entry*>& all_install_entries);
void extract_worker(thread_key thread_data_key);
std::deque<install_entry> m_install_entries;
std::string m_install_path;
atomic_t<bool> m_aborted = false;
atomic_t<usz> m_num_failures = 0;
atomic_t<usz> m_entry_indexer = 0;
atomic_t<usz> m_written_bytes = 0;
bool m_was_null = false;
@@ -379,6 +377,7 @@ private:
static constexpr usz BUF_SIZE = 8192 * 1024; // 8 MB
bool m_is_valid = false;
result m_result = result::not_started;
std::string m_path{};
std::string m_install_dir{};
+16 -1
View File
@@ -159,6 +159,10 @@ target_link_libraries(rpcs3_emu
PUBLIC
3rdparty::soundtouch)
target_link_libraries(rpcs3_emu
PUBLIC
3rdparty::miniupnpc)
# Cell
target_sources(rpcs3_emu PRIVATE
Cell/MFC.cpp
@@ -389,6 +393,7 @@ target_sources(rpcs3_emu PRIVATE
Io/pad_config_types.cpp
Io/PadHandler.cpp
Io/usb_device.cpp
Io/usb_vfs.cpp
Io/Skylander.cpp
Io/GHLtar.cpp
Io/Buzz.cpp
@@ -410,6 +415,8 @@ target_sources(rpcs3_emu PRIVATE
NP/np_structs_extra.cpp
NP/rpcn_client.cpp
NP/rpcn_config.cpp
NP/upnp_config.cpp
NP/upnp_handler.cpp
)
# Memory
@@ -436,12 +443,20 @@ target_sources(rpcs3_emu PRIVATE
RSX/Common/TextureUtils.cpp
RSX/Common/texture_cache.cpp
RSX/Null/NullGSRender.cpp
RSX/Overlays/HomeMenu/overlay_home_menu.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_main_menu.cpp
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/overlay_animated_icon.cpp
RSX/Overlays/overlay_animation.cpp
RSX/Overlays/overlay_controls.cpp
RSX/Overlays/overlay_cursor.cpp
RSX/Overlays/overlay_edit_text.cpp
RSX/Overlays/overlay_fonts.cpp
RSX/Overlays/overlay_list_view.cpp
RSX/Overlays/overlay_manager.cpp
RSX/Overlays/overlay_media_list_dialog.cpp
RSX/Overlays/overlay_message.cpp
RSX/Overlays/overlay_message_dialog.cpp
@@ -450,10 +465,10 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/overlay_perf_metrics.cpp
RSX/Overlays/overlay_progress_bar.cpp
RSX/Overlays/overlay_save_dialog.cpp
RSX/Overlays/overlay_shader_compile_notification.cpp
RSX/Overlays/overlay_user_list_dialog.cpp
RSX/Overlays/overlay_utils.cpp
RSX/Overlays/overlays.cpp
RSX/Overlays/overlay_shader_compile_notification.cpp
RSX/Overlays/overlay_trophy_notification.cpp
RSX/Overlays/Shaders/shader_loading_dialog.cpp
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
+1 -1
View File
@@ -34,7 +34,7 @@ class error_code
{
}
public:
public:
// Implementation must be provided independently
static s32 error_report(s32 result, const logs::message* channel, const char* fmt, const fmt_type_info* sup, const u64* args);
+1 -1
View File
@@ -8,7 +8,7 @@
#include <thread>
// SONIC THE HEDGEDOG: a fix for a race condition between SPUs and PPUs causing missing graphics (SNR is overriden when non-empty)
// SONIC THE HEDGEDOG: a fix for a race condition between SPUs and PPUs causing missing graphics (SNR is overriden when non-empty)
void WaitForSPUsToEmptySNRs(ppu_thread& ppu, u32 spu_id, u32 snr_mask)
{
ppu.state += cpu_flag::wait;
+1 -1
View File
@@ -427,5 +427,5 @@ using cell_audio = named_thread<cell_audio_thread>;
namespace audio
{
cell_audio_config::raw_config get_raw_config();
void configure_audio(bool force_reset = false);
extern void configure_audio(bool force_reset = false);
}
+8 -8
View File
@@ -147,7 +147,7 @@ void camera_context::save(utils::serial& ar)
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellCamera);
ar(notify_data_map, start_timestamp, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
ar(notify_data_map, start_timestamp_us, read_mode, is_streaming, is_attached, is_open, info, attr, frame_num);
}
static bool check_dev_num(s32 dev_num)
@@ -1268,7 +1268,7 @@ error_code cellCameraStart(s32 dev_num)
// TODO: Yet another CELL_CAMERA_ERROR_TIMEOUT
g_camera.start_timestamp = get_guest_system_time();
g_camera.start_timestamp_us = get_guest_system_time();
g_camera.is_streaming = true;
return CELL_OK;
@@ -1372,7 +1372,7 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
}
g_camera.bytes_read = bytes_read;
g_camera.bytes_read = ::narrow<u32>(bytes_read);
cellCamera.trace("cellCameraRead: frame_number=%d, width=%d, height=%d. bytes_read=%d (passed to game: frame=%d, bytesread=%d)",
frame_number, width, height, bytes_read, read ? read->frame.get() : 0, read ? read->bytesread.get() : 0);
@@ -1385,18 +1385,18 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
if (has_new_frame)
{
g_camera.frame_timestamp = (get_guest_system_time() - g_camera.start_timestamp);
g_camera.frame_timestamp_us = get_guest_system_time() - g_camera.start_timestamp_us;
}
if (read) // NULL returns CELL_OK
{
read->timestamp = g_camera.frame_timestamp;
read->timestamp = g_camera.frame_timestamp_us;
read->frame = g_camera.frame_num;
read->bytesread = g_camera.bytes_read;
auto& shared_data = g_fxo->get<gem_camera_shared>();
shared_data.frame_timestamp.store(read->timestamp);
shared_data.frame_timestamp_us.store(read->timestamp);
}
return CELL_OK;
@@ -1672,7 +1672,7 @@ void camera_context::operator()()
const u64 camera_id = 0;
data2 = image_data_size << 32 | buffer_number << 16 | camera_id;
data3 = get_guest_system_time() - start_timestamp; // timestamp
data3 = get_guest_system_time() - start_timestamp_us; // timestamp
}
else // CELL_CAMERA_READ_FUNCCALL, also default
{
@@ -1801,7 +1801,7 @@ void camera_context::reset_state()
pbuf_locked[0] = false;
pbuf_locked[1] = false;
has_new_frame = false;
frame_timestamp = 0;
frame_timestamp_us = 0;
bytes_read = 0;
if (info.buffer)
+4 -4
View File
@@ -384,7 +384,7 @@ struct CellCameraReadEx
be_t<s32> version;
be_t<u32> frame;
be_t<u32> bytesread;
be_t<s64> timestamp;
be_t<u64> timestamp; // system_time_t (microseconds)
vm::bptr<u8> pbuf;
};
@@ -422,7 +422,7 @@ public:
shared_mutex mutex;
shared_mutex mutex_notify_data_map;
u64 start_timestamp = 0;
u64 start_timestamp_us = 0;
atomic_t<u8> read_mode{CELL_CAMERA_READ_FUNCCALL};
atomic_t<bool> is_streaming{false};
@@ -444,7 +444,7 @@ public:
attr_t attr[500]{};
atomic_t<bool> has_new_frame = false;
atomic_t<u32> frame_num = 0;
atomic_t<u32> frame_timestamp = 0;
atomic_t<u64> frame_timestamp_us = 0;
atomic_t<u32> bytes_read = 0;
atomic_t<u8> init = 0;
@@ -471,7 +471,7 @@ using camera_thread = named_thread<camera_context>;
/// Shared data between cellGem and cellCamera
struct gem_camera_shared
{
atomic_t<s64> frame_timestamp{}; // latest read timestamp from cellCamera (cellCameraRead(Ex))
atomic_t<u64> frame_timestamp_us{}; // latest read timestamp from cellCamera (cellCameraRead(Ex))
atomic_t<s32> width{640};
atomic_t<s32> height{480};
atomic_t<s32> size{0};
+11 -3
View File
@@ -466,7 +466,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
// psf::assign(sfo, "CATEGORY", psf::string(3, "HG"));
// }
// psf::assign(sfo, "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, setParam->titleId));
// psf::assign(sfo, "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, setParam->titleId));
// psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
// psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
// psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
@@ -990,7 +990,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
psf::assign(sfo, "CATEGORY", psf::string(3, "GD"));
}
psf::assign(sfo, "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, setParam->titleId));
psf::assign(sfo, "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, setParam->titleId, true));
psf::assign(sfo, "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->title));
psf::assign(sfo, "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, setParam->dataVersion));
psf::assign(sfo, "PARENTAL_LEVEL", +setParam->parentalLevel);
@@ -1005,6 +1005,14 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
psf::assign(sfo, fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, setParam->titleLang[i]));
}
if (!psf::check_registry(sfo))
{
// This results in CELL_OK, broken SFO and CELL_GAMEDATA_ERROR_BROKEN on the next load
// Avoid creation for now
cellGame.error("Broken SFO paramters: %s", sfo);
return CELL_OK;
}
fs::pending_file temp(vfs::get(dir + "/PARAM.SFO"));
temp.file.write(psf::save_object(sfo));
ensure(temp.commit());
@@ -1125,7 +1133,7 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
perm.sfo =
{
{ "CATEGORY", psf::string(3, "GD") },
{ "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, init->titleId) },
{ "TITLE_ID", psf::string(TITLEID_SFO_ENTRY_SIZE, init->titleId) },
{ "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, init->title) },
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, init->version) },
};
+5
View File
@@ -216,6 +216,11 @@ enum // old consts
CELL_DISCGAME_SYSP_TITLEID_SIZE=10,
};
enum
{
TITLEID_SFO_ENTRY_SIZE = 16, // This is the true length on PS3 (TODO: Fix in more places)
};
struct CellGameDataSystemFileParam
{
char title[CELL_GAMEDATA_SYSP_TITLE_SIZE];
+27 -15
View File
@@ -215,7 +215,7 @@ public:
shared_mutex mtx;
u64 start_timestamp = 0;
u64 start_timestamp_us = 0;
// helper functions
bool is_controller_ready(u32 gem_num) const
@@ -299,10 +299,20 @@ public:
return;
}
GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem);
[[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem);
ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers
, connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp);
, connected_controllers, update_started, camera_frame, memory_ptr);
if (version == 1 && !ar.is_writing())
{
u32 ts = ar;
start_timestamp_us = ts;
}
else
{
ar(start_timestamp_us);
}
}
gem_config_data(utils::serial& ar)
@@ -894,23 +904,25 @@ static bool gun_input_to_pad(const u32 gem_no, be_t<u16>& digital_buttons, be_t<
digital_buttons |= CELL_GEM_CTRL_MOVE;
if (gun.handler.get_button(gem_no, gun_button::btn_middle) == 1)
digital_buttons |= CELL_GEM_CTRL_SELECT;
if (gun.handler.get_button(gem_no, gun_button::btn_1) == 1)
digital_buttons |= CELL_GEM_CTRL_START;
if (gun.handler.get_button(gem_no, gun_button::btn_2) == 1)
if (gun.handler.get_button(gem_no, gun_button::btn_1) == 1)
digital_buttons |= CELL_GEM_CTRL_CROSS;
if (gun.handler.get_button(gem_no, gun_button::btn_3) == 1)
if (gun.handler.get_button(gem_no, gun_button::btn_2) == 1)
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
if (gun.handler.get_button(gem_no, gun_button::btn_4) == 1)
digital_buttons |= CELL_GEM_CTRL_SQUARE;
if (gun.handler.get_button(gem_no, gun_button::btn_3) == 1)
digital_buttons |= CELL_GEM_CTRL_SELECT;
if (gun.handler.get_button(gem_no, gun_button::btn_5) == 1)
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
if (gun.handler.get_button(gem_no, gun_button::btn_6) == 1)
digital_buttons |= CELL_GEM_CTRL_SQUARE;
analog_t = gun.handler.get_button(gem_no, gun_button::btn_left) ? 0xFFFF : 0;
return true;
}
@@ -1326,7 +1338,7 @@ error_code cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> gem_imag
if (g_cfg.io.move != move_handler::null)
{
auto& shared_data = g_fxo->get<gem_camera_shared>();
gem_image_state->frame_timestamp = shared_data.frame_timestamp.load();
gem_image_state->frame_timestamp = shared_data.frame_timestamp_us.load();
gem_image_state->timestamp = gem_image_state->frame_timestamp + 10;
gem_image_state->r = gem.controllers[gem_num].radius; // Radius in camera pixels
gem_image_state->distance = gem.controllers[gem_num].distance; // 1.5 meters away from camera
@@ -1383,7 +1395,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
{
ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext);
inertial_state->timestamp = (get_guest_system_time() - gem.start_timestamp);
inertial_state->timestamp = (get_guest_system_time() - gem.start_timestamp_us);
inertial_state->counter = gem.inertial_counter++;
inertial_state->accelerometer[0] = 10; // Current gravity in m/s²
@@ -1559,7 +1571,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
tracking_flags |= CELL_GEM_TRACKING_FLAG_POSITION_TRACKED;
gem_state->tracking_flags = tracking_flags;
gem_state->timestamp = (get_guest_system_time() - gem.start_timestamp);
gem_state->timestamp = (get_guest_system_time() - gem.start_timestamp_us);
gem_state->camera_pitch_angle = 0.f;
gem_state->quat[3] = 1.f;
@@ -1761,7 +1773,7 @@ error_code cellGemInit(ppu_thread& ppu, vm::cptr<CellGemAttribute> attribute)
}
// TODO: is this correct?
gem.start_timestamp = get_guest_system_time();
gem.start_timestamp_us = get_guest_system_time();
return CELL_OK;
}
@@ -1937,7 +1949,7 @@ error_code cellGemReset(u32 gem_num)
gem.reset_controller(gem_num);
// TODO: is this correct?
gem.start_timestamp = get_guest_system_time();
gem.start_timestamp_us = get_guest_system_time();
return CELL_OK;
}
+4 -4
View File
@@ -200,8 +200,8 @@ struct CellGemExtPortData
struct CellGemImageState
{
be_t<u64> frame_timestamp; // time the frame was captured by libCamera (usecs)
be_t<u64> timestamp; // time processing of the frame was finished (usecs)
be_t<u64> frame_timestamp; // time the frame was captured by libCamera. system_time_t (usecs)
be_t<u64> timestamp; // time processing of the frame was finished. system_time_t (usecs)
be_t<f32> u; // horizontal screen position in pixels
be_t<f32> v; // vertical screen position in pixels
be_t<f32> r; // size of sphere on screen in pixels
@@ -226,7 +226,7 @@ struct CellGemInertialState
be_t<f32> gyro_bias[4]; // gyro bias (radians/s)
CellGemPadData pad;
CellGemExtPortData ext;
be_t<u64> timestamp;
be_t<u64> timestamp; // system_time_t (microseconds)
be_t<s32> counter;
be_t<f32> temperature;
};
@@ -256,7 +256,7 @@ struct CellGemState
be_t<f32> handle_accel[4]; // acceleration of controller handle (mm/s²)
CellGemPadData pad;
CellGemExtPortData ext;
be_t<u64> timestamp;
be_t<u64> timestamp; // system_time_t (microseconds)
be_t<f32> temperature;
be_t<f32> camera_pitch_angle;
be_t<u32> tracking_flags;
+3 -3
View File
@@ -248,7 +248,7 @@ error_code cellHttpUtilBuildRequestLine(vm::cptr<CellHttpRequestLine> req, vm::p
if (required)
{
*required = result.size();
*required = ::narrow<u32>(result.size());
}
return CELL_OK;
@@ -277,7 +277,7 @@ error_code cellHttpUtilBuildHeader(vm::cptr<CellHttpHeader> header, vm::ptr<char
if (required)
{
*required = result.size();
*required = ::narrow<u32>(result.size());
}
return CELL_OK;
@@ -336,7 +336,7 @@ error_code cellHttpUtilBuildUri(vm::cptr<CellHttpUri> uri, vm::ptr<char> buf, u3
fmt::append(result, "%s", uri->path);
}
const u32 size_needed = result.size() + 1; // Including '\0'
const u32 size_needed = ::narrow<u32>(result.size() + 1); // Including '\0'
if (buf)
{
+3 -3
View File
@@ -63,7 +63,7 @@ void MouseHandlerBase::save(utils::serial& ar)
error_code cellMouseInit(u32 max_connect)
{
sys_io.warning("cellMouseInit(max_connect=%d)", max_connect);
sys_io.notice("cellMouseInit(max_connect=%d)", max_connect);
auto& handler = g_fxo->get<MouseHandlerBase>();
@@ -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.warning("cellMouseGetDataList(port_no=%d, data=0x%x)", port_no, data);
sys_io.notice("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.warning("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
sys_io.notice("cellMouseGetRawData(port_no=%d, data=*0x%x)", port_no, data);
auto& handler = g_fxo->get<MouseHandlerBase>();
+37 -12
View File
@@ -5,6 +5,8 @@
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/Io/interception.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_message_dialog.h"
#include "cellSysutil.h"
@@ -135,19 +137,21 @@ struct msg_dlg_thread_info
using msg_dlg_thread = named_thread<msg_dlg_thread_info>;
// variable used to immediately get the response from auxiliary message dialogs (callbacks would be async)
atomic_t<s32> g_last_user_response = CELL_MSGDIALOG_BUTTON_NONE;
// forward declaration for open_msg_dialog
error_code cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam);
// wrapper to call for other hle dialogs
error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam, s32* return_code)
{
cellSysutil.notice("open_msg_dialog(is_blocking=%d, type=0x%x, msgString=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", is_blocking, type, msgString, callback, userData, extParam);
cellSysutil.notice("open_msg_dialog(is_blocking=%d, type=0x%x, msgString=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x, return_code=*0x%x)", is_blocking, type, msgString, callback, userData, extParam, return_code);
const MsgDialogType _type{ type };
if (return_code)
{
*return_code = CELL_MSGDIALOG_BUTTON_NONE;
}
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (manager->get<rsx::overlays::message_dialog>())
@@ -160,10 +164,15 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
return CellSysutilError{ret + 0u};
}
g_last_user_response = CELL_MSGDIALOG_BUTTON_NONE;
const auto notify = std::make_shared<atomic_t<bool>>(false);
const auto res = manager->create<rsx::overlays::message_dialog>()->show(is_blocking, msgString.get_ptr(), _type, [callback, userData](s32 status)
const auto res = manager->create<rsx::overlays::message_dialog>()->show(is_blocking, msgString.get_ptr(), _type, [callback, userData, &return_code, is_blocking, notify](s32 status)
{
if (is_blocking && return_code)
{
*return_code = status;
}
sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0);
if (callback)
@@ -174,8 +183,20 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
return CELL_OK;
});
}
if (is_blocking && notify)
{
*notify = true;
notify->notify_one();
}
});
// Wait for on_close
while (is_blocking && !Emu.IsStopped() && !*notify)
{
notify->wait(false, atomic_wait_timeout{1'000'000});
}
return res;
}
@@ -193,8 +214,13 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
dlg->type = _type;
dlg->on_close = [callback, userData, wptr = std::weak_ptr<MsgDialogBase>(dlg)](s32 status)
dlg->on_close = [callback, userData, &return_code, wptr = std::weak_ptr<MsgDialogBase>(dlg)](s32 status)
{
if (return_code)
{
*return_code = status;
}
const auto dlg = wptr.lock();
if (dlg && dlg->state.compare_and_swap_test(MsgDialogState::Open, MsgDialogState::Close))
@@ -222,13 +248,12 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
auto& ppu = *get_current_cpu_thread();
lv2_obj::sleep(ppu);
// PS3 memory must not be accessed by Main thread
// PS3 memory must not be accessed by Main thread
std::string msg_string = msgString.get_ptr();
// Run asynchronously in GUI thread
Emu.CallFromMainThread([&, msg_string = std::move(msg_string)]()
{
g_last_user_response = CELL_MSGDIALOG_BUTTON_NONE;
dlg->Create(msg_string);
lv2_obj::awake(&ppu);
});
@@ -369,11 +394,11 @@ error_code cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMs
if (_type.se_normal)
{
cellSysutil.warning("%s", msgString);
cellSysutil.warning("Opening message dialog with message: %s", msgString);
}
else
{
cellSysutil.error("%s", msgString);
cellSysutil.error("Opening error message dialog with message: %s", msgString);
}
return open_msg_dialog(false, type, msgString, callback, userData, extParam);
+1 -3
View File
@@ -89,10 +89,8 @@ enum class MsgDialogState
Close,
};
extern atomic_t<s32> g_last_user_response;
void close_msg_dialog();
error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback = vm::null, vm::ptr<void> userData = vm::null, vm::ptr<void> extParam = vm::null);
error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback = vm::null, vm::ptr<void> userData = vm::null, vm::ptr<void> extParam = vm::null, s32* return_code = nullptr);
error_code open_exit_dialog(const std::string& message, bool is_exit_requested);
class MsgDialogBase
@@ -140,7 +140,7 @@ void music_selection_context::create_playlist(const std::string& new_hash)
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
{
cellMusicSelectionContext.error("Failed to create music playlist file %s (%s)", yaml_path, fs::g_tls_error);
cellMusicSelectionContext.error("Failed to create music playlist file %s (error=%s)", yaml_path, fs::g_tls_error);
}
}
@@ -302,7 +302,7 @@ u32 music_selection_context::step_track(bool next)
// Play the previous track. Start with the last track if we reached the start of the playlist.
if (current_track == 0)
{
current_track = playlist.size() - 1;
current_track = ::narrow<u32>(playlist.size() - 1);
}
else
{
+21 -5
View File
@@ -90,6 +90,23 @@ void fmt_class_string<CellNetCtlState>::format(std::string& out, u64 arg)
});
}
struct CellGameUpdateResult
{
be_t<s32> status; // CellGameUpdateResultStatus
be_t<s32> error_code;
char app_ver[CELL_GAME_SYSP_APP_VER_SIZE];
char padding[2];
};
struct CellGameUpdateParam
{
be_t<u32> size;
be_t<u32> cid;
};
using CellGameUpdateCallback = void(s32 status, s32 error_code, vm::ptr<void> userdata);
using CellGameUpdateCallbackEx = void(vm::ptr<CellGameUpdateResult> result, vm::ptr<void> userdata);
error_code cellNetCtlInit()
{
cellNetCtl.warning("cellNetCtlInit()");
@@ -204,16 +221,15 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
switch (code)
{
case CELL_NET_CTL_INFO_DEVICE: info->device = CELL_NET_CTL_DEVICE_WIRED; break;
// case CELL_NET_CTL_INFO_ETHER_ADDR: std::memset(info->ether_addr.data, 0xFF, sizeof(info->ether_addr.data)); break;
case CELL_NET_CTL_INFO_MTU: info->mtu = 1500; break;
case CELL_NET_CTL_INFO_LINK: info->link = CELL_NET_CTL_LINK_CONNECTED; break;
case CELL_NET_CTL_INFO_LINK_TYPE: info->link_type = CELL_NET_CTL_LINK_TYPE_10BASE_FULL; break;
case CELL_NET_CTL_INFO_LINK_TYPE: info->link_type = CELL_NET_CTL_LINK_TYPE_100BASE_FULL; break;
case CELL_NET_CTL_INFO_IP_CONFIG: info->ip_config = CELL_NET_CTL_IP_STATIC; break;
case CELL_NET_CTL_INFO_DEFAULT_ROUTE: strcpy_trunc(info->default_route, "192.168.1.1"); break;
case CELL_NET_CTL_INFO_PRIMARY_DNS: strcpy_trunc(info->primary_dns, np::ip_to_string(nph.get_dns_ip())); break;
case CELL_NET_CTL_INFO_SECONDARY_DNS: strcpy_trunc(info->secondary_dns, np::ip_to_string(nph.get_dns_ip())); break;
case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_public_ip_addr())); break;
case CELL_NET_CTL_INFO_NETMASK: strcpy_trunc(info->netmask, "255.255.255.255"); break;
case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_local_ip_addr())); break; // verified on HW
case CELL_NET_CTL_INFO_NETMASK: strcpy_trunc(info->netmask, "255.255.255.0"); break;
case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG: info->http_proxy_config = 0; break;
case CELL_NET_CTL_INFO_DHCP_HOSTNAME: strcpy_trunc(info->dhcp_hostname, nph.get_hostname()); break;
default: cellNetCtl.error("Unsupported request: %s", InfoCodeToName(code)); break;
@@ -331,7 +347,7 @@ error_code cellNetCtlGetNatInfo(vm::ptr<CellNetCtlNatInfo> natInfo)
natInfo->nat_type = CELL_NET_CTL_NATINFO_NAT_TYPE_2;
natInfo->stun_status = CELL_NET_CTL_NATINFO_STUN_OK;
natInfo->upnp_status = CELL_NET_CTL_NATINFO_UPNP_NO;
natInfo->upnp_status = nph.get_upnp_status();
return CELL_OK;
}
-19
View File
@@ -1,7 +1,5 @@
#pragma once
#include "cellGame.h"
#include "Emu/Memory/vm_ptr.h"
// Error Codes
@@ -335,20 +333,3 @@ enum
CELL_GAMEUPDATE_RESULT_STATUS_ABORTED = 6,
CELL_GAMEUPDATE_RESULT_STATUS_SYSTEM_UPDATE_NEEDED = 7
};
struct CellGameUpdateResult
{
be_t<s32> status; // CellGameUpdateResultStatus
be_t<s32> error_code;
char app_ver[CELL_GAME_SYSP_APP_VER_SIZE];
char padding[2];
};
struct CellGameUpdateParam
{
be_t<u32> size;
be_t<u32> cid;
};
using CellGameUpdateCallback = void(s32 status, s32 error_code, vm::ptr<void> userdata);
using CellGameUpdateCallbackEx = void(vm::ptr<CellGameUpdateResult> result, vm::ptr<void> userdata);
+370 -211
View File
@@ -4,14 +4,15 @@
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/Keyboard.h"
#include "Emu/RSX/Overlays/overlay_osk.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_osk.h"
#include "cellSysutil.h"
#include "cellOskDialog.h"
#include "cellMsgDialog.h"
#include "util/init_mutex.hpp"
#include "cellImeJp.h"
#include <thread>
@@ -51,77 +52,73 @@ void fmt_class_string<CellOskDialogContinuousMode>::format(std::string& out, u64
});
}
OskDialogBase::~OskDialogBase()
void osk_info::reset()
{
std::lock_guard lock(text_mtx);
dlg.reset();
valid_text = {};
use_separate_windows = false;
lock_ext_input_device = false;
device_mask = 0;
input_field_window_width = 0;
input_field_background_transparency = 1.0f;
input_field_layout_info = {};
input_panel_layout_info = {};
key_layout_options = CELL_OSKDIALOG_10KEY_PANEL;
initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
clipboard_enabled = false;
half_byte_kana_enabled = false;
supported_languages = 0;
dimmer_enabled = true;
base_color = OskDialogBase::color{ 0.2f, 0.2f, 0.2f, 1.0f };
pointer_enabled = false;
pointer_x = 0.0f;
pointer_y = 0.0f;
initial_scale = 1.0f;
layout = {};
osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED;
osk_confirm_callback.store({});
osk_force_finish_callback.store({});
osk_hardware_keyboard_event_hook_callback.store({});
hook_event_mode.store(0);
}
struct osk_info
// Align horizontally
u32 osk_info::get_aligned_x(u32 layout_mode)
{
std::shared_ptr<OskDialogBase> dlg;
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{};
shared_mutex text_mtx;
atomic_t<bool> use_separate_windows = false;
atomic_t<bool> lock_ext_input = false;
atomic_t<u32> device_mask = 0; // 0 means all devices can influence the OSK
atomic_t<u32> key_layout = CELL_OSKDIALOG_10KEY_PANEL;
atomic_t<CellOskDialogInitialKeyLayout> initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
atomic_t<CellOskDialogInputDevice> initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
atomic_t<bool> clipboard_enabled = false; // For copy and paste
atomic_t<bool> half_byte_kana_enabled = false;
atomic_t<u32> supported_languages = 0; // Used to enable non-default languages in the OSK
atomic_t<bool> dimmer_enabled = true;
atomic_t<OskDialogBase::color> base_color = OskDialogBase::color{ 0.2f, 0.2f, 0.2f, 1.0f };
atomic_t<bool> pointer_enabled = false;
CellOskDialogPoint pointer_pos{0.0f, 0.0f};
atomic_t<f32> initial_scale = 1.0f;
atomic_t<u32> layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
atomic_t<CellOskDialogContinuousMode> osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
atomic_t<u32> last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED; // Used for continuous seperate window dialog
atomic_t<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
atomic_t<vm::ptr<cellOskDialogForceFinishCallback>> osk_force_finish_callback{};
atomic_t<vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback>> osk_hardware_keyboard_event_hook_callback{};
atomic_t<u16> hook_event_mode{0};
stx::init_mutex init;
void reset()
// Let's prefer a centered alignment.
if (layout_mode & CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_CENTER)
{
std::lock_guard lock(text_mtx);
dlg.reset();
valid_text = {};
use_separate_windows = false;
lock_ext_input = false;
device_mask = 0;
key_layout = CELL_OSKDIALOG_10KEY_PANEL;
initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM;
initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD;
clipboard_enabled = false;
half_byte_kana_enabled = false;
supported_languages = 0;
dimmer_enabled = true;
base_color = OskDialogBase::color{ 0.2f, 0.2f, 0.2f, 1.0f };
pointer_enabled = false;
pointer_pos = {0.0f, 0.0f};
initial_scale = 1.0f;
layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED;
osk_confirm_callback.store({});
osk_force_finish_callback.store({});
osk_hardware_keyboard_event_hook_callback.store({});
hook_event_mode.store(0);
return CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_CENTER;
}
};
if (layout_mode & CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT)
{
return CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT;
}
return CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_RIGHT;
}
// Align vertically
u32 osk_info::get_aligned_y(u32 layout_mode)
{
// Let's prefer a centered alignment.
if (layout_mode & CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_CENTER)
{
return CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_CENTER;
}
if (layout_mode & CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP)
{
return CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
}
return CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_BOTTOM;
}
// TODO: don't use this function
std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create)
@@ -160,6 +157,67 @@ std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create)
return osk.dlg;
}
extern bool close_osk_from_ps_button()
{
const auto osk = _get_osk_dialog(false);
if (!osk)
{
// The OSK is not open
return true;
}
osk_info& info = g_fxo->get<osk_info>();
// We can only close the osk in separate window mode when it is hidden (continuous_mode is set to CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
if (!info.use_separate_windows || osk->continuous_mode != CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
cellOskDialog.warning("close_osk_from_ps_button: can't close OSK (use_separate_windows=%d, continuous_mode=%s)", info.use_separate_windows.load(), osk->continuous_mode.load());
return false;
}
std::lock_guard lock(info.text_mtx);
if (auto cb = info.osk_force_finish_callback.load())
{
bool done = false;
bool close_osk = false;
sysutil_register_cb([&](ppu_thread& cb_ppu) -> s32
{
cellOskDialog.notice("osk_force_finish_callback()");
close_osk = cb(cb_ppu);
cellOskDialog.notice("osk_force_finish_callback returned %d", close_osk);
done = true;
return 0;
});
// wait for check callback
while (!done && !Emu.IsStopped())
{
std::this_thread::yield();
}
if (!close_osk)
{
// We are not allowed to close the OSK
cellOskDialog.warning("close_osk_from_ps_button: can't close OSK (osk_force_finish_callback returned false)");
return false;
}
}
// Forcefully terminate the OSK
cellOskDialog.warning("close_osk_from_ps_button: Terminating the OSK ...");
osk->Close(FAKE_CELL_OSKDIALOG_CLOSE_TERMINATE);
osk->state = OskDialogState::Closed;
cellOskDialog.notice("close_osk_from_ps_button: sending CELL_SYSUTIL_OSKDIALOG_FINISHED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
return true;
}
error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogParam, vm::ptr<CellOskDialogInputFieldInfo> inputFieldInfo)
{
cellOskDialog.warning("cellOskDialogLoadAsync(container=0x%x, dialogParam=*0x%x, inputFieldInfo=*0x%x)", container, dialogParam, inputFieldInfo);
@@ -169,6 +227,9 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
return CELL_OSKDIALOG_ERROR_PARAM;
}
cellOskDialog.notice("cellOskDialogLoadAsync: dialogParam={ allowOskPanelFlg=0x%x, prohibitFlgs=0x%x, firstViewPanel=%d, controlPoint=(%.2f,%.2f) }",
dialogParam->allowOskPanelFlg, dialogParam->prohibitFlgs, dialogParam->firstViewPanel, dialogParam->controlPoint.x, dialogParam->controlPoint.y);
auto osk = _get_osk_dialog(true);
// Can't open another dialog if this one is already open.
@@ -183,10 +244,12 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
const u32 prohibitFlgs = dialogParam->prohibitFlgs;
const u32 allowOskPanelFlg = dialogParam->allowOskPanelFlg;
const u32 firstViewPanel = dialogParam->firstViewPanel;
info.layout.x_offset = dialogParam->controlPoint.x;
info.layout.y_offset = dialogParam->controlPoint.y;
// Get init text and prepare return value
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK;
std::memset(osk->osk_text, 0, sizeof(osk->osk_text));
osk->osk_text = {};
// Also clear the info text just to be sure (it should be zeroed at this point anyway)
{
@@ -214,11 +277,16 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
}
}
osk->on_osk_close = [wptr = std::weak_ptr<OskDialogBase>(osk)](s32 status)
osk->on_osk_close = [](s32 status)
{
cellOskDialog.notice("on_osk_close(status=%d)", status);
const auto osk = wptr.lock();
const auto osk = _get_osk_dialog(false);
if (!osk)
{
return;
}
osk->state = OskDialogState::Closed;
auto& info = g_fxo->get<osk_info>();
@@ -229,7 +297,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
{
case CELL_OSKDIALOG_CLOSE_CONFIRM:
{
if (auto ccb = info.osk_confirm_callback.exchange({}))
if (auto ccb = info.osk_confirm_callback.load())
{
std::vector<u16> string_to_send(CELL_OSKDIALOG_STRING_SIZE);
atomic_t<bool> done = false;
@@ -266,7 +334,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
if (info.use_separate_windows.load() && osk->osk_text[0] == 0)
{
cellOskDialog.warning("cellOskDialogLoadAsync: input result is CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT");
cellOskDialog.warning("on_osk_close: input result is CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT");
osk->osk_input_result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_NO_INPUT_TEXT;
}
else
@@ -300,12 +368,16 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
case CELL_OSKDIALOG_CLOSE_CONFIRM:
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED;
cellOskDialog.notice("on_osk_close: sending CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED, 0);
break;
}
case CELL_OSKDIALOG_CLOSE_CANCEL:
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED;
cellOskDialog.notice("on_osk_close: sending CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED, 0);
break;
}
@@ -320,32 +392,34 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
break;
}
}
if (info.osk_continuous_mode.load() == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_HIDE);
}
}
else if (status != FAKE_CELL_OSKDIALOG_CLOSE_ABORT) // Handled in cellOskDialogAbort
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
cellOskDialog.notice("on_osk_close: sending CELL_SYSUTIL_OSKDIALOG_FINISHED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
}
input::SetIntercepted(false);
// The interception status of the continuous separate window is handled differently
if (!keep_seperate_window_open)
{
input::SetIntercepted(false);
}
};
// Set key callback
osk->on_osk_key_input_entered = [wptr = std::weak_ptr<OskDialogBase>(osk)](CellOskDialogKeyMessage key_message)
osk->on_osk_key_input_entered = [](CellOskDialogKeyMessage key_message)
{
const auto osk = wptr.lock();
auto& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
auto event_hook_callback = info.osk_hardware_keyboard_event_hook_callback.load();
cellOskDialog.notice("on_osk_key_input_entered: led=%d, mkey=%d, keycode=%d, hook_event_mode=%d, event_hook_callback=*0x%x", key_message.led, key_message.mkey, key_message.keycode, info.hook_event_mode.load(), event_hook_callback);
if (!event_hook_callback)
const auto osk = _get_osk_dialog(false);
if (!osk || !event_hook_callback)
{
// Nothing to do here
return;
@@ -409,39 +483,43 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
break;
}
}
if (!is_kook_key)
{
cellOskDialog.notice("on_osk_key_input_entered: not a hook key: led=%d, mkey=%d, keycode=%d, hook_event_mode=%d", key_message.led, key_message.mkey, key_message.keycode, info.hook_event_mode.load());
return;
}
// The max size is 100 characters plus '\0'. Apparently this used to be 30 plus '\0' in older firmware.
constexpr u32 max_size = 101;
std::vector<u16> string_to_send(max_size, 0);
atomic_t<bool> done = false;
for (u32 i = 0; i < max_size - 1; i++)
{
string_to_send[i] = osk->osk_text[i];
if (osk->osk_text[i] == 0) break;
}
// TODO: Send unconfirmed string if there is one.
// As far as I understand, this is for example supposed to be the IME preview.
// So when you type in japanese, you get some word propositions which you can select and confirm.
// The "confirmed" string is basically everything you already wrote, while the "unconfirmed"
// string is the auto-completion part that you haven't accepted as proper word yet.
// The game expects you to send this "preview", or an empty string if there is none.
std::array<u16, max_size> string_to_send{};
sysutil_register_cb([&](ppu_thread& cb_ppu) -> s32
sysutil_register_cb([key_message, string_to_send, event_hook_callback](ppu_thread& cb_ppu) -> s32
{
// Prepare callback variables
vm::var<CellOskDialogKeyMessage> keyMessage(key_message);
vm::var<u32> action(CELL_OSKDIALOG_CHANGE_NO_EVENT);
vm::var<u16[], vm::page_allocator<>> pActionInfo(max_size, string_to_send.data());
vm::var<u16[], vm::page_allocator<>> pActionInfo(string_to_send.size(), string_to_send.data());
std::u16string utf16_string;
utf16_string.insert(0, reinterpret_cast<char16_t*>(string_to_send.data()), string_to_send.size());
std::string action_info = utf16_to_ascii8(utf16_string);
// Create helpers for logging
std::u16string utf16_string(reinterpret_cast<const char16_t*>(string_to_send.data()), string_to_send.size());
std::string utf8_string = utf16_to_ascii8(utf16_string);
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback(led=%d, mkey=%d, keycode=%d, action=%d, pActionInfo='%s')", keyMessage->led, keyMessage->mkey, keyMessage->keycode, *action, action_info);
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback(led=%d, mkey=%d, keycode=%d, action=%d, pActionInfo='%s')", keyMessage->led, keyMessage->mkey, keyMessage->keycode, *action, utf8_string);
const bool return_value = event_hook_callback(cb_ppu, keyMessage, action, pActionInfo.begin());
// Call the hook function. The game reads and writes pActionInfo. We need to react based on the returned action.
const bool return_value = event_hook_callback(cb_ppu, keyMessage, action, pActionInfo);
ensure(action);
ensure(pActionInfo);
// Parse returned text for logging
utf16_string.clear();
for (u32 i = 0; i < max_size; i++)
{
@@ -449,12 +527,23 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
if (!code) break;
utf16_string.push_back(code);
}
action_info = utf16_to_ascii8(utf16_string);
utf8_string = utf16_to_ascii8(utf16_string);
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: return_value=%d, action=%d, pActionInfo='%s'", return_value, *action, action_info);
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: return_value=%d, action=%d, pActionInfo='%s'", return_value, *action, utf8_string);
// Check if the hook function was successful
if (return_value)
{
const auto osk = _get_osk_dialog(false);
if (!osk)
{
cellOskDialog.error("osk_hardware_keyboard_event_hook_callback: osk is null");
return 0;
}
auto& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
switch (*action)
{
case CELL_OSKDIALOG_CHANGE_NO_EVENT:
@@ -465,31 +554,25 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
}
case CELL_OSKDIALOG_CHANGE_WORDS_INPUT:
{
// Set unconfirmed string and reset unconfirmed string
for (u32 i = 0; i < max_size; i++)
{
osk->osk_text[i] = pActionInfo.begin()[i];
}
// TODO: Replace unconfirmed string.
cellOskDialog.todo("osk_hardware_keyboard_event_hook_callback: replace unconfirmed string with '%s'", utf8_string);
break;
}
case CELL_OSKDIALOG_CHANGE_WORDS_INSERT:
{
// TODO: Remove unconfirmed string
cellOskDialog.todo("osk_hardware_keyboard_event_hook_callback: remove unconfirmed string");
// Set confirmed string and reset unconfirmed string
for (u32 i = 0; i < max_size; i++)
{
info.valid_text[i] = pActionInfo.begin()[i];
osk->osk_text[i] = 0;
}
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: inserting string '%s'", utf8_string);
osk->Insert(utf16_string);
break;
}
case CELL_OSKDIALOG_CHANGE_WORDS_REPLACE_ALL:
{
// Set confirmed string and reset all strings
for (u32 i = 0; i < max_size; i++)
{
info.valid_text[i] = pActionInfo.begin()[i];
osk->osk_text[i] = 0;
}
// Replace confirmed string and remove unconfirmed string.
cellOskDialog.notice("osk_hardware_keyboard_event_hook_callback: replacing all strings with '%s'", utf8_string);
osk->SetText(utf16_string);
break;
}
default:
@@ -500,19 +583,14 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
}
}
done = true;
return 0;
});
// wait for callback
while (!done && !Emu.IsStopped())
{
std::this_thread::yield();
}
};
// Set device mask and event lock
osk->ignore_input_events = info.lock_ext_input.load();
osk->ignore_device_events = info.lock_ext_input_device.load();
osk->input_device = info.initial_input_device.load();
osk->continuous_mode = info.osk_continuous_mode.load();
if (info.use_separate_windows)
{
@@ -520,28 +598,47 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
osk->mouse_input_enabled = (info.device_mask != CELL_OSKDIALOG_DEVICE_MASK_PAD);
}
if (info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
{
info.last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
return CELL_OK;
}
input::SetIntercepted(osk->pad_input_enabled, osk->keyboard_input_enabled, osk->mouse_input_enabled);
cellOskDialog.notice("cellOskDialogLoadAsync: creating OSK dialog ...");
Emu.BlockingCallFromMainThread([=, &info]()
{
osk->Create(get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE), message, osk->osk_text, maxLength, prohibitFlgs, allowOskPanelFlg, firstViewPanel, info.base_color.load(), info.dimmer_enabled.load(), false);
osk->Create({
.title = get_localized_string(localized_string_id::CELL_OSK_DIALOG_TITLE),
.message = message,
.init_text = osk->osk_text.data(),
.charlimit = maxLength,
.prohibit_flags = prohibitFlgs,
.panel_flag = allowOskPanelFlg,
.support_language = info.supported_languages,
.first_view_panel = firstViewPanel,
.layout = info.layout,
.input_layout = info.input_field_layout_info,
.panel_layout = info.input_panel_layout_info,
.input_field_window_width = info.input_field_window_width,
.input_field_background_transparency = info.input_field_background_transparency,
.initial_scale = info.initial_scale,
.base_color = info.base_color,
.dimmer_enabled = info.dimmer_enabled,
.use_separate_windows = info.use_separate_windows,
.intercept_input = false // We handle the interception manually based on the device mask
});
});
if (info.osk_continuous_mode == CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE)
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
if (info.use_separate_windows)
{
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, CELL_OSKDIALOG_DISPLAY_STATUS_SHOW);
const bool visible = osk->continuous_mode != CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE;
cellOskDialog.notice("cellOskDialogLoadAsync: sending CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED with %s", visible ? "CELL_OSKDIALOG_DISPLAY_STATUS_SHOW" : "CELL_OSKDIALOG_DISPLAY_STATUS_HIDE");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_DISPLAY_CHANGED, visible ? CELL_OSKDIALOG_DISPLAY_STATUS_SHOW : CELL_OSKDIALOG_DISPLAY_STATUS_HIDE);
}
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_LOADED;
cellOskDialog.notice("cellOskDialogLoadAsync: sending CELL_SYSUTIL_OSKDIALOG_LOADED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_LOADED, 0);
cellOskDialog.notice("cellOskDialogLoadAsync: created OSK dialog");
return CELL_OK;
}
@@ -626,7 +723,15 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
info.reset();
}
if (keep_seperate_window_open)
{
cellOskDialog.notice("cellOskDialogUnloadAsync: terminating continuous overlay");
osk->Close(FAKE_CELL_OSKDIALOG_CLOSE_TERMINATE);
}
osk->state = OskDialogState::Unloaded;
cellOskDialog.notice("cellOskDialogUnloadAsync: sending CELL_SYSUTIL_OSKDIALOG_UNLOADED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_UNLOADED, 0);
}
else if (keep_seperate_window_open)
@@ -638,8 +743,14 @@ error_code getText(vm::ptr<CellOskDialogCallbackReturnParam> OutputInfo, bool is
case CELL_SYSUTIL_OSKDIALOG_UNLOADED:
case CELL_SYSUTIL_OSKDIALOG_INPUT_CANCELED:
case CELL_SYSUTIL_OSKDIALOG_INPUT_ENTERED:
{
auto& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
info.valid_text = {};
osk->Clear(true);
break;
}
default:
break;
}
@@ -708,6 +819,8 @@ error_code cellOskDialogAbort()
}
g_fxo->get<osk_info>().last_dialog_state = CELL_SYSUTIL_OSKDIALOG_FINISHED;
cellOskDialog.notice("cellOskDialogAbort: sending CELL_SYSUTIL_OSKDIALOG_FINISHED");
sysutil_send_system_cmd(CELL_SYSUTIL_OSKDIALOG_FINISHED, 0);
return CELL_OK;
@@ -742,9 +855,13 @@ error_code cellOskDialogSetDeviceMask(u32 deviceMask)
error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWindowOption> windowOption)
{
cellOskDialog.todo("cellOskDialogSetSeparateWindowOption(windowOption=*0x%x)", windowOption);
cellOskDialog.warning("cellOskDialogSetSeparateWindowOption(windowOption=*0x%x)", windowOption);
if (!windowOption)
if (!windowOption ||
!windowOption->inputFieldLayoutInfo ||
!!windowOption->reserved ||
windowOption->continuousMode > CELL_OSKDIALOG_CONTINUOUS_MODE_SHOW ||
windowOption->deviceMask > CELL_OSKDIALOG_DEVICE_MASK_PAD)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
@@ -753,20 +870,43 @@ error_code cellOskDialogSetSeparateWindowOption(vm::ptr<CellOskDialogSeparateWin
osk.use_separate_windows = true;
osk.osk_continuous_mode = static_cast<CellOskDialogContinuousMode>(+windowOption->continuousMode);
osk.device_mask = windowOption->deviceMask;
// TODO: handle rest of windowOption
// inputFieldWindowWidth;
// inputFieldBackgroundTrans;
// inputFieldLayoutInfo;
// inputPanelLayoutInfo;
osk.input_field_window_width = windowOption->inputFieldWindowWidth;
osk.input_field_background_transparency = std::clamp<f32>(windowOption->inputFieldBackgroundTrans, 0.0f, 1.0f);
cellOskDialog.warning("cellOskDialogSetSeparateWindowOption: use_separate_windows=true, continuous_mode=%s, device_mask=0x%x)", osk.osk_continuous_mode.load(), osk.device_mask.load());
// Choose proper alignments, since the devs didn't make them exclusive for some reason.
const auto aligned_layout = [](const CellOskDialogLayoutInfo& info) -> osk_window_layout
{
osk_window_layout res{};
res.layout_mode = info.layoutMode;
res.x_align = osk_info::get_aligned_x(res.layout_mode);
res.y_align = osk_info::get_aligned_y(res.layout_mode);
res.x_offset = info.position.x;
res.y_offset = info.position.y;
return res;
};
osk.input_field_layout_info = aligned_layout(*windowOption->inputFieldLayoutInfo);
// Panel layout is optional
if (windowOption->inputPanelLayoutInfo)
{
osk.input_panel_layout_info = aligned_layout(*windowOption->inputPanelLayoutInfo);
}
else
{
// Align to input field
osk.input_panel_layout_info = osk.input_field_layout_info;
}
cellOskDialog.warning("cellOskDialogSetSeparateWindowOption: use_separate_windows=true, continuous_mode=%s, device_mask=0x%x, input_field_window_width=%d, input_field_background_transparency=%.2f, input_field_layout_info=%s, input_panel_layout_info=%s)",
osk.osk_continuous_mode.load(), osk.device_mask.load(), osk.input_field_window_width.load(), osk.input_field_background_transparency.load(), osk.input_field_layout_info, osk.input_panel_layout_info);
return CELL_OK;
}
error_code cellOskDialogSetInitialInputDevice(u32 inputDevice)
{
cellOskDialog.todo("cellOskDialogSetInitialInputDevice(inputDevice=%d)", inputDevice);
cellOskDialog.warning("cellOskDialogSetInitialInputDevice(inputDevice=%d)", inputDevice);
if (inputDevice > CELL_OSKDIALOG_INPUT_DEVICE_KEYBOARD)
{
@@ -775,24 +915,24 @@ error_code cellOskDialogSetInitialInputDevice(u32 inputDevice)
g_fxo->get<osk_info>().initial_input_device = static_cast<CellOskDialogInputDevice>(inputDevice);
// TODO: use value
// TODO: Signal CELL_SYSUTIL_OSKDIALOG_INPUT_DEVICE_CHANGED if the input device changed (probably only when the dialog is already open)
return CELL_OK;
}
error_code cellOskDialogSetInitialKeyLayout(u32 initialKeyLayout)
{
cellOskDialog.todo("cellOskDialogSetInitialKeyLayout(initialKeyLayout=%d)", initialKeyLayout);
cellOskDialog.warning("cellOskDialogSetInitialKeyLayout(initialKeyLayout=%d)", initialKeyLayout);
if (initialKeyLayout > (CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_10KEY | CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_FULLKEY))
if (initialKeyLayout > CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_FULLKEY)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
g_fxo->get<osk_info>().initial_key_layout = static_cast<CellOskDialogInitialKeyLayout>(initialKeyLayout);
auto& osk = g_fxo->get<osk_info>();
// TODO: use value
if (osk.key_layout_options & initialKeyLayout)
{
osk.initial_key_layout = static_cast<CellOskDialogInitialKeyLayout>(initialKeyLayout);
}
return CELL_OK;
}
@@ -808,53 +948,37 @@ error_code cellOskDialogDisableDimmer()
error_code cellOskDialogSetKeyLayoutOption(u32 option)
{
cellOskDialog.todo("cellOskDialogSetKeyLayoutOption(option=0x%x)", option);
cellOskDialog.warning("cellOskDialogSetKeyLayoutOption(option=0x%x)", option);
if (option == 0 || option > 3) // CELL_OSKDIALOG_10KEY_PANEL OR CELL_OSKDIALOG_FULLKEY_PANEL
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
g_fxo->get<osk_info>().key_layout = option;
// TODO: use value
g_fxo->get<osk_info>().key_layout_options = option;
return CELL_OK;
}
error_code cellOskDialogAddSupportLanguage(u32 supportLanguage)
{
cellOskDialog.todo("cellOskDialogAddSupportLanguage(supportLanguage=0x%x)", supportLanguage);
// TODO: error checks
cellOskDialog.warning("cellOskDialogAddSupportLanguage(supportLanguage=0x%x)", supportLanguage);
g_fxo->get<osk_info>().supported_languages = supportLanguage;
// TODO: disable extra languages unless they were enabled here
// Extra languages are:
// CELL_OSKDIALOG_PANELMODE_POLISH
// CELL_OSKDIALOG_PANELMODE_KOREAN
// CELL_OSKDIALOG_PANELMODE_TURKEY
// CELL_OSKDIALOG_PANELMODE_TRADITIONAL_CHINESE
// CELL_OSKDIALOG_PANELMODE_SIMPLIFIED_CHINESE
// CELL_OSKDIALOG_PANELMODE_PORTUGUESE_BRAZIL
// CELL_OSKDIALOG_PANELMODE_DANISH
// CELL_OSKDIALOG_PANELMODE_SWEDISH
// CELL_OSKDIALOG_PANELMODE_NORWEGIAN
// CELL_OSKDIALOG_PANELMODE_FINNISH
return CELL_OK;
}
error_code cellOskDialogSetLayoutMode(s32 layoutMode)
{
cellOskDialog.todo("cellOskDialogSetLayoutMode(layoutMode=%d)", layoutMode);
cellOskDialog.warning("cellOskDialogSetLayoutMode(layoutMode=0x%x)", layoutMode);
// TODO: error checks
auto& osk = g_fxo->get<osk_info>();
osk.layout.layout_mode = layoutMode;
g_fxo->get<osk_info>().layout_mode = layoutMode;
// TODO: use layout mode
// Choose proper alignments, since the devs didn't make them exclusive for some reason.
osk.layout.x_align = osk_info::get_aligned_x(layoutMode);
osk.layout.y_align = osk_info::get_aligned_y(layoutMode);
return CELL_OK;
}
@@ -907,6 +1031,26 @@ error_code cellOskDialogExtRegisterKeyboardEventHookCallbackEx(u16 hookEventMode
error_code cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
{
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=**0x%0x)", filePath);
std::vector<std::string> paths;
if (filePath)
{
for (u32 i = 0; i < 4; i++)
{
if (!filePath[i])
{
break;
}
std::array<char, CELL_IMEJP_DIC_PATH_MAXLENGTH + 1> path{};
std::memcpy(path.data(), filePath[i].get_ptr(), CELL_IMEJP_DIC_PATH_MAXLENGTH);
paths.push_back(path.data());
}
}
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary: got %d dictionaries:\n%s", paths.size(), fmt::merge(paths, "\n"));
return CELL_OK;
}
@@ -914,8 +1058,6 @@ error_code cellOskDialogExtEnableClipboard()
{
cellOskDialog.todo("cellOskDialogExtEnableClipboard()");
// TODO: error checks
g_fxo->get<osk_info>().clipboard_enabled = true;
// TODO: implement copy paste
@@ -940,15 +1082,43 @@ error_code cellOskDialogExtSendFinishMessage(u32 /*CellOskDialogFinishReason*/ f
return CELL_OK;
}
error_code cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictionaryInfo> dictionaryInfo)
error_code cellOskDialogExtAddOptionDictionary(vm::cpptr<CellOskDialogImeDictionaryInfo> dictionaryInfo)
{
cellOskDialog.todo("cellOskDialogExtAddOptionDictionary(dictionaryInfo=*0x%x)", dictionaryInfo);
if (!dictionaryInfo)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
std::vector<std::pair<u32, std::string>> paths; // language and path
for (u32 i = 0; i < 10; i++)
{
if (!dictionaryInfo[i] || !dictionaryInfo[i]->dictionaryPath)
{
break;
}
std::array<char, CELL_IMEJP_DIC_PATH_MAXLENGTH + 1> path{};
std::memcpy(path.data(), dictionaryInfo[i]->dictionaryPath.get_ptr(), CELL_IMEJP_DIC_PATH_MAXLENGTH);
paths.push_back({ dictionaryInfo[i]->targetLanguage, path.data() });
}
std::vector<std::string> msgs;
for (const auto& entry : paths)
{
msgs.push_back(fmt::format("languages=0x%x, path='%s'", entry.first, entry.second));
}
cellOskDialog.todo("cellOskDialogExtAddOptionDictionary: got %d dictionaries:\n%s", msgs.size(), fmt::merge(msgs, "\n"));
return CELL_OK;
}
error_code cellOskDialogExtSetInitialScale(f32 initialScale)
{
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
cellOskDialog.warning("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
if (initialScale < CELL_OSKDIALOG_SCALE_MIN || initialScale > CELL_OSKDIALOG_SCALE_MAX)
{
@@ -957,8 +1127,6 @@ error_code cellOskDialogExtSetInitialScale(f32 initialScale)
g_fxo->get<osk_info>().initial_scale = initialScale;
// TODO: implement overlay scaling
return CELL_OK;
}
@@ -966,13 +1134,11 @@ error_code cellOskDialogExtInputDeviceLock()
{
cellOskDialog.warning("cellOskDialogExtInputDeviceLock()");
// TODO: error checks
g_fxo->get<osk_info>().lock_ext_input = true;
g_fxo->get<osk_info>().lock_ext_input_device = true;
if (const auto osk = _get_osk_dialog(false))
{
osk->ignore_input_events = true;
osk->ignore_device_events = true;
}
return CELL_OK;
@@ -982,13 +1148,11 @@ error_code cellOskDialogExtInputDeviceUnlock()
{
cellOskDialog.warning("cellOskDialogExtInputDeviceUnlock()");
// TODO: error checks
g_fxo->get<osk_info>().lock_ext_input = false;
g_fxo->get<osk_info>().lock_ext_input_device = false;
if (const auto osk = _get_osk_dialog(false))
{
osk->ignore_input_events = false;
osk->ignore_device_events = false;
}
return CELL_OK;
@@ -1052,30 +1216,27 @@ error_code cellOskDialogExtUpdateInputText()
error_code cellOskDialogExtSetPointerEnable(b8 enable)
{
cellOskDialog.todo("cellOskDialogExtSetPointerEnable(enable=%d)", enable);
cellOskDialog.warning("cellOskDialogExtSetPointerEnable(enable=%d)", enable);
// TODO: error checks
// TODO: While the pointer is already displayed in the osk overlay, it is not really useful right now.
// On real hardware, this may be used for actual PS Move or mouse input.
g_fxo->get<osk_info>().pointer_enabled = enable;
// TODO: Show/hide pointer at the specified position in the OSK overlay.
return CELL_OK;
}
error_code cellOskDialogExtUpdatePointerDisplayPos(vm::cptr<CellOskDialogPoint> pos)
{
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos(pos=0x%x, posX=%f, posY=%f)", pos->x, pos->y);
// TODO: error checks
cellOskDialog.warning("cellOskDialogExtUpdatePointerDisplayPos(pos=0x%x, posX=%f, posY=%f)", pos, pos->x, pos->y);
if (pos)
{
g_fxo->get<osk_info>().pointer_pos = *pos;
osk_info& osk = g_fxo->get<osk_info>();
osk.pointer_x = pos->x;
osk.pointer_y = pos->y;
}
// TODO: Update pointer position in the OSK overlay.
return CELL_OK;
}
@@ -1083,8 +1244,6 @@ error_code cellOskDialogExtEnableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtEnableHalfByteKana()");
// TODO: error checks
g_fxo->get<osk_info>().half_byte_kana_enabled = true;
// TODO: use new value in osk
@@ -1096,8 +1255,6 @@ error_code cellOskDialogExtDisableHalfByteKana()
{
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
// TODO: error checks
g_fxo->get<osk_info>().half_byte_kana_enabled = false;
// TODO: use new value in osk
@@ -1107,21 +1264,23 @@ error_code cellOskDialogExtDisableHalfByteKana()
error_code cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForceFinishCallback> pCallback)
{
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback(pCallback=*0x%x)", pCallback);
cellOskDialog.warning("cellOskDialogExtRegisterForceFinishCallback(pCallback=*0x%x)", pCallback);
if (!pCallback)
{
return CELL_OSKDIALOG_ERROR_PARAM;
}
g_fxo->get<osk_info>().osk_force_finish_callback = pCallback;
osk_info& info = g_fxo->get<osk_info>();
std::lock_guard lock(info.text_mtx);
info.osk_force_finish_callback = pCallback;
// TODO: use force finish callback when the PS-Button is pressed and a System dialog shall be spawned while the OSK is loaded
// 1. Check if current mode is CELL_OSKDIALOG_CONTINUOUS_MODE_HIDE or (CELL_OSKDIALOG_CONTINUOUS_MODE_SHOW && hidden)
// 2. If one of the above is true, call osk_force_finish_callback
// 3. Check the return value of osk_force_finish_callback. If false, ignore the PS-Button press, else go to the next step
// 4. Close dialog etc., send CELL_SYSUTIL_OSKDIALOG_FINISHED
// 5. After we return from the System dialog, send CELL_SYSUTIL_SYSTEM_MENU_CLOSE to notify the game that it can load the OSK and resume.
// We use the force finish callback when the PS-Button is pressed and a System dialog shall be spawned while the OSK is loaded
// 1. Check if we are in any continuous mode and the dialog is hidden
// 2. If the above is true, call osk_force_finish_callback, deny the PS-Button press otherwise
// 3. Check the return value of osk_force_finish_callback.
// if false, ignore the PS-Button press,
// else close the dialog etc., send CELL_SYSUTIL_OSKDIALOG_FINISHED
return CELL_OK;
}
+104 -7
View File
@@ -2,6 +2,8 @@
#include "util/types.hpp"
#include "util/atomic.hpp"
#include "util/init_mutex.hpp"
#include "Utilities/mutex.h"
#include "Emu/Memory/vm_ptr.h"
#include <string>
#include <functional>
@@ -86,6 +88,7 @@ enum CellOskDialogFinishReason
enum CellOskDialogFinishReasonFake // Helper. Must be negative values.
{
FAKE_CELL_OSKDIALOG_CLOSE_ABORT = -1,
FAKE_CELL_OSKDIALOG_CLOSE_TERMINATE = -2,
};
enum CellOskDialogType
@@ -242,6 +245,20 @@ using cellOskDialogConfirmWordFilterCallback = int(vm::ptr<u16> pConfirmString,
using cellOskDialogHardwareKeyboardEventHookCallback = class b8(vm::ptr<CellOskDialogKeyMessage> keyMessage, vm::ptr<u32> action, vm::ptr<void> pActionInfo);
using cellOskDialogForceFinishCallback = class b8();
struct osk_window_layout
{
u32 layout_mode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
u32 x_align = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_LEFT;
u32 y_align = CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP;
f32 x_offset = 0.0f;
f32 y_offset = 0.0f;
std::string to_string() const
{
return fmt::format("{ layout_mode=0x%x, x_align=0x%x, y_align=0x%x, x_offset=%.2f, y_offset=%.2f }", layout_mode, x_align, y_align, x_offset, y_offset);
}
};
enum class OskDialogState
{
Unloaded,
@@ -261,23 +278,103 @@ public:
f32 a = 1.0f;
};
virtual void Create(const std::string& title, const std::u16string& message, char16_t* init_text, u32 charlimit, u32 prohibit_flags, u32 panel_flag, u32 first_view_panel, color base_color, bool dimmer_enabled, bool intercept_input) = 0;
struct osk_params
{
std::string title;
std::u16string message;
char16_t* init_text = nullptr;
u32 charlimit = 0;
u32 prohibit_flags = 0;
u32 panel_flag = 0;
u32 support_language = 0;
u32 first_view_panel = 0;
osk_window_layout layout{};
osk_window_layout input_layout{}; // Only used with separate windows
osk_window_layout panel_layout{}; // Only used with separate windows
u32 input_field_window_width = 0; // Only used with separate windows
f32 input_field_background_transparency = 1.0f; // Only used with separate windows
f32 initial_scale = 1.0f;
color base_color{};
bool dimmer_enabled = false;
bool use_separate_windows = false;
bool intercept_input = false;
};
virtual void Create(const osk_params& params) = 0;
virtual void Clear(bool clear_all_data) = 0;
virtual void Insert(const std::u16string& text) = 0;
virtual void SetText(const std::u16string& text) = 0;
// Closes the dialog.
// Set status to CELL_OSKDIALOG_CLOSE_CONFIRM or CELL_OSKDIALOG_CLOSE_CANCEL for user input.
// Set status to -1 if closed by the game or system.
// Set status to -2 if terminated by the system.
virtual void Close(s32 status) = 0;
virtual ~OskDialogBase();
virtual ~OskDialogBase() {};
std::function<void(s32 status)> on_osk_close;
std::function<void(CellOskDialogKeyMessage key_message)> on_osk_key_input_entered;
atomic_t<OskDialogState> state{ OskDialogState::Unloaded };
atomic_t<bool> pad_input_enabled{ true }; // Determines if the OSK consumes the device's events.
atomic_t<bool> mouse_input_enabled{ true }; // Determines if the OSK consumes the device's events.
atomic_t<bool> keyboard_input_enabled{ true }; // Determines if the OSK consumes the device's events.
atomic_t<bool> ignore_input_events{ false }; // Determines if the OSK ignores all consumed events.
atomic_t<CellOskDialogContinuousMode> continuous_mode{ CELL_OSKDIALOG_CONTINUOUS_MODE_NONE };
atomic_t<CellOskDialogInputDevice> input_device{ CELL_OSKDIALOG_INPUT_DEVICE_PAD }; // The current input device.
atomic_t<bool> pad_input_enabled{ true }; // Determines if the OSK consumes the device's input.
atomic_t<bool> mouse_input_enabled{ true }; // Determines if the OSK consumes the device's input.
atomic_t<bool> keyboard_input_enabled{ true }; // Determines if the OSK consumes the device's input.
atomic_t<bool> ignore_device_events{ false }; // Determines if the OSK ignores device events.
atomic_t<CellOskDialogInputFieldResult> osk_input_result{ CellOskDialogInputFieldResult::CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK };
char16_t osk_text[CELL_OSKDIALOG_STRING_SIZE]{};
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> osk_text{};
};
struct osk_info
{
std::shared_ptr<OskDialogBase> dlg;
std::array<char16_t, CELL_OSKDIALOG_STRING_SIZE> valid_text{}; // The string that's going to be served to the game.
shared_mutex text_mtx;
atomic_t<bool> use_separate_windows = false;
atomic_t<bool> lock_ext_input_device = false;
atomic_t<u32> device_mask = 0; // OSK ignores input from the specified devices. 0 means all devices can influence the OSK
atomic_t<u32> input_field_window_width = 0;
atomic_t<f32> input_field_background_transparency = 1.0f;
osk_window_layout input_field_layout_info{};
osk_window_layout input_panel_layout_info{};
atomic_t<u32> key_layout_options = CELL_OSKDIALOG_10KEY_PANEL;
atomic_t<CellOskDialogInitialKeyLayout> initial_key_layout = CELL_OSKDIALOG_INITIAL_PANEL_LAYOUT_SYSTEM; // TODO: use
atomic_t<CellOskDialogInputDevice> initial_input_device = CELL_OSKDIALOG_INPUT_DEVICE_PAD; // OSK at first only receives input from the initial device
atomic_t<bool> clipboard_enabled = false; // For copy and paste
atomic_t<bool> half_byte_kana_enabled = false;
atomic_t<u32> supported_languages = 0; // Used to enable non-default languages in the OSK
atomic_t<bool> dimmer_enabled = true;
atomic_t<OskDialogBase::color> base_color = OskDialogBase::color{ 0.2f, 0.2f, 0.2f, 1.0f };
atomic_t<bool> pointer_enabled = false;
atomic_t<f32> pointer_x = 0.0f;
atomic_t<f32> pointer_y = 0.0f;
atomic_t<f32> initial_scale = 1.0f;
osk_window_layout layout = {};
atomic_t<CellOskDialogContinuousMode> osk_continuous_mode = CELL_OSKDIALOG_CONTINUOUS_MODE_NONE;
atomic_t<u32> last_dialog_state = CELL_SYSUTIL_OSKDIALOG_UNLOADED; // Used for continuous seperate window dialog
atomic_t<vm::ptr<cellOskDialogConfirmWordFilterCallback>> osk_confirm_callback{};
atomic_t<vm::ptr<cellOskDialogForceFinishCallback>> osk_force_finish_callback{};
atomic_t<vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback>> osk_hardware_keyboard_event_hook_callback{};
atomic_t<u16> hook_event_mode{0};
stx::init_mutex init;
void reset();
// Align horizontally
static u32 get_aligned_x(u32 layout_mode);
// Align vertically
static u32 get_aligned_y(u32 layout_mode);
};
+48 -102
View File
@@ -141,7 +141,7 @@ error_code cellPadClearBuf(u32 port_no)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -174,7 +174,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -207,7 +207,7 @@ void pad_get_data(u32 port_no, CellPadData* data)
}
else if (pad->ldd)
{
std::memcpy(data, pad->ldd_data, sizeof(CellPadData));
pad->ldd_data = *data;
if (setting & CELL_PAD_SETTING_SENSOR_ON)
data->len = CELL_PAD_LEN_CHANGE_SENSOR_ON;
else
@@ -219,6 +219,15 @@ void pad_get_data(u32 port_no, CellPadData* data)
const u16 d1Initial = pad->m_digital_1;
const u16 d2Initial = pad->m_digital_2;
const auto set_value = [&btnChanged](u16& value, u16 new_value)
{
if (value != new_value)
{
btnChanged = true;
value = new_value;
}
};
for (Button& button : pad->m_buttons)
{
// here we check btns, and set pad accordingly,
@@ -233,22 +242,10 @@ void pad_get_data(u32 port_no, CellPadData* data)
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_LEFT:
if (pad->m_press_left != button.m_value) btnChanged = true;
pad->m_press_left = button.m_value;
break;
case CELL_PAD_CTRL_DOWN:
if (pad->m_press_down != button.m_value) btnChanged = true;
pad->m_press_down = button.m_value;
break;
case CELL_PAD_CTRL_RIGHT:
if (pad->m_press_right != button.m_value) btnChanged = true;
pad->m_press_right = button.m_value;
break;
case CELL_PAD_CTRL_UP:
if (pad->m_press_up != button.m_value) btnChanged = true;
pad->m_press_up = button.m_value;
break;
case CELL_PAD_CTRL_LEFT: set_value(pad->m_press_left, button.m_value); break;
case CELL_PAD_CTRL_DOWN: set_value(pad->m_press_down, button.m_value); break;
case CELL_PAD_CTRL_RIGHT: set_value(pad->m_press_right, button.m_value); break;
case CELL_PAD_CTRL_UP: set_value(pad->m_press_up, button.m_value); break;
// These arent pressure btns
case CELL_PAD_CTRL_R3:
case CELL_PAD_CTRL_L3:
@@ -266,38 +263,14 @@ void pad_get_data(u32 port_no, CellPadData* data)
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_SQUARE:
if (pad->m_press_square != button.m_value) btnChanged = true;
pad->m_press_square = button.m_value;
break;
case CELL_PAD_CTRL_CROSS:
if (pad->m_press_cross != button.m_value) btnChanged = true;
pad->m_press_cross = button.m_value;
break;
case CELL_PAD_CTRL_CIRCLE:
if (pad->m_press_circle != button.m_value) btnChanged = true;
pad->m_press_circle = button.m_value;
break;
case CELL_PAD_CTRL_TRIANGLE:
if (pad->m_press_triangle != button.m_value) btnChanged = true;
pad->m_press_triangle = button.m_value;
break;
case CELL_PAD_CTRL_R1:
if (pad->m_press_R1 != button.m_value) btnChanged = true;
pad->m_press_R1 = button.m_value;
break;
case CELL_PAD_CTRL_L1:
if (pad->m_press_L1 != button.m_value) btnChanged = true;
pad->m_press_L1 = button.m_value;
break;
case CELL_PAD_CTRL_R2:
if (pad->m_press_R2 != button.m_value) btnChanged = true;
pad->m_press_R2 = button.m_value;
break;
case CELL_PAD_CTRL_L2:
if (pad->m_press_L2 != button.m_value) btnChanged = true;
pad->m_press_L2 = button.m_value;
break;
case CELL_PAD_CTRL_SQUARE: set_value(pad->m_press_square, button.m_value); break;
case CELL_PAD_CTRL_CROSS: set_value(pad->m_press_cross, button.m_value); break;
case CELL_PAD_CTRL_CIRCLE: set_value(pad->m_press_circle, button.m_value); break;
case CELL_PAD_CTRL_TRIANGLE: set_value(pad->m_press_triangle, button.m_value); break;
case CELL_PAD_CTRL_R1: set_value(pad->m_press_R1, button.m_value); break;
case CELL_PAD_CTRL_L1: set_value(pad->m_press_L1, button.m_value); break;
case CELL_PAD_CTRL_R2: set_value(pad->m_press_R2, button.m_value); break;
case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, button.m_value); break;
default: break;
}
}
@@ -307,22 +280,10 @@ void pad_get_data(u32 port_no, CellPadData* data)
{
switch (stick.m_offset)
{
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X:
if (pad->m_analog_left_x != stick.m_value) btnChanged = true;
pad->m_analog_left_x = stick.m_value;
break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y:
if (pad->m_analog_left_y != stick.m_value) btnChanged = true;
pad->m_analog_left_y = stick.m_value;
break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X:
if (pad->m_analog_right_x != stick.m_value) btnChanged = true;
pad->m_analog_right_x = stick.m_value;
break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y:
if (pad->m_analog_right_y != stick.m_value) btnChanged = true;
pad->m_analog_right_y = stick.m_value;
break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: set_value(pad->m_analog_left_x, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: set_value(pad->m_analog_left_y, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: set_value(pad->m_analog_right_x, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: set_value(pad->m_analog_right_y, stick.m_value); break;
default: break;
}
}
@@ -333,22 +294,10 @@ void pad_get_data(u32 port_no, CellPadData* data)
{
switch (sensor.m_offset)
{
case CELL_PAD_BTN_OFFSET_SENSOR_X:
if (pad->m_sensor_x != sensor.m_value) btnChanged = true;
pad->m_sensor_x = sensor.m_value;
break;
case CELL_PAD_BTN_OFFSET_SENSOR_Y:
if (pad->m_sensor_y != sensor.m_value) btnChanged = true;
pad->m_sensor_y = sensor.m_value;
break;
case CELL_PAD_BTN_OFFSET_SENSOR_Z:
if (pad->m_sensor_z != sensor.m_value) btnChanged = true;
pad->m_sensor_z = sensor.m_value;
break;
case CELL_PAD_BTN_OFFSET_SENSOR_G:
if (pad->m_sensor_g != sensor.m_value) btnChanged = true;
pad->m_sensor_g = sensor.m_value;
break;
case CELL_PAD_BTN_OFFSET_SENSOR_X: set_value(pad->m_sensor_x, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Y: set_value(pad->m_sensor_y, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Z: set_value(pad->m_sensor_z, sensor.m_value); break;
case CELL_PAD_BTN_OFFSET_SENSOR_G: set_value(pad->m_sensor_g, sensor.m_value); break;
default: break;
}
}
@@ -536,7 +485,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr<CellPadData> data)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -551,7 +500,7 @@ error_code cellPadGetDataExtra(u32 port_no, vm::ptr<u32> device_type, vm::ptr<Ce
sys_io.trace("cellPadGetDataExtra(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data);
// TODO: This is used just to get data from a BD/CEC remote,
// but if the port isnt a remote, device type is set to 0 and just regular cellPadGetData is returned
// but if the port isnt a remote, device type is set to CELL_PAD_DEV_TYPE_STANDARD and just regular cellPadGetData is returned
if (auto err = cellPadGetData(port_no, data))
{
@@ -560,7 +509,7 @@ error_code cellPadGetDataExtra(u32 port_no, vm::ptr<u32> device_type, vm::ptr<Ce
if (device_type) // no error is returned on NULL
{
*device_type = 0;
*device_type = CELL_PAD_DEV_TYPE_STANDARD;
}
// Set BD data
@@ -602,7 +551,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -750,7 +699,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo>
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -782,7 +731,7 @@ error_code cellPadSetPortSetting(u32 port_no, u32 port_setting)
config.port_setting[port_no] = port_setting;
// can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD
// can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD <- Update: seems to be just internal and ignored
return CELL_OK;
}
@@ -808,7 +757,7 @@ error_code cellPadInfoPressMode(u32 port_no)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -837,7 +786,7 @@ error_code cellPadInfoSensorMode(u32 port_no)
if (port_no >= config.max_connect)
return CELL_PAD_ERROR_NO_DEVICE;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
@@ -867,7 +816,7 @@ error_code cellPadSetPressMode(u32 port_no, u32 mode)
if (port_no >= CELL_PAD_MAX_PORT_NUM)
return CELL_OK;
const auto pad = pads[port_no];
const auto& pad = pads[port_no];
// TODO: find out if this is checked here or later or at all
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE))
@@ -952,15 +901,15 @@ error_code cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
auto& pads = handler->GetPads();
if (handle < 0 || !data) // data == NULL stalls on decr
if (handle < 0 || static_cast<u32>(handle) >= pads.size() || !data) // data == NULL stalls on decr
return CELL_PAD_ERROR_INVALID_PARAMETER;
auto& pads = handler->GetPads();
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_NO_DEVICE;
memcpy(pads[handle]->ldd_data, data.get_ptr(), sizeof(CellPadData));
pads[handle]->ldd_data = *data;
return CELL_OK;
}
@@ -977,11 +926,11 @@ error_code cellPadLddGetPortNo(s32 handle)
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
auto& pads = handler->GetPads();
if (handle < 0)
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
return CELL_PAD_ERROR_INVALID_PARAMETER;
auto& pads = handler->GetPads();
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_FATAL; // might be incorrect
@@ -1001,13 +950,10 @@ error_code cellPadLddUnregisterController(s32 handle)
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
if (handle < 0)
return CELL_PAD_ERROR_INVALID_PARAMETER;
const auto& pads = handler->GetPads();
// TODO: check if handle >= pads.size()
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_NO_DEVICE;
-6
View File
@@ -126,12 +126,6 @@ enum
CELL_PADFILTER_IIR_CUTOFF_2ND_LPF_BT_010 = 2, // 10% Nyquist frequency
};
struct CellPadData
{
be_t<s32> len;
be_t<u16> button[CELL_PAD_MAX_CODES];
};
struct CellPadInfo
{
be_t<u32> max_connect;
+2 -2
View File
@@ -1311,7 +1311,7 @@ u32 cellRecQueryMemSize(vm::cptr<CellRecParam> pParam)
{
return 0x900000;
}
u32 video_size = 0x600000; // 6 MB
u32 audio_size = 0x100000; // 1 MB
u32 external_input_size = 0;
@@ -1616,7 +1616,7 @@ error_code cellRecSetInfo(s32 setInfo, u64 value)
{
return CELL_REC_ERROR_INVALID_VALUE;
}
for (u32 i = 0; i < scene_metadata->tagNum; i++)
{
if (!scene_metadata->tag[i] ||
+29 -29
View File
@@ -259,8 +259,8 @@ error_code cellRtcFormatRfc2822(vm::ptr<char> pszDateTime, vm::cptr<CellRtcTick>
pszDateTime[0xb] = ' ';
// year
char yearDigits[5];
sprintf(yearDigits, "%04hi", u16{date_time->year});
std::array<char, 5> yearDigits{};
snprintf(yearDigits.data(), yearDigits.size(), "%04hi", u16{date_time->year});
pszDateTime[0xc] = yearDigits[0];
pszDateTime[0xd] = yearDigits[1];
pszDateTime[0xe] = yearDigits[2];
@@ -268,22 +268,22 @@ error_code cellRtcFormatRfc2822(vm::ptr<char> pszDateTime, vm::cptr<CellRtcTick>
pszDateTime[0x10] = ' ';
// Hours
char hourDigits[3];
sprintf(hourDigits, "%02hi", u16{date_time->hour});
std::array<char, 3> hourDigits{};
snprintf(hourDigits.data(), hourDigits.size(), "%02hi", u16{date_time->hour});
pszDateTime[0x11] = hourDigits[0];
pszDateTime[0x12] = hourDigits[1];
pszDateTime[0x13] = ':';
// Minutes
char minDigits[3];
sprintf(minDigits, "%02hi", u16{date_time->minute});
std::array<char, 3> minDigits{};
snprintf(minDigits.data(), minDigits.size(), "%02hi", u16{date_time->minute});
pszDateTime[0x14] = minDigits[0];
pszDateTime[0x15] = minDigits[1];
pszDateTime[0x16] = ':';
// Seconds
char secDigits[3];
sprintf(secDigits, "%02hi", u16{date_time->second});
std::array<char, 3> secDigits{};
snprintf(secDigits.data(), secDigits.size(), "%02hi", u16{date_time->second});
pszDateTime[0x17] = secDigits[0];
pszDateTime[0x18] = secDigits[1];
pszDateTime[0x19] = ' ';
@@ -379,8 +379,8 @@ error_code cellRtcFormatRfc3339(vm::ptr<char> pszDateTime, vm::cptr<CellRtcTick>
s32 tzone = iTimeZone;
// Year - XXXX-04-13T10:56:31.35+66:40
char yearDigits[5];
sprintf(yearDigits, "%04hi", u16{date_time->year});
std::array<char, 5> yearDigits{};
snprintf(yearDigits.data(), yearDigits.size(), "%04hi", u16{date_time->year});
pszDateTime[0x0] = yearDigits[0];
pszDateTime[0x1] = yearDigits[1];
pszDateTime[0x2] = yearDigits[2];
@@ -388,43 +388,43 @@ error_code cellRtcFormatRfc3339(vm::ptr<char> pszDateTime, vm::cptr<CellRtcTick>
pszDateTime[0x4] = '-';
// Month - 2020-XX-13T10:56:31.35+66:40
char monthDigits[3];
sprintf(monthDigits, "%02hi", u16{date_time->month});
std::array<char, 3> monthDigits{};
snprintf(monthDigits.data(), monthDigits.size(), "%02hi", u16{date_time->month});
pszDateTime[0x5] = monthDigits[0];
pszDateTime[0x6] = monthDigits[1];
pszDateTime[0x7] = '-';
// Day - 2020-04-XXT10:56:31.35+66:40
char dayDigits[3];
sprintf(dayDigits, "%02hi", u16{date_time->day});
std::array<char, 3> dayDigits{};
snprintf(dayDigits.data(), dayDigits.size(), "%02hi", u16{date_time->day});
pszDateTime[0x8] = dayDigits[0];
pszDateTime[0x9] = dayDigits[1];
pszDateTime[0xa] = 'T';
// Hours - 2020-04-13TXX:56:31.35+66:40
char hourDigits[3];
sprintf(hourDigits, "%02hi", u16{date_time->hour});
std::array<char, 3> hourDigits{};
snprintf(hourDigits.data(), hourDigits.size(), "%02hi", u16{date_time->hour});
pszDateTime[0xb] = hourDigits[0];
pszDateTime[0xc] = hourDigits[1];
pszDateTime[0xd] = ':';
// Minutes - 2020-04-13T10:XX:31.35+66:40
char minDigits[3];
sprintf(minDigits, "%02hi", u16{date_time->minute});
std::array<char, 3> minDigits{};
snprintf(minDigits.data(), minDigits.size(), "%02hi", u16{date_time->minute});
pszDateTime[0xe] = minDigits[0];
pszDateTime[0xf] = minDigits[1];
pszDateTime[0x10] = ':';
// Seconds - 2020-04-13T10:56:XX.35+66:40
char secDigits[3];
sprintf(secDigits, "%02hi", u16{date_time->second});
std::array<char, 3> secDigits{};
snprintf(secDigits.data(), secDigits.size(), "%02hi", u16{date_time->second});
pszDateTime[0x11] = secDigits[0];
pszDateTime[0x12] = secDigits[1];
pszDateTime[0x13] = '.';
// Microseconds - 2020-04-13T10:56:31.XX+66:40
char microDigits[3];
sprintf(microDigits, "%02u", u32{date_time->microsecond});
std::array<char, 3> microDigits{};
snprintf(microDigits.data(), microDigits.size(), "%02u", u32{date_time->microsecond});
pszDateTime[0x14] = microDigits[0];
pszDateTime[0x15] = microDigits[1];
@@ -846,7 +846,7 @@ error_code cellRtcSetTick(vm::ptr<CellRtcDateTime> pTime, vm::cptr<CellRtcTick>
pTime->second = seconds;
pTime->minute = minutes;
pTime->hour = hours;
pTime->day = days_tmp + 1;
pTime->day = ::narrow<u16>(days_tmp + 1);
pTime->month = months;
pTime->year = years;
@@ -1006,10 +1006,10 @@ error_code cellRtcTickAddMonths(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTic
// Not pretty, but works
s64 total_months = (date_time->year * 12ULL) + date_time->month + iAdd + -1;
s32 total_months_s32 = total_months;
s32 total_months_s32 = ::narrow<s32>(total_months);
u32 unk_1 = total_months_s32 >> 0x1f;
u64 unk_2 = ((total_months_s32 / 6 + unk_1) >> 1) - unk_1;
u32 unk_3 = unk_2;
u32 unk_3 = ::narrow<u32>(unk_2);
unk_1 = unk_3 & 0xffff;
u64 unk_4 = (total_months - ((u64{unk_3} << 4) - (unk_3 << 2))) + 1;
if (((unk_2 & 0xffff) == 0) || ((unk_3 = unk_4 & 0xffff, (unk_4 & 0xffff) == 0 || (0xc < unk_3))))
@@ -1036,8 +1036,8 @@ error_code cellRtcTickAddMonths(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTic
date_time->day = month_days;
}
date_time->month = unk_4;
date_time->year = unk_2;
date_time->month = ::narrow<u16>(unk_4);
date_time->year = ::narrow<u16>(unk_2);
cellRtcGetTick(date_time, pTick0);
return CELL_OK;
@@ -1086,7 +1086,7 @@ error_code cellRtcTickAddYears(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick
date_time->day = month_days;
}
date_time->year = total_years;
date_time->year = ::narrow<u16>(total_years);
cellRtcGetTick(date_time, pTick0);
return CELL_OK;
@@ -1351,7 +1351,7 @@ error_code cellRtcSetCurrentTick(vm::cptr<CellRtcTick> pTick)
// TODO syscall not implemented
/*
u32 tmp2 = sys_time_get_system_time(tmp / cellRtcGetTickResolution(), (tmp % cellRtcGetTickResolution()) * 1000);
u64 tmp2 = sys_time_get_system_time(tmp / cellRtcGetTickResolution(), (tmp % cellRtcGetTickResolution()) * 1000);
return (tmp2 & (tmp2 | tmp2 - 1) >> 0x1f);
*/
+9 -6
View File
@@ -319,7 +319,8 @@ static error_code select_and_delete(ppu_thread& ppu)
lv2_obj::sleep(ppu);
// Get user confirmation by opening a blocking dialog
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(msg));
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE;
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(msg), vm::null, vm::null, vm::null, &return_code);
// Reschedule after a blocking dialog returns
if (ppu.check_state())
@@ -332,7 +333,7 @@ static error_code select_and_delete(ppu_thread& ppu)
return CELL_SAVEDATA_ERROR_INTERNAL;
}
if (g_last_user_response.load() == CELL_MSGDIALOG_BUTTON_YES)
if (return_code == CELL_MSGDIALOG_BUTTON_YES)
{
// Remove directory
const std::string path = base_dir + save_entries[selected].escaped;
@@ -1157,7 +1158,8 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
lv2_obj::sleep(ppu);
// Get user confirmation by opening a blocking dialog
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(message));
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE;
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(message), vm::null, vm::null, vm::null, &return_code);
// Reschedule after a blocking dialog returns
if (ppu.check_state())
@@ -1170,7 +1172,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
return CELL_SAVEDATA_ERROR_INTERNAL;
}
if (g_last_user_response != CELL_MSGDIALOG_BUTTON_YES)
if (return_code != CELL_MSGDIALOG_BUTTON_YES)
{
if (selected >= 0)
{
@@ -1287,7 +1289,8 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
lv2_obj::sleep(ppu);
// Get user confirmation by opening a blocking dialog
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(message));
s32 return_code = CELL_MSGDIALOG_BUTTON_NONE;
error_code res = open_msg_dialog(true, CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO, vm::make_str(message), vm::null, vm::null, vm::null, &return_code);
// Reschedule after a blocking dialog returns
if (ppu.check_state())
@@ -1300,7 +1303,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
return CELL_SAVEDATA_ERROR_INTERNAL;
}
if (g_last_user_response != CELL_MSGDIALOG_BUTTON_YES)
if (return_code != CELL_MSGDIALOG_BUTTON_YES)
{
return CELL_CANCEL;
}
+1 -1
View File
@@ -5461,7 +5461,7 @@ DECLARE(ppu_module_manager::cellSpurs)("cellSpurs", [](ppu_static_module* _this)
_this->add_init_func([](ppu_static_module*)
{
const auto val = g_cfg.core.spu_accurate_reservations ? MFF_PERFECT : MFF_FORCED_HLE;
REINIT_FUNC(cellSpursSetPriorities).flag(val);
REINIT_FUNC(cellSpursAddWorkload).flag(val);
REINIT_FUNC(cellSpursAddWorkloadWithAttribute).flag(val);
+21
View File
@@ -161,6 +161,27 @@ extern u64 get_sysutil_cb_manager_read_count()
return 0;
}
extern bool send_open_home_menu_cmds()
{
// TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE
if (sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0) < 0 ||
sysutil_send_system_cmd(CELL_SYSUTIL_SYSTEM_MENU_OPEN, 0) < 0 ||
sysutil_send_system_cmd(CELL_SYSUTIL_BGMPLAYBACK_PLAY, 0) < 0)
{
return false;
}
return true;
}
extern void send_close_home_menu_cmds()
{
// TODO: handle CELL_SYSUTIL_BGMPLAYBACK_STATUS_DISABLE
sysutil_send_system_cmd(CELL_SYSUTIL_BGMPLAYBACK_STOP, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_SYSTEM_MENU_CLOSE, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_END, 0);
}
template <>
void fmt_class_string<CellSysutilLang>::format(std::string& out, u64 arg)
{
+5 -1
View File
@@ -4,6 +4,8 @@
#include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/RSX/Overlays/overlay_manager.h"
#include "Emu/RSX/Overlays/overlay_user_list_dialog.h"
#include "cellUserInfo.h"
@@ -159,6 +161,7 @@ error_code cellUserInfoSelectUser_ListType(vm::ptr<CellUserInfoTypeSet> listType
if (s32 ret = sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0)
{
g_fxo->get<user_info_manager>().dialog_opened = false;
return CELL_USERINFO_ERROR_BUSY;
}
@@ -252,7 +255,7 @@ error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList,
funcSelect(ppu, CELL_USERINFO_ERROR_NOUSER, selectUser, userdata);
return CELL_OK;
});
return CELL_OK;
}
@@ -267,6 +270,7 @@ error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList,
if (s32 ret = sysutil_send_system_cmd(CELL_SYSUTIL_DRAWING_BEGIN, 0); ret < 0)
{
g_fxo->get<user_info_manager>().dialog_opened = false;
return CELL_USERINFO_ERROR_BUSY;
}
+9 -19
View File
@@ -205,7 +205,7 @@ struct vdec_context final
std::deque<vdec_frame> out_queue;
const u32 out_max = 60;
atomic_t<u32> au_count{0};
atomic_t<s32> au_count{0};
lf_queue<vdec_cmd> in_cmd;
@@ -334,7 +334,6 @@ struct vdec_context final
out_queue.clear(); // Flush image queue
log_time_base = {};
au_count = 0;
frc_set = 0; // TODO: ???
next_pts = 0;
@@ -540,14 +539,11 @@ struct vdec_context final
{
// Send AUDONE even if the current sequence was reset and a new sequence was started.
cellVdec.trace("Sending CELL_VDEC_MSG_TYPE_AUDONE (handle=0x%x, seq_id=%d, cmd_id=%d)", handle, cmd->seq_id, cmd->id);
ensure(au_count.try_dec(0));
cb_func(ppu, vid, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, cb_arg);
lv2_obj::sleep(ppu);
if (au_count > 0)
{
--au_count;
}
while (!decoded_frames.empty() && seq_id == cmd->seq_id)
{
// Wait until there is free space in the image queue.
@@ -984,8 +980,8 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle)
ppu_execute<&sys_interrupt_thread_disestablish>(ppu, tid);
}
std::lock_guard lock{vdec->mutex};
vdec->seq_state = sequence_state::closed;
vdec->mutex.lock_unlock();
if (!idm::remove_verify<vdec_context>(handle, std::move(vdec)))
{
@@ -1302,7 +1298,7 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecP
u8* in_data[4] = { frame->data[0], frame->data[1], frame->data[2], alpha_plane.get() };
int in_line[4] = { frame->linesize[0], frame->linesize[1], frame->linesize[2], w * 1 };
u8* out_data[4] = { outBuff.get_ptr() };
int out_line[4] = { w * 4 }; // RGBA32 or ARGB32
int out_line[4] = { static_cast<int>(utils::align(w * 4u, 128)) }; // RGBA32 or ARGB32
if (!alpha_plane)
{
@@ -1314,19 +1310,13 @@ error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecP
{
fmt::throw_exception("cellVdecGetPictureExt: av_image_fill_linesizes failed (handle=0x%x, seq_id=%d, cmd_id=%d, ret=0x%x): %s", handle, frame.seq_id, frame.cmd_id, ret, utils::av_error_to_string(ret));
}
// TODO:
// It's possible that we need to align the pitch to 128 here as well.
// But I'd wait for a game with broken images before tackling this.
}
sws_scale(vdec->sws, in_data, in_line, 0, h, out_data, out_line);
//const u32 buf_size = utils::align(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1), 128);
//// TODO: zero padding bytes
//int err = av_image_copy_to_buffer(outBuff.get_ptr(), buf_size, frame->data, frame->linesize, vdec->ctx->pix_fmt, frame->width, frame->height, 1);
//if (err < 0)
//{
// cellVdec.fatal("cellVdecGetPictureExt: av_image_copy_to_buffer failed (handle=0x%x, seq_id=%d, cmd_id=%d, err=0x%x='%s')", handle, frame.seq_id, frame.cmd_id, err, utils::av_error_to_string(err));
//}
}
return CELL_OK;
+2 -2
View File
@@ -164,7 +164,7 @@ error_code cellVideoExportInitialize2(u32 version, vm::ptr<CellVideoExportUtilFi
error_code cellVideoExportInitialize(u32 version, u32 container, vm::ptr<CellVideoExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
{
cellVideoExport.notice("cellVideoExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata);
if (version != CELL_VIDEO_EXPORT_UTIL_VERSION_CURRENT)
{
return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM;
@@ -282,7 +282,7 @@ error_code cellVideoExportFromFileWithCopy(vm::cptr<char> srcHddDir, vm::cptr<ch
error_code cellVideoExportFromFile(vm::cptr<char> srcHddDir, vm::cptr<char> srcHddFile, vm::ptr<CellVideoExportSetParam> param, vm::ptr<CellVideoExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
{
cellVideoExport.todo("cellVideoExportFromFile(srcHddDir=%s, srcHddFile=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", srcHddDir, srcHddFile, param, funcFinish, userdata);
if (!param || !funcFinish || !srcHddDir || !srcHddDir[0] || !srcHddFile || !srcHddFile[0])
{
return CELL_VIDEO_EXPORT_UTIL_ERROR_PARAM;
+33 -45
View File
@@ -18,6 +18,7 @@
#include "Emu/Cell/lv2/sys_fs.h"
#include "Emu/NP/np_handler.h"
#include "Emu/NP/np_contexts.h"
#include "Emu/NP/np_helpers.h"
#include "Emu/system_config.h"
LOG_CHANNEL(sceNp);
@@ -3854,7 +3855,7 @@ error_code sceNpScoreCreateTransactionCtx(s32 titleCtxId)
}
auto score = idm::get<score_ctx>(titleCtxId);
if (!score)
{
return SCE_NP_COMMUNITY_ERROR_INVALID_ID;
@@ -4409,7 +4410,7 @@ error_code scenp_score_get_ranking_by_range(s32 transId, SceNpScoreBoardId board
if (opt_ptr[1])
{
vm::ptr<u32> ssr_ptr = vm::cast(opt_ptr[1]);
startSerialRank = *ssr_ptr;
startSerialRank = *ssr_ptr;
}
// It also uses opt_ptr[2] for unknown purposes
@@ -5237,11 +5238,11 @@ error_code sceNpSignalingActivateConnection(u32 ctx_id, vm::ptr<SceNpId> npId, v
return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT;
}
if (strncmp(nph.get_npid().handle.data, npId->handle.data, 16) == 0)
if (np::is_same_npid(nph.get_npid(), *npId))
return SCE_NP_SIGNALING_ERROR_OWN_NP_ID;
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
*conn_id = sigh.init_sig_infos(npId.get_ptr());
*conn_id = sigh.init_sig1(*npId);
return CELL_OK;
}
@@ -5272,7 +5273,7 @@ error_code sceNpSignalingTerminateConnection(u32 ctx_id, u32 conn_id)
}
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
sigh.stop_sig(conn_id);
return CELL_OK;
@@ -5298,19 +5299,15 @@ error_code sceNpSignalingGetConnectionStatus(u32 ctx_id, u32 conn_id, vm::ptr<s3
const auto si = sigh.get_sig_infos(conn_id);
if (si.connStatus == SCE_NP_SIGNALING_CONN_STATUS_ACTIVE && si.ext_status == ext_sign_peer)
{
*conn_status = SCE_NP_SIGNALING_CONN_STATUS_INACTIVE;
}
else
{
*conn_status = si.connStatus;
}
if (!si)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
*conn_status = si->conn_status;
if (peer_addr)
(*peer_addr).np_s_addr = si.addr; // infos.addr is already BE
(*peer_addr).np_s_addr = si->addr; // infos.addr is already BE
if (peer_port)
*peer_port = si.port;
*peer_port = si->port;
return CELL_OK;
}
@@ -5334,33 +5331,36 @@ error_code sceNpSignalingGetConnectionInfo(u32 ctx_id, u32 conn_id, s32 code, vm
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig_infos(conn_id);
if (!si)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
switch (code)
{
case SCE_NP_SIGNALING_CONN_INFO_RTT:
{
info->rtt = si.rtt;
info->rtt = si->rtt;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
{
info->bandwidth = 10'000'000; // 10 MBPS HACK
info->bandwidth = 100'000'000; // 100 MBPS HACK
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
{
info->npId = si.npid;
info->npId = si->npid;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
{
info->address.port = std::bit_cast<u16, be_t<u16>>(si.port);
info->address.addr.np_s_addr = si.addr;
info->address.port = std::bit_cast<u16, be_t<u16>>(si->port);
info->address.addr.np_s_addr = si->addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
{
info->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
info->address.addr.np_s_addr = si.mapped_addr;
info->address.port = std::bit_cast<u16, be_t<u16>>(si->mapped_port);
info->address.addr.np_s_addr = si->mapped_addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS:
@@ -5393,8 +5393,13 @@ error_code sceNpSignalingGetConnectionFromNpId(u32 ctx_id, vm::ptr<SceNpId> npId
return SCE_NP_SIGNALING_ERROR_INVALID_ARGUMENT;
}
if (np::is_same_npid(*npId, nph.get_npid()))
{
return SCE_NP_SIGNALING_ERROR_OWN_NP_ID;
}
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto found_conn_id = sigh.get_conn_id_from_npid(npId.get_ptr());
const auto found_conn_id = sigh.get_conn_id_from_npid(*npId);
if (!found_conn_id)
{
@@ -5455,9 +5460,9 @@ error_code sceNpSignalingGetLocalNetInfo(u32 ctx_id, vm::ptr<SceNpSignalingNetIn
info->mapped_addr = nph.get_public_ip_addr();
// Pure speculation below
info->nat_status = 0;
info->upnp_status = 0;
info->npport_status = 0;
info->nat_status = SCE_NP_SIGNALING_NETINFO_NAT_STATUS_TYPE2;
info->upnp_status = nph.get_upnp_status();
info->npport_status = SCE_NP_SIGNALING_NETINFO_NPPORT_STATUS_OPEN;
info->npport = SCE_NP_PORT;
return CELL_OK;
@@ -5537,25 +5542,8 @@ error_code sceNpUtilCmpNpId(vm::ptr<SceNpId> id1, vm::ptr<SceNpId> id2)
return SCE_NP_UTIL_ERROR_INVALID_ARGUMENT;
}
// Unknown what this constant means
// if (id1->reserved[0] != 1 || id2->reserved[0] != 1)
// {
// return SCE_NP_UTIL_ERROR_INVALID_NP_ID;
// }
if (strncmp(id1->handle.data, id2->handle.data, 16))// || id1->unk1[0] != id2->unk1[0])
{
if (!np::is_same_npid(*id1, *id2))
return not_an_error(SCE_NP_UTIL_ERROR_NOT_MATCH);
}
// if (id1->unk1[1] != id2->unk1[1])
// {
// // If either is zero they match
// if (id1->opt[4] && id2->opt[4])
// {
// return SCE_NP_UTIL_ERROR_NOT_MATCH;
// }
// }
return CELL_OK;
}
+1 -1
View File
@@ -20,7 +20,7 @@ struct np_in_addr
using sys_memory_container_t = u32;
using system_time_t = s64;
using system_time_t = u64; // s64 in documentation. But since this is in microseconds, it doesn't seem to make much sense.
using second_t = u32;
using usecond_t = u64;
+59 -20
View File
@@ -6,6 +6,7 @@
#include "sceNp2.h"
#include "Emu/NP/np_handler.h"
#include "Emu/NP/np_contexts.h"
#include "Emu/NP/np_helpers.h"
#include "cellSysutil.h"
LOG_CHANNEL(sceNp2);
@@ -444,20 +445,35 @@ error_code sceNpMatching2SignalingGetConnectionStatus(
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
auto [res, npid] = nph.local_get_npid(roomId, memberId);
if (res)
return res;
if (np::is_same_npid(nph.get_npid(), *npid))
return SCE_NP_SIGNALING_ERROR_OWN_NP_ID;
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
auto conn_id = sigh.get_conn_id_from_npid(*npid);
if (!conn_id)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
*connStatus = si.connStatus;
const auto si = sigh.get_sig_infos(*conn_id);
if (!si)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
*connStatus = si->conn_status;
if (peerAddr)
{
(*peerAddr).np_s_addr = si.addr; // infos.addr is already BE
(*peerAddr).np_s_addr = si->addr; // infos.addr is already BE
}
if (peerPort)
{
*peerPort = si.port;
*peerPort = si->port;
}
return CELL_OK;
@@ -613,45 +629,61 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
if (!connInfo)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
auto [res, npid] = nph.local_get_npid(roomId, memberId);
if (res)
return res;
if (np::is_same_npid(nph.get_npid(), *npid))
return SCE_NP_SIGNALING_ERROR_OWN_NP_ID;
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const auto si = sigh.get_sig2_infos(roomId, memberId);
auto conn_id = sigh.get_conn_id_from_npid(*npid);
if (!conn_id)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
const auto si = sigh.get_sig_infos(*conn_id);
if (!si)
return SCE_NP_SIGNALING_ERROR_CONN_NOT_FOUND;
switch (code)
{
case SCE_NP_SIGNALING_CONN_INFO_RTT:
{
connInfo->rtt = si.rtt;
connInfo->rtt = si->rtt;
sceNp2.warning("Returning a RTT of %d microseconds", connInfo->rtt);
break;
}
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
{
connInfo->bandwidth = 10'000'000; // 10 MBPS HACK
connInfo->bandwidth = 100'000'000; // 100 MBPS HACK
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
{
connInfo->npId = si.npid;
connInfo->npId = si->npid;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
{
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.port);
connInfo->address.addr.np_s_addr = si.addr;
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si->port);
connInfo->address.addr.np_s_addr = si->addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
{
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
connInfo->address.addr.np_s_addr = si.mapped_addr;
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si->mapped_port);
connInfo->address.addr.np_s_addr = si->mapped_addr;
break;
}
case SCE_NP_SIGNALING_CONN_INFO_PACKET_LOSS:
{
connInfo->packet_loss = 1; // HACK
connInfo->packet_loss = 0; // HACK
break;
}
default:
@@ -709,7 +741,7 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c
error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatching2RequestId reqId)
{
sceNp2.todo("sceNpMatching2AbortRequest(ctxId=%d, reqId=%d)", ctxId, reqId);
sceNp2.warning("sceNpMatching2AbortRequest(ctxId=%d, reqId=%d)", ctxId, reqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@@ -728,6 +760,9 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
}
if (!nph.abort_request(reqId))
return SCE_NP_MATCHING2_ERROR_REQUEST_NOT_FOUND;
return CELL_OK;
}
@@ -1591,7 +1626,7 @@ error_code sceNpMatching2SignalingCancelPeerNetInfo(SceNpMatching2ContextId ctxI
error_code sceNpMatching2SignalingGetLocalNetInfo(vm::ptr<SceNpMatching2SignalingNetInfo> netinfo)
{
sceNp2.todo("sceNpMatching2SignalingGetLocalNetInfo(netinfo=*0x%x)", netinfo);
sceNp2.warning("sceNpMatching2SignalingGetLocalNetInfo(netinfo=*0x%x)", netinfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@@ -1600,12 +1635,17 @@ error_code sceNpMatching2SignalingGetLocalNetInfo(vm::ptr<SceNpMatching2Signalin
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!netinfo)
if (!netinfo || netinfo->size != sizeof(SceNpMatching2SignalingNetInfo))
{
// TODO: check netinfo->size
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
netinfo->localAddr = nph.get_local_ip_addr();
netinfo->mappedAddr = nph.get_public_ip_addr();
// Pure speculation below
netinfo->natStatus = SCE_NP_SIGNALING_NETINFO_NAT_STATUS_TYPE2;
return CELL_OK;
}
@@ -1639,9 +1679,8 @@ error_code sceNpMatching2SignalingGetPeerNetInfoResult(SceNpMatching2ContextId c
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!netinfo)
if (!netinfo || netinfo->size != sizeof(SceNpMatching2SignalingNetInfo))
{
// TODO: check netinfo->size
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
+2 -2
View File
@@ -1555,9 +1555,9 @@ struct SceNpMatching2CbQueueInfo
u8 reserved[12];
};
union SceNpMatching2SignalingNetInfo // TODO check values
struct SceNpMatching2SignalingNetInfo
{
be_t<u64> size;
be_t<u32> size;
be_t<u32> localAddr;
be_t<u32> mappedAddr;
be_t<u32> natStatus;
+2 -2
View File
@@ -39,8 +39,8 @@ struct bandwidth_test
}
}
test_result.upload_bps = 16000.0;
test_result.download_bps = 16000.0;
test_result.upload_bps = 100'000'000.0;
test_result.download_bps = 100'000'000.0;
test_result.result = CELL_OK;
status = SCE_NP_UTIL_BANDWIDTH_TEST_STATUS_FINISHED;
finished = true;
+1 -1
View File
@@ -16,7 +16,7 @@ vm::gvar<vm::ptr<void()>> g_ppu_atexitspawn;
vm::gvar<vm::ptr<void()>> g_ppu_at_Exitspawn;
extern vm::gvar<u32> g_ppu_exit_mutex;
s64 sys_time_get_system_time()
u64 sys_time_get_system_time()
{
sysPrxForUser.trace("sys_time_get_system_time()");
+2 -2
View File
@@ -80,7 +80,7 @@ std::pair<PPUDisAsm::const_op, u64> PPUDisAsm::try_get_const_op_gpr_value(u32 re
if (pc == umax)
{
// Default arg: choose pc of previous instruction
// Default arg: choose pc of previous instruction
if (dump_pc == 0)
{
@@ -108,7 +108,7 @@ std::pair<PPUDisAsm::const_op, u64> PPUDisAsm::try_get_const_op_gpr_value(u32 re
auto is_branch = [](enum ppu_itype::type itype)
{
return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR;
return itype == ppu_itype::BC || itype == ppu_itype::B || itype == ppu_itype::BCLR || itype == ppu_itype::BCCTR;
};
if (is_branch(type) || type == ppu_itype::UNK)
+29 -18
View File
@@ -37,6 +37,14 @@ extern void ppu_register_function_at(u32 addr, u32 size, ppu_intrp_func_t ptr);
extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
std::unordered_map<std::string, ppu_static_module*>& ppu_module_manager::get()
{
// In C++ the order of static initialization is undefined if it happens in
// separate compilation units, therefore we have to initialize the map on first use.
static std::unordered_map<std::string, ppu_static_module*> s_module_map;
return s_module_map;
}
// HLE function name cache
std::vector<std::string> g_ppu_function_names;
@@ -82,12 +90,12 @@ void ppu_static_module::initialize()
void ppu_module_manager::register_module(ppu_static_module* _module)
{
ppu_module_manager::s_module_map.emplace(_module->name, _module);
ppu_module_manager::get().emplace(_module->name, _module);
}
ppu_static_function& ppu_module_manager::access_static_function(const char* _module, u32 fnid)
{
auto& res = ::at32(ppu_module_manager::s_module_map, _module)->functions[fnid];
auto& res = ::at32(ppu_module_manager::get(), _module)->functions[fnid];
if (res.name)
{
@@ -99,7 +107,7 @@ ppu_static_function& ppu_module_manager::access_static_function(const char* _mod
ppu_static_variable& ppu_module_manager::access_static_variable(const char* _module, u32 vnid)
{
auto& res = ::at32(ppu_module_manager::s_module_map, _module)->variables[vnid];
auto& res = ::at32(ppu_module_manager::get(), _module)->variables[vnid];
if (res.name)
{
@@ -111,14 +119,14 @@ ppu_static_variable& ppu_module_manager::access_static_variable(const char* _mod
const ppu_static_module* ppu_module_manager::get_module(const std::string& name)
{
const auto& map = ppu_module_manager::s_module_map;
const auto& map = ppu_module_manager::get();
const auto found = map.find(name);
return found != map.end() ? found->second : nullptr;
}
void ppu_module_manager::initialize_modules()
{
for (auto& _module : s_module_map)
for (auto& _module : ppu_module_manager::get())
{
_module.second->initialize();
}
@@ -678,10 +686,10 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
std::lock_guard lock(link->mutex);
usz unload_index = 0;
ppu_prx_module_info lib{};
for (u32 addr = exports_start; addr < exports_end; unload_index++)
for (u32 addr = exports_start; addr < exports_end; unload_index++, addr += lib.size ? lib.size : sizeof(ppu_prx_module_info))
{
ppu_prx_module_info lib{};
std::memcpy(&lib, vm::base(addr), sizeof(lib));
const bool is_library = !!(lib.attributes & PRX_EXPORT_LIBRARY_FLAG);
@@ -712,20 +720,17 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
result.emplace(nid, addr);
}
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
if (!is_library)
{
// Skipped if none of the flags is set
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
if (for_observing_callbacks)
{
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
@@ -738,7 +743,6 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
ppu_register_library_lock(module_name, false);
}
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
@@ -759,7 +763,6 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
if (!should_load)
{
ppu_loader.notice("** Skipped module '%s' (already loaded)", module_name);
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
@@ -861,8 +864,6 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
}
}
}
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
}
return result;
@@ -1516,10 +1517,20 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->module_info_version[0] = lib_info->version[0];
prx->module_info_version[1] = lib_info->version[1];
prx->module_info_attributes = lib_info->attributes;
prx->exports_start = lib_info->exports_start;
prx->exports_end = lib_info->exports_end;
for (usz start = prx->exports_start, size = 0;; size++, start += vm::read8(start) ? vm::read8(start) : sizeof(ppu_prx_module_info))
{
if (start >= prx->exports_end)
{
// Preallocate storage
prx->m_external_loaded_flags.resize(size);
break;
}
}
ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true);
@@ -2040,7 +2051,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
// Initialize memory stats (according to sdk version)
u32 mem_size;
if (g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
if (Emu.IsVsh())
{
// Because vsh.self comes before any generic application, more memory is available to it
mem_size = 0xF000000;
@@ -2093,7 +2104,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
load_libs.emplace("libsysmodule.sprx");
}
if (ar || g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
if (ar || Emu.IsVsh())
{
// Cannot be used with vsh.self or savestates (they self-manage itself)
load_libs.clear();
@@ -2268,7 +2279,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
ppu->cmd_push({ppu_cmd::initialize, 0});
if (!entry && g_ps3_process_info.get_cellos_appname() != "vsh.self"sv)
if (!entry && !Emu.IsVsh())
{
// Set TLS args, call sys_initialize_tls
ppu->cmd_list
+1 -7
View File
@@ -163,10 +163,7 @@ public:
}
// We need this to deal with the enumeration over all ppu_static_modules that happens in ppu_initialize_modules
static const std::unordered_map<std::string, ppu_static_module*>& get()
{
return s_module_map;
}
static std::unordered_map<std::string, ppu_static_module*>& get();
static const ppu_static_module cellAdec;
static const ppu_static_module cellAtrac;
@@ -279,9 +276,6 @@ public:
static const ppu_static_module sys_lv2dbg;
static const ppu_static_module static_hle;
static const ppu_static_module hle_patches;
private:
inline static std::unordered_map<std::string, ppu_static_module*> s_module_map;
};
template <auto* Func>
+38 -5
View File
@@ -9,7 +9,7 @@
#include "Emu/perf_meter.hpp"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/Memory/vm_locking.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/VFS.h"
#include "Emu/system_progress.hpp"
#include "Emu/system_utils.hpp"
@@ -1060,7 +1060,32 @@ void ppu_thread::dump_regs(std::string& ret) const
is_const = false;
}
fmt::append(ret, "r%d%s%s 0x%-8llx", i, i <= 9 ? " " : "", is_const ? "©" : ":", reg);
fmt::append(ret, "r%d%s%s ", i, i <= 9 ? " " : "", is_const ? "©" : ":");
bool printed_error = false;
if ((reg >> 31) == 0x1'ffff'ffff)
{
const usz old_size = ret.size();
fmt::append(ret, "%s (0x%x)", CellError{static_cast<u32>(reg)}, reg);
// Test if failed to format (appended " 0x8".. in such case)
if (ret[old_size] == '0')
{
// Failed
ret.resize(old_size);
}
else
{
printed_error = true;
}
}
if (!printed_error)
{
fmt::append(ret, "0x%-8llx", reg);
}
constexpr u32 max_str_len = 32;
constexpr u32 hex_count = 8;
@@ -1167,6 +1192,7 @@ void ppu_thread::dump_regs(std::string& ret) const
}
}
fmt::append(ret, "CIA: 0x%x\n", cia);
fmt::append(ret, "CR: 0x%08x\n", cr.pack());
fmt::append(ret, "LR: 0x%llx\n", lr);
fmt::append(ret, "CTR: 0x%llx\n", ctr);
@@ -2876,7 +2902,7 @@ extern void ppu_finalize(const ppu_module& info)
fmt::append(cache_path, "ppu-%s-%s/", fmt::base57(info.sha1), info.path.substr(info.path.find_last_of('/') + 1));
#ifdef LLVM_AVAILABLE
g_fxo->get<jit_module_manager>().remove(cache_path + info.name);
g_fxo->get<jit_module_manager>().remove(cache_path + info.name + "_" + std::to_string(info.segs[0].addr));
#endif
}
@@ -3208,6 +3234,11 @@ extern void ppu_initialize()
idm::select<lv2_obj, lv2_prx>([&](u32, lv2_prx& _module)
{
if (_module.funcs.empty())
{
return;
}
if (_module.path.starts_with(firmware_sprx_path))
{
// Postpone testing
@@ -3245,7 +3276,7 @@ extern void ppu_initialize()
const std::string mount_point = vfs::get("/dev_flash/");
bool dev_flash_located = Emu.GetCat().back() != 'P' && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point);
bool dev_flash_located = !Emu.GetCat().ends_with('P') && Emu.IsPathInsideDir(Emu.GetBoot(), mount_point);
if (compile_fw || dev_flash_located)
{
@@ -3345,6 +3376,8 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
{ "sys_game_watchdog_clear", reinterpret_cast<u64>(ppu_execute_syscall) },
{ "sys_game_get_system_sw_version", reinterpret_cast<u64>(ppu_execute_syscall) },
{ "sys_game_board_storage_read", reinterpret_cast<u64>(ppu_execute_syscall) },
{ "sys_game_board_storage_write", reinterpret_cast<u64>(ppu_execute_syscall) },
{ "sys_game_get_rtc_status", reinterpret_cast<u64>(ppu_execute_syscall) },
{ "__trap", reinterpret_cast<u64>(&ppu_trap) },
{ "__error", reinterpret_cast<u64>(&ppu_error) },
{ "__check", reinterpret_cast<u64>(&ppu_check) },
@@ -3425,7 +3458,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
};
// Permanently loaded compiled PPU modules (name -> data)
jit_module& jit_mod = g_fxo->get<jit_module_manager>().get(cache_path + info.name);
jit_module& jit_mod = g_fxo->get<jit_module_manager>().get(cache_path + info.name + "_" + std::to_string(info.segs[0].addr));
// Compiler instance (deferred initialization)
std::shared_ptr<jit_compiler>& jit = jit_mod.pjit;
+1 -1
View File
@@ -188,7 +188,7 @@ public:
std::unique_ptr<CPUDisAsm> copy_type_erased() const override;
std::pair<bool, v128> try_get_const_value(u32 reg, u32 pc = -1, u32 TTL = 10) const;
// Get constant value if the original array is made of only repetitions of the same value
// Get constant value if the original array is made of only repetitions of the same value
template <typename T> requires (sizeof(T) < sizeof(v128) && !(sizeof(v128) % sizeof(T)))
std::pair<bool, T> try_get_const_equal_value_array(u32 reg, u32 pc = -1, u32 TTL = 10) const
{
+1 -1
View File
@@ -7709,7 +7709,7 @@ public:
const auto ax = vdbpsadbw(a, zeroes, 0);
const auto bx = vdbpsadbw(b, zeroes, 0);
set_vr(op.rt, shuffle2(ax, bx, 0, 8, 2, 10, 4, 12, 6, 14));
set_vr(op.rt, shuffle2(ax, bx, 0, 9, 2, 11, 4, 13, 6, 15));
return;
}
+38 -13
View File
@@ -9,7 +9,6 @@
#include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "Emu/perf_meter.hpp"
#include "Emu/RSX/RSXThread.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/lv2/sys_spu.h"
@@ -23,6 +22,9 @@
#include "Emu/Cell/SPURecompiler.h"
#include "Emu/Cell/timers.hpp"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/RSX/RSXThread.h"
#include <cmath>
#include <cfenv>
#include <thread>
@@ -1072,8 +1074,31 @@ void spu_thread::dump_regs(std::string& ret) const
if (is_packed)
{
// Shortand formatting
fmt::append(ret, "%08x", i3);
bool printed_error = false;
if (i3 >> 31)
{
const usz old_size = ret.size();
fmt::append(ret, "%s (0x%x)", CellError{i3}, i3);
// Test if failed to format (appended " 0x8".. in such case)
if (ret[old_size] == '0')
{
// Failed
ret.resize(old_size);
}
else
{
printed_error = true;
}
}
if (!printed_error)
{
// Shortand formatting
fmt::append(ret, "%08x", i3);
}
}
else
{
@@ -1265,7 +1290,7 @@ std::string spu_thread::dump_misc() const
void spu_thread::cpu_on_stop()
{
if (current_func)
if (current_func && is_stopped(state - cpu_flag::stop))
{
if (start_time)
{
@@ -1494,7 +1519,7 @@ void spu_thread::cpu_task()
unsavable = false;
// Print some stats
spu_log.notice("Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure);
(!group || group->stop_count < 5 ? spu_log.notice : spu_log.trace)("Stats: Block Weight: %u (Retreats: %u);", block_counter, block_failure);
}
else
{
@@ -2743,7 +2768,7 @@ bool spu_thread::do_list_transfer(spu_mfc_cmd& args)
u32 s_size = data0._u32[0];
// We need to verify matching between odd and even elements (vector test is position independant)
// We need to verify matching between odd and even elements (vector test is position independent)
// 0-5 is the most unlikely couple match for many reasons so it skips the entire check very efficiently in most cases
// Assumes padding bits should match
if (optimization_compatible == MFC_GET_CMD && s_size == data0._u32[2] && arg_size >= fetch_size * 8)
@@ -3928,7 +3953,7 @@ bool spu_thread::process_mfc_cmd()
if (getllar_busy_waiting_switch == umax)
{
// Evalute its value (shift-right to ensure its randomness with different CPUs)
getllar_busy_waiting_switch = ((perf0.get() >> 8) % 100 < g_cfg.core.spu_getllar_busy_waiting_percentage);
getllar_busy_waiting_switch = ((perf0.get() >> 8) % 100 < g_cfg.core.spu_getllar_busy_waiting_percentage) ? 1 : 0;
}
return !!getllar_busy_waiting_switch || getllar_spin_count < 3;
@@ -3946,7 +3971,7 @@ bool spu_thread::process_mfc_cmd()
last_getllar_id = mfc_cmd_id;
last_gtsc = perf0.get();
if (getllar_busy_waiting_switch == true)
if (getllar_busy_waiting_switch == 1)
{
busy_wait(300);
}
@@ -4078,7 +4103,7 @@ bool spu_thread::process_mfc_cmd()
raddr = addr;
rtime = ntime;
mov_rdata(_ref<spu_rdata_t>(ch_mfc_cmd.lsa & 0x3ff80), rdata);
ch_atomic_stat.set_value(MFC_GETLLAR_SUCCESS);
if (g_cfg.core.mfc_debug)
@@ -4510,7 +4535,7 @@ u32 spu_thread::get_ch_count(u32 ch)
case SPU_RdSigNotify1: return ch_snr1.get_count();
case SPU_RdSigNotify2: return ch_snr2.get_count();
case MFC_RdAtomicStat: return ch_atomic_stat.get_count();
case SPU_RdEventStat: return get_events().count;
case SPU_RdEventStat: return static_cast<u32>(get_events().count);
case MFC_Cmd: return 16 - mfc_size;
// Channels with a constant count of 1:
@@ -4744,7 +4769,7 @@ s64 spu_thread::get_ch_value(u32 ch)
}
const bool reservation_busy_waiting = ((utils::get_tsc() >> 8) % 100 + ((raddr == spurs_addr) ? 50 : 0)) < g_cfg.core.spu_reservation_busy_waiting_percentage;
for (; !events.count; events = get_events(mask1 & ~SPU_EVENT_LR, true, true))
{
const auto old = +state;
@@ -4780,7 +4805,7 @@ s64 spu_thread::get_ch_value(u32 ch)
// Wait without timeout, in this situation we have notifications for all writes making it possible
// Abort notifications are handled specially for performance reasons
vm::reservation_notifier(raddr).wait(rtime, -128);
continue;
continue;
}
atomic_wait_engine::set_one_time_use_wait_callback(mask1 != SPU_EVENT_LR ? nullptr : +[](u64 attempts) -> bool
@@ -5456,7 +5481,7 @@ bool spu_thread::stop_and_signal(u32 code)
if (Emu.IsStarting())
{
// Deregister lv2_obj::g_to_sleep entry (savestates related)
// Deregister lv2_obj::g_to_sleep entry (savestates related)
lv2_obj::sleep(*this);
}
+1 -1
View File
@@ -234,7 +234,7 @@ public:
data.notify_one();
}
// Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs
// Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs
return !pushed_to_data || (old & bit_count) == 0;
}
}
+30 -19
View File
@@ -394,7 +394,7 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
BIND_SYSC(sys_memory_container_destroy), //342 (0x156)
BIND_SYSC(sys_memory_container_get_size), //343 (0x157)
NULL_FUNC(sys_memory_budget_set), //344 (0x158)
null_func,//BIND_SYSC(sys_memory_...), //345 (0x159)
BIND_SYSC(sys_memory_container_destroy_parent_with_childs), //345 (0x159)
null_func,//BIND_SYSC(sys_memory_...), //346 (0x15A)
uns_func, //347 (0x15B) UNS
BIND_SYSC(sys_memory_allocate), //348 (0x15C)
@@ -420,11 +420,11 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
BIND_SYSC(sys_uart_send), //369 (0x171) ROOT
BIND_SYSC(sys_uart_get_params), //370 (0x172) ROOT
uns_func, //371 (0x173) UNS
BIND_SYSC(_sys_game_watchdog_start), //372 (0x174)
BIND_SYSC(_sys_game_watchdog_stop), //373 (0x175)
BIND_SYSC(_sys_game_watchdog_clear), //374 (0x176)
BIND_SYSC(_sys_game_watchdog_start), //372 (0x174)
BIND_SYSC(_sys_game_watchdog_stop), //373 (0x175)
BIND_SYSC(_sys_game_watchdog_clear), //374 (0x176)
NULL_FUNC(sys_game_set_system_sw_version), //375 (0x177) ROOT
BIND_SYSC(_sys_game_get_system_sw_version), //376 (0x178) ROOT
BIND_SYSC(_sys_game_get_system_sw_version), //376 (0x178) ROOT
BIND_SYSC(sys_sm_set_shop_mode), //377 (0x179) ROOT
BIND_SYSC(sys_sm_get_ext_event2), //378 (0x17A) ROOT
BIND_SYSC(sys_sm_shutdown), //379 (0x17B) ROOT
@@ -459,8 +459,8 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
NULL_FUNC(sys_sm_get_tzpb), //408 (0x198) PM
NULL_FUNC(sys_sm_get_fan_policy), //409 (0x199) PM
BIND_SYSC(_sys_game_board_storage_read), //410 (0x19A)
NULL_FUNC(sys_game_board_storage_write), //411 (0x19B)
NULL_FUNC(sys_game_get_rtc_status), //412 (0x19C)
BIND_SYSC(_sys_game_board_storage_write), //411 (0x19B)
BIND_SYSC(_sys_game_get_rtc_status), //412 (0x19C)
null_func,//BIND_SYSC(sys_...), //413 (0x19D) ROOT
null_func,//BIND_SYSC(sys_...), //414 (0x19E) ROOT
null_func,//BIND_SYSC(sys_...), //415 (0x19F) ROOT
@@ -1093,21 +1093,23 @@ class ppu_syscall_usage
{
// Internal buffer
std::string m_stats;
u64 m_old_stat[1024]{};
public:
// Public info collection buffers
atomic_t<u64> stat[1024]{};
void print_stats() noexcept
void print_stats(bool force_print) noexcept
{
std::multimap<u64, u64, std::greater<u64>> usage;
for (u32 i = 0; i < 1024; i++)
{
if (u64 v = stat[i])
if (u64 v = stat[i]; m_old_stat[i] != v || (force_print && v))
{
// Only add syscalls with non-zero usage counter
// Only add syscalls with non-zero usage counter and only if caught new calls since last print
usage.emplace(v, i);
m_old_stat[i] = v;
}
}
@@ -1118,27 +1120,36 @@ public:
fmt::append(m_stats, u8"\n\t⁂ %s [%u]", ppu_get_syscall_name(pair.second), pair.first);
}
ppu_log.notice("PPU Syscall Usage Stats: %s", m_stats);
if (!m_stats.empty())
{
ppu_log.notice("PPU Syscall Usage Stats: %s", m_stats);
}
}
void operator()()
{
while (thread_ctrl::state() != thread_state::aborting)
bool was_paused = false;
for (u32 i = 1; thread_ctrl::state() != thread_state::aborting; i++)
{
thread_ctrl::wait_for(10000'000);
thread_ctrl::wait_for(1'000'000);
if (Emu.IsPaused())
const bool is_paused = Emu.IsPaused();
// Force-print all if paused
const bool force_print = is_paused && !was_paused;
if (force_print || i % 10 == 0)
{
continue;
was_paused = is_paused;
print_stats(force_print);
}
print_stats();
}
}
~ppu_syscall_usage()
{
print_stats();
print_stats(true);
}
static constexpr auto thread_name = "PPU Syscall Usage Thread"sv;
@@ -1686,7 +1697,7 @@ void lv2_obj::schedule_all(u64 current_time)
{
if (g_ppu && cpu_flag::preempt - g_ppu->state)
{
// Don't be picky, pick up any running PPU thread even it has a wait flag
// Don't be picky, pick up any running PPU thread even it has a wait flag
cpu = g_ppu;
}
// TODO: If this case is common enough it may be valuable to iterate over all CPU threads to find a perfect candidate (one without a wait or suspend flag)
+1 -1
View File
@@ -5,4 +5,4 @@
// SysCalls
error_code sys_console_write(vm::cptr<char> buf, u32 len);
constexpr auto sys_console_write2 = sys_console_write;
constexpr auto sys_console_write2 = sys_console_write;
+4 -4
View File
@@ -157,7 +157,7 @@ CellError lv2_event_queue::send(lv2_event event)
{
// Store event in In_MBox
auto& spu = static_cast<spu_thread&>(*schedule<spu_thread>(sq, protocol));
if (spu.state & cpu_flag::again)
{
if (auto cpu = get_current_cpu_thread())
@@ -325,7 +325,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
queue->append(cpu);
}
atomic_storage<ppu_thread*>::release(queue->pq, nullptr);
atomic_storage<ppu_thread*>::release(queue->pq, nullptr);
lv2_obj::awake_all();
}
else
@@ -336,7 +336,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
resume_spu_thread_group_from_waiting(*cpu);
}
atomic_storage<spu_thread*>::release(queue->sq, nullptr);
atomic_storage<spu_thread*>::release(queue->sq, nullptr);
}
qlock.unlock();
@@ -432,7 +432,7 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_e
// "/dev_flash/vsh/module/msmw2.sprx" seems to rely on some cryptic shared memory behaviour that we don't emulate correctly
// This is a hack to avoid waiting for 1m40s every time we boot vsh
if (queue.key == 0x8005911000000012 && g_ps3_process_info.get_cellos_appname() == "vsh.self"sv)
if (queue.key == 0x8005911000000012 && Emu.IsVsh())
{
sys_event.todo("sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx) Bypassing timeout for msmw2.sprx", equeue_id, dummy_event, timeout);
timeout = 1;
+1 -1
View File
@@ -370,7 +370,7 @@ error_code sys_event_flag_set(cpu_thread& cpu, u32 id, u64 bitptn)
{
s32 prio = smax;
ppu_thread* it{};
for (auto ppu = first; ppu; ppu = ppu->next_cpu)
{
if (!ppu->gpr[7] && (flag->protocol != SYS_SYNC_PRIORITY || ppu->prio <= prio))
+4 -69
View File
@@ -12,10 +12,8 @@
#include "Emu/IdManager.h"
#include "Emu/system_utils.hpp"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/RSX/Overlays/overlay_utils.h" // for ascii8_to_utf16
#include "Utilities/StrUtil.h"
#include <charconv>
#include <span>
LOG_CHANNEL(sys_fs);
@@ -2117,33 +2115,8 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
}
const cfg::device_info device = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, vpath);
std::tie(arg->vendorID, arg->productID) = device.get_usb_ids();
if (device.path.empty() || device.vid.empty() || device.pid.empty())
{
arg->out_code = CELL_ENOTSUP;
break;
}
u16 vid{};
{
auto [ptr, err] = std::from_chars(device.vid.data(), device.vid.data() + device.vid.size(), vid, 16);
if (err != std::errc())
{
fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message());
}
}
u16 pid{};
{
auto [ptr, err] = std::from_chars(device.pid.data(), device.pid.data() + device.pid.size(), pid, 16);
if (err != std::errc())
{
fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message());
}
}
arg->vendorID = vid;
arg->productID = pid;
arg->out_code = CELL_OK;
sys_fs.trace("sys_fs_fcntl(0xc0000015): found device '%s' (vid=0x%x, pid=0x%x)", vpath, arg->vendorID, arg->productID);
@@ -2192,49 +2165,11 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
}
const cfg::device_info device = g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, vpath);
if (device.path.empty() || device.vid.empty() || device.pid.empty())
{
arg->out_code = CELL_ENOTSUP;
break;
}
u16 vid{};
{
auto [ptr, err] = std::from_chars(device.vid.data(), device.vid.data() + device.vid.size(), vid, 16);
if (err != std::errc())
{
fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message());
}
}
u16 pid{};
{
auto [ptr, err] = std::from_chars(device.pid.data(), device.pid.data() + device.pid.size(), pid, 16);
if (err != std::errc())
{
fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message());
}
}
arg->vendorID = vid;
arg->productID = pid;
std::tie(arg->vendorID, arg->productID) = device.get_usb_ids();
// Serial needs to be encoded to utf-16 BE
const std::u16string serial = ascii8_to_utf16(device.serial);
ensure((serial.size() * sizeof(u16)) <= sizeof(arg->serial));
std::memset(arg->serial, 0, sizeof(arg->serial));
const auto write_byteswapped = [](const void* src, void* dst) -> void
{
*static_cast<u16*>(dst) = *static_cast<const be_t<u16>*>(src);
};
for (size_t i = 0; i < serial.size(); i++)
{
write_byteswapped(&serial[i], &arg->serial[i * 2]);
}
const std::u16string serial = utf8_to_utf16(device.serial);
std::copy_n(serial.begin(), std::min(serial.size(), sizeof(arg->serial) / sizeof(u16)), arg->serial);
arg->out_code = CELL_OK;
+3 -3
View File
@@ -214,7 +214,7 @@ public:
struct lv2_file final : lv2_fs_object
{
static constexpr u32 id_type = 1;
fs::file file;
const s32 mode;
const s32 flags;
@@ -303,7 +303,7 @@ struct lv2_file final : lv2_fs_object
struct lv2_dir final : lv2_fs_object
{
static constexpr u32 id_type = 2;
const std::vector<fs::dir_entry> entries;
// Current reading position
@@ -533,7 +533,7 @@ struct lv2_file_c000001c : lv2_file_op
be_t<u16> vendorID;
be_t<u16> productID;
be_t<u32> out_code; // set to 0
u8 serial[64];
be_t<u16> serial[32];
};
CHECK_SIZE(lv2_file_c000001c, 0x60);
+89 -20
View File
@@ -1,10 +1,10 @@
#include "stdafx.h"
#include "util/sysinfo.hpp"
#include "util/v128.hpp"
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include "Emu/system_utils.hpp"
#include "Emu/IdManager.h"
#include "Utilities/Thread.h"
@@ -12,6 +12,61 @@
LOG_CHANNEL(sys_game);
struct board_storage
{
public:
bool read(u8* buffer)
{
if (!buffer)
return false;
const auto data = storage.load();
memcpy(buffer, &data, size);
return true;
}
bool write(u8* buffer)
{
if (!buffer)
return false;
storage.store(read_from_ptr<be_t<v128>>(buffer));
written = true;
return true;
}
board_storage()
{
memset(&storage.raw(), -1, size);
if (fs::file file; file.open(file_path, fs::read))
file.read(&storage.raw(), std::min(file.size(), size));
}
board_storage(const board_storage&) = delete;
board_storage& operator =(const board_storage&) = delete;
~board_storage()
{
if (written)
{
if (fs::file file; file.open(file_path, fs::create + fs::write + fs::lock))
{
file.write(&storage.raw(), size);
file.trunc(size);
}
}
}
private:
atomic_be_t<v128> storage;
bool written = false;
const std::string file_path = rpcs3::utils::get_hdd1_dir() + "/caches/board_storage.bin";
static constexpr u64 size = sizeof(v128);
};
struct watchdog_t
{
struct alignas(8) control_t
@@ -159,30 +214,44 @@ u64 _sys_game_get_system_sw_version()
return stof(utils::get_firmware_version()) * 10000;
}
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer1, vm::ptr<u8> buffer2)
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer, vm::ptr<u8> status)
{
sys_game.trace("sys_game_board_storage_read(buffer1=*0x%x, buffer2=*0x%x)", buffer1, buffer2);
sys_game.trace("sys_game_board_storage_read(buffer=*0x%x, status=*0x%x)", buffer, status);
if (!buffer1)
if (!buffer || !status)
{
return CELL_EFAULT;
}
be_t<u64> psid[2] = { +g_cfg.sys.console_psid_high, +g_cfg.sys.console_psid_low };
u8* psid_bytes = reinterpret_cast<u8*>(psid);
u8 response[16] = { 0x01, 0xFC, 0x43, 0x50, 0xA7, 0x9B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
for (int i = 0; i < 16; i++)
{
response[i] ^= psid_bytes[i];
}
memcpy(buffer1.get_ptr(), response, 16);
if (!buffer2)
{
return CELL_EFAULT;
}
*buffer2 = 0x00;
*status = g_fxo->get<board_storage>().read(buffer.get_ptr()) ? 0x00 : 0xFF;
return CELL_OK;
}
error_code _sys_game_board_storage_write(vm::ptr<u8> buffer, vm::ptr<u8> status)
{
sys_game.trace("sys_game_board_storage_write(buffer=*0x%x, status=*0x%x)", buffer, status);
if (!buffer || !status)
{
return CELL_EFAULT;
}
*status = g_fxo->get<board_storage>().write(buffer.get_ptr()) ? 0x00 : 0xFF;
return CELL_OK;
}
error_code _sys_game_get_rtc_status(vm::ptr<s32> status)
{
sys_game.trace("sys_game_get_rtc_status(status=*0x%x)", status);
if (!status)
{
return CELL_EFAULT;
}
*status = 0;
return CELL_OK;
}
+3 -1
View File
@@ -6,4 +6,6 @@ error_code _sys_game_watchdog_start(u32 timeout);
error_code _sys_game_watchdog_stop();
error_code _sys_game_watchdog_clear();
u64 _sys_game_get_system_sw_version();
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer1, vm::ptr<u8> buffer2);
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer, vm::ptr<u8> status);
error_code _sys_game_board_storage_write(vm::ptr<u8> buffer, vm::ptr<u8> status);
error_code _sys_game_get_rtc_status(vm::ptr<s32> status);
+4 -9
View File
@@ -35,7 +35,7 @@ error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr<u32> handl
if (device_type == 1)
{
cellPadInit(7);
cellPadSetPortSetting(port_no /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON);
cellPadSetPortSetting(port_no /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_ON);
}
return CELL_OK;
@@ -110,8 +110,6 @@ error_code sys_hid_manager_513(u64 a1, u64 a2, vm::ptr<void> buf, u64 buf_size)
error_code sys_hid_manager_514(u32 pkg_id, vm::ptr<void> buf, u64 buf_size)
{
sys_hid.todo("sys_hid_manager_514(pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", pkg_id, buf, buf_size);
if (pkg_id == 0xE)
{
sys_hid.trace("sys_hid_manager_514(pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", pkg_id, buf, buf_size);
@@ -161,10 +159,11 @@ error_code sys_hid_manager_read(u32 handle, u32 pkg_id, vm::ptr<void> buf, u64 b
return CELL_EFAULT;
}
(pkg_id == 2 || pkg_id == 0x81 ? sys_hid.trace : sys_hid.todo)
("sys_hid_manager_read(handle=0x%x, pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", handle, pkg_id, buf, buf_size);
if (pkg_id == 2)
{
sys_hid.trace("sys_hid_manager_read(handle=0x%x, pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", handle, pkg_id, buf, buf_size);
// cellPadGetData
// it returns just button array from 'CellPadData'
//auto data = vm::static_ptr_cast<u16[64]>(buf);
@@ -179,8 +178,6 @@ error_code sys_hid_manager_read(u32 handle, u32 pkg_id, vm::ptr<void> buf, u64 b
}
else if (pkg_id == 0x81)
{
sys_hid.trace("sys_hid_manager_read(handle=0x%x, pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", handle, pkg_id, buf, buf_size);
// cellPadGetDataExtra?
vm::var<CellPadData> tmpData;
if ((cellPadGetData(0, +tmpData) == CELL_OK) && tmpData->len > 0)
@@ -191,7 +188,5 @@ error_code sys_hid_manager_read(u32 handle, u32 pkg_id, vm::ptr<void> buf, u64 b
}
}
sys_hid.todo("sys_hid_manager_read(handle=0x%x, pkg_id=0x%x, buf=*0x%x, buf_size=0x%llx)", handle, pkg_id, buf, buf_size);
return CELL_OK;
}
+1 -1
View File
@@ -268,7 +268,7 @@ error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
auto sq = cond.sq;
atomic_storage<ppu_thread*>::release(cond.sq, nullptr);
while (const auto cpu = cond.schedule<ppu_thread>(sq, cond.protocol))
{
if (mode == 2)

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