Compare commits

...
609 Commits
Author SHA1 Message Date
Frog Business 2ba2f75bce Wiimote to GunCon3 (#18154)
So I could play Deadstorm Pirates 2 players with DolphinBar + 2 Wiimotes
99% of this is AI assisted development.
I reviewed the code and the functionality.
- There is this logic where you might find it weird.
It maps first available player with GunCon3 to slot 0.
So if you have P4:G and P6:G, you'll have P4:0, P6:1
This is intentional because I couldn't figure out why P2 is reserved and
I needed to map to P1 and P2.
- Has a GUI so you can see your Wiimotes and remap them.
- Enabling PS Move cursor overlay will also show GunCon3 IRs on the
screen.

### NOTE

On Linux you'll need udev rules (same as Dolphin) for this to work. 
Put [THIS
FILE](https://github.com/dolphin-emu/dolphin/blob/master/Data/51-usb-device.rules)
in `/etc/udev/rules.d/`
2026-10-11 10:01:57 +00:00
Megamouse 26098fb42d Qt: Always detect app icon based on emulator variables 2026-10-11 10:56:06 +02:00
Sean Brar d1d0cbccea Savestates: Fix saving in-memory file descriptors 2026-10-11 09:55:22 +03:00
yahfz 7b4de2f13d PPU Loader: Skip ppc_seg mapping during virtual loads 2026-10-11 01:42:04 +03:00
Der_Floh dfc486fc6b Skylanders Portal IPC Server 2026-10-10 19:58:42 +00:00
izcarti 6a900d00e6 PPU Interpreter: Fix VSCR element in MFVSCR and MTVSCR
VSCR is the last big-endian word, stored in _u32[0], as the LLVM recompiler uses.
2026-10-10 22:32:11 +03:00
izcarti 31bd702e39 PPU: Add accurate vector estimates option
vrefp and vrsqrtefp are hardware exact only when enabled.
vexptefp and vlogefp are now exact by default.
2026-10-10 22:06:19 +03:00
izcarti 63ad856afa PPU LLVM: Estimate a whole vector per call 2026-10-10 22:06:19 +03:00
izcarti cb450d7e8f PPU: Hardware exact VMX estimates
vrefp, vrsqrtefp, vexptefp and vlogefp return the Cell's estimates instead
of exact results, matched bit for bit against a real PS3 with VSCR[NJ] set
and clear.
2026-10-10 22:06:19 +03:00
Max Gautier 68e0d6021c Include udev/devd rules in Linux/FreeBSD install
This allows RPCS3 to communicate with Dualshock 3,4 and DualSense
controller out of the box on Linux distributions and FreeBSD, without
the need for manual actions.
2026-10-10 19:56:54 +03:00
digant73andClaude Opus 5.5 10adf2ec39 rsx: Split the vertex interpreter source into two string literals
The single raw string literal exceeded the MSVC string literal size limit
(error C2026: string too big, trailing characters truncated).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-10 18:41:28 +03:00
Megamouse 987129bc91 Fix some warnings 2026-10-10 12:32:17 +02:00
Megamouse b195c2ff1b cellMusicDecode: fix deadlock and make members private 2026-10-10 12:32:17 +02:00
Malcolm f9b8c209a3 BufferUtils: Address reviews
-drop iota16, remove avx2 check, better reduction
2026-10-10 12:40:20 +03:00
Malcolm cd4d4e02ec BufferUtils: Optimize restart removal with SVE and SVE2.2 2026-10-10 12:40:20 +03:00
Malcolm d21c0d808d BufferUtils: Add table-based NEON and AVX2 compaction 2026-10-10 12:40:20 +03:00
Malcolm 52343fa2f3 BufferUtils: Add native NEON upload paths 2026-10-10 12:40:20 +03:00
Antonino Di Guardo ff91404b7c Qt: Fix race condition in qt_video_source_wrapper (#19774) 2026-10-10 08:15:18 +00:00
kd-11 e38d27157e rsx/vk: Implement rendering using no attachments 2026-10-10 10:30:35 +03:00
kd-11 0be621eb62 vk: Add support for attachment-less renderpasses 2026-10-10 10:30:35 +03:00
kd-11 78f27eaad6 rsx: Skip framebufferless draws if we have no side-effects 2026-10-10 10:30:35 +03:00
kd-11 66ba732829 rsx/gl: Implement support for framebuffer-less rendering 2026-10-10 10:30:35 +03:00
Liam 8f2fe1a99c rsx: Emulate float depth range on hosts without extended depth range support 2026-10-09 15:43:15 +00:00
kd-11 0be6d12140 vk: Remove outdated warning message about D16F being unsupported 2026-10-09 12:38:31 +03:00
kd-11 d6295528d7 rsx: Ensure F32->D24 conversions correctly round to nearest to match real hardware 2026-10-09 12:38:31 +03:00
kd-11 510f4c243e rsx: Fix depth_bounds range computation 2026-10-09 12:38:31 +03:00
kd-11 ffa4c71088 rsx: Fix DEPTH_FLOAT decoding for clear values 2026-10-09 12:38:31 +03:00
kd-11 5e89a0061e rsx: Update D16F pack/unpack logic to match hardware tests
- The format is insane. Bias is 16, not 7
- This means values outside [0,1] are impossible to encode
2026-10-09 12:38:31 +03:00
kd-11 b26e0cfa91 vk: Pause the driver manager routine when capturing media
- These are out-of-band events and should not be treated as 'real' sync points.
2026-10-09 12:38:31 +03:00
kd-11 33b2958da5 gl: Fixes for depth format upload and download 2026-10-09 12:38:31 +03:00
kd-11 cfd03fc026 vk: Fix edge cases in DEPTH format transfer logic
- Lots of edge cases and wrong sizes used for barriers
- Incorrect e4m12 conversion logic caused readback errors
2026-10-09 12:38:31 +03:00
kd-11 7c572eb4dd gl/vk: Correctly halve the invocation count when shrinking F32 to F16 2026-10-09 12:38:31 +03:00
kd-11 4d4c07c18a vk: Ensure input attachments are in uncompressed layouts 2026-10-09 12:38:31 +03:00
digant73 4c7d08582a overlays: Fix missing symbol glyphs (e.g. ★) in native UI on Windows 2026-10-09 07:52:44 +02:00
kd-11 304d544bc1 ci: Skip cache load step if PR is marked with 'CI: Disable Build Caches' label 2026-10-08 23:07:27 +03:00
kd-11 e4694a7a2c rsx/vp: Adjust constant access behavior to match real hardware
- Real hardware behavior for constants table access is complicated.
1. If you try to encode access of c[N] where N > 467 you read back 0 in the shader
2. If you try to bypass the limitation using ARL then the index wraps around 512.
3. Using the ARL bypass between 468 and 512 returns garbage.
2026-10-08 23:07:27 +03:00
kd-11 7f476dad89 rsx: Rework transform constant update logic
- This reverts commit 193975556d.
- Out of bounds writes are treated as NOP, same as real hardware
2026-10-08 23:07:27 +03:00
kd-11 222754bfd3 rsx: Use framebuffer aliasing bias to break ties between same-tag color and depth overlaps 2026-10-08 10:15:36 +03:00
Acts1631 d064a83d11 Keep title IDs within host path components
Crafted title IDs could escape serial-derived directories and redirect
recursive cache cleanup outside RPCS3's cache root. Reject structural
path components, escape identifiers used as host names, and make
cleanup matching literal and exact.
2026-10-08 08:02:13 +03:00
digant73 8ffc512beb Remove duplicate in emucore.vcxproj.filters 2026-10-07 20:25:47 +02:00
Antonino Di GuardoandClaude Opus 5.5 2bf8677d3c cellSearch: Put the whole search into the music selection context (#19750)
Analysis and fixes made by Opus 5.5.

## Bug
`cellSearchGetMusicSelectionContext` only put the selected (or a random)
track into the context, even when the search contained many tracks.
Games that pick custom music through cellSearch got a one-track
playlist, so with repeat ALL the same song played on loop forever.

## Fix
- For a search of single tracks the playlist now contains every track of
the search, and `first_track` / `current_track` point to the selected
one (random when shuffled, otherwise the first).
- `music_selection_context::find_content_id` starts from the selected
track instead of the first entry of the playlist.
- `audio_decoder` keeps the selected track first when shuffling and only
shuffles the rest.

Tested with NHL 14.

Fixes #14738
Part of #18779, #11596

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-07 17:41:28 +00:00
Yahfz ab412d5ddb rsx: Fix shader compilation (FP16 round_to_8bit) at low setting 2026-10-07 18:43:26 +03:00
Acts1631 e97200a884 Reject invalid RPCN message types
RPCN forwards MessageDetails from other users. The protobuf uint16
wrappers can carry 32-bit values, and narrowing them on the recipient
aborts RPCS3.

Validate both values before casting so malformed messages are
discarded.
2026-10-07 17:30:37 +03:00
Acts1631 cb63cc82ca Validate RPCN room message fields
RPCN room peers control message payloads and destination lists. Values
above the Matching2 limits overflow the recipient's fixed event buffer
and terminate the NP handler.

Reject malformed room message notifications before allocating event
data.
2026-10-07 16:17:15 +03:00
Acts1631 5f8dd1de61 Reject invalid signaling RTT timestamps
Signaling peers control the timestamps used for RTT calculation. A
future timestamp wraps the unsigned delta, and the fatal checked cast
terminates the signaling thread.

Reject future and oversized RTT values before updating peer state.
2026-10-07 15:19:45 +03:00
Megamouse 479ca74cad Update 7zip to 26.04 2026-10-07 08:20:07 +02:00
digant73andClaude Opus 5.5 925fbb2607 media_utils: Flush the audio decoder and resampler at the end of a track
The resampler buffers part of its input whenever the file's sample rate differs from the 48 kHz output,
because the output buffer was only sized for the frame's own samples. These samples piled up and were
never flushed, so a 44.1 kHz track came out 8.125% short (5-20 s cut from the end of every song).
The decoder itself was never drained either.

- Size the output for the pending resampler samples too and only append what swr_convert actually wrote.
- Flush the decoder and the resampler once the last packet has been read.
- Reserve the whole track up front, reuse the resampler output buffer and byteswap in place before
  appending, instead of growing the track buffer and allocating a buffer for every frame.

Fixes #18930 (custom music cut in Gran Turismo 5).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 07:31:29 +02:00
Megamouse 55a3aff337 gl: fix screenshots and recordings 2026-10-06 20:53:00 +02:00
brian218 12178de458 QT: Made HDD Model Name & HDD Serial Number settings accessible via GUI 2026-10-06 19:00:35 +02:00
Antonino Di Guardo 8e4659a6cb LV2: Fix sys_ppu_thread_start gate freezing PPU threads (#19707) 2026-10-06 15:59:20 +00:00
Antonino Di Guardo 532b6520b4 Add IRD based integrity check (#19421) 2026-10-06 10:22:13 +00:00
Whatcookie 1b90d1c149 rsx: Optimize recursion in the vertex program analyzer's instruction walk
Avoid use of std::function for recursion, and ensure that std::stack is backed by a vector, instead of the default deque.
2026-10-06 10:00:59 +00:00
brian218 c7642cb875 USIO: Eliminated frequent buffer memory reallocation to optimize memory performance 2026-10-06 12:31:13 +03:00
Elad 4dfdda9789 sys_net/Win32: Fix ECONNRESET on UDP socket 2026-10-06 11:59:40 +03:00
Elad 191932b854 Fix RSX capture memory allocations 2026-10-06 11:59:40 +03:00
Elad b1e8e74619 vm: Fix stack pages 2026-10-06 10:56:17 +03:00
Malcolm 183597deea RSX: use cache-line waits for event queue draining 2026-10-06 09:03:01 +02:00
brian218 2ff18d0fae QT: Added USIO Emulated Mode options to Settings GUI 2026-10-06 08:12:31 +02:00
Elad 3e7477107a Update contributors 2026-10-05 21:46:59 +03:00
Elad 7877ebe43d Fix RSX Debugger 2026-10-05 21:46:59 +03:00
Megamouse 30ee6cc358 Add brian218 to contributors 2026-10-05 20:07:01 +03:00
Megamouse 3db7c53c67 vm: fix fill64 2026-10-05 20:07:01 +03:00
Megamouse 70c8683ae2 usio: add proper mapping 2026-10-05 18:02:29 +02:00
brian218 b240813958 USIO: Made gun handler config player-specific in usio_config 2026-10-05 18:02:29 +02:00
brian218 c9fe397116 USIO & Pad: Added the ability to retrieve angle from analog sticks 2026-10-05 18:02:29 +02:00
brian218 2cc650538d USIO: Added support for analog stick input for crosshair 2026-10-05 18:02:29 +02:00
Megamouse b444d6ad24 qt: add value labels to sliders in pad settings 2026-10-05 17:08:02 +02:00
schm1dtmac ba4a4b561e [Mac OS X CI] Fixup for #19684, missed a path ugh 2026-10-05 11:16:30 +02:00
Antonino Di Guardo 7c3d7c7a23 Fix check on game data path (cellGameData API) 2026-10-05 08:52:32 +00:00
yahfz 52de29af04 vk: Don't use rebar for the vertex layout ring 2026-10-05 08:58:41 +03:00
yahfz 43a42d1a86 rsx: Fix FIFO reads while waiting for PUT 2026-10-05 01:44:55 +03:00
Elad 7b99edcfd6 rsx/savestates: Fix FIFO_control::restore_state() 2026-10-05 01:10:48 +03:00
Elad 7b7983ca1a vm/savestates: Rewrite and fix stack_guarded 2026-10-05 01:10:48 +03:00
digant73 a72c4c319b sys_fs: Add an option to emulate the BD-ROM read speed 2026-10-04 22:44:28 +03:00
Antonino Di Guardo fcbed0bc6d cellVdec: Fix decoding of streams without a leading sequence header and of reused AU buffers- #19658 2026-10-04 17:45:05 +00:00
kd-11 32b54f1864 rsx/vp: Process TXL input for potential ATTR references 2026-10-04 20:05:58 +03:00
Ani 193975556d Revert "rsx: Add bounds checking to transform constant loading"
This reverts commit 373d0ea7f0.
2026-10-04 18:16:56 +02:00
IdanandClaude Sonnet 5 d9608bf0f0 settings_dialog: fix Ctrl+Tab only cycling tabs once
Qt's QTabWidget only intercepts Ctrl+Tab/Ctrl+Shift+Tab while the tab
widget itself has keyboard focus. The dialog's own currentChanged
handler moves focus to the Close button after every tab switch, and
clicking into a tab's content moves focus to a child widget, so the
shortcut stopped working after the first switch.

Add explicit window-scoped shortcuts that cycle the tab index
directly, so Ctrl+Tab/Ctrl+Shift+Tab keep working regardless of which
widget currently has focus.

Fixes #19425

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-10-04 18:19:03 +03:00
Megamouse 46aee28f85 Remove log message and update comment 2026-10-04 08:54:12 +02:00
Elad 998298f9a8 Fix cellGemSetYaw's function unique signature (#19696)
This is the only known function that passes arguments in vector
registers.
Although we had an implementation for it, it was never used before.
Hopefully it works.
Attempting to fix "invalid" argument passed to cellGemSetYaw due to
wrong register decoding.
2026-10-04 08:20:42 +02:00
Megamouse c673da45f8 cellGem: Implement cellGemSetYaw (well, at least make it work for calibration) 2026-10-03 22:36:20 +02:00
Megamouse 1ce71092c8 Qt: Fix clearing mappings with rightclick on buttons that live in a groupbox with event filters 2026-10-03 21:55:44 +02:00
Megamouse 0d866154aa ps move: only try to reconnect once per second 2026-10-03 19:35:09 +02:00
Megamouse 0374854c80 Apply FusionBias 2026-10-03 19:35:09 +02:00
Megamouse f5edadb2ef ps move: add drift correction 2026-10-03 19:35:09 +02:00
Megamouse 103d41944c ps move: Only update ahrs orientation if a new input report arrived and handle ZCM1 half frames 2026-10-03 19:35:09 +02:00
Megamouse a664f405bc ps move: fix orientation mapping 2026-10-03 19:35:09 +02:00
Megamouse 5da9f4ab32 ps move: improve sample period 2026-10-03 19:35:09 +02:00
kd-11 db7d845e09 rsx: Implement optimized variants of iota16 and iota32
- Upto 20x faster than std::iota on msvc
- 1-3x bump for GCC and clang
2026-10-03 18:27:05 +03:00
yahfz e68ae2d051 RSX: Use 32-bit generated indices 2026-10-03 18:27:05 +03:00
Megamouse 1814fefb95 Update SDL to 3.4.18 2026-10-03 16:47:46 +02:00
Megamouse a1dc53fdf2 cellGem/Mouse: use float for x_pos and y_pos 2026-10-03 15:20:15 +02:00
Megamouse 0ca1c58e75 ps move: fix zcm2 calibration dump logging 2026-10-03 12:44:40 +02:00
Megamouse 8e245a6e15 ps move: ignore wrong input reports 2026-10-03 12:44:40 +02:00
Megamouse df7964560b ps move: make sure to read all calibration blocks 2026-10-03 12:44:40 +02:00
Megamouse 7d69855fbb ps move: fix accelerator typos in calibration 2026-10-03 12:44:40 +02:00
Megamouse 2f5c31155a ps move: mark garbage calibration as invalid and use default values 2026-10-03 12:44:40 +02:00
Megamouse 40da906c91 ps move: Ignore invalid ahrs data 2026-10-03 12:44:40 +02:00
Megamouse 5298e3b7a1 ps move: always apply calibration. we skipped that part if orientation was disabled (although you shouldn't disable it in the first place) 2026-10-03 12:44:40 +02:00
brian218 e8b4646145 USIO: Minor fixups for mouse input 2026-10-03 10:31:16 +02:00
schm1dtmac d5213de6dd [Mac OS X CI] Fix translations, Qt 6.12 changed the path 2026-10-02 23:23:02 +02:00
Yahfzandkd-11 b1f51cfaa6 rsx: Check Z-buffer dimensions when comparing framebuffer layouts
Co-authored-by: kd-11 <[email protected]>
2026-10-02 18:47:50 +00:00
kd-11 b7f6f4e80f rsx/fifo: Revert change in strict FIFO behavior when using atomic puller 2026-10-02 21:28:25 +03:00
kd-11 1c757181a0 rsx/lv2: Make the hw cursor resolution-aware 2026-10-02 19:56:35 +03:00
kd-11 5d8efbfeb9 rsx/lv2: Implement hardware bitmap cursor 2026-10-02 19:56:35 +03:00
kd-11 dd12be585e rsx/overlays: Use std::span in place of std::vector to allow arbitrary memory reads 2026-10-02 19:56:35 +03:00
kd-11 fc746de37d rsx/lv2: Document cellGcmCursor callbacks 2026-10-02 19:56:35 +03:00
yahfz f2b7edff27 acverdict 2026-10-02 18:42:30 +03:00
kd-11 18f18b305f gl/vk: Accept single-index draws for POINT rendering 2026-10-02 13:33:48 +03:00
kd-11 d10658b818 gl: Fix 2-sided stencil configuration caching 2026-10-02 13:33:48 +03:00
kd-11 b87a56979e vk/gl: Allow cached resources with upto 512 depth slices 2026-10-02 13:33:48 +03:00
kd-11 5df3970a75 vk: Fix leaky ref-counts for reused query pools (unused quirk) 2026-10-02 13:33:48 +03:00
kd-11 a3409242ce gl: Fix texture layout for RGB5A1 and BGR5A1 surfaces for readback/upload 2026-10-02 13:33:48 +03:00
kd-11 54ab16913e vk: Fix WAW barrier when clearing the WSI present surface 2026-10-02 13:33:48 +03:00
kd-11 2ccc4983fa gl: Fix partial clear of depth-only image (z16) when the DSV is dirty 2026-10-02 13:33:48 +03:00
kd-11 5741d891bb vk: Only request UAB-compatible set layouts if the bindings require it 2026-10-02 13:33:48 +03:00
kd-11 4dce4ecbe5 gl: Use proper custom slot for polygon offset state caching 2026-10-02 13:33:48 +03:00
kd-11 a7e274234f rsx/mtrsx: Fix fault range of emulated index expansion 2026-10-02 13:33:48 +03:00
kd-11 f1574d7060 rsx/nv3089: Clamp input height to 1 row
- Prevent underflow when calculating addresses. which can discard valid blit requests
2026-10-02 13:33:48 +03:00
kd-11 08b226b8f8 rsx/zcull: Fix find_query behavior
- Use umax instead of 0 to mean not-found.
2026-10-02 13:33:48 +03:00
kd-11 373d0ea7f0 rsx: Add bounds checking to transform constant loading 2026-10-02 13:33:48 +03:00
kd-11 babeda0ed2 rsx: Fix typo when replaying a constant_load_modifier_barrier 2026-10-02 13:33:48 +03:00
kd-11 c3f3529b98 rsx: Reset color write mask when re-evaluating color masks 2026-10-02 13:33:48 +03:00
brian218 96ccd89cd6 USIO: Added mouse and key/pad input support for shooter games 2026-10-02 11:13:40 +02:00
Megamouse 0850e0ff5c Update Qt to 6.12.0 2026-10-02 10:02:49 +02:00
Elad a7cbd46749 rsx: Debug commit 2026-10-02 04:56:21 +03:00
Elad 0e0cdc7c29 rsx: Make the initial GET update seq-cst 2026-10-02 04:56:21 +03:00
Elad acb249f057 rsx: Fix GET updates in NOP cmd 2026-10-02 04:56:21 +03:00
Elad a3dc7e6af4 rsx: Fix FIFO GET updates (Atomic FIFO exclusive fix) 2026-10-02 04:56:21 +03:00
Elad 982337435a rsx: Fix FIFO GET ordering after NV4097/NV406E-REF labels release 2026-10-02 04:56:21 +03:00
Elad cdc04e0d59 Fix _cellGcmInitBody 2026-10-02 04:56:21 +03:00
Elad 7aa2422a07 Fix RSX Debugger 2026-10-02 04:56:21 +03:00
Megamouse 17320679e3 VS: disable ZSTD in curl 2026-10-01 22:35:46 +02:00
Megamouse 1dd70e036e cellGem: fix cellGemHSVtoRGB 2026-10-01 22:35:46 +02:00
Megamouse 53d9522073 cellGem: Fix handle offset orientation 2026-10-01 21:18:49 +02:00
Megamouse 3b3c7f4f07 Fix infinite recursion when Emu.GetDir is /app_home/ 2026-10-01 21:00:18 +03:00
Megamouse a5515d2371 Update FAudio to 26.10 2026-10-01 18:28:25 +02:00
Antonino Di Guardo dfc0542a9f savedata: scoped filesystem sync 2026-10-01 13:38:26 +00:00
Vishrut Sachan 4d88114c92 ci: Auto-label PRs from title and linked issues 2026-10-01 15:09:35 +03:00
RipleyTom 81cbf7ba3d fix: various bugfixes
- sys_net: align sendmsg behaviour to sendto's
- np: ensure TUS info size fits into buffer
- signaling: retire all packets on timeout
2026-10-01 14:42:27 +03:00
Megamouse 83b1e07299 Fix some log messages 2026-10-01 12:42:01 +02:00
Megamouse 8307c54cd4 cellCamera: handle nullptr in cellCameraReadEx 2026-10-01 12:42:01 +02:00
Megamouse 805c931d49 cellGem: fix racing wheel mapping 2026-10-01 12:42:01 +02:00
Megamouse be69c054ee cellVoice: do not allow more than CELLVOICE_MAX_PORT ports 2026-10-01 12:25:46 +03:00
Megamouse d98d378704 cellAudioInGetDeviceInfo: fix error value 2026-10-01 12:25:46 +03:00
Megamouse d9a59820ba cellSearch: fix tag checks 2026-10-01 12:25:46 +03:00
Megamouse a1c6b12311 cellSearch: fix video content memcpy 2026-10-01 12:25:46 +03:00
Megamouse a3232fddee Update VulkanMemoryAllocator to 3.4.0 2026-10-01 10:52:00 +02:00
Megamouse 71d1a4527d _sys_ppu_thread_create: fix memory leak on error 2026-10-01 10:49:10 +03:00
Megamouse 5b350ff3e1 RawSpu: only work mmio stats when spu_log is trace 2026-10-01 09:12:56 +03:00
Megamouse 1c460fdd7f input: Fix SDL reconnect: Fix SDL open/close reference count and only try once per second 2026-10-01 07:43:05 +02:00
Ani 53d44aa4a1 rpcs3_version: Bump to 0.0.43 2026-10-01 00:11:48 +02:00
Antonino Di Guardo d42fc3dc41 LV2: Let the caller leave sys_ppu_thread_start before a lower-priorit… (#19655) 2026-10-01 00:21:49 +03:00
yahfz 1331e307ce rsx: Reuse texture views for remap changes 2026-09-30 21:00:45 +02:00
yahfz 058b5fb556 gt5 2026-09-30 21:00:45 +02:00
Megamouse 7b1d4f98ef Remaining fixes 2026-09-30 20:13:01 +02:00
yahfz fd818e7016 vk: Complete pending unresolve before feedback loops 2026-09-30 19:51:13 +02:00
RipleyTom 431b169358 fix: don't put 0 sized data packets in received_data 2026-09-30 18:29:21 +02:00
DevX-Cipher 38d27036f9 Building on windows made easier (#19507) 2026-09-30 16:08:29 +00:00
Megamouse da0dc28841 rsx: use std::span and const ref 2026-09-30 16:33:05 +02:00
Megamouse a8fabaf570 cellVdec: cache alpha channel per thread and only fill it on change 2026-09-30 16:33:05 +02:00
Antonino Di Guardo 1fe12b7778 Rename Close to Cancel button on some panels (#19468) 2026-09-30 13:50:36 +00:00
Megamouse 276ead3cef elf: Fix offset in case of fixup_shdrs 2026-09-30 16:05:46 +03:00
Megamouse a321a0a18d unself: fix iv_idx range checks 2026-09-30 16:05:46 +03:00
Megamouse 96bb9740bf rsx: fix on_notify_pre_memory_unmapped. eaAddress[ea] was set to null instead of ioAddress (typo ?) 2026-09-30 14:07:29 +02:00
Megamouse db90035c49 texture_cache: fix hash calculation 2026-09-30 14:07:29 +02:00
Megamouse 2ccdb73a31 rsx_cache: throw exception on OOB write to vp_jump_table 2026-09-30 14:07:29 +02:00
digant73 7390f3dd9a Fix FMV audio crackling with Audio Buffering enabled 2026-09-30 14:31:59 +03:00
Malcolm f0d2485981 CMake: Add LSE2 option 2026-09-30 13:46:49 +03:00
Malcolm be39b3b9f0 atomic: Detect LSE2 at runtime on ARM64 2026-09-30 13:46:49 +03:00
Antonino Di Guardo 80567b686b cellAudio: Acknowledge suspend_all while waiting for the audio mutex (#19607) 2026-09-30 10:05:37 +00:00
Megamouse fb4a09215a vm: fix endless loop in map_self if map returns nullptr 2026-09-30 12:06:39 +03:00
Megamouse dcbc44a399 cellGcmSys: Do not read past end in cellGcmMapMainMemory 2026-09-30 12:06:39 +03:00
Megamouse 2c961d0614 sceNpTrophy: limit count to 128 in sceNpTrophyGetTrophyUnlockState to prevent OOB in loop below 2026-09-30 12:06:39 +03:00
Megamouse 93eefd3820 cellImeJp: actually write all candidate chars to dst buffer 2026-09-30 12:06:39 +03:00
Megamouse 5753d99db1 cellHttpUtil: fix off by one in cellHttpUtilEscapeUri (looped for inSize+1) 2026-09-30 12:06:39 +03:00
Megamouse b4e2e9aae6 usb: fix OOB reads and throw exception on infinite loop in read_descriptors 2026-09-30 12:06:39 +03:00
Megamouse 38c6b90d76 usb: throw exception on OOB packet r/w 2026-09-30 12:06:39 +03:00
Megamouse cf6c06eb66 sys_fs: fix potential OOB write 2026-09-30 12:06:39 +03:00
Megamouse ad7c7a28aa cli: fix emu_argv propagation 2026-09-30 11:35:06 +03:00
Megamouse 00fe384258 fs::get_config_dir: set size to 0 on GetEnvironmentVariable failure 2026-09-30 11:35:06 +03:00
Megamouse 19ae2e9b23 overlays: Fix warning (font_name is assigned a value that is only valid in the if scope) 2026-09-30 08:48:19 +02:00
Megamouse 488c71dfd7 save_manager_dialog: update icons if any row visibility changes (could be negated before) 2026-09-30 08:48:19 +02:00
Megamouse 634c76f0bc g27 settings: break loop if mapping is not in progress anymore 2026-09-30 08:48:19 +02:00
Megamouse 622771ec00 media encoder: ignore decoders in av_codec_iterate 2026-09-30 08:48:19 +02:00
Megamouse 5e4d17e198 kernel_explorer: fix width and height in fmt::format 2026-09-30 08:48:19 +02:00
Megamouse 2ab2c23462 rsx_debugger: fix wrong pitch used for dst buffer in g8b8 mode 2026-09-30 08:48:19 +02:00
Megamouse 3363a48127 overlays: fix OOB in settings dropdown sync 2026-09-30 08:48:19 +02:00
Megamouse 7ae033756a debugger: exit loop deletion loop properly if m_item_count is 0 2026-09-30 08:48:19 +02:00
Megamouse f97106bdc2 gdb: handle EOF in cmd_write_memory 2026-09-30 08:48:19 +02:00
Megamouse 1cf89b1bda perf_meter: fix bucket logging 2026-09-30 09:25:06 +03:00
Megamouse 2d1ce572be Remove unused slow_mutex 2026-09-30 08:44:14 +03:00
Megamouse b69f207406 serialization: fix lv2_dir 2026-09-30 08:44:14 +03:00
Megamouse cefa1285a8 GUI: Fix claimed_paths push after game list refresh 2026-09-30 04:27:49 +02:00
RipleyTom c96c2099b7 fix: make p2ps msgs multimap & set min rtt_time to 1ms 2026-09-30 00:40:12 +03:00
Megamouse 9d52b33805 overlays: refactor fonts 2026-09-29 21:00:42 +02:00
Megamouse c68b528a73 VS: Fix some warnings 2026-09-29 21:00:42 +02:00
Megamouse 479d5c9c9e cellVdec: fix some VS warnings 2026-09-29 21:00:42 +02:00
Megamouse 8d501fb9d4 rsx: tidy up some includes 2026-09-29 21:00:42 +02:00
Ani c5d6b85c4c Update VulkanMemoryAllocator to 3.4.0 2026-09-29 20:00:28 +02:00
Yahfz 7f2e02671e Add option to emulate PS3 HDD read speed (#19593)
Fixes crashes in Armored Core Verdict and Armored Core V. 

RPCS3 HDD reads were finishing much faster than on PS3, so emulating
their timing gives the audio worker time to free old sound banks before
the new ones exhaust the heap.
2026-09-29 16:52:19 +00:00
FlexBy420 3e9d881aa6 Minor fixes to old steam theme (#19560) 2026-09-29 14:24:56 +00:00
schm1dtmac 615f529e65 [Mac OS X] Force enable LSE2 on Apple AArch64 as a workaround for missing macros.
AppleClang defines the macros noted in types.hpp, but not LLVM clang for some obnoxious reason.
2026-09-29 15:01:34 +02:00
Ani d9a72d6ca7 gui: General UX pass on the Settings Dialog (#19604)
### Remove 
- gui: Remove Debug "Approximate PPU Vector NaN Handling"
- gui: Remove I/O "Load SDL GameController Mappings"
- gui: Remove Advanced "VBlank NTSC Fixup", enable by default
- gui: Remove Debug "Accurate DFMA"

### Move
- gui: Move Advanced "Enable SPU Events Busy Loop" to Debug
- gui: Move Advanced "Set DAZ and FTZ" to Debug
- gui: Move Audio Device to the left column in Audio

### Group
- gui: Add "Savestates" section in Advanced

### Order
- gui: Alphabetically order the Advanced Tab
- gui: Alphabetically order the I/O tab

### Visibility
- gui: Hide formats list when Audio Format is not Manual
- gui: Hide 3D settings if GPU "Enable 3D Support" is disabled
- gui: Hide RCAS if GPU "Output Scaling" is not FSR1
2026-09-29 10:25:30 +00:00
Megamouse 56fabd9760 Update libpng to 1.6.59 2026-09-29 11:26:43 +02:00
Antonino Di GuardoandClaude Opus 5.5 58b3505593 Qt: Keep the game window fullscreen when it is shown again (#19541)
Fixes #18381

### Bug:
`gs_frame::show()` went through `QWindow::show()`. On desktop platforms
`QPlatformIntegration::defaultWindowState()` is `Qt::WindowNoState`, so
that always routes through `showNormal()`, which pushes
`Qt::WindowNoState` to the native window before the startup visibility
is applied. On a window that is already fullscreen this is a real "leave
fullscreen" request immediately followed by an "enter fullscreen" one,
and a window manager is free to resolve that pair as windowed.
`QXcbWindow::setWindowState()` returns early when the requested state
matches the current one, so the first call of a session is a no-op, but
a later one on a fullscreen window is not, and
`QWaylandWindow::setWindowState()` forwards unconditionally.

Only the OpenGL frame runs into this. `gl_gs_frame` shows itself from
its constructor so that wayland-egl has a mapped surface before the
context is created (c7b2a21f), so by the time `gs_frame::flip()` shows
the frame again on the first flip, the window is already fullscreen.
Vulkan and Null only ever show the frame on that first flip, which is
why they are fine. It also explains why this never reproduced on
Windows: `ShowWindow()` applies both states synchronously and the second
one wins.

### TO BE TESTED ON LINUX
Not runtime tested yet: it needs a run on X11 and on Wayland with an
OpenGL game and "Start games in fullscreen mode" enabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-09-29 08:32:23 +00:00
Antonino Di Guardo 52616e0f10 Display ISO key mismatch (#19471)
Display an error message when booting a game from an encrypted ISO file
/ BD Drive in case:
- no decryption key file found
- wrong decryption key file provided (e.g. with key of another game)

Further, code improved to skip a key file for an ISO file recognized as
decrypted (avoid to decrypt (to corrupt) the ISO file with a wrongly
provided key).
2026-09-29 09:52:55 +02:00
kd-11 6fc79f7ccf Revert "rsx: keep depth buffers for zpass queries"
This reverts commit 2d293d8e5a.
2026-09-29 07:55:59 +03:00
digant73 1707d7fc88 remove unnecessary #ifdef 2026-09-28 22:18:44 +02:00
digant73 5c1ce9cbef cellVdec: Report the userdata of the AU each picture was decoded from 2026-09-28 22:18:44 +02:00
Yahfzandkd-11 2d293d8e5a rsx: keep depth buffers for zpass queries
ZPASS_COUNT is an observable side-effect of Z-buffer access, despite Z-TEST and Z-WRITE being disabled. Technically, this is a pure Z-READ operation.

Co-authored-by: kd-11 <[email protected]>
2026-09-28 17:20:17 +00:00
Megamouse 105c4988f0 mic: fix some warnings 2026-09-28 12:27:27 +02:00
Megamouse 9a8a66a29c Update wolfssl to 5.9.4 2026-09-28 12:27:27 +02:00
Ani 1f080485b9 ci: Bump FreeBSD to 14.5 2026-09-28 03:48:48 +02:00
Antonino Di Guardo c405227129 CPUThread: Fix deadlock when a CPU thread terminates abnormally 2026-09-27 23:54:51 +03:00
kd-11 3fa07db78b rsx/qt: Move the "disable blit engine upscaling" option to the advanced tab 2026-09-27 18:17:26 +03:00
digant73 205a4481f3 Fix ISO device paths 2026-09-27 15:10:08 +03:00
digant73 dad97b9a96 cellVdec: Output the pictures still held by the decoder on EndSeq 2026-09-27 02:53:16 +03:00
digant73 8e2d700fab Fix BD Drive read speed 2026-09-27 02:29:27 +03:00
Antonino Di Guardo 77cb9423db LV2: Wait for new PPU threads to start in sys_ppu_thread_start 2026-09-26 15:33:22 +00:00
Antonino Di Guardo a1c317a8c4 cellVdec: Postpone Context Destruction 2026-09-26 14:37:31 +00:00
Elad 3aab4c5d59 VM/sys_mmapper: Fix sys_mmapper error checking 2026-09-26 14:35:38 +03:00
Elad 063586ea81 Utilities\cfmt.h: Add binary formatting to fmt::format 2026-09-26 14:35:38 +03:00
Elad 6d22e2165d Fix vm::page_protect 2026-09-26 13:17:00 +03:00
Elad a70f698569 vm: Fix confusing enum flags naming 2026-09-26 13:17:00 +03:00
Elad 13eaa2b03c rsx/vm: Do not pause RSX on non-RSX allocations 2026-09-26 13:17:00 +03:00
Elad 4514ad6216 Fix PPU start delay accumulation 2026-09-26 00:01:32 +03:00
digant73 6e6ffc5058 Fix thread affinity on Linux for thread scheduler different than OS 2026-09-25 23:34:58 +03:00
digant73 9f696db955 Fix slow audio 2026-09-25 21:03:26 +03:00
kd-11 7f5f8c4a70 vk: Use all available threads for shader compilation during bootup 2026-09-25 20:33:15 +03:00
Megamouse e447511a67 CI: try to split msvc build and unit test build 2026-09-25 15:22:49 +02:00
ShadelessFox 38eba804f4 gdb: fix address check in the memory read routine 2026-09-25 08:37:02 +03:00
RipleyTom ef65627338 fix: fix build with LLVM 23.1 2026-09-25 05:57:53 +03:00
Megamouse a106fb0ee7 qt: don't treat empty savestates folder as error 2026-09-24 21:22:24 +02:00
MananSharma12 fadced35a5 gui: Display warning for corrupted items in Savestate and Trophy managers 2026-09-24 18:48:11 +02:00
Megamouse 3660716e4f Build/CI: Stop overriding '/W0' with '/W4' spam 2026-09-24 17:34:00 +02:00
YahfzandElad 712ab24775 Update rpcs3/rpcs3qt/tooltips.h
Co-authored-by: Elad <[email protected]>
2026-09-24 16:34:25 +03:00
yahfz 083859b94c putllc 2026-09-24 16:34:25 +03:00
Antonino Di GuardoandClaude Opus 5 0c382f4fc9 patch_engine: don't emit path-typed patch addresses as hex
save_patches always emitted the address element as fmt::format("0x%.8x"),
but for move_file and hide_file that element is a VFS path: load() keeps the
text in original_offset and skips u32 validation for those two types.
Round-tripping such a patch through save_patches replaced the source path
with 0x00000000.

The entry stays syntactically valid and keeps loading, so the patch is still
listed and can still be toggled in the Patch Manager while silently matching
no file. Re-downloading does not repair it either, since append_patches
discards an incoming patch whose Patch Version is not strictly greater.

Gate the emit on patch_type_uses_hex_offset, the predicate that already
existed for this and was used only on the load side. The numeric branch
keeps using offset rather than original_offset on purpose: an address
modifier is folded into offset at load time and the flat form emitted here
has nowhere to put it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-24 13:45:26 +02:00
Antonino Di GuardoandClaude Opus 5 dc3c7327d8 patch_engine: make save_patches write atomically
save_patches opened the target with fs::rewrite (write + create + trunc),
then discarded the result of file.write() and returned true unconditionally.
A write that fails part way leaves a truncated file, load() rejects the
whole file on a parse error, and import_patches refuses to write when
load() fails, so every later import returns -1 with no in-app recovery.

75e66ab5e made the patch config and the pad config atomic but missed this
function; save_config, 70 lines up in the same file, already writes through
fs::pending_file and checks the result. Do the same here.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-24 13:45:26 +02:00
Megamouse c82221f292 cellGem: Allow custom camera_frame buffers
TV superstars demo passes its own buffer.
If we early out here we never increase the frame count and tracking
does not work properly.
2026-09-24 07:52:39 +02:00
Elad b5a174e933 LV2: Wait for an observable PPU schedular change from new threads 2026-09-23 23:13:17 +03:00
Elad f9b4d121ea Make sys_cond_signal_to permissive to CPU cycle errors 2026-09-23 23:13:17 +03:00
EladandAntonino Di Guardo c82e598950 LV2: Increase HW PPU Thread Signal Delay
Co-Authored-By: Antonino Di Guardo <[email protected]>
2026-09-23 23:13:17 +03:00
Elad 1e0e352f95 Util/stack_trace.cpp: Clean duplicate functions in CallStack dump 2026-09-23 23:13:17 +03:00
kd-11 56237b6858 rsx/lv2: Add a timeout to the queue drain logic
- Also adds an optimization to skip draining the queue if the command arg is the same.
2026-09-23 17:54:33 +03:00
kd-11 99a926d278 rsx: Force a full pipeline stall before invoking USER_COMMAND callback
- Proven with hardware tests
2026-09-23 17:54:33 +03:00
kd-11 ff40118b94 rsx/lv2: Fix back-to-back USER_COMMAND method invocation cloberring the callback argument 2026-09-23 17:54:33 +03:00
jpolo1224 fe9281f44a vk: run the conversion kernels 64-wide on Adreno
Adreno waves are 64 lanes, and a workgroup narrower than the wave does not pack
together with its neighbours -- it occupies a whole wave and masks the surplus
lanes off. At 32 that idled half of every wave on every dispatch, and these
kernels run on every texture upload of a kilobyte or more, plus every deswizzle
and detile.

Nothing argues the other way here. The kernels carry no shared memory and no
barriers, so group size is a scheduling hint with no LDS or synchronisation
cost to trade against.
2026-09-23 13:20:53 +03:00
Val Packett fdffb7d5f9 vk: Recognize Qualcomm Adreno drivers (blob and Mesa Turnip) 2026-09-23 13:20:53 +03:00
digant73 3fa2bf6688 Fix crash on DLC unlocked 2026-09-22 23:52:18 +03:00
Antonino Di Guardo c4d834480a Fix unpkg bugs- #19528
- PKG entry paths are converted to std::filesystem::path through explicit UTF-8 (utf8_to_wchar / wchar_to_utf8) instead of the narrow constructor, which read the bytes in the host ANSI code page and terminated the process on any it could not map (Shift-JIS file names such as s10c3_R126_<90 AC 91 CC>.mdat in Darkside Chronicles).
- The canonical path is converted back with UTF-8 as well, so a dev_hdd0 path with non-ASCII characters yields a valid key and    passes the containment check (upstream rejected every entry as "escapes installation directory").
- The installation directory is canonicalized without its closing / separators, like every entry: on a file system that cannot resolve it, the lexical form no longer keeps an empty last element that no entry can match (network drives).
- Absolute / . / .. checks run on the raw name bytes via a shared leaves_directory() helper; the .. case is now an installation failure instead of an emergency exit.
- The installation directory name read from the package is validated with the same helper and escaped.
- weakly_canonical is computed once per directory and reused for files not yet on disk (existing paths and folders are still    resolved whole): identical keys and verdicts, ~25x faster enumeration (6000 files: 1.7 s → 63 ms).
- Supersedes *19463 (trailing separator after the lexical fallback)
- Supersedes *19492
- Minor: map key moved instead of copied.
2026-09-22 20:07:28 +00:00
Elad 3efbf060a6 sys_fs: Fix SYS_APP_HOME readability control for disc games 2026-09-22 16:16:51 +03:00
kd-11 d08d568d5e vk: Fix renderpass keys attached to property blobs when seeding interpreter variants 2026-09-21 19:00:51 +03:00
kd-11 c938d3e049 vk: Support multiple attachments on renderpass key generation 2026-09-21 19:00:51 +03:00
kd-11 e59ed70470 rsx: Tweak logic regarding reuse of blit target sections
- Also optimizes for the common case where we overwrite the destination in full.
2026-09-21 19:00:51 +03:00
kd-11 accfecd2d1 vk: Expand the interpreter seed by taking most common formats into account 2026-09-20 21:09:01 +03:00
kd-11 507b395d4b vk: Support linking shaders with a common pipeline cache 2026-09-20 21:09:01 +03:00
kd-11 7022a322a6 vk/interpreter: Add more seed variants and remove pipeline cache from interpreter code 2026-09-20 21:09:01 +03:00
kd-11 71f8a5e09d vk: Clean up flat shading implementation 2026-09-20 21:09:01 +03:00
Walter 9e86f165d1 Revert "[LLVM SPU] Add InstCombine pass"
This reverts commit f4a74819d3.
2026-09-19 15:16:28 +03:00
Walter f4a74819d3 [LLVM SPU] Add InstCombine pass
The `InstCombine` is a very important pass for optimizing common expressions and simplifying logic. It could help produce sustainably better code at little cost.
2026-09-19 02:12:24 +03:00
Antonino Di Guardo 8db660b185 Fix sysFsReaddir #19512 2026-09-17 19:21:44 +00:00
kd-11 a90c841b39 rsx: Fix flush predictor behavior when using surface cache targets for blit engine 2026-09-17 21:42:07 +03:00
Megamouse e76fbf4489 Increase MSVC ccache to 7GB 2026-09-17 18:19:00 +02:00
Walter 92721aedfe [SPU LLVM] Avoid the SHUFB GFNI path when it prevents a OR combine
On non-AVX512 CPUs, the GFNI path can be expensive (on Intel) due to it requiring a variable blend. This patch prevents that case.
2026-09-17 14:09:29 +03:00
Edvard 6c0e2823f8 Add --big-picture CLI flag to force Big Picture Mode on launch (#19509) 2026-09-16 22:04:56 +00:00
Malcolmandjpolo1224 e13ee15791 SPU LLVM: Stop inverting float-to-int saturation on ARM64
- Fix CFLTS saturation in the approximate and accurate xfloat paths
- Original fix credit to jpolo1224 of armsx3 https://github.com/ARMSX2/ARMSX3/commit/424514fde67adb83540d39feea860b5eb17b3500
- Accurate-xfloat follow-up https://github.com/ARMSX2/ARMSX3/commit/19d23eb6913c34cb63c00861bf95e58c3144e78e

Co-authored-by: jpolo1224 <[email protected]>
2026-09-16 14:04:34 +03:00
Antonino Di Guardo 394a9e4b52 Unedat: Mute wrong log on edat and minor fixes 2026-09-16 09:45:36 +00:00
Yahfzandkd-11 a659805470 rsx: Reuse discarded render targets during surface splitting
Co-authored-by: kd-11 <[email protected]>
2026-09-16 08:06:56 +00:00
Malcolm e826098bcb SPU LLVM: Omit test_state and spu-null if they are unreachable 2026-09-16 09:28:19 +03:00
Megamouse 2d103407f8 cellMic: add EyeToy and PsEye types 2026-09-16 01:42:59 +02:00
schm1dtmac 4b9f2af41f [Mac OS X] Switch all Mac OS X CI to use MacPorts because of Homebrew 7 dropping Intel far too early.
This change really is the last straw I've had with Homebrew, I simply cannot trust that they won't do something similar to Apple Silicon in the future. Inevitably much of the Intel CI's downloaded deps will get built from source due to this which would cause build times to skyrocket, were we to keep using Homebrew.
2026-09-15 23:35:50 +02:00
EBgames7 ac6dc84e84 Infamous 2 Trophy Overlay Fix (#19473)
Infamous 2 displays infamous 1 trophy data in overlay if trophies are
present on local user account. If infamous 1 trophies are not present,
the overlay does not display any trophy data.

This code addition sets the trophy name list once (if empty) and
prevents future trophy contexts from changing it. The overlay now
displays the proper trophy data for infamous 2.
2026-09-15 20:47:40 +00:00
Megamouse fb62c6d03a clean up some includes 2026-09-15 22:01:55 +02:00
Megamouse be509deda0 Move emu callbacks to own file
Reduces some System.h includes
2026-09-15 22:01:55 +02:00
MohamedandClaude Fable 5 1b3e02044f LogitechG27: mirror mapped steering device presence on the usb bus
The emulated G27 was always attached while enabled, so games kept
reading a phantom wheel (centered steering after the previous commit,
formerly hard-left) and ignored pad steering input. Attach the emulated
device only while the mapped steering device is connected: games now
see the wheel plug in and out exactly like on real hardware, and pad
play works without disabling the option and restarting the game.

Also guard the personality reconnect against the device having been
detached between the game's mode-change request and the housekeeping
tick.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-15 21:10:22 +02:00
MohamedandClaude Fable 5 b7056bc88a LogitechG27: report neutral axis positions when the mapped device is missing
The emulated G27 fell back to MIN for every axis whose mapped SDL device
was not connected. For the steering axis MIN encodes full-left, so enabling
the emulated wheel without the physical device attached (or while playing
with a regular gamepad) made games receive permanent hard-left steering.
Fall back to the axis' neutral position instead: center for steering,
released for pedals (unchanged behavior for pedals).

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-15 21:10:22 +02:00
Megamouse 2868fbe4ed BUILDING.md: update vulkan install on linux 2026-09-15 20:16:21 +02:00
FlexBy420 c1b1d00b4c Qt: Overhaul trophy manager (#19428)
* Allow to search game by title or via comm id
* More info columns
* Seperated games list and trophy list instead of displaying both at the
same time
2026-09-15 17:23:28 +00:00
Megamouse 0646d36708 ps move tracker: only create threads on demand 2026-09-15 18:18:34 +02:00
Megamouse ca522cf84c fix cctype usage
According to the documentation, we should cast to unsigned char.
Anything negative is undefined behavior.
2026-09-15 17:38:26 +02:00
digant73 b6bdf0d92d Fix UTF-8 filename 2026-09-15 11:59:38 +03:00
Malcolm ec4b1ae655 SPU LLVM: Use TBL for ARM64 byteswaps 2026-09-15 10:50:56 +03:00
Megamouse 1b569fde4d Fix some warnings 2026-09-15 03:10:16 +02:00
Megamouse 6c4b749fb8 Add new stylesheets to VS filters 2026-09-15 03:10:16 +02:00
Megamouse 932336ac30 VS: Use Ninja to build glslang and add ccache 2026-09-15 03:10:16 +02:00
digant73 b14b6d3cef Fix read file on system attribute 2026-09-15 01:08:48 +03:00
Elad 4f11259e58 Fix savestates 2026-09-14 18:35:47 +03:00
Elad bcd8a09f49 VM: Fix vm::main for SDK ver <2.00 2026-09-14 17:44:52 +03:00
Elad c74adaba8e Fix uncalled for ppu_register_function_at call
It was decoding bad data, was discovered when vm::main address changed so the data changed as well
2026-09-14 17:44:52 +03:00
Elad 12b4d3d50c Loader: Fix SDK ver <2.00 user memory pool size
Tested today.
2026-09-14 17:44:52 +03:00
Elad da2153db2c sys_memory: Optimize allocations 2026-09-14 17:44:52 +03:00
Elad e6c3cce5e2 sys_fs: Add debug stats 2026-09-14 17:44:52 +03:00
Malcolm 41f0ecc17b SPU LLVM: Ifdef some code to be x86 only
- might speedup arm via avoiding some x86 optimizations that don't use intrinsics
2026-09-14 08:35:50 +03:00
Yahfz 2003f2404f Reserve properly user mrm allocations (#19482) 2026-09-13 20:02:48 +00:00
Megamouse 726cd2d358 Remove some outdated includes 2026-09-13 16:44:38 +02:00
Megamouse c5375ccadf Update 7zip to 26.03 2026-09-13 16:44:38 +02:00
Yahfz 43cedf366b sysutil/audio: Implement shared memory handling (#19467) 2026-09-13 08:39:10 +00:00
digant73 6ba56a52a4 Fix silence on some 8ch to 2ch audio output configuration 2026-09-13 00:01:20 +03:00
Cri4Key 961c86fbbf Ensure SO_BROADCAST is set when sending to broadcast address in P2P 2026-09-11 20:54:05 +02:00
Cri4Key de9184f03c Add broadcast socket option keeper 2026-09-11 20:54:05 +02:00
Cri4Key 48cc1ac0a9 Allow broadcast P2P socket 2026-09-11 20:54:05 +02:00
MananSharma12 954d7968c7 gui: Fix ComboBox dropdown contrast in Kuroi (Dark) theme 2026-09-11 13:43:19 +02:00
Antonino Di Guardo 028d1e8f4a Fix some VFS issues - #19378 2026-09-10 18:56:40 +00:00
FlexBy420 448fa5dd3a Add new theme based on old steam look (#19446) 2026-09-10 17:50:18 +00:00
Malcolm Jestadt bcb2d22311 BufferUtils: Optimize upload_untouched_skip_restart with AVX-512 paths
- u16 path needs AVX-512-ICL because vpcompressw isn't included in skylake-x level AVX-512

- u32 path is untested as I couldn't find any games that hit it
2026-09-10 19:17:26 +02:00
BeezBumba 8d414937d9 Fix slow startup with BPM (#19388) 2026-09-10 16:39:34 +00:00
Elad 99a91bb562 SPU LLVM: Always write cache when using debug option 2026-09-10 19:06:37 +03:00
Elad c59667b689 Fix theoretical PKG_MAX_FILE_COUNT limit
As long as there is no proven calculation that limits it we should not lower it.
2026-09-10 18:32:18 +03:00
Acts1631 36eaed481c Abort on parent-directory PKG entries 2026-09-10 18:32:18 +03:00
Acts1631 71e9f4ea57 Bound PKG entry table allocation
PKG file_count was used for vector allocation before validating
that the encrypted package data contained the entry table. Limit the
count and require the entry table to fit in the declared data region
before allocating.
2026-09-10 18:32:18 +03:00
Acts1631 09bb101de1 Fix PKG extraction path traversal
PKG entry names could contain traversal components and escape the
installation directory. Reject absolute and special components, then
require the resolved target to remain below the installation root
before creating or writing files.
2026-09-10 18:32:18 +03:00
Elad 973bb520ac More CellError formatting handlers 2026-09-10 17:33:45 +03:00
Elad fb2f1326ad Silence syscall 579 2026-09-10 17:33:45 +03:00
Elad 9c6df388e8 Debugger: Relax thread updates 2026-09-10 17:33:45 +03:00
kd-11 f2ac83f5eb ci/build: Fix missing include for MacOS builds 2026-09-10 15:57:17 +02:00
kd-11 a226238355 rsx: Extra memory safety when rsx::thread crashes 2026-09-10 15:57:17 +02:00
MananSharma12 774ff4933d gui: set vertical alignment for sizeSlider 2026-09-10 15:24:05 +02:00
Sean Brar 17075366c6 Loader: reject unreadable ISO directory metadata 2026-09-10 14:30:55 +03:00
Sean Brar a89b77c1c5 Loader: stop ISO extent reads at logical EOF 2026-09-10 14:30:55 +03:00
Sean Brar aed242aa3f Loader: recover when an ISO member is missing 2026-09-10 14:30:55 +03:00
Walter ca223f70bc [SPU LLVM] 8-bit add/sub renarrowing + compare fast paths
This patch adds a optimization to fold 8-bit addition/subtraction idioms back into a native 8-bit add. Also adds fast paths to the `SHUFB` and `ABSDB` instructions for when a operand came from a compare.
2026-09-10 11:54:14 +03:00
kd-11 14bc19b7bd ci: Remove code caches if PR has 'shaders' label 2026-09-10 10:20:50 +03:00
kd-11 06d28a5f11 rsx: Fix alpha test compilation when using native float16 types 2026-09-10 10:20:50 +03:00
Karim DRIDI ceb1ab0718 GDB: Fix PPU single-step state setup 2026-09-09 19:51:03 +03:00
kd-11 54014a7de4 rsx/qt: Add a debug setting to force programmable blending 2026-09-07 21:09:01 +03:00
kd-11 9a26a70e71 rsx: Fix decoding of 16-bit blend constants 2026-09-07 21:09:01 +03:00
kd-11 5aa23d0276 rsx: Don't force PB when we don't need to for performance reasons 2026-09-07 21:09:01 +03:00
kd-11 8a78c3ca59 rsx: Fix a really weird quirk when blending with XRGB output formats
- Regardless of the O/Z tag on the format, DST_ALPHA is always 1
- I don't know why it's that way, it shouldn't be but we're probably just looking at a hardware bug
2026-09-07 21:09:01 +03:00
kd-11 9c850a34f2 rsx: Fix ROP operation order.
- The correct order is sRGB -> ROUND -> ALPHA_TEST -> BLEND
- Blending DOES NOT run in linear space at all. The bytes are decoded as-is.
2026-09-07 21:09:01 +03:00
kd-11 78cb66ecfe vk/gl: Fix byte order for RGB565 formats in line with hardware tests 2026-09-07 21:09:01 +03:00
kd-11 244491298b vk: Clean up decode_rsx_state in preparation for refactor 2026-09-07 21:09:01 +03:00
kd-11 d55e9fcf22 rsx/vk: Implement programmable blending for MSAA targets 2026-09-07 21:09:01 +03:00
kd-11 0885cc617e rsx: Minor optimization wrt shader export program options
- No need to rerun the blend config and depth state evaluation every time the shader changes.
- There's probably more that can be done, but I'll check later.
2026-09-07 21:09:01 +03:00
kd-11 b99fbe7bec rsx: Minor refactor to prepare for MSAA support 2026-09-07 21:09:01 +03:00
kd-11 5a43724b44 rsx: Support variable blend target masks with programmable blending 2026-09-07 21:09:01 +03:00
kd-11 8e92a8d845 rsx/fp: Fix failing test cases for programmable blending 2026-09-07 21:09:01 +03:00
kd-11 05b4b10360 rsx: Properly implement signed blending modes 2026-09-07 21:09:01 +03:00
kd-11 87676e3632 rsx/vk: Respect programmable blending when requested 2026-09-07 21:09:01 +03:00
kd-11 e606570b44 vk: Set attachment usage flags to allow input attachments always
- Temporary measure. That really needs to be handled carefullly since most games will not actually need programmable blending.
2026-09-07 21:09:01 +03:00
kd-11 b6dc4e8e04 vk: Properly handle sub-pass dependency barriers 2026-09-07 21:09:01 +03:00
kd-11 284233bc6d vk: Bind input attachments if required by pipeline configuration 2026-09-07 21:09:01 +03:00
kd-11 d7d1d9133b vk: Properly propagate programmable blending flag down to renderpass caching 2026-09-07 21:09:01 +03:00
kd-11 c47bbab85f rsx/vk: Plumb programmable blending shader emitter 2026-09-07 21:09:01 +03:00
kd-11 ab95783bc9 rsx: Update programmable blending code to be in line with current emulator state 2026-09-07 21:09:01 +03:00
kd-11 065b490eb4 rsx: Force a backend sync when calling nv4097::set_notify 2026-09-06 12:55:51 +03:00
kd-11 57dd6e763a rsx/zcull: Use partial MM flush for report sync 2026-09-06 12:55:51 +03:00
kd-11 7a953ecf04 rsx/zcull: Fix broken nv3089 optimization 2026-09-06 12:55:51 +03:00
kd-11 0ead9328f2 rsx/mm: Implement partial queue flush 2026-09-06 12:55:51 +03:00
kd-11 1559b15afe rsx: Fix broken ZCULL optimization 2026-09-06 12:55:51 +03:00
kd-11 25f1a9dfca rsx/sync: Improve guest synchronization with host GPU operations 2026-09-06 12:55:51 +03:00
kd-11 2f53c4389f rsx: Refactor rsx_utils header into smaller pieces 2026-09-06 11:54:20 +03:00
BeezBumba de6c1b526d fix button labels in BPM 2026-09-05 07:08:08 +02:00
Megamouse 33a723affa CI: Use ccache in VS cmake submodules 2026-09-05 00:27:02 +02:00
Megamouse bf949109ad Build curl with cmake 2026-09-04 16:40:18 +02:00
Megamouse fbaaf2b57f Update curl to 8.22.0 2026-09-04 16:40:18 +02:00
Acts1631 7be182e33b Bound Clans member attribute encoding 2026-09-04 09:14:33 +03:00
RipleyTom e6235886f1 fix: check ticket encoded correctly 2026-09-04 07:11:47 +03:00
Elad 5ab2398b29 SaveStates: Reject unrealistic memory allocations 2026-09-04 00:12:17 +03:00
Megamouse db543e095e Update SDL to 3.4.16 2026-09-03 21:51:30 +02:00
Acts1631 645458b0fc Validate SELF metadata header sizes 2026-09-03 22:09:02 +03:00
Acts1631 0a85b394ec Reject overflowing ELF section ranges 2026-09-03 18:53:46 +03:00
Acts1631 275c2d4093 Reject overflowing savestate collection counts 2026-09-03 13:03:26 +03:00
Megamouse 4cb260c555 Add lock to handle_button_press
We don't want to change UI elements while we are compiling.
2026-09-03 11:13:13 +02:00
Megamouse 8ac75b7dc4 Do not exit big picture with circle
This is the default behavior of the home_menu_page, which was only
intended for use during gameplay originally.
We also expect the game grid to have a parent.
2026-09-03 11:13:13 +02:00
Megamouse 65c8fb5699 big picture: initialize missing input handlers
We need to init the input handlers, otherwise we have a chance to crash
when the fxo is used in some input loop
2026-09-03 11:13:13 +02:00
Acts1631 df92b1d886 Bounds-check Cg binary byte swapping 2026-09-02 22:13:12 +03:00
Megamouse 0514f521e9 big picture: fix image/video aspect ratios 2026-09-02 16:23:30 +02:00
Megamouse cd7d81639a big picture: lock reload mutex at function entry 2026-09-02 16:23:30 +02:00
Megamouse 216b74426a big picture: increase auto repeat interval. It was way too fast. 2026-09-02 16:23:30 +02:00
Megamouse 1a907b5659 big picture: don't jump to the last item when pressing down in the last row 2026-09-02 16:23:30 +02:00
schm1dtmac c19594254e [Mac OS X] Fix sys_ss_random_number_generator & cleanup some dead code (#19382) 2026-09-02 12:00:05 +00:00
Megamouse 9bb7e2e749 Update FAudio to 26.09 2026-09-02 08:08:41 +02:00
Megamouse 17711f219f Update fusion to 1.3.3 2026-09-02 08:08:41 +02:00
Megamouse 677e13da42 overlays: Fix iso media path loading 2026-09-01 23:20:37 +02:00
Megamouse e04198e825 iso: Improve movie and audio path checks
Also reduce need for checking iso files for non-iso games
2026-09-01 23:20:37 +02:00
Megamouse 4048b7aaef big picture mode: Create tiles multithreaded 2026-09-01 23:20:37 +02:00
Megamouse 83a522a8e1 overlays: parse big picture game entries multithreaded 2026-09-01 23:20:37 +02:00
Megamouse 6a5eb794cb Parse games dir before entering big picture mode 2026-09-01 23:20:37 +02:00
Megamouse 5955980aea overlays: use new game enumeration in big picture mode 2026-09-01 23:20:37 +02:00
Megamouse 7338e1610f Refactor game enumeration 2026-09-01 23:20:37 +02:00
Megamouse 2893b42a6f Qt: log some game window settings 2026-09-01 11:44:57 +02:00
Megamouse 12619da666 overlays: localize shader compilation dialogs 2026-09-01 11:44:57 +02:00
Megamouse a7a3e4fc90 Qt: add horizontal scrollbar to changelog 2026-09-01 10:14:53 +02:00
FlexBy420 42e9eda2c5 RPCN: Add delete trophies from rpcn button (#19306)
Allows user to delete their synced trophies
2026-09-01 07:35:45 +00:00
Megamouse 5e40242bdd Qt: Don't set game window visibility on boot if resize is enabled 2026-09-01 08:56:47 +02:00
Megamouse 44a2e4d663 Loader: Only use a single thread when installing packages from an optical drive 2026-08-31 21:04:33 +02:00
Antonino Di Guardo a99fde6f5b Add BD Drive support for Linux, macOS and FreeBSD (#19233)
Follow up of #18648 and #18345 adding BD Drive support to the remaining supported OSes.
As it happens on Windows, both physical BD Drive and virtual drive (e.g. mounted `.iso` file) are supported.

Code for mac OS and FreeBSD provided by AI.

- key files need to be provided under `<rpcs3>/data/redump` folder (currently it needs to be manually created). This folder is also already the fallback folder for any encrypted ISO file

Fixes #18345
2026-08-31 16:44:29 +02:00
Gabriel Costa c6e96729cc rsx/fp: Fix SAT and ABS modifier precedence
- Real RSX applies the SAT modifier before ABS
2026-08-31 10:31:46 +00:00
Masamune3210 d9799c7f38 unpkg: use lexical path normalization for package entries 2026-08-31 10:18:12 +03:00
Masamune3210 54993de362 unpkg: handle canonicalization failures on mounted filesystems 2026-08-31 10:18:12 +03:00
Megamouse 0c0614a4df big picture: navigate down even if there is no tile directly below 2026-08-31 04:12:56 +02:00
kd-11 c006a7f1a5 rsx/qt: Move blit engine upscaling option to debug settings 2026-08-31 02:15:50 +03:00
kd-11 d267b420f8 rsx: Don't overfit memory allocations in system RAM
- Avoids a lot of faults that would trigger slow flush operations when CELL is touching memory outside of the range we care about.
2026-08-31 01:27:51 +03:00
kd-11 726263377f rsx/mm: Add unit tests 2026-08-31 01:27:51 +03:00
kd-11 0c486fb63a rsx/mm: Implement range merging mechanics 2026-08-31 01:27:51 +03:00
kd-11 a8bfc50762 rsx/mm: Avoid flushing entire queue unless actually necessary
- Host MM operations are slow af
2026-08-31 01:27:51 +03:00
kd-11 85f0cc01ea rsx: Loosen the access ordering guarantees a bit 2026-08-31 01:27:51 +03:00
kd-11 78e70af989 rsx: Add missing mm_flush in blit engine before RCB reload 2026-08-31 01:27:51 +03:00
Megamouse 3ef20ebb01 Fix iso path checks 2026-08-30 16:23:38 +03:00
Elad da5d5e5027 SaveStates: Fix sys_prx saving 2026-08-30 11:57:23 +03:00
Elad e2b2493c7f sys_prx: Add some logging 2026-08-30 11:57:23 +03:00
Elad 30f56dd404 SaveState: Fix sys_overlay saving 2026-08-30 11:57:23 +03:00
Elad 7bd312f128 Fix vfs::retrieve for ISO files 2026-08-30 11:57:23 +03:00
BeezBumba e674414875 Add Audio support for BPM ICON1.PAM (#19336)
Previously in the Big Picture Mode when selecting a game to look at its
details, the ICON1.PAM video would start to play but there would be no
audio playing if the .PAM file had it. This PR fixes that.

Note: the change in qt_video_source.cpp is needed because it seemed like
it didn't use it's own parameter. Without that, the audio would not fade
out even after you left the game details.
2026-08-29 21:11:51 +00:00
Megamouse c85105a7fd overlays: use get_game_content_path in BPM 2026-08-29 22:26:39 +02:00
Megamouse 1d644186f4 VS: move BigPicture to own filter 2026-08-29 22:26:39 +02:00
Antonino Di Guardo 010bf1753e Improve Game Collections (#19349)
Follow up of #19292 to improve Game Collections in context menu:
- Add and remove the game to/from multiple collections (e.g. "3rd person" and "Fantasy")
- Create a collection and add the game to it
- Remove the game from current collection if it is not the "All games"
2026-08-29 18:56:56 +00:00
BeezBumba 01c59b99a7 Fix font issues with game titles (#19341)
It fixes certain Japanese characters in game titles and some missing
characters in the BPM.
2026-08-29 18:14:20 +00:00
schm1dtmac eb61fc1fbd [Mac OS X] Fix ISO Verification Failed crashes 2026-08-29 16:01:12 +03:00
mariobob 98906eb035 gui: Fix randomly untranslated game list categories 2026-08-28 16:46:11 +02:00
kd-11 48d2b24813 rsx: Implement workaround for pitch compatibility of overlapping sections with blit engine targets
- Avoid generating surfaces that introduce pitch conflict!
2026-08-28 14:55:44 +03:00
kd-11 42cfad20eb vk: Set up easy debug printing for captured traces 2026-08-28 14:55:44 +03:00
kd-11 85bc1d7074 rsx: Ensure blit targets do not straddle tile boundaries 2026-08-28 14:55:44 +03:00
kd-11 7a4c82aec9 rsx: Allow unlocked DST surfaces to still take writes from the host side 2026-08-28 14:55:44 +03:00
kd-11 0d9afb039e rsx: Do not force blit writes to LOCATION_MAIN to use CPU blit unconditionally
- Blit DST targets are now owned by the surface cache. So always try matching there first.
- Block transfers still go through CPU copy path.
2026-08-28 14:55:44 +03:00
kd-11 71a6aeadcf rsx: Propagate swizzle flag to surface cache from blit engine 2026-08-28 14:55:44 +03:00
kd-11 124462fbab rsx: Avoid false surface match rejection when data is uploaded into the surface cache during RTT scan 2026-08-28 14:55:44 +03:00
kd-11 7d1b67b17a rsx: Avoid unconditional interpolation in blit engine if the original PS3 request did not actually do interpolation 2026-08-28 14:55:44 +03:00
kd-11 6f3df0ba7b rsx: Disable blit engine upscaling by default
- Temporary measure to keep regression testing on track.
2026-08-28 14:55:44 +03:00
kd-11 26209734e3 rsx: Fix potential section leak when discarding locked framebuffer region 2026-08-28 14:55:44 +03:00
kd-11 37d64bf887 rsx: Limit full range blit target invalidation to nul sections only 2026-08-28 14:55:44 +03:00
kd-11 e3585a4ec1 rsx: Ensure blit targets are always locked regardless of origin 2026-08-28 14:55:44 +03:00
kd-11 ac50c09b9d rsx: Fix BGRA8 gcm_format -> surface_format selection 2026-08-28 14:55:44 +03:00
kd-11 cb53f2c4d6 rsx: Fix build on non-windows platforms 2026-08-28 14:55:44 +03:00
kd-11 802320ec67 rsx: Allow blit engine to preserve resolution scaling
- Since we're using the surface cache for storage, might as well enable resolution scaling on blit resources.
2026-08-28 14:55:44 +03:00
kd-11 27bfd637e6 rsx: Move blit_engine_utils nested group to protected section (refactor part10) 2026-08-28 14:55:44 +03:00
kd-11 2caa367615 rsx: Move some blit engine helpers to cpp file (refactor part9) 2026-08-28 14:55:44 +03:00
kd-11 34c34504f3 rsx: Move blit functions to the subclass (refactor part7) 2026-08-28 14:55:44 +03:00
kd-11 5d8862f6dd rsx: Move blit engine target creation to separate function (refactor part6) 2026-08-28 14:55:44 +03:00
kd-11 eea763352a rsx: Move blit_src creation to a separate helper 2026-08-28 14:55:44 +03:00
kd-11 5b2039d588 rsx: Drop unused paths from blit engine (refactor part4)
- We now create all targets in the surface cache. We don't need to do any local scans.
2026-08-28 14:55:44 +03:00
kd-11 69ba2e2f14 rsx: Blit engine refactor part3
- Move some lambdas into the blit_engine_utils wrapper
2026-08-28 14:55:44 +03:00
kd-11 14b71c2e11 rsx: Refactor upload_scaled_image part2 2026-08-28 14:55:44 +03:00
kd-11 bbdf5465f9 rsx: Refactor upload_scaled_image part1
- Moves some common logic into a pseudo-namespace. More to come.
2026-08-28 14:55:44 +03:00
kd-11 985ac336f5 rsx: Minor despaghetification of some texture cache logic 2026-08-28 14:55:44 +03:00
kd-11 45c42814ef rsx: Fix blit surface intersection logic in the surface cache
- Ensure intersection results are committed by pushing a write barrier before the write tag operation
2026-08-28 14:55:44 +03:00
kd-11 f4a2b507f1 rsx: Move blit target storage to the surface cache 2026-08-28 14:55:44 +03:00
Elad 03d03b2bb4 PPU Debugger: Fix R0 insert to callstack 2026-08-27 23:04:19 +03:00
Elad 46718dcb33 PPU Debugger: Support tail calls in callstack 2026-08-27 23:04:19 +03:00
Megamouse 6567a5a2f8 Qt: Also search in notes of "All titles" in the patch manager 2026-08-26 20:15:41 +02:00
Megamouse 11aa943145 Qt: use hash instead of counter for identical patch names 2026-08-26 20:15:41 +02:00
Megamouse f2d44c63af rsx: default initialize some members 2026-08-26 20:15:41 +02:00
Megamouse 5d3625b7eb Fix typo c_moudles_per_jit 2026-08-26 20:15:41 +02:00
Walter 2416d65262 [SPU LLVM] SHUFB known special constants fast paths
Adds additional paths for the special index constants calculation where we know certain constants can't occur due to LLVM's known bits analysis. Also, adds a path for when we know that only the constants will be used.

This was based on patterns found in LBP2 and RE3.
2026-08-26 09:52:11 +03:00
schm1dtmac e8961edf40 [macOS] Disable Apple-specific hacks for formats now supported by MVK on Apple GPUs, enable async compute 2026-08-25 17:39:12 +02:00
BeezBumba fdcfded8df Add Big Picture Mode using RSX overlay UI 2026-08-25 11:11:08 +00:00
Megamouse 19b9c2e60b cellVdecGetPicItem: Fix potential use after free of AVFrame
The frames are protected by vdec->mutex.
But in cellVdecGetPicItem we fetched a raw pointer to picture.avf and
used it after releasing the lock.
2026-08-25 11:11:03 +02:00
schm1dtmac bd9c4705f2 [macOS] Attempt to hackfix the PPU LLVM compilation regression (#19103) 2026-08-25 09:32:12 +03:00
Antonino Di Guardo 448cc23fac Add Game Collections support (#19292) 2026-08-24 23:52:32 +00:00
Megamouse f9a843306f Fix some warnings 2026-08-25 00:32:39 +02:00
Megamouse 85c9fa215b Qt: improve ps move tracker dialog layout a bit 2026-08-25 00:32:39 +02:00
Megamouse 8a3061d175 Qt: add ps move default brightness
The RGB values will not be at 1% anymore, but the default brightness.
2026-08-25 00:32:39 +02:00
Megamouse 9ead7c34b1 Qt: don't update color sliders recursively with hue change 2026-08-25 00:32:39 +02:00
Megamouse 6a249ffdc9 Qt: set ps move slider ranges before connecting their signals
Signals are sent when the initial value is 0 and range doesn't contain 0
2026-08-25 00:32:39 +02:00
Megamouse b848546d19 Qt: fix some warnings in ps move dialog 2026-08-25 00:32:39 +02:00
digant73 3aea3b15dc Fix ISO timestamps 2026-08-24 05:12:07 +02:00
kd-11 071c9f10f1 rsx: Relax checking of NV4097_SET_SHADE_MODE inputs to match hw 2026-08-24 03:22:05 +03:00
Megamouse 68f260a698 Update Qt to 6.11.2 2026-08-23 19:11:06 +02:00
Megamouse 261b467b83 Qt: Allow to select ps move hue by mouse click 2026-08-23 15:24:13 +02:00
Megamouse 9bcd45d70c opencv: simplify includes. there's no need for photo 2026-08-23 15:24:13 +02:00
Megamouse 8bbdd3715a psmove: Add minor optimization for non diagnostic mode 2026-08-23 15:24:13 +02:00
Megamouse b8816a1e0e Update opencv to 4.14.0 2026-08-23 15:24:13 +02:00
Ani 5bc2928588 gui: Fix firmware install through drag-and-drop
Some browsers such as Firefox download the PS3UPDAT.pup file with a lowercase extension
2026-08-23 14:12:24 +02:00
kd-11 e678a8e572 rsx: Fix tiled region matching 2026-08-23 13:28:50 +02:00
Gabriel Costa 3aac7d776d rsx/fp: Improve DP3 instruction precision 2026-08-22 14:17:01 +00:00
RipleyTom 8bbcd5936d Bugfixes 2026-08-22 14:56:31 +03:00
Tagflag2d b97f4bd8da rsx: Implement flat shading semantics 2026-08-21 17:50:34 +00:00
RipleyTom 8fd2ae954d Filter UnlockTrophy reply 2026-08-21 07:35:12 +02:00
Megamouse 243d7db5b5 vfs: avoid double slash 2026-08-21 02:26:59 +03:00
Megamouse 809359cb20 sys_fs: simplify get_name 2026-08-21 02:26:59 +03:00
Megamouse bab81aa23e Fix rpcn type cast warnings 2026-08-20 11:03:16 +02:00
Megamouse 8a6c96745a Fix iso magic read check 2026-08-20 11:03:16 +02:00
Megamouse 358101c47b Fix unused variable warnings 2026-08-20 11:03:16 +02:00
yahfz c4eff69711 SPU LLVM: Document AVX-512 XFloat lowering 2026-08-20 09:36:19 +03:00
yahfz 5d2601599e [SPU LLVM] Optimize AVX-512 XFloat conversion 2026-08-20 09:36:19 +03:00
digant73 e5e280cd3f Fix duplicate path addition 2026-08-20 00:27:30 +02:00
Megamouse c122edd638 unpkg: Fix buffer size checks 2026-08-19 23:30:34 +02:00
Megamouse d12d8782af unpkg: Mark pkg installation as failed if any thread throws an exception 2026-08-19 23:30:34 +02:00
kd-11 64da425e81 gl: Fix copy_image_static behavior when formats are mismatched 2026-08-19 20:43:05 +02:00
kd-11 b78bae0b9f rsx: Integrate ROP remapping to the interpreter system
- Base pipelines have the remapping active, optimized variants can toggle it away.
2026-08-19 14:08:55 +03:00
kd-11 ca25fbaa5d vk/gl: Support ROP output remap in the interpreter 2026-08-19 14:08:55 +03:00
kd-11 323e2d35a2 rsx/fp: Implement support for ROP_OUTPUT_REMAP in the backends 2026-08-19 14:08:55 +03:00
kd-11 57ecb42433 rsx/fp: Plumb through support for channel remapping during ROP 2026-08-19 14:08:55 +03:00
kd-11 84ceef9717 rsx/glsl: Shrink command space in ROP_CONTROL structure to make room 2026-08-19 14:08:55 +03:00
kd-11 0dd3d6528c rsx/prog: Move in-shader color remap to FP prolog
- We need it for some other stuff
2026-08-19 14:08:55 +03:00
Antonino Di Guardo ddd82ecada Make optional VSH on File ➔ All Titles ➔ Create LLVM Caches dialog (#19270)
Make optional VSH CPU compilation on `File ➔ All Titles ➔ Create LLVM
Caches` dialog. By default it is now excluded due to VSH caches are
fully covered by the dedicated submenu `File ➔ Firmware`.
2026-08-19 08:15:27 +02:00
Ani 7973b8ac6d windows: Fix clang x64/arm64 builds 2026-08-18 21:54:09 +02:00
FlexBy420 3c15df4e4d Update sceNpTrophy.cpp 2026-08-18 14:19:42 +02:00
kd-11 719cf8a54a gl: Fix build warning 2026-08-18 13:47:28 +03:00
kd-11 1b879360b8 rsx: Fix OOB section writes generated due to mipmap dimension clamping 2026-08-18 13:47:28 +03:00
kd-11 ad059d03af vk: Avoid redundant copy when writing to mip level or Z layer 2026-08-18 13:47:28 +03:00
kd-11 78ba581137 vk: Extend copy image API to support 3D offsets and extents 2026-08-18 13:47:28 +03:00
kd-11 cd044148be gl: Avoid redundant copies when copying to mipmaps or 3D slices 2026-08-18 13:47:28 +03:00
kd-11 91cd82a0c2 gl: Extend copy image API to allow explicit 3D offsets 2026-08-18 13:47:28 +03:00
kd-11 e7c3d6ab26 gl: Implement support for explicit multi-layer image copy operations 2026-08-18 13:47:28 +03:00
kd-11 a88331bb22 rsx/vk: Implement support for multi-layer, multi-level and explicit mip/layer image transfers 2026-08-18 13:47:28 +03:00
kd-11 6892ee4c2e rsx: Fix mipmap gather source offsets 2026-08-18 13:47:28 +03:00
Megamouse 0059e4e92e unpkg: fix OOB read at end of file
Use sizeof(u128) instead of 16.
Clear padding after archive_read_block.
Use aligned_div instead of manually aligning blocks.
Fix local_buf size when using raw ptr of the original buffer.
2026-08-18 10:20:30 +02:00
Megamouse 46b7428fad unpkg: also check potential overflow in pkg data size check 2026-08-18 10:20:30 +02:00
Megamouse 582b5ba29e unpkg: fix OOB memset, use safe versions of write_to_ptr and read_from_ptr 2026-08-18 10:20:30 +02:00
FlexBy420 cb175278b6 RPCN: Sync trophies (#18760)
Add synching local trophies with RPCN server trophies, allows users to
essentially cloud save their trophies when they are connected to RPCN.

This PR also aims to add support for global earned % trophies simillar
to steam for RPCN part of website.

No local trophies and not connected to RPCN
<img width="1183" height="717" alt="obraz"
src="https://github.com/user-attachments/assets/f0b3add1-506d-43e5-8dae-cd34e041e2bd"
/>
After booting the game with RPCN enabled
<img width="1184" height="713" alt="obraz"
src="https://github.com/user-attachments/assets/d5992ddc-7fad-4b38-b4f9-006a832ffed5"
/>

RPCN Side PR
https://github.com/RipleyTom/rpcn/pull/140
2026-08-18 06:54:51 +02:00
yahfz 82164a54c1 [SPU LLVM] Avoid redundant XFloat normalization in SELB 2026-08-18 03:48:46 +02:00
Malcolmandjpolo1224 6161ecd7aa PPU: Stop inverting float-to-int saturation on ARM64
- This was breaking armored core 4/4a on ARM
- Original fix credit to jpolo1224 of armsx3 https://github.com/ARMSX2/ARMSX3/commit/87ccdb85152a707f5d0c8122ed39344b4648501e

Co-authored-by: jpolo1224 <[email protected]>
2026-08-18 03:12:13 +02:00
Megamouse de33fda28c rsx_debugger: fix g8b8 conversion 2026-08-17 22:42:05 +02:00
Neil Monday f9f88aa9e5 Use max() to bring negative floats up to 0.0 before uint conversion. 2026-08-17 15:17:17 +02:00
Ani 3be5aa99cc gui: Correctly disable the View Folder button 2026-08-17 02:22:11 +02:00
Zion Nimchuk 3e68a7f385 Add 60 second retry to translation downloads in CI to avoid rate limits 2026-08-17 01:20:35 +02:00
kd-11 9a4b849260 rsx: Fix fbo offset scaling when src and dst bpp is mismatched 2026-08-17 00:15:54 +03:00
kd-11 9a24c8d11f gl/vk: Fix flattened interpreter input subresource range computation 2026-08-17 00:15:54 +03:00
kd-11 0617eff348 rsx: Ensure ref_address is properly set for all sampled images 2026-08-17 00:15:54 +03:00
kd-11 003b368980 rsx: Drill down the copy specification from descriptors when handling dynamic copies 2026-08-17 00:15:54 +03:00
kd-11 6f5f198acd rsx: Fix check for cyclic ref in fast_fbo_check 2026-08-17 00:15:54 +03:00
kd-11 107b751a43 rsx: Enable fast path when scanning for 3D mipmaps 2026-08-17 00:15:54 +03:00
kd-11 4475671bbf RSX: Allow process_framebuffer_resource_fast to take in descriptors with an offset
- Allows to skip going through the merge route when we already have a good match
2026-08-17 00:15:54 +03:00
kd-11 7e35f59997 RSX: Implement host-side mipmap scanning for 3D textures 2026-08-17 00:15:54 +03:00
kd-11 970d745818 rsx: Respect mip levels actually used during image reconstruction 2026-08-17 00:15:54 +03:00
kd-11 adec3ae7f9 rsx: Implement per-mip-level size calculation logic and use it to properly compute 3D texture slice height 2026-08-17 00:15:54 +03:00
kd-11 5e2d0eb762 rsx: Fix incorrect calculation of texture size when border texels are present
- The computation did not match get_subresources_layout behavior.
2026-08-17 00:15:54 +03:00
kd-11 26782525f4 rsx: Fix get_texture_size for 3D textures with mipmaps
- Depth also shrinks for every mip level
2026-08-17 00:15:54 +03:00
kd-11 d0e6d4eefc rsx: Minor improvements to texture cache
- Adds depth to temp subresource key to avoid 3D mismatch (resample)
- Fix narrowing warning for block_h calculations
2026-08-17 00:15:54 +03:00
kd-11 f19d398bec rsx: Check for completeness in X when gathering slices
- This was intentionally ommitted as a speedhack before, but makes sense to check just in case.
2026-08-17 00:15:54 +03:00
kd-11 6b80ac4805 rsx: Simplify merged source sorting when selecting slices
- Sort ranges is a relic and the information is duplicated in the sort_list object
2026-08-17 00:15:54 +03:00
kd-11 13ceef9f46 rsx: Fix slice gather from local resource (e.g blit engine output) 2026-08-17 00:15:54 +03:00
Walter ffc50905a6 [SPU LLVM] Avoid GFNI combine bug in SHUFB
Due to a bug where `SHUFB`'s GFNI constant generation path expects to be combine using a select instead of a OR, it was causing issues with on non-AVX512 CPUs so support was reverted (see #19217). That can still happen on AVX512 CPUs when the shuffle is single source. This patch fixes it and re-lower the target requirements back to just GFNI by avoiding the OR well on the GFNI path. I also renamed a variable and added a comment to better clarify its behavior.
2026-08-16 19:35:42 +03:00
kd-11 cbc7b60ba5 rsx: Cleanup 2026-08-16 13:50:03 +03:00
kd-11 47efd770a8 rsx: Clean up get_merged_texture_memory_region
- Cleaner generation of src_area and dst_area outputs.
- Normalized comparisons in 1bpp space then convert to target space after.
2026-08-16 13:50:03 +03:00
kd-11 f53547b173 rsx: Refactor deferred_subresource constructor into discrete wrappers for each output intent
- Instead of filling over 10 arguments and having the ctor silently drop half of them, we create proper wrappers to construct objects for a singular purpose.
2026-08-16 13:50:03 +03:00
kd-11 cb5b866c61 rsx: Refactor deferred_subresource to be more explicit
- Use defined src and dst rects as well as the transformation if any to be applied.
2026-08-16 13:50:03 +03:00
Zion Nimchuk 2f3c0f04d2 Update docker with updated SDL3 2026-08-16 09:57:33 +02:00
schm1dtmac 3f493fb209 [Qt] Hide titlebars by default 2026-08-16 02:07:48 +02:00
Antonino Di Guardo f7eb0d8d76 Enrich game list title (#19229)
Add on Game List title a brief recap of total number of entries in the
list and total number of Disc, HDD and all the other remaining types of
content.
2026-08-15 23:19:01 +00:00
digant73 fc93d932c8 Swap PR number with PR text in update manager 2026-08-15 13:28:58 +02:00
digant73 3cbf9b8b6c fix crash with vfs exception 2026-08-15 04:39:42 +03:00
kd-11 12b1efc266 rsx/fp: Fix decoding of LOOP and REP instructions
- Verified with hardware tests. RSX does not support proper loops.
- The LOOP/REP instruction simply codes a "REPEAT n" instruction for a block of code.
- There is no accumulator register. The compiler emits a preamble inside the loop block to simulate the running counter.
- Oddly enough, the original start and step values are stored in the instruction but are unused. Maybe useful for debugging real hardware?
2026-08-14 11:15:20 +03:00
Megamouse 4c63acfb40 Qt: Add unofficial build warning 2026-08-14 02:07:18 +02:00
Megamouse 3f4364fe74 Qt: Decrease layout margin in settings_dialog 2026-08-14 01:02:56 +02:00
Ani ee43ab7362 Revert "[SPU LLVM] Decrease SHUFB's constant generation target requirements"
This reverts commit 26e37d8c8c.
2026-08-14 00:19:17 +02:00
Megamouse 2dc6cf014c Qt: add Open Custom Gamepad Config Folder action to game list context menu 2026-08-13 21:36:51 +02:00
Megamouse c285c2fb41 Qt: fix settings_dialog tab index 2026-08-13 19:14:26 +02:00
Megamouse c41595e79f Qt: implement auto_scroll_label and use it for the settings descriptions 2026-08-13 14:42:31 +02:00
Lalit Shankar Chowdhury bf541b5828 qt: make settings description static 2026-08-13 14:42:31 +02:00
Megamouse 41e2d101e7 Update discord-rpc 2026-08-13 11:55:39 +02:00
kd-11 c27b38f300 vk: Run GC on the driver manager thread 2026-08-13 11:39:34 +03:00
kd-11 5bd7fd7817 vk: Implement an asynchronous driver manager thread 2026-08-13 11:39:34 +03:00
kd-11 7f9b8d23cd vk: Enhanced thread safety when handling the query subpool allocation cache 2026-08-13 11:39:34 +03:00
kd-11 01e3fed466 vk: Enhanced thread safety when handling descriptor subpools 2026-08-13 11:39:34 +03:00
kd-11 ea0e10e704 vk: Ensure all drawable surfaces invalidate fbo cache on deletion 2026-08-13 11:39:34 +03:00
kd-11 c2eb11eae6 vk: Seal some data leaks with the framebuffer cache
- Maybe fixes some device lost crashes when running in VRAM-constrained situations
2026-08-13 11:39:34 +03:00
Walter 26e37d8c8c [SPU LLVM] Decrease SHUFB's constant generation target requirements
The constant generation AVX512-ICL path only requires the feature GFNI, which exists on non-AVX512 CPUs. This was a hold-over from when the shuffle step was merged together. (The proceeding unsigned minimum is from SSE2)
2026-08-12 15:38:43 +03:00
Lalit Shankar Chowdhury 9b1eb45a47 PPU: implement AVX2 path for gv_rol32 2026-08-12 14:01:33 +03:00
kd-11 92870a3d4e rsx: nv0039 cleanup
- Enforce some behavior observed on real hardware
2026-08-12 03:33:10 +03:00
Nick Gregory f7cfdc6570 rsx: Fix nv0039 image (de)interleaving functionality 2026-08-11 19:40:15 +00:00
Megamouse a603fbba8c Update discord-rpc 2026-08-11 15:52:57 +02:00
Megamouse db907a2586 TAR: Simplify result string creation 2026-08-11 10:55:02 +02:00
Megamouse b9cee7a3a9 Fix IsPathInsideDir arguments during file extraction 2026-08-11 10:55:02 +02:00
Megamouse d7c15851b4 Qt: run initial dialogs on the main event loop 2026-08-11 08:56:42 +02:00
Megamouse a723152dea Qt: make sure progress dialog stays hidden in the beginning 2026-08-11 08:56:42 +02:00
RipleyTom 2f4034590f sys_net: fix possible event gap in recvfrom 2026-08-10 20:03:56 +03:00
RipleyTom 7b6a8cc2d6 sys_net: more fixes
Add parameter checks to sys_net_infoctl
Fix P2P getpeername() fatal
Fix possible deadlock in tcp_timeout_monitor
Remove vport assert from poll
Add upgrade path for new P2PS state in savestates
Fix P2PS close_stream() not waking threads
Fix possible deadlock in P2PS connect()
Ensure RST is sent on unhandled packets
2026-08-10 20:03:56 +03:00
RipleyTom 6df35891ad sys_net: fixes and improvements
Add thread lock for p2p sockets(used for poll/select to avoid event gap)
Fix poll/select returning EINTR if no sockets were polled
Implement sys_net_infoctl cmd 6(sys_net_get_sockinfo)
Cleanup sys_net_infoctl cmd 9 code
Add SYS_NET_STATE_* values to header
Rewrite P2P and P2PS poll/select implementations
Add extra P2PS state for disconnected and report it as readable with 0 bytes(EOF)
Fix P2PS sockets connecting without being bound first missing hashmap insert
Add missing error check in sceNpSignalingActivateConnection
2026-08-10 20:03:56 +03:00
Megamouse eaef23d43a unself: fix overflow checks 2026-08-10 17:35:26 +02:00
Megamouse 8cde4b2153 unself: fix more potential OOB 2026-08-10 17:35:26 +02:00
Megamouse ed3af84437 unself: add some sanity checks and optimize uncompress a bit 2026-08-10 17:35:26 +02:00
Megamouse 81b85e55e4 unself: cache buffer during decrypt 2026-08-10 17:35:26 +02:00
Megamouse 9af11f5cf1 unself: add some OOB checks 2026-08-10 17:35:26 +02:00
Megamouse d6d5c60823 ISO: mark archive as invalid on error and add sanity checks 2026-08-10 14:45:00 +02:00
Megamouse 0230580a88 ISO: exit loop at end of file 2026-08-10 14:45:00 +02:00
Megamouse ae98583ed2 ISO: ensure filename size during parsing 2026-08-10 14:45:00 +02:00
Megamouse 628ea5ec15 ISO: ensure we're not trying to install files outside of their parent 2026-08-10 14:45:00 +02:00
Megamouse 400d9a1c24 ISO: fix potential overflow 2026-08-10 14:45:00 +02:00
Megamouse 58ef670992 Add file path validations during extraction 2026-08-10 12:35:49 +02:00
Megamouse de13ae4753 Initialize emu callbacks before initializing the emulator 2026-08-10 12:35:49 +02:00
Megamouse f48ca59235 unedat: fix division by 0 2026-08-10 10:53:30 +02:00
Megamouse 804e06356b unedat: fix some data types to prevent overflow 2026-08-10 10:53:30 +02:00
Megamouse 70e3e15e2f unedat: Fix more potential OOB reads 2026-08-10 10:53:30 +02:00
Megamouse 852407b8cb unedat: don't use raw pointers everywhere and use more const 2026-08-10 10:53:30 +02:00
Megamouse f945ab62c0 Fix steam shortcut creation
Look for AutoLogin by first.
Look for MostRecent and Timestamp as fallbacks.
2026-08-10 09:10:42 +02:00
Megamouse ec208289fa elf: remove unnecessary -1 on both sides of a size check to prevent underflow 2026-08-10 02:34:41 +02:00
Megamouse b93c4733a8 Fix OOB buffer read in TRPLoader::LoadHeader 2026-08-10 01:33:53 +02:00
Walter 6d42df0ffc CPUTranslator: Add missing Intel Intrinsic header
MSVC compiles without it, but other compilers need it to be explicitly included.
2026-08-09 09:13:41 +03:00
Walter 502ea1f436 CPUTranslator: Additional constant folding for intrinsics
LLVM is unable to constant fold its X86 intrinsics directly, so this patch adds manual evaluation by calling their equivalent Intel Intrinsic.
2026-08-09 09:13:41 +03:00
Lalit Shankar Chowdhury 8d034a36e8 vk: Sort enumerated GPUs according to priority 2026-08-08 18:39:16 +03:00
Lalit Shankar Chowdhury 75fea2216b Qt: Remember last used path when adding games from folder or ISO
Signed-off-by: Lalit Shankar Chowdhury <[email protected]>
2026-08-08 16:29:09 +02:00
Florin9doi 3d587726a2 spu: Clear the MFC_LSA_offs bits higher than the limit 2026-08-05 15:19:12 +03:00
Sanjay Govind f3f52feddf Update SDL to 3.4.14 2026-08-05 08:47:26 +02:00
AniandMegamouse 652cf60bfe Update .github/ISSUE_TEMPLATE/1-regression-report.yml
Co-authored-by: Megamouse <[email protected]>
2026-08-04 22:34:03 +02:00
Ani 96fee314f0 .github: Improve regression issue template
- Require 3 log files
- Remove quick summary
- Remove other details
- Add dropdown to confirm user has uploaded the required log files
- Specify visual capture as RSX .rrc or RenderDoc .rdc
2026-08-04 22:34:03 +02:00
Megamouse a6d07c0e55 rsx: fix exception data_heap in debug builds
The AUDIT in .inside() triggers whenever m_cached_buffer_range was not
assigned yet.
Added another AUDIT to ensure that m_cached_buffer_range is valid after
assignment.
2026-08-04 08:32:51 +02:00
Antonino Di Guardo 4214dff35e rsx: Apply alpha test for all primitive types
Enables alpha test for point sprites, lines, etc as required by the GL spec.
2026-08-03 22:58:25 +00:00
Megamouse 228d04195d Fix infinite ensure recursion in debug builds 2026-08-03 23:33:45 +02:00
Megamouse ee8988ea21 msvc: fix openal debug build 2026-08-03 22:45:24 +02:00
Megamouse efee6e2423 msvc: fix libprotobuf-lite debug dependency 2026-08-03 21:45:12 +02:00
Elad c36590bb06 VSH fix 2026-08-03 18:53:00 +03:00
Elad daa437904e Fix memory leak in cellCamerEnd
cellCameraOpenEx already dealloacted pbuf, but this was not updated by the internal structure and caused cellCameraEnd to call vm::dealloc on the same addresses again. This can lead to cellCameraEnd to deallocate memory that is not related to cellCamera at all!
2026-08-03 11:20:53 +03:00
Elad df47950f98 lv2: Support more formatters for CellError 2026-08-03 11:20:53 +03:00
Elad 2d88aefbba Implement vm::hle_malloc_allocator
Memory allocator using C-memalign and C-free functions of CellOS.
Safer alternative to page allocator in HLE.
2026-08-03 11:20:53 +03:00
Elad 7bdd927957 Ancient emulation bug fixes
* Register wait flat for sys_overlay_load_module and sys_overlay_load_module_by_fd
* Register wait flag for cellOskDialogLoadAsync
* Register wait flag for cellCameraOpenEx
2026-08-03 11:20:53 +03:00
Alasonga f61c0cb5df cmake: fix Windows/MSVC build issues with zlib path and wolfSSL TLS1.3 2026-08-03 09:24:56 +02:00
Megamouse 59e59c7de6 Fix recording codec selection 2026-08-02 23:46:45 +02:00
Megamouse 401cc4ae8f recording: support AV_SAMPLE_FMT_FLT 2026-08-02 23:46:45 +02:00
Megamouse 9d91ba6168 Log recording config 2026-08-02 23:46:45 +02:00
Elad a0bebfa5ca Remove unused setting 2026-08-02 15:54:27 +03:00
Elad d7ed328f4f Fix vm::writer_lock deadlock-inducing usage on PPU 2026-08-02 15:54:27 +03:00
Megamouse d509eb8664 qt: reduce lines of code in settings_dialog 2026-08-02 09:53:33 +02:00
Megamouse 84d5056e44 Update FAudio to 26.08 2026-08-02 00:11:19 +02:00
Antonino Di Guardo f113e7bb93 rsx/copypasta: Fix reversed 2-sided lighting input registers for FS stage (#19123) 2026-08-01 12:48:07 +00:00
digant73 5295f3ac18 fix emulator crash on crashed game window closure 2026-08-01 01:58:20 +03:00
626 changed files with 35084 additions and 15194 deletions
+10 -25
View File
@@ -9,23 +9,12 @@ AVVER="${COMM_TAG}-${COMM_COUNT}"
export LVER="${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}"
echo "AVVER=$AVVER" >> .ci/ci-vars.env
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk
brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true
# Clang depends on LLVM_COMPILER_VER so it needs to be installed here.
sudo port install "clang-$LLVM_COMPILER_VER"
export CXX=clang++
export CC=clang
export BREW_PATH;
BREW_PATH="$(brew --prefix)"
export BREW_BIN="$BREW_PATH/bin"
export BREW_SBIN="$BREW_PATH/sbin"
export WORKDIR;
WORKDIR="$(pwd)"
@@ -53,20 +42,16 @@ cd "$WORKDIR"
ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER"
export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN"
export SDL3_DIR="$BREW_PATH/opt/sdl3/lib/cmake/SDL3"
export SDL3_DIR="/opt/local/lib/cmake/SDL3"
export OpenCV_DIR="/opt/local/libexec/opencv4"
export PATH="/opt/local/libexec/llvm-$LLVM_COMPILER_VER/bin:/$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH"
export LDFLAGS="-L/opt/local/lib -Wl,-rpath,/opt/local/lib"
export VULKAN_SDK="/opt/local"
export LLVM_DIR="/opt/local/libexec/llvm-$LLVM_COMPILER_VER"
export PATH="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$PATH"
export LDFLAGS="-L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++ -L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/unwind -lunwind"
export VULKAN_SDK
VULKAN_SDK="$BREW_PATH/opt/molten-vk"
ln -s "$BREW_PATH/opt/vulkan-loader/lib/libvulkan.dylib" "$VULKAN_SDK/lib/libvulkan.dylib"
export LLVM_DIR
LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER"
# Pull all the submodules except some
# shellcheck disable=SC2046
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules)
git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/opencv/ && !/libsdl-org/ && !/feralinteractive/ && !/curl/ && !/zlib/ { print $3 }' .gitmodules)
mkdir build && cd build || exit 1
# The below should be uncommented once bugs with Qt 6 QListWidgets when using the OS 26 visual style are resolved.
@@ -91,7 +76,7 @@ cmake .. \
-DUSE_SYSTEM_OPENCV=ON \
-G Ninja
ninja -j4; build_status=$?;
ninja; build_status=$?;
cd ..
+2 -2
View File
@@ -37,7 +37,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
# Download translations
mkdir -p "./AppDir/usr/translations"
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
ZIP_URL=$(curl -fsSL --retry 3 --retry-delay 60 "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
@@ -45,7 +45,7 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
curl -fsSL --retry 3 --retry-delay 60 -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
+8 -31
View File
@@ -5,15 +5,12 @@ cd build || exit 1
cd bin
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.2-rc1/MoltenVK-macos-privateapi.tar
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.2/MoltenVK-macos-privateapi.tar
tar -xvf MoltenVK-macos-privateapi.tar
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
cp "MoltenVK/MoltenVK/dynamic/dylib/macOS/MoltenVK_icd.json" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
sed -i '' "s/.\//..\/..\/..\/Frameworks\//g" "rpcs3.app/Contents/Resources/vulkan/icd.d/MoltenVK_icd.json"
cp "$(realpath $BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib"
cp "$(realpath $BREW_PATH/opt/gcc/lib/gcc/current/libgcc_s.1.1.dylib)" "rpcs3.app/Contents/Frameworks/libgcc_s.1.1.dylib"
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
@@ -26,12 +23,12 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
../../.ci/optimize-mac.sh rpcs3.app
# Download translations
mkdir -p "rpcs3.app/Contents/translations"
mkdir -p "rpcs3.app/Contents/Resources/translations"
ZIP_URL="https://github.com/RPCS3/rpcs3_translations/releases/latest/download/RPCS3-languages.zip"
echo "Downloading translations from: $ZIP_URL"
if curl -fsSL "$ZIP_URL" -o "translations.zip"; then
if curl -fsSL --retry 3 --retry-delay 60 "$ZIP_URL" -o "translations.zip"; then
echo "Successfully downloaded translations."
if unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1; then
if unzip -o translations.zip -d "rpcs3.app/Contents/Resources/translations" >/dev/null 2>&1; then
rm -f translations.zip
else
echo "Failed to extract translations.zip. Continuing without translations."
@@ -43,35 +40,15 @@ fi
# Copy Qt translations manually
QT_TRANS="$WORKDIR/qt-downloader/$QT_VER/clang_64/translations"
cp $QT_TRANS/qt_*.qm rpcs3.app/Contents/translations
cp $QT_TRANS/qtbase_*.qm rpcs3.app/Contents/translations
cp $QT_TRANS/qtmultimedia_*.qm rpcs3.app/Contents/translations
rm -f rpcs3.app/Contents/translations/qt_help_*.qm || true
cp $QT_TRANS/qt_*.qm rpcs3.app/Contents/Resources/translations
cp $QT_TRANS/qtbase_*.qm rpcs3.app/Contents/Resources/translations
cp $QT_TRANS/qtmultimedia_*.qm rpcs3.app/Contents/Resources/translations
rm -f rpcs3.app/Contents/Resources/translations/qt_help_*.qm || true
# Need to do this rename hack due to case insensitive filesystem
mv rpcs3.app RPCS3_.app
mv RPCS3_.app RPCS3.app
# Hack to fix rpath issues
BIN="RPCS3.app/Contents/MacOS/rpcs3"
install_name_tool -delete_rpath /opt/homebrew/lib $BIN || true
install_name_tool -delete_rpath /usr/local/lib $BIN || true
# Fix dylib IDs
for lib in RPCS3.app/Contents/Frameworks/*.dylib; do
name=$(basename "$lib")
install_name_tool -id "@rpath/$name" "$lib"
done
# Rewrite any hardcoded Homebrew paths to use @rpath
find "RPCS3.app/Contents/" -type f \( -perm +111 -o -name "*.dylib" \) | while read -r bin; do
otool -L "$bin" | grep -E "/opt/homebrew|/usr/local" | awk '{print $1}' | while read -r dep; do
base=$(basename "$dep")
echo "Fixing $dep -> @rpath/$base in $bin"
install_name_tool -change "$dep" "@rpath/$base" "$bin"
done
done
# NOTE: "--deep" is deprecated
codesign --deep -fs - RPCS3.app
+2 -2
View File
@@ -28,7 +28,7 @@ curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -f ISO-8859-1 -t UTF-8
# Download translations
mkdir -p ./bin/share/qt6/translations
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
ZIP_URL=$(curl -fsSL --retry 3 --retry-delay 60 "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
@@ -36,7 +36,7 @@ if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
curl -fsSL --retry 3 --retry-delay 60 -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
+2 -2
View File
@@ -18,7 +18,7 @@ curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -t UTF-8 1> ./bin/GuiC
# Download translations
mkdir -p ./bin/qt6/translations
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
ZIP_URL=$(curl -fsSL --retry 3 --retry-delay 60 "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
@@ -26,7 +26,7 @@ if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
curl -fsSL --retry 3 --retry-delay 60 -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
+32 -25
View File
@@ -7,14 +7,7 @@ body:
attributes:
value: |
# Summary
Please do not ask for help or report compatibility regressions here, use [RPCS3 Discord server](https://discord.gg/rpcs3) or [forums](https://forums.rpcs3.net/) instead.
- type: textarea
id: quick-summary
attributes:
label: Quick summary
description: Please briefly describe what has stopped working correctly.
validations:
required: true
Please do not ask for help here, use the [RPCS3 Discord server](https://discord.gg/rpcs3) or the [RPCS3 Forum](https://forums.rpcs3.net/) instead.
- type: textarea
id: details
attributes:
@@ -26,21 +19,29 @@ body:
attributes:
value: |
# Identify the regressed build
Please provide the _exact_ build (or commit) information that introduced the regression you're reporting.
Please provide the _exact_ build information that **introduced** the regression you're reporting (i.e. the first version where it happens).
* 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.
* You can find the [History of RPCS3 builds](https://rpcs3.net/builds) here.
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
* **Only change settings that are required for the game to work**
- type: input
id: regressed-build
id: last-good-build
attributes:
label: Build with regression
description: Provide _exact_ build (or commit) information that introduced the regression you're reporting.
label: Last working build
description: Provide the _exact_ last working build. This needs to be the build right before the first broken build.
placeholder: v0.0.23-13948-31df99f7
validations:
required: true
- type: input
id: first-bad-build
attributes:
label: First broken build
description: Provide the _exact_ first broken build. This needs to be the build right after the last working build.
placeholder: v0.0.23-13949-d1a46b49
validations:
required: true
- type: markdown
attributes:
value: |
@@ -56,12 +57,25 @@ body:
- type: textarea
id: logs
attributes:
label: Attach two log files
label: Attach 3 log files
description: |
Attach one file for the build with regression and another for a build that works.
* A RPCS3 log file for the last working build
* A RPCS3 log file for the first broken build
* A RPCS3 log file for the latest master build (not required if latest master build is the first broken build)
Drag & drop the files into this input field, or upload them to another service (f.ex. Dropbox, Mega) and provide a link.
validations:
required: true
- type: dropdown
id: require-log-files
attributes:
label: I have attached all 3 required log files
options:
- "Yes"
- "No"
default: 1
validations:
required: true
- type: markdown
attributes:
value: |
@@ -73,8 +87,8 @@ body:
- 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.
label: Attach the RSX (.rrc) or RenderDoc (.rdc) capture files for visual issues
description: Compress your capture with .7z, .gz, etc. and attach it here, or upload it to another service (f.ex. Dropbox, Mega) and provide a link.
validations:
required: false
- type: textarea
@@ -84,10 +98,3 @@ body:
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
id: other-details
attributes:
label: Other details
description: Include anything else you deem to be important.
validations:
required: false
+9
View File
@@ -0,0 +1,9 @@
ports:
- name: abseil
- name: ccache
- name: gtest
- name: opencv4
- name: sdl3
- name: vulkan-headers
- name: vulkan-loader
- name: MoltenVK
+51
View File
@@ -0,0 +1,51 @@
name: Label PRs
on:
pull_request_target:
types:
- opened
- edited
jobs:
Label_PRs:
name: Label from title and linked issues
runs-on: ubuntu-latest
if: github.repository == 'RPCS3/rpcs3'
permissions:
issues: read
pull-requests: write
steps:
- name: Add labels from title and linked issues
uses: actions/github-script@v9
with:
retries: 3
script: |
const AREAS = [
'Build and CI', 'CLI', 'Filesystem', 'GUI', 'Input', 'ISO', 'Localization', 'Overlay', 'Render: Vulkan', 'RSX', 'Savestates',
];
const {repository: {pullRequest: pull}} = await github.graphql(`
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
labels(first: 100) { nodes { name } }
closingIssuesReferences(first: 50) { nodes { labels(first: 100) { nodes { name } } } }
timelineItems(itemTypes: [UNLABELED_EVENT], last: 100) { nodes { ... on UnlabeledEvent { label { name } } } }
}
}
}`, {...context.repo, number: context.issue.number});
if (pull.labels.nodes.some(x => AREAS.includes(x.name)))
return;
// Skip labels a maintainer removed
const removed = pull.timelineItems.nodes.map(x => x.label.name);
const fromTitle = /^rsx\b/i.test(context.payload.pull_request.title) ? ['RSX'] : [];
const fromIssues = pull.closingIssuesReferences.nodes.flatMap(i => i.labels.nodes.map(x => x.name));
const labels = [...new Set([...fromTitle, ...fromIssues])]
.filter(name => AREAS.includes(name) && !removed.includes(name));
if (labels.length > 0) {
console.log(`Adding labels: ${labels.join(', ')}`);
await github.rest.issues.addLabels({...context.repo, issue_number: context.issue.number, labels});
}
+215 -45
View File
@@ -22,31 +22,95 @@ env:
BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/
jobs:
Clean_Code_Caches:
name: Clean caches
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
pull-requests: read
outputs:
skip_ccache: ${{ steps.pr_labels.outputs.skip_ccache }}
steps:
- name: "Look for the 'CI: Disable Build Caches' label on the PR"
id: pr_labels
if: github.event_name == 'pull_request'
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
# Query the live labels so adding the label and re-running all jobs works without a new push
labels=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" --jq '.labels[].name' 2>/dev/null) \
|| labels=$(jq -r '.pull_request.labels[]?.name' "$GITHUB_EVENT_PATH")
echo "Labels: ${labels:-<none>}"
skip=false
if grep -qix 'ci: disable build caches' <<< "$labels"; then
skip=true
echo "Builds will not restore ccache because of the 'CI: Disable Build Caches' label." >> "$GITHUB_STEP_SUMMARY"
fi
echo "skip_ccache=$skip" >> "$GITHUB_OUTPUT"
- name: "Look for the 'Shaders' or 'CI: Disable Build Caches' label on the merged PR"
id: labels
if: github.event_name == 'push' && github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master'
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
found=false
for sha in $(jq -r '[.after, (.commits[]?.id)] | map(select(. != null)) | unique[]' "$GITHUB_EVENT_PATH"); do
labels=$(gh api "repos/${{ github.repository }}/commits/$sha/pulls" --jq '.[].labels[].name' 2>/dev/null || true)
echo "$sha: ${labels:-<no associated PR>}"
if grep -qixE 'shaders|ci: disable build caches' <<< "$labels"; then
found=true
fi
done
echo "clean=$found" >> "$GITHUB_OUTPUT"
- name: Remove all code caches
if: steps.labels.outputs.clean == 'true'
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
gh api --paginate "repos/${{ github.repository }}/actions/caches?per_page=100" \
--jq '.actions_caches[] | select(.key | contains("-ccache-")) | "\(.id)\t\(.key)"' > code_caches.tsv
if [ ! -s code_caches.tsv ]; then
echo "No code caches to remove."
exit 0
fi
while IFS=$'\t' read -r id key; do
echo "Removing $key (id $id)"
gh api -X DELETE "repos/${{ github.repository }}/actions/caches/$id" --silent
done < code_caches.tsv
echo "Removed $(wc -l < code_caches.tsv) code cache(s) after merging a 'Shaders' or 'CI: Disable Build Caches' PR." >> "$GITHUB_STEP_SUMMARY"
Linux_Build:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:2.0"
docker_img: "rpcs3/rpcs3-ci-jammy:2.1"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: clang
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:2.0"
docker_img: "rpcs3/rpcs3-ci-jammy:2.1"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.0"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.1"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: clang
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.0"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:2.1"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
@@ -68,6 +132,7 @@ jobs:
fetch-depth: 0
- name: Restore build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
@@ -117,6 +182,7 @@ jobs:
Mac_Build:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
strategy:
fail-fast: false
@@ -134,20 +200,26 @@ jobs:
runs-on: ${{ matrix.runs-on }}
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.11.1'
QT_VER: '6.12.0'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '22'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }}
UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }}
RUN_UNIT_TESTS: ${{ matrix.name == 'Apple Silicon' && (github.event_name == 'pull_request' && 'ON') || 'OFF' }}
RUN_UNIT_TESTS: github.event_name == 'pull_request' && 'ON' || 'OFF'
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Setup MacPorts
uses: melusina-org/setup-macports@v1
with:
cache: 'disable'
- name: Restore Build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
@@ -206,15 +278,16 @@ jobs:
Windows_Build:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 Windows
runs-on: windows-2025-vs2026
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.11.1'
QT_VER: '6.12.0'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202605090529'
QT_DATE: '202609280346'
LLVM_VER: '22.1.8'
VULKAN_VER: '1.4.341.1'
VULKAN_SDK_SHA: 'bcf2d75aa9556889ab974858666e20b3655b6055a0db704ccb47279ff33b5bfe'
@@ -223,9 +296,12 @@ jobs:
CCACHE_DIR: 'C:\ccache'
CCACHE_INODECACHE: 'true'
CCACHE_SLOPPINESS: 'time_macros'
CCACHE_MAXSIZE: '7G'
CCACHE_LAUNCH_ARGS: '-DCMAKE_C_COMPILER_LAUNCHER=C:\ccache_bin\ccache.exe -DCMAKE_CXX_COMPILER_LAUNCHER=C:\ccache_bin\ccache.exe'
DEPS_CACHE_DIR: ./dependency_cache
UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win"
SKIP_RPCS3_TEST: 'true'
steps:
- name: Checkout repository
@@ -249,6 +325,132 @@ jobs:
run: .ci/get_keys-windows.sh
- name: Restore Build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
path: ${{ env.CCACHE_DIR }}
key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}"
restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}-
- name: Restore Dependencies Cache
uses: actions/cache/restore@main
id: restore-dependencies-cache
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}"
restore-keys: ${{ runner.os }}-${{ env.COMPILER }}-
- name: Download and unpack dependencies
run: |
.ci/setup-windows.sh
.ci/setup-windows-ci-vars.sh win64 msvc
- name: Export Variables
run: |
while IFS='=' read -r key val; do
# Skip lines that are empty or start with '#'
[[ -z "$key" || "$key" =~ ^# ]] && continue
echo "$key=$val" >> "${{ github.env }}"
done < .ci/ci-vars.env
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@main
- name: Compile RPCS3
shell: pwsh
run: msbuild rpcs3.sln /p:Configuration=Release /v:minimal /p:Platform=x64 /p:PreferredToolArchitecture=x64 /p:CLToolPath=${{ env.CCACHE_BIN_DIR }} /p:UseMultiToolTask=true /p:CustomAfterMicrosoftCommonTargets="${{ github.workspace }}\buildfiles\msvc\ci_only.targets"
- name: Pack up build artifacts
run: |
mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}"
.ci/deploy-windows.sh
- name: Upload artifacts (7z)
uses: actions/upload-artifact@main
with:
name: RPCS3 for Windows (MSVC)
path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}
compression-level: 0
if-no-files-found: error
- name: Deploy master build to GitHub Releases
if: |
github.event_name != 'pull_request' &&
github.repository == 'RPCS3/rpcs3' &&
github.ref == 'refs/heads/master'
env:
RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }}
run: .ci/github-upload.sh
- name: Show ccache stats
if: github.ref == 'refs/heads/master'
run: ccache --show-stats --verbose
- name: Save Build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
- name: Save Dependencies Cache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }}
Windows_Build_Test:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 Windows Unit Test
runs-on: windows-2025-vs2026
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.12.0'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202609280346'
LLVM_VER: '22.1.8'
VULKAN_VER: '1.4.341.1'
VULKAN_SDK_SHA: 'bcf2d75aa9556889ab974858666e20b3655b6055a0db704ccb47279ff33b5bfe'
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
CCACHE_BIN_DIR: 'C:\ccache_bin'
CCACHE_DIR: 'C:\ccache'
CCACHE_INODECACHE: 'true'
CCACHE_SLOPPINESS: 'time_macros'
CCACHE_MAXSIZE: '7G'
CCACHE_LAUNCH_ARGS: '-DCMAKE_C_COMPILER_LAUNCHER=C:\ccache_bin\ccache.exe -DCMAKE_CXX_COMPILER_LAUNCHER=C:\ccache_bin\ccache.exe'
DEPS_CACHE_DIR: ./dependency_cache
UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win"
SKIP_RPCS3_EXE: 'true'
steps:
- name: Checkout repository
uses: actions/checkout@main
with:
fetch-depth: 0
- name: Setup NuGet
uses: nuget/setup-nuget@v4
- name: Restore NuGet packages
run: nuget restore rpcs3.sln
- name: Setup env
shell: pwsh
run: |
echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }}
echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }}
- name: Get Cache Keys
run: .ci/get_keys-windows.sh
- name: Restore Build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
@@ -289,44 +491,9 @@ jobs:
shell: pwsh
run: build\lib\Release-x64\rpcs3_test.exe
- name: Pack up build artifacts
run: |
mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}"
.ci/deploy-windows.sh
- name: Upload artifacts (7z)
uses: actions/upload-artifact@main
with:
name: RPCS3 for Windows (MSVC)
path: ${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}
compression-level: 0
if-no-files-found: error
- name: Deploy master build to GitHub Releases
if: |
github.event_name != 'pull_request' &&
github.repository == 'RPCS3/rpcs3' &&
github.ref == 'refs/heads/master'
env:
RPCS3_TOKEN: ${{ secrets.RPCS3_TOKEN }}
run: .ci/github-upload.sh
- name: Save Build Ccache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ steps.restore-build-ccache.outputs.cache-primary-key }}
- name: Save Dependencies Cache
if: github.ref == 'refs/heads/master'
uses: actions/cache/save@main
with:
path: ${{ env.DEPS_CACHE_DIR }}
key: ${{ steps.restore-dependencies-cache.outputs.cache-primary-key }}
Windows_Build_Clang:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
strategy:
fail-fast: false
@@ -386,6 +553,7 @@ jobs:
p7zip
- name: Restore build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
@@ -436,6 +604,7 @@ jobs:
FreeBSD_Build:
# Only run push event on master branch of main repo, but run all PRs
needs: Clean_Code_Caches
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 FreeBSD
runs-on: ubuntu-latest
@@ -455,6 +624,7 @@ jobs:
fetch-depth: 0
- name: Restore Build Ccache
if: needs.Clean_Code_Caches.outputs.skip_ccache != 'true'
uses: actions/cache/restore@main
id: restore-build-ccache
with:
@@ -469,7 +639,7 @@ jobs:
envs: 'QT_VER_MAIN LLVM_COMPILER_VER CCACHE_DIR CC CXX LLVM_CONFIG'
usesh: true
copyback: false
release: "14.4"
release: "14.5"
run: .ci/install-freebsd.sh && .ci/build-freebsd.sh
- name: Save Build Ccache
+3
View File
@@ -126,3 +126,6 @@ CMakeUserPresets.json
.cache/
.lldbinit
# claude
CLAUDE.md
+11 -3
View File
@@ -49,9 +49,11 @@
<PropertyGroup Label="UserMacros">
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Release" ^
-DCMAKE_INSTALL_PREFIX="./Release" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
@@ -70,14 +72,15 @@
</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Debug" ^
-DCMAKE_INSTALL_PREFIX="./Debug" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug ^
-DCMAKE_SYSTEM_VERSION=10.0 ^
-DLIBTYPE=STATIC ^
-DALSOFT_UTILS=false ^
-DALSOFT_EXAMPLES=false ^
-DALSOFT_INSTALL=false ^
@@ -92,6 +95,11 @@
echo Copying..
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\OpenAL32.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)"
</CmakeCopyCLI>
<CmakeCopyDebugCLI>
echo Copying..
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\OpenAL32.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\OpenAL32.dll" "$(SolutionDir)bin"
</CmakeCopyDebugCLI>
<CmakeCleanCLI>
echo Cleaning..
del "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\OpenAL32.lib"
@@ -104,13 +112,13 @@
<NMakeBuildCommandLine>
$(CmakeDebugCLI)
ninja
$(CmakeCopyCLI)
$(CmakeCopyDebugCLI)
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>
$(CmakeCleanCLI)
$(CmakeDebugCLI)
ninja
$(CmakeCopyCLI)
$(CmakeCopyDebugCLI)
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>
$(CmakeCleanCLI)
+117 -430
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
@@ -11,16 +11,20 @@
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<Keyword>MakeFileProj</Keyword>
<ProjectGuid>{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}</ProjectGuid>
<RootNamespace>libcurl</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 Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseOfMfc>false</UseOfMfc>
<CharacterSet>Unicode</CharacterSet>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Makefile</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
@@ -31,436 +35,119 @@
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<PropertyGroup Label="UserMacros">
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Release" ^
-DCMAKE_INSTALL_PREFIX="./Release" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
-DCMAKE_SYSTEM_VERSION=10.0 ^
-DBUILD_SHARED_LIBS=OFF ^
-DBUILD_CURL_EXE=OFF ^
-DBUILD_TESTING=OFF ^
-DCURL_USE_WOLFSSL=ON ^
-DCURL_USE_IPV6=ON ^
-DCURL_USE_SCHANNEL=OFF ^
-DCURL_USE_OPENSSL=OFF ^
-DCURL_USE_LIBPSL=OFF ^
-DCURL_ZLIB=OFF ^
-DCURL_ZSTD=OFF ^
-DCURL_USE_LIBSSH2=OFF ^
-DCMAKE_C_FLAGS="/DWOLFSSL_ALT_CERT_CHAINS /DHAVE_SNI /DNO_MD4 /DWOLFSSL_USER_SETTINGS /DUSE_IPV6 /I\"$(SolutionDir)3rdparty\curl\extra\" /I\"$(SolutionDir)3rdparty\wolfssl\wolfssl\" /I\"$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl\" /I\"$(SolutionDir)3rdparty\wolfssl\extra\win32\"" ^
-DCMAKE_C_FLAGS_RELEASE="/O2 /Ob1 /GF /Gy" ^
-DWOLFSSL_INCLUDE_DIR="$(SolutionDir)3rdparty\wolfssl\wolfssl" ^
-DWOLFSSL_LIBRARY="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\wolfssl.lib" ^
"$(SolutionDir)3rdparty\curl\curl"
</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Debug" ^
-DCMAKE_INSTALL_PREFIX="./Debug" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug ^
-DCMAKE_SYSTEM_VERSION=10.0 ^
-DBUILD_SHARED_LIBS=OFF ^
-DBUILD_CURL_EXE=OFF ^
-DBUILD_TESTING=OFF ^
-DCURL_USE_WOLFSSL=ON ^
-DCURL_USE_IPV6=ON ^
-DCURL_USE_SCHANNEL=OFF ^
-DCURL_USE_OPENSSL=OFF ^
-DCURL_USE_LIBPSL=OFF ^
-DCURL_ZLIB=OFF ^
-DCURL_USE_LIBSSH2=OFF ^
-DCMAKE_C_FLAGS="/DWOLFSSL_ALT_CERT_CHAINS /DHAVE_SNI /DNO_MD4 /DWOLFSSL_USER_SETTINGS /DUSE_IPV6 /I\"$(SolutionDir)3rdparty\curl\extra\" /I\"$(SolutionDir)3rdparty\wolfssl\wolfssl\" /I\"$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl\" /I\"$(SolutionDir)3rdparty\wolfssl\extra\win32\"" ^
-DCMAKE_C_FLAGS_DEBUG="/Od /Ob0 /GF /Gy" ^
-DWOLFSSL_INCLUDE_DIR="$(SolutionDir)3rdparty\wolfssl\wolfssl" ^
-DWOLFSSL_LIBRARY="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\wolfssl.lib" ^
"$(SolutionDir)3rdparty\curl\curl"
</CmakeDebugCLI>
<CmakeCopyCLI>
echo Copying..
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\lib\libcurl.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)"
</CmakeCopyCLI>
<CmakeCopyDebugCLI>
echo Copying..
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\lib\libcurl-d.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)"
</CmakeCopyDebugCLI>
<CmakeCleanCLI>
echo Cleaning..
del "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\libcurl.lib"
rmdir /s /q "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
</CmakeCleanCLI>
<CmakeCleanDebugCLI>
echo Cleaning..
del "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\libcurl-d.lib"
rmdir /s /q "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
</CmakeCleanDebugCLI>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir)build\lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<NMakeBuildCommandLine>
$(CmakeDebugCLI)
ninja
$(CmakeCopyDebugCLI)
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>
$(CmakeCleanDebugCLI)
$(CmakeDebugCLI)
ninja
$(CmakeCopyDebugCLI)
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>
$(CmakeCleanDebugCLI)
</NMakeCleanCommandLine>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)build\lib\$(Configuration)-$(Platform)\</OutDir>
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<NMakeBuildCommandLine>
$(CmakeReleaseCLI)
ninja
$(CmakeCopyCLI)
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>
$(CmakeCleanCLI)
$(CmakeReleaseCLI)
ninja
$(CmakeCopyCLI)
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>
$(CmakeCleanCLI)
</NMakeCleanCommandLine>
</PropertyGroup>
<ItemDefinitionGroup>
<Midl>
<TargetEnvironment>X64</TargetEnvironment>
</Midl>
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<AdditionalIncludeDirectories>curl\include;curl\lib;extra;$(SolutionDir)3rdparty\wolfssl\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\wolfssl;$(SolutionDir)3rdparty\wolfssl\extra\win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WOLFSSL_ALT_CERT_CHAINS;HAVE_SNI;NDEBUG;BUILDING_LIBCURL;CURL_STATICLIB;USE_WOLFSSL;NO_MD4;WOLFSSL_USER_SETTINGS;USE_IPV6;SIZEOF_LONG=4;SIZEOF_LONG_LONG=8;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<FunctionLevelLinking>true</FunctionLevelLinking>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0409</Culture>
</ResourceCompile>
<Lib>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<TargetMachine>MachineX64</TargetMachine>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="curl\lib\altsvc.c" />
<ClCompile Include="curl\lib\amigaos.c" />
<ClCompile Include="curl\lib\asyn-ares.c" />
<ClCompile Include="curl\lib\asyn-base.c" />
<ClCompile Include="curl\lib\asyn-thrdd.c" />
<ClCompile Include="curl\lib\bufq.c" />
<ClCompile Include="curl\lib\bufref.c" />
<ClCompile Include="curl\lib\cf-dns.c" />
<ClCompile Include="curl\lib\cf-h1-proxy.c" />
<ClCompile Include="curl\lib\cf-h2-proxy.c" />
<ClCompile Include="curl\lib\cf-haproxy.c" />
<ClCompile Include="curl\lib\cf-https-connect.c" />
<ClCompile Include="curl\lib\cf-ip-happy.c" />
<ClCompile Include="curl\lib\cf-recvbuf.c" />
<ClCompile Include="curl\lib\cf-setup.c" />
<ClCompile Include="curl\lib\cf-socket.c" />
<ClCompile Include="curl\lib\cfilters.c" />
<ClCompile Include="curl\lib\conncache.c" />
<ClCompile Include="curl\lib\connect.c" />
<ClCompile Include="curl\lib\content_encoding.c" />
<ClCompile Include="curl\lib\cookie.c" />
<ClCompile Include="curl\lib\creds.c" />
<ClCompile Include="curl\lib\cshutdn.c" />
<ClCompile Include="curl\lib\curlx\base64.c" />
<ClCompile Include="curl\lib\curlx\basename.c" />
<ClCompile Include="curl\lib\curlx\dynbuf.c" />
<ClCompile Include="curl\lib\curlx\fopen.c" />
<ClCompile Include="curl\lib\curlx\inet_ntop.c" />
<ClCompile Include="curl\lib\curlx\inet_pton.c" />
<ClCompile Include="curl\lib\curlx\multibyte.c" />
<ClCompile Include="curl\lib\curlx\nonblock.c" />
<ClCompile Include="curl\lib\curlx\snprintf.c" />
<ClCompile Include="curl\lib\curlx\strcopy.c" />
<ClCompile Include="curl\lib\curlx\strdup.c" />
<ClCompile Include="curl\lib\curlx\strerr.c" />
<ClCompile Include="curl\lib\curlx\strparse.c" />
<ClCompile Include="curl\lib\curlx\timediff.c" />
<ClCompile Include="curl\lib\curlx\timeval.c" />
<ClCompile Include="curl\lib\curlx\version_win32.c" />
<ClCompile Include="curl\lib\curlx\wait.c" />
<ClCompile Include="curl\lib\curlx\warnless.c" />
<ClCompile Include="curl\lib\curlx\winapi.c" />
<ClCompile Include="curl\lib\curl_addrinfo.c" />
<ClCompile Include="curl\lib\curl_endian.c" />
<ClCompile Include="curl\lib\curl_fnmatch.c" />
<ClCompile Include="curl\lib\curl_fopen.c" />
<ClCompile Include="curl\lib\curl_gethostname.c" />
<ClCompile Include="curl\lib\curl_get_line.c" />
<ClCompile Include="curl\lib\curl_gssapi.c" />
<ClCompile Include="curl\lib\curl_memrchr.c" />
<ClCompile Include="curl\lib\curl_ntlm_core.c" />
<ClCompile Include="curl\lib\curl_range.c" />
<ClCompile Include="curl\lib\curl_sasl.c" />
<ClCompile Include="curl\lib\curl_sha512_256.c" />
<ClCompile Include="curl\lib\curl_share.c" />
<ClCompile Include="curl\lib\curl_sspi.c" />
<ClCompile Include="curl\lib\curl_threads.c" />
<ClCompile Include="curl\lib\curl_trc.c" />
<ClCompile Include="curl\lib\cw-out.c" />
<ClCompile Include="curl\lib\cw-pause.c" />
<ClCompile Include="curl\lib\dict.c" />
<ClCompile Include="curl\lib\dllmain.c" />
<ClCompile Include="curl\lib\dnscache.c" />
<ClCompile Include="curl\lib\doh.c" />
<ClCompile Include="curl\lib\dynhds.c" />
<ClCompile Include="curl\lib\easy.c" />
<ClCompile Include="curl\lib\easygetopt.c" />
<ClCompile Include="curl\lib\easyoptions.c" />
<ClCompile Include="curl\lib\escape.c" />
<ClCompile Include="curl\lib\fake_addrinfo.c" />
<ClCompile Include="curl\lib\file.c" />
<ClCompile Include="curl\lib\fileinfo.c" />
<ClCompile Include="curl\lib\formdata.c" />
<ClCompile Include="curl\lib\ftp.c" />
<ClCompile Include="curl\lib\ftplistparser.c" />
<ClCompile Include="curl\lib\getenv.c" />
<ClCompile Include="curl\lib\getinfo.c" />
<ClCompile Include="curl\lib\gopher.c" />
<ClCompile Include="curl\lib\hash.c" />
<ClCompile Include="curl\lib\headers.c" />
<ClCompile Include="curl\lib\hmac.c" />
<ClCompile Include="curl\lib\hostip.c" />
<ClCompile Include="curl\lib\hostip4.c" />
<ClCompile Include="curl\lib\hostip6.c" />
<ClCompile Include="curl\lib\hsts.c" />
<ClCompile Include="curl\lib\http.c" />
<ClCompile Include="curl\lib\http1.c" />
<ClCompile Include="curl\lib\http2.c" />
<ClCompile Include="curl\lib\httpsrr.c" />
<ClCompile Include="curl\lib\http_aws_sigv4.c" />
<ClCompile Include="curl\lib\http_chunks.c" />
<ClCompile Include="curl\lib\http_digest.c" />
<ClCompile Include="curl\lib\http_negotiate.c" />
<ClCompile Include="curl\lib\http_ntlm.c" />
<ClCompile Include="curl\lib\http_proxy.c" />
<ClCompile Include="curl\lib\idn.c" />
<ClCompile Include="curl\lib\if2ip.c" />
<ClCompile Include="curl\lib\imap.c" />
<ClCompile Include="curl\lib\ldap.c" />
<ClCompile Include="curl\lib\llist.c" />
<ClCompile Include="curl\lib\macos.c" />
<ClCompile Include="curl\lib\md4.c" />
<ClCompile Include="curl\lib\md5.c" />
<ClCompile Include="curl\lib\memdebug.c" />
<ClCompile Include="curl\lib\mime.c" />
<ClCompile Include="curl\lib\mprintf.c" />
<ClCompile Include="curl\lib\mqtt.c" />
<ClCompile Include="curl\lib\multi.c" />
<ClCompile Include="curl\lib\multi_ev.c" />
<ClCompile Include="curl\lib\multi_ntfy.c" />
<ClCompile Include="curl\lib\netrc.c" />
<ClCompile Include="curl\lib\openldap.c" />
<ClCompile Include="curl\lib\parsedate.c" />
<ClCompile Include="curl\lib\peer.c" />
<ClCompile Include="curl\lib\pingpong.c" />
<ClCompile Include="curl\lib\pop3.c" />
<ClCompile Include="curl\lib\progress.c" />
<ClCompile Include="curl\lib\protocol.c" />
<ClCompile Include="curl\lib\proxy.c" />
<ClCompile Include="curl\lib\psl.c" />
<ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\ratelimit.c" />
<ClCompile Include="curl\lib\request.c" />
<ClCompile Include="curl\lib\rtsp.c" />
<ClCompile Include="curl\lib\select.c" />
<ClCompile Include="curl\lib\sendf.c" />
<ClCompile Include="curl\lib\setopt.c" />
<ClCompile Include="curl\lib\sha256.c" />
<ClCompile Include="curl\lib\slist.c" />
<ClCompile Include="curl\lib\smb.c" />
<ClCompile Include="curl\lib\smtp.c" />
<ClCompile Include="curl\lib\socketpair.c" />
<ClCompile Include="curl\lib\socks.c" />
<ClCompile Include="curl\lib\socks_gssapi.c" />
<ClCompile Include="curl\lib\socks_sspi.c" />
<ClCompile Include="curl\lib\splay.c" />
<ClCompile Include="curl\lib\strcase.c" />
<ClCompile Include="curl\lib\strequal.c" />
<ClCompile Include="curl\lib\strerror.c" />
<ClCompile Include="curl\lib\system_win32.c" />
<ClCompile Include="curl\lib\telnet.c" />
<ClCompile Include="curl\lib\tftp.c" />
<ClCompile Include="curl\lib\thrdpool.c" />
<ClCompile Include="curl\lib\thrdqueue.c" />
<ClCompile Include="curl\lib\transfer.c" />
<ClCompile Include="curl\lib\uint-bset.c" />
<ClCompile Include="curl\lib\uint-hash.c" />
<ClCompile Include="curl\lib\uint-spbset.c" />
<ClCompile Include="curl\lib\uint-table.c" />
<ClCompile Include="curl\lib\url.c" />
<ClCompile Include="curl\lib\urlapi.c" />
<ClCompile Include="curl\lib\vauth\gsasl.c" />
<ClCompile Include="curl\lib\version.c" />
<ClCompile Include="curl\lib\vquic\capsule.c" />
<ClCompile Include="curl\lib\vquic\cf-capsule.c" />
<ClCompile Include="curl\lib\vquic\cf-ngtcp2-cmn.c" />
<ClCompile Include="curl\lib\vquic\cf-ngtcp2-proxy.c" />
<ClCompile Include="curl\lib\vquic\cf-ngtcp2.c" />
<ClCompile Include="curl\lib\vquic\cf-quiche.c" />
<ClCompile Include="curl\lib\vquic\vquic-tls.c" />
<ClCompile Include="curl\lib\vssh\vssh.c" />
<ClCompile Include="curl\lib\vtls\apple.c" />
<ClCompile Include="curl\lib\vtls\cipher_suite.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" />
<ClCompile Include="curl\lib\vtls\rustls.c" />
<ClCompile Include="curl\lib\vtls\vtls_config.c" />
<ClCompile Include="curl\lib\vtls\vtls_scache.c" />
<ClCompile Include="curl\lib\vtls\vtls_spack.c" />
<ClCompile Include="curl\lib\vtls\x509asn1.c" />
<ClCompile Include="curl\lib\vauth\cleartext.c" />
<ClCompile Include="curl\lib\vauth\cram.c" />
<ClCompile Include="curl\lib\vauth\digest.c" />
<ClCompile Include="curl\lib\vauth\digest_sspi.c" />
<ClCompile Include="curl\lib\vauth\krb5_gssapi.c" />
<ClCompile Include="curl\lib\vauth\krb5_sspi.c" />
<ClCompile Include="curl\lib\vauth\ntlm.c" />
<ClCompile Include="curl\lib\vauth\ntlm_sspi.c" />
<ClCompile Include="curl\lib\vauth\oauth2.c" />
<ClCompile Include="curl\lib\vauth\spnego_gssapi.c" />
<ClCompile Include="curl\lib\vauth\spnego_sspi.c" />
<ClCompile Include="curl\lib\vauth\vauth.c" />
<ClCompile Include="curl\lib\vquic\vquic.c" />
<ClCompile Include="curl\lib\vssh\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vtls\gtls.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" />
<ClCompile Include="curl\lib\vtls\mbedtls.c" />
<ClCompile Include="curl\lib\vtls\openssl.c" />
<ClCompile Include="curl\lib\vtls\schannel.c" />
<ClCompile Include="curl\lib\vtls\schannel_verify.c" />
<ClCompile Include="curl\lib\vtls\vtls.c" />
<ClCompile Include="curl\lib\vtls\wolfssl.c" />
<ClCompile Include="curl\lib\ws.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="curl\include\curl\curl.h" />
<ClInclude Include="curl\include\curl\curlver.h" />
<ClInclude Include="curl\include\curl\easy.h" />
<ClInclude Include="curl\include\curl\mprintf.h" />
<ClInclude Include="curl\include\curl\multi.h" />
<ClInclude Include="curl\include\curl\stdcheaders.h" />
<ClInclude Include="curl\include\curl\system.h" />
<ClInclude Include="curl\include\curl\typecheck-gcc.h" />
<ClInclude Include="curl\include\curl\urlapi.h" />
<ClInclude Include="curl\lib\altsvc.h" />
<ClInclude Include="curl\lib\amigaos.h" />
<ClInclude Include="curl\lib\arpa_telnet.h" />
<ClInclude Include="curl\lib\asyn.h" />
<ClInclude Include="curl\lib\bufq.h" />
<ClInclude Include="curl\lib\bufref.h" />
<ClInclude Include="curl\lib\cf-dns.h" />
<ClInclude Include="curl\lib\cf-h1-proxy.h" />
<ClInclude Include="curl\lib\cf-h2-proxy.h" />
<ClInclude Include="curl\lib\cf-haproxy.h" />
<ClInclude Include="curl\lib\cf-https-connect.h" />
<ClInclude Include="curl\lib\cf-ip-happy.h" />
<ClInclude Include="curl\lib\cf-recvbuf.h" />
<ClInclude Include="curl\lib\cf-setup.h" />
<ClInclude Include="curl\lib\cf-socket.h" />
<ClInclude Include="curl\lib\cfilters.h" />
<ClInclude Include="curl\lib\config-mac.h" />
<ClInclude Include="curl\lib\config-os400.h" />
<ClInclude Include="curl\lib\config-plan9.h" />
<ClInclude Include="curl\lib\config-riscos.h" />
<ClInclude Include="curl\lib\config-win32.h" />
<ClInclude Include="curl\lib\conncache.h" />
<ClInclude Include="curl\lib\connect.h" />
<ClInclude Include="curl\lib\content_encoding.h" />
<ClInclude Include="curl\lib\cookie.h" />
<ClInclude Include="curl\lib\creds.h" />
<ClInclude Include="curl\lib\cshutdn.h" />
<ClInclude Include="curl\lib\curlx\base64.h" />
<ClInclude Include="curl\lib\curlx\basename.h" />
<ClInclude Include="curl\lib\curlx\binmode.h" />
<ClInclude Include="curl\lib\curlx\dynbuf.h" />
<ClInclude Include="curl\lib\curlx\fopen.h" />
<ClInclude Include="curl\lib\curlx\inet_ntop.h" />
<ClInclude Include="curl\lib\curlx\inet_pton.h" />
<ClInclude Include="curl\lib\curlx\multibyte.h" />
<ClInclude Include="curl\lib\curlx\nonblock.h" />
<ClInclude Include="curl\lib\curlx\snprintf.h" />
<ClInclude Include="curl\lib\curlx\strcopy.h" />
<ClInclude Include="curl\lib\curlx\strdup.h" />
<ClInclude Include="curl\lib\curlx\strerr.h" />
<ClInclude Include="curl\lib\curlx\strparse.h" />
<ClInclude Include="curl\lib\curlx\timediff.h" />
<ClInclude Include="curl\lib\curlx\timeval.h" />
<ClInclude Include="curl\lib\curlx\version_win32.h" />
<ClInclude Include="curl\lib\curlx\wait.h" />
<ClInclude Include="curl\lib\curlx\warnless.h" />
<ClInclude Include="curl\lib\curlx\winapi.h" />
<ClInclude Include="curl\lib\curl_addrinfo.h" />
<ClInclude Include="curl\lib\curl_ctype.h" />
<ClInclude Include="curl\lib\curl_endian.h" />
<ClInclude Include="curl\lib\curl_fnmatch.h" />
<ClInclude Include="curl\lib\curl_fopen.h" />
<ClInclude Include="curl\lib\curl_gethostname.h" />
<ClInclude Include="curl\lib\curl_get_line.h" />
<ClInclude Include="curl\lib\curl_gssapi.h" />
<ClInclude Include="curl\lib\curl_hmac.h" />
<ClInclude Include="curl\lib\curl_ldap.h" />
<ClInclude Include="curl\lib\curl_md4.h" />
<ClInclude Include="curl\lib\curl_md5.h" />
<ClInclude Include="curl\lib\curl_memrchr.h" />
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
<ClInclude Include="curl\lib\curl_printf.h" />
<ClInclude Include="curl\lib\curl_range.h" />
<ClInclude Include="curl\lib\curl_sasl.h" />
<ClInclude Include="curl\lib\curl_setup.h" />
<ClInclude Include="curl\lib\curl_setup_once.h" />
<ClInclude Include="curl\lib\curl_sha256.h" />
<ClInclude Include="curl\lib\curl_sha512_256.h" />
<ClInclude Include="curl\lib\curl_share.h" />
<ClInclude Include="curl\lib\curl_sspi.h" />
<ClInclude Include="curl\lib\curl_threads.h" />
<ClInclude Include="curl\lib\curl_trc.h" />
<ClInclude Include="curl\lib\cw-out.h" />
<ClInclude Include="curl\lib\cw-pause.h" />
<ClInclude Include="curl\lib\dict.h" />
<ClInclude Include="curl\lib\dnscache.h" />
<ClInclude Include="curl\lib\doh.h" />
<ClInclude Include="curl\lib\dynhds.h" />
<ClInclude Include="curl\lib\easyif.h" />
<ClInclude Include="curl\lib\easyoptions.h" />
<ClInclude Include="curl\lib\easy_lock.h" />
<ClInclude Include="curl\lib\escape.h" />
<ClInclude Include="curl\lib\fake_addrinfo.h" />
<ClInclude Include="curl\lib\file.h" />
<ClInclude Include="curl\lib\fileinfo.h" />
<ClInclude Include="curl\lib\formdata.h" />
<ClInclude Include="curl\lib\ftp-int.h" />
<ClInclude Include="curl\lib\ftp.h" />
<ClInclude Include="curl\lib\ftplistparser.h" />
<ClInclude Include="curl\lib\functypes.h" />
<ClInclude Include="curl\lib\getinfo.h" />
<ClInclude Include="curl\lib\gopher.h" />
<ClInclude Include="curl\lib\hash.h" />
<ClInclude Include="curl\lib\headers.h" />
<ClInclude Include="curl\lib\hostip.h" />
<ClInclude Include="curl\lib\hsts.h" />
<ClInclude Include="curl\lib\http.h" />
<ClInclude Include="curl\lib\http1.h" />
<ClInclude Include="curl\lib\http2.h" />
<ClInclude Include="curl\lib\httpsrr.h" />
<ClInclude Include="curl\lib\http_aws_sigv4.h" />
<ClInclude Include="curl\lib\http_chunks.h" />
<ClInclude Include="curl\lib\http_digest.h" />
<ClInclude Include="curl\lib\http_negotiate.h" />
<ClInclude Include="curl\lib\http_ntlm.h" />
<ClInclude Include="curl\lib\http_proxy.h" />
<ClInclude Include="curl\lib\idn.h" />
<ClInclude Include="curl\lib\if2ip.h" />
<ClInclude Include="curl\lib\imap.h" />
<ClInclude Include="curl\lib\llist.h" />
<ClInclude Include="curl\lib\macos.h" />
<ClInclude Include="curl\lib\mime.h" />
<ClInclude Include="curl\lib\mqtt.h" />
<ClInclude Include="curl\lib\multihandle.h" />
<ClInclude Include="curl\lib\multiif.h" />
<ClInclude Include="curl\lib\multi_ev.h" />
<ClInclude Include="curl\lib\multi_ntfy.h" />
<ClInclude Include="curl\lib\netrc.h" />
<ClInclude Include="curl\lib\parsedate.h" />
<ClInclude Include="curl\lib\peer.h" />
<ClInclude Include="curl\lib\pingpong.h" />
<ClInclude Include="curl\lib\pop3.h" />
<ClInclude Include="curl\lib\progress.h" />
<ClInclude Include="curl\lib\protocol.h" />
<ClInclude Include="curl\lib\proxy.h" />
<ClInclude Include="curl\lib\psl.h" />
<ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\ratelimit.h" />
<ClInclude Include="curl\lib\request.h" />
<ClInclude Include="curl\lib\rtsp.h" />
<ClInclude Include="curl\lib\select.h" />
<ClInclude Include="curl\lib\sendf.h" />
<ClInclude Include="curl\lib\setopt.h" />
<ClInclude Include="curl\lib\setup-os400.h" />
<ClInclude Include="curl\lib\setup-vms.h" />
<ClInclude Include="curl\lib\setup-win32.h" />
<ClInclude Include="curl\lib\sigpipe.h" />
<ClInclude Include="curl\lib\slist.h" />
<ClInclude Include="curl\lib\smb.h" />
<ClInclude Include="curl\lib\smtp.h" />
<ClInclude Include="curl\lib\sockaddr.h" />
<ClInclude Include="curl\lib\socketpair.h" />
<ClInclude Include="curl\lib\socks.h" />
<ClInclude Include="curl\lib\splay.h" />
<ClInclude Include="curl\lib\strcase.h" />
<ClInclude Include="curl\lib\strdup.h" />
<ClInclude Include="curl\lib\strerror.h" />
<ClInclude Include="curl\lib\strtok.h" />
<ClInclude Include="curl\lib\strtoofft.h" />
<ClInclude Include="curl\lib\system_win32.h" />
<ClInclude Include="curl\lib\telnet.h" />
<ClInclude Include="curl\lib\tftp.h" />
<ClInclude Include="curl\lib\thrdpool.h" />
<ClInclude Include="curl\lib\thrdqueue.h" />
<ClInclude Include="curl\lib\transfer.h" />
<ClInclude Include="curl\lib\uint-bset.h" />
<ClInclude Include="curl\lib\uint-hash.h" />
<ClInclude Include="curl\lib\uint-spbset.h" />
<ClInclude Include="curl\lib\uint-table.h" />
<ClInclude Include="curl\lib\url.h" />
<ClInclude Include="curl\lib\urlapi-int.h" />
<ClInclude Include="curl\lib\urldata.h" />
<ClInclude Include="curl\lib\vquic\capsule.h" />
<ClInclude Include="curl\lib\vquic\cf-capsule.h" />
<ClInclude Include="curl\lib\vquic\cf-ngtcp2-cmn.h" />
<ClInclude Include="curl\lib\vquic\cf-ngtcp2-proxy.h" />
<ClInclude Include="curl\lib\vquic\cf-ngtcp2.h" />
<ClInclude Include="curl\lib\vquic\cf-quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic-tls.h" />
<ClInclude Include="curl\lib\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vssh\vssh.h" />
<ClInclude Include="curl\lib\vtls\apple.h" />
<ClInclude Include="curl\lib\vtls\cipher_suite.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" />
<ClInclude Include="curl\lib\vtls\rustls.h" />
<ClInclude Include="curl\lib\vtls\schannel_int.h" />
<ClInclude Include="curl\lib\vtls\vtls_config.h" />
<ClInclude Include="curl\lib\vtls\vtls_int.h" />
<ClInclude Include="curl\lib\vtls\vtls_scache.h" />
<ClInclude Include="curl\lib\vtls\vtls_spack.h" />
<ClInclude Include="curl\lib\vtls\x509asn1.h" />
<ClInclude Include="curl\lib\vauth\digest.h" />
<ClInclude Include="curl\lib\vauth\ntlm.h" />
<ClInclude Include="curl\lib\vauth\vauth.h" />
<ClInclude Include="curl\lib\vquic\vquic.h" />
<ClInclude Include="curl\lib\vssh\ssh.h" />
<ClInclude Include="curl\lib\vtls\gtls.h" />
<ClInclude Include="curl\lib\vtls\keylog.h" />
<ClInclude Include="curl\lib\vtls\mbedtls.h" />
<ClInclude Include="curl\lib\vtls\openssl.h" />
<ClInclude Include="curl\lib\vtls\schannel.h" />
<ClInclude Include="curl\lib\vtls\vtls.h" />
<ClInclude Include="curl\lib\vtls\wolfssl.h" />
<ClInclude Include="curl\lib\ws.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="curl\lib\libcurl.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
File diff suppressed because it is too large Load Diff
+4
View File
@@ -15,7 +15,9 @@
<ClInclude Include="fusion\Fusion\FusionAhrs.h" />
<ClInclude Include="fusion\Fusion\FusionBias.h" />
<ClInclude Include="fusion\Fusion\FusionCompass.h" />
<ClInclude Include="fusion\Fusion\FusionConfig.h" />
<ClInclude Include="fusion\Fusion\FusionConvention.h" />
<ClInclude Include="fusion\Fusion\FusionInline.h" />
<ClInclude Include="fusion\Fusion\FusionMath.h" />
<ClInclude Include="fusion\Fusion\FusionModel.h" />
<ClInclude Include="fusion\Fusion\FusionRemap.h" />
@@ -24,6 +26,8 @@
<ClCompile Include="fusion\Fusion\FusionAhrs.c" />
<ClCompile Include="fusion\Fusion\FusionBias.c" />
<ClCompile Include="fusion\Fusion\FusionCompass.c" />
<ClCompile Include="fusion\Fusion\FusionConvention.c" />
<ClCompile Include="fusion\Fusion\FusionRemap.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<RootNamespace>Fusion</RootNamespace>
+8
View File
@@ -14,6 +14,8 @@
<ClInclude Include="fusion\Fusion\FusionBias.h" />
<ClInclude Include="fusion\Fusion\FusionModel.h" />
<ClInclude Include="fusion\Fusion\FusionRemap.h" />
<ClInclude Include="fusion\Fusion\FusionConfig.h" />
<ClInclude Include="fusion\Fusion\FusionInline.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="fusion\Fusion\FusionAhrs.c">
@@ -25,5 +27,11 @@
<ClCompile Include="fusion\Fusion\FusionBias.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fusion\Fusion\FusionConvention.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="fusion\Fusion\FusionRemap.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+23 -18
View File
@@ -37,9 +37,13 @@
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros">
<CmakeGenerator>"Visual Studio $(VisualStudioVersion.Substring(0,2))"</CmakeGenerator>
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 ^
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Release" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ^
-DGLSLANG_TESTS=OFF ^
@@ -48,11 +52,15 @@
-DENABLE_SPVREMAPPER=OFF ^
-DENABLE_HLSL=OFF ^
-DENABLE_OPT=OFF ^
-S glslang ^
-B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
-S "$(SolutionDir)3rdparty\glslang\glslang"
</CmakeReleaseCLI>
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
cmake -G $(CmakeGenerator) -A x64 ^
cd "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_BUILD_TYPE="Debug" ^
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDebug ^
-DGLSLANG_TESTS=OFF ^
@@ -61,15 +69,14 @@
-DENABLE_SPVREMAPPER=OFF ^
-DENABLE_HLSL=OFF ^
-DENABLE_OPT=OFF ^
-S glslang ^
-B "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)"
-S "$(SolutionDir)3rdparty\glslang\glslang"
</CmakeDebugCLI>
<CmakeCopyCLI>
echo Copying..
mkdir "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\SPIRV\$(CONFIGURATION)\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\glslang\OSDependent\Windows\$(CONFIGURATION)\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\glslang\$(CONFIGURATION)\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\SPIRV\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\glslang\OSDependent\Windows\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
copy "$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\glslang\*.lib" "$(SolutionDir)build\lib\$(Configuration)-$(Platform)\$(ProjectName)"
</CmakeCopyCLI>
<CmakeCleanCLI>
echo Cleaning..
@@ -83,17 +90,16 @@
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<NMakeBuildCommandLine>
$(CmakeReleaseCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:build /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
ninja
$(CmakeCopyCLI)
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>
$(CmakeCleanCLI)
$(CmakeReleaseCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:rebuild /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
ninja
$(CmakeCopyCLI)
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>
$(CmakeReleaseCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:clean /p:Configuration=Release /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
$(CmakeCleanCLI)
</NMakeCleanCommandLine>
</PropertyGroup>
@@ -102,17 +108,16 @@
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<NMakeBuildCommandLine>
$(CmakeDebugCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:build /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
ninja
$(CmakeCopyCLI)
</NMakeBuildCommandLine>
<NMakeReBuildCommandLine>
$(CmakeCleanCLI)
$(CmakeDebugCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:rebuild /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
ninja
$(CmakeCopyCLI)
</NMakeReBuildCommandLine>
<NMakeCleanCommandLine>
$(CmakeDebugCLI)
msbuild.exe "$(IntDir)\ALL_BUILD.vcxproj" /t:clean /p:Configuration=Debug /p:ForceImportBeforeCppTargets="$(PropsAbsPath)" /m
$(CmakeCleanCLI)
</NMakeCleanCommandLine>
</PropertyGroup>
-2
View File
@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
+64 -54
View File
@@ -56,6 +56,63 @@ namespace pine
typedef unsigned long long SOCKET;
#ifndef _WIN32
inline std::string get_socket_path(std::string_view file_name, int slot, int default_slot)
{
#ifdef __APPLE__
const char* runtime_dir = std::getenv("TMPDIR");
#else
const char* runtime_dir = std::getenv("XDG_RUNTIME_DIR");
#endif
// fallback in case macOS or other OSes don't implement the XDG base
// spec
std::string path = runtime_dir ? std::string(runtime_dir) + '/' : std::string("/tmp/");
path += file_name;
if (slot != default_slot)
{
fmt::append(path, ".%d", slot);
}
return path;
}
#endif
#ifdef _WIN32
inline const char* bind_and_listen(SOCKET sock, u16 port)
{
const int exclusive = 1;
if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive), sizeof(exclusive)) != 0)
return "setsockopt(SO_EXCLUSIVEADDRUSE) failed";
sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_port = htons(port);
if (inet_pton(AF_INET, "127.0.0.1", &server.sin_addr.s_addr) != 1)
return "inet_pton failed";
#else
inline const char* bind_and_listen(int sock, const std::string& socket_path)
{
sockaddr_un server{};
if (socket_path.size() >= sizeof(server.sun_path))
return "socket path is too long";
server.sun_family = AF_UNIX;
std::memcpy(server.sun_path, socket_path.c_str(), socket_path.size() + 1);
unlink(socket_path.c_str());
#endif
if (bind(sock, reinterpret_cast<sockaddr*>(&server), sizeof(server)) != 0)
return "bind failed";
if (listen(sock, 4096) != 0)
return "listen failed";
return nullptr;
}
template<typename Impl>
class pine_server : public Impl
{
@@ -528,7 +585,6 @@ namespace pine
{
#ifdef _WIN32
WSADATA wsa {};
struct sockaddr_in server {};
m_sock = INVALID_SOCKET;
m_msgsock = INVALID_SOCKET;
@@ -544,45 +600,9 @@ namespace pine
return;
}
// yes very good windows s/sun/sin/g sure is fine
server.sin_family = AF_INET;
// localhost only
if (!inet_pton(server.sin_family, "127.0.0.1", &server.sin_addr.s_addr))
{
fmt::throw_exception("IPC: Failed to convert localhost");
}
server.sin_port = htons(Impl::get_port());
if (bind(m_sock, reinterpret_cast<struct sockaddr*>(&server), sizeof(server)) == SOCKET_ERROR)
{
Impl::error("IPC: Error while binding to socket! Shutting down...");
return;
}
const char* failure = bind_and_listen(m_sock, static_cast<u16>(Impl::get_port()));
#else
char* runtime_dir = nullptr;
#ifdef __APPLE__
runtime_dir = std::getenv("TMPDIR");
#else
runtime_dir = std::getenv("XDG_RUNTIME_DIR");
#endif
// fallback in case macOS or other OSes don't implement the XDG base
// spec
if (runtime_dir == nullptr)
m_socket_name = "/tmp/rpcs3.sock";
else
{
m_socket_name = runtime_dir;
m_socket_name += "/rpcs3.sock";
}
const int slot = Impl::get_port();
if (slot != IPC_DEFAULT_SLOT)
{
fmt::append(m_socket_name, ".%d", slot);
}
struct sockaddr_un server {};
m_socket_name = get_socket_path("rpcs3.sock", Impl::get_port(), IPC_DEFAULT_SLOT);
m_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (m_sock < 0)
@@ -590,23 +610,13 @@ namespace pine
Impl::error("IPC: Cannot open socket! Shutting down...");
return;
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, m_socket_name.c_str());
// we unlink the socket so that when releasing this thread the socket gets
// freed even if we didn't close correctly the loop
unlink(m_socket_name.c_str());
if (bind(m_sock, std::bit_cast<struct sockaddr*>(&server), sizeof(struct sockaddr_un)))
{
Impl::error("IPC: Error while binding to socket! Shutting down...");
return;
}
const char* failure = bind_and_listen(m_sock, m_socket_name);
#endif
// maximum queue of 4096 commands before refusing, approximated to the
// nearest legal value. We do not use SOMAXCONN as windows have this idea
// that a "reasonable" value is 5, which is not.
listen(m_sock, 4096);
if (failure)
{
Impl::error("IPC: Error while setting up socket (%s)! Shutting down...", failure);
}
}
pine_server(const pine_server&) = delete;
+4
View File
@@ -41,9 +41,11 @@
<CmakeReleaseCLI>call vsdevcmd.bat -arch=amd64
if not exist "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)" mkdir "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
cd "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_CXX_STANDARD=20 ^
-DCMAKE_BUILD_TYPE="Release" ^
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" ^
@@ -67,9 +69,11 @@
<CmakeDebugCLI>call vsdevcmd.bat -arch=amd64
if not exist "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)" mkdir "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
cd "$(SolutionDir)build\tmp\protobuf_build-$(Configuration)-$(Platform)"
if not defined CCACHE_LAUNCH_ARGS set CCACHE_LAUNCH_ARGS=
cmake -G Ninja ^
-DCMAKE_CXX_COMPILER="cl.exe" ^
-DCMAKE_C_COMPILER="cl.exe" ^
%25CCACHE_LAUNCH_ARGS%25 ^
-DCMAKE_CXX_STANDARD=20 ^
-DCMAKE_BUILD_TYPE="Debug" ^
-DCMAKE_INSTALL_PREFIX="$(SolutionDir)build\lib\$(Configuration)-$(Platform)\protobuf_build" ^
+3 -2
View File
@@ -4,7 +4,7 @@ if(USE_SYSTEM_WOLFSSL)
add_library(wolfssl INTERFACE)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
set(WOLFSSL_TLS13 OFF CACHE INTERNAL "")
set(WOLFSSL_TLS13 ON CACHE INTERNAL "" FORCE)
set(WOLFSSL_SHA3 ON CACHE INTERNAL "")
set(WOLFSSL_SHAKE256 ON CACHE INTERNAL "")
set(WOLFSSL_BASE64_ENCODE OFF CACHE INTERNAL "")
@@ -21,5 +21,6 @@ else()
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_USE_OPTIONS_H)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192)
target_compile_definitions(wolfssl INTERFACE WOLFSSL_USE_OPTIONS_H)
endif()
+4
View File
@@ -86,17 +86,21 @@
<ClCompile Include="wolfssl\src\ssl_api_crl_ocsp.c" />
<ClCompile Include="wolfssl\src\ssl_api_dtls.c" />
<ClCompile Include="wolfssl\src\ssl_api_ext.c" />
<ClCompile Include="wolfssl\src\ssl_api_hs.c" />
<ClCompile Include="wolfssl\src\ssl_api_pk.c" />
<ClCompile Include="wolfssl\src\ssl_api_rw.c" />
<ClCompile Include="wolfssl\src\ssl_asn1.c" />
<ClCompile Include="wolfssl\src\ssl_bn.c" />
<ClCompile Include="wolfssl\src\ssl_certman.c" />
<ClCompile Include="wolfssl\src\ssl_crypto.c" />
<ClCompile Include="wolfssl\src\ssl_ech.c" />
<ClCompile Include="wolfssl\src\ssl_err.c" />
<ClCompile Include="wolfssl\src\ssl_load.c" />
<ClCompile Include="wolfssl\src\ssl_misc.c" />
<ClCompile Include="wolfssl\src\ssl_p7p12.c" />
<ClCompile Include="wolfssl\src\ssl_sess.c" />
<ClCompile Include="wolfssl\src\ssl_sk.c" />
<ClCompile Include="wolfssl\src\ssl_tsp.c" />
<ClCompile Include="wolfssl\src\wolfio.c" />
<ClCompile Include="wolfssl\src\keys.c" />
<ClCompile Include="wolfssl\src\ocsp.c" />
+22 -13
View File
@@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.11.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Qt 6.12.0](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.4.341.1](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)). Note that future SDKs may not work.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.11.1\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.12.0\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.11.1\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.12.0\msvc2022_64\`
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.11.1](https://www.qt.io/download-qt-installer)
- [Qt 6.12.0](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.4.341.1](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)). Note that future SDKs may not work.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@@ -72,14 +72,7 @@ You can either use `update-alternatives` to setup `gcc-13`/`g++-13` as your defa
##### Vulkan SDK
For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](https://packages.lunarg.com/) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
```
. /etc/os-release
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.268-$UBUNTU_CODENAME.list https://packages.lunarg.com/vulkan/1.3.268/lunarg-vulkan-1.3.268-$UBUNTU_CODENAME.list
sudo apt update
sudo apt install vulkan-sdk
```
For Ubuntu systems, it is strongly recommended to use the official [Linux Tarball Vulkan SDK](https://vulkan.lunarg.com/doc/view/1.4.341.0/linux/getting_started.html) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
##### CMake
@@ -123,7 +116,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.11.1\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.12.0\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)
@@ -179,6 +172,22 @@ In case you preferred to install and use the standalone **CMake** tool:
```
3) once the build is completed, the **RPCS3** application will be available under the `<rpcs3_root>\build-msvc\bin` folder
#### Building with the PowerShell script
As an alternative to building through Visual Studio, run the PowerShell helper from the repository root:
```powershell
.\scripts\win_build.ps1
```
The script locates a supported Visual Studio installation, finds Qt from `Qt6_ROOT` or a standard `C:\Qt` installation, configures a Release Ninja Multi-Config build, and then builds it. Pass `-Cores <count>` to set the parallel build count; by default it uses all logical processors:
```powershell
.\scripts\win_build.ps1 -Cores 8
```
The resulting application is placed in `<rpcs3_root>\build\bin`.
### Linux
While still in the project root:
+2
View File
@@ -29,6 +29,7 @@ else()
endif()
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
option(USE_ARM_LSE2 "Compile with LSE2 instructions" OFF)
option(WITH_LLVM "Enable usage of LLVM library" ON)
option(BUILD_LLVM "Build LLVM from git submodule" OFF)
option(STATIC_LINK_LLVM "Link against LLVM statically. This will get set to ON if you build LLVM from the submodule." OFF)
@@ -63,6 +64,7 @@ option(USE_LTO "Use LTO for building" ON)
option(BUILD_RPCS3_TESTS "Build RPCS3 unit tests." OFF)
option(RUN_RPCS3_TESTS "Run RPCS3 unit tests. Requires BUILD_RPCS3_TESTS" OFF)
option(USE_GAMEMODE "Choose whether to enable GameMode features or not." ON)
option(BUNDLE_UDEV_RULES "Include udev rules on Linux/FreeBSD" OFF)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake")
+21
View File
@@ -2,6 +2,7 @@
#include "Config.h"
#include "util/types.hpp"
#include "util/yaml.hpp"
#include "util/cctype.hpp"
#include <charconv>
@@ -712,6 +713,26 @@ bool cfg::node::validate(std::string_view value)
return false;
}
bool cfg::_bool::from_string(std::string_view value, bool /*dynamic*/)
{
if (value.size() != 4 && value.size() != 5)
{
return false;
}
char copy[5];
std::transform(value.begin(), value.end(), std::begin(copy), utils::tolower<char>);
if (value.size() == 5 && std::string_view{copy, 5} == "false")
m_value = false;
else if (value.size() == 4 && std::string_view{copy, 4} == "true")
m_value = true;
else
return false;
return true;
}
std::string cfg::map_entry::get_value(std::string_view key)
{
if (auto it = m_map.find(key); it != m_map.end())
+1 -19
View File
@@ -212,25 +212,7 @@ namespace cfg
return def ? "true" : "false";
}
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
if (value.size() != 4 && value.size() != 5)
{
return false;
}
char copy[5];
std::transform(value.begin(), value.end(), std::begin(copy), ::tolower);
if (value.size() == 5 && std::string_view{copy, 5} == "false")
m_value = false;
else if (value.size() == 4 && std::string_view{copy, 4} == "true")
m_value = true;
else
return false;
return true;
}
bool from_string(std::string_view value, bool dynamic = false) override;
void set(const bool& value)
{
+312 -23
View File
@@ -148,6 +148,7 @@ static fs::error to_error(DWORD e)
#include <sys/statvfs.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
@@ -159,12 +160,19 @@ static fs::error to_error(DWORD e)
#include <copyfile.h>
#include <mach-o/dyld.h>
#include <limits.h>
#include <sys/disk.h>
#include <sys/param.h>
#include <sys/mount.h>
#elif defined(__linux__) || defined(__sun)
#include <sys/sendfile.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <linux/fs.h>
#else
#include <fstream>
#include <sys/disk.h>
#include <sys/param.h>
#include <sys/mount.h>
#endif
static fs::error to_error(int e)
@@ -185,6 +193,96 @@ static fs::error to_error(int e)
}
}
#if defined(__linux__)
// Major numbers of the block devices an optical disc can be accessed through (see "Documentation/admin-guide/devices.txt")
constexpr unsigned int s_scsi_cdrom_major = 11; // Optical drive (e.g. "/dev/sr0")
constexpr unsigned int s_loopback_major = 7; // Disc image attached with "mount"/"losetup" (e.g. "/dev/loop0")
#endif
// Check whether the provided device node is an optical drive (e.g. a Blu-Ray Disc drive) or a mounted disc image
static bool is_optical_device_node(const struct ::stat& file_info)
{
#if defined(__linux__)
// An optical drive is exposed as a block device using the SCSI CD-ROM major number (e.g. "/dev/sr0", "/dev/scd0" or the "/dev/cdrom" link).
// A disc image attached through "mount -o loop" (or "losetup") is exposed as a loopback block device instead (e.g. "/dev/loop0"):
// it is the counterpart of an ISO file mounted on Windows, where the resulting virtual drive is reported as DRIVE_CDROM
if (!S_ISBLK(file_info.st_mode))
{
return false;
}
const unsigned int device_major = major(file_info.st_rdev);
return device_major == s_scsi_cdrom_major || device_major == s_loopback_major;
#elif defined(__APPLE__)
// The raw (unbuffered) device is a character device (e.g. "/dev/rdisk2").
// NOTE: there is no cheap way to tell an optical drive from any other raw disk here, so any raw disk node is accepted
return S_ISCHR(file_info.st_mode);
#else
// On the BSDs an optical drive is a character device (e.g. "/dev/cd0")
return S_ISCHR(file_info.st_mode) || S_ISBLK(file_info.st_mode);
#endif
}
// Check whether the path is the root of a mounted filesystem (e.g. the mount point of a disc): "st_dev" changes when crossing a mount point
[[maybe_unused]] static bool is_mount_point(const std::string& path, const struct ::stat& file_info)
{
// A file is never a mount point: discard it without any syscall (the "stat()" below would fail with ENOTDIR anyway)
if (!S_ISDIR(file_info.st_mode))
{
return false;
}
struct ::stat parent_info;
if (::stat((path + "/..").c_str(), &parent_info) != 0)
{
return false;
}
return parent_info.st_dev != file_info.st_dev;
}
// Retrieve the size of a raw device: "fstat()" reports no size (0) on such a device
static u64 get_raw_device_size(int fd)
{
#if defined(__linux__)
if (u64 size = 0; ::ioctl(fd, BLKGETSIZE64, &size) == 0)
{
return size;
}
#elif defined(__APPLE__)
u64 block_count = 0;
u32 block_size = 0;
if (::ioctl(fd, DKIOCGETBLOCKCOUNT, &block_count) == 0 && ::ioctl(fd, DKIOCGETBLOCKSIZE, &block_size) == 0)
{
if (block_size && block_count > u64{umax} / block_size)
{
fmt::throw_exception("Raw device size overflow (block count: 0x%x, block size: 0x%x)", block_count, block_size);
}
return block_count * block_size;
}
#elif defined(__FreeBSD__) || defined(__DragonFly__)
if (off_t media_size = 0; ::ioctl(fd, DIOCGMEDIASIZE, &media_size) == 0)
{
return static_cast<u64>(media_size);
}
#endif
// Fallback: seek to the end of the device (restoring the current position afterwards)
const off_t old_pos = ::lseek(fd, 0, SEEK_CUR);
const off_t end_pos = ::lseek(fd, 0, SEEK_END);
if (old_pos >= 0)
{
::lseek(fd, old_pos, SEEK_SET);
}
return end_pos > 0 ? static_cast<u64>(end_pos) : 0;
}
#endif
static std::string path_append(std::string_view path, std::string_view more)
@@ -412,12 +510,12 @@ namespace fs
class windows_file final : public file_base
{
HANDLE m_handle;
bool m_raw_device;
u64 m_raw_device_size; // Size of the raw device, 0 if it's not one: it never changes, so it's retrieved once when the file is opened
atomic_t<u64> m_pos {0};
public:
windows_file(HANDLE handle, bool raw_device = false)
: m_handle(handle), m_raw_device(raw_device)
windows_file(HANDLE handle, u64 raw_device_size = 0)
: m_handle(handle), m_raw_device_size(raw_device_size)
{
}
@@ -577,7 +675,7 @@ namespace fs
u64 size() override
{
if (!m_raw_device)
if (!m_raw_device_size)
{
// NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
LARGE_INTEGER size;
@@ -586,11 +684,8 @@ namespace fs
return size.QuadPart;
}
// For a raw device, we need to use DeviceIoControl.
DISK_GEOMETRY_EX geometry;
ensure(DeviceIoControl(m_handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, &geometry, sizeof(geometry), nullptr, nullptr));
return geometry.DiskSize.QuadPart;
// For a raw device, the size was already retrieved with DeviceIoControl() when the file was opened
return m_raw_device_size;
}
native_handle get_handle() override
@@ -629,10 +724,11 @@ namespace fs
class unix_file final : public file_base
{
int m_fd;
u64 m_raw_device_size; // Size of the raw device, 0 if it's not one: it never changes, so it's retrieved once when the file is opened
public:
unix_file(int fd)
: m_fd(fd)
unix_file(int fd, u64 raw_device_size = 0)
: m_fd(fd), m_raw_device_size(raw_device_size)
{
}
@@ -651,8 +747,8 @@ namespace fs
stat_t info {};
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
info.is_writable = !m_raw_device_size && (file_info.st_mode & 0200); // HACK: approximation
info.size = m_raw_device_size ? m_raw_device_size : static_cast<u64>(file_info.st_size); // A raw device reports no size through "fstat()"
info.atime = file_info.st_atime;
info.mtime = file_info.st_mtime;
info.ctime = info.mtime;
@@ -758,10 +854,16 @@ namespace fs
u64 size() override
{
struct ::stat file_info;
ensure(::fstat(m_fd, &file_info) == 0); // "file::size"
if (!m_raw_device_size)
{
struct ::stat file_info;
ensure(::fstat(m_fd, &file_info) == 0); // "file::size"
return file_info.st_size;
return file_info.st_size;
}
// For a raw device, the size was already retrieved with an ioctl when the file was opened
return m_raw_device_size;
}
native_handle get_handle() override
@@ -1113,15 +1215,39 @@ bool fs::is_symlink(const std::string& path)
return true;
}
bool fs::is_optical_raw_device([[maybe_unused]] const std::string& path)
bool fs::is_optical_raw_device(const std::string& path)
{
#ifdef _WIN32
if (path.starts_with("\\\\.\\"))
{
return true;
}
#endif
return false;
#else
#ifdef __APPLE__
// On Mac OS X optical disks will only ever be mounted under /dev/disk(whatever), similar for raw devices,
// so reject anything not containing the disk string.
if (!path.starts_with("/dev/disk") && !path.starts_with("/dev/rdisk"))
{
return false;
}
#else
// Skip a useless check if the path cannot point to a device node (device nodes always live in "/dev")
if (!path.starts_with("/dev/"))
{
return false;
}
#endif
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
return false;
}
return is_optical_device_node(file_info);
#endif
}
bool fs::get_optical_raw_device(const std::string& path, std::string* raw_device)
@@ -1158,8 +1284,132 @@ bool fs::get_optical_raw_device(const std::string& path, std::string* raw_device
return true;
}
#endif
return false;
#elif defined(__linux__)
// Here the path points to a mounted optical disc (e.g. "/media/user/PS3_DISC") or to a mounted disc image
// (e.g. "mount -o loop game.iso /mnt/game"), so retrieve the device backing the filesystem it belongs to
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
return false;
}
// "st_dev" of anything stored on a filesystem backed by a block device matches "st_rdev" of the device itself
const dev_t device_id = file_info.st_dev;
const unsigned int device_major = major(device_id);
// Discard at once anything which is not backed by an optical drive or by a loopback device, so that the checks
// below (each one costing at least a syscall) only run on an actual candidate
if (device_major != s_scsi_cdrom_major && device_major != s_loopback_major)
{
return false;
}
// Skip a useless check to detect an optical raw device if the path is not the mount point of the disc, it means we are
// navigating on subfolders (on Windows, in the same way, only the drive root is accepted)
if (!is_mount_point(path, file_info))
{
return false;
}
const unsigned int device_minor = minor(device_id);
// Retrieve the device name from sysfs (e.g. "/sys/dev/block/11:0" -> "../../devices/[...]/block/sr0")
std::string device_path;
char link_target[1024]; // NOTE: "readlink()" never null terminates, so the buffer is used through the returned length only
if (const ssize_t len = ::readlink(("/sys/dev/block/" + std::to_string(device_major) + ":" + std::to_string(device_minor)).c_str(), link_target, sizeof(link_target)); len > 0)
{
const std::string_view target{link_target, static_cast<usz>(len)};
device_path = "/dev/";
device_path += target.substr(target.find_last_of('/') + 1);
}
else if (device_major == s_scsi_cdrom_major)
{
// Fallback (sysfs not available): the minor number matches the index of the SCSI CD-ROM device
device_path = "/dev/sr" + std::to_string(device_minor);
}
else
{
return false;
}
// Ensure the resolved node really points to the same optical device
struct ::stat device_info;
if (::stat(device_path.c_str(), &device_info) != 0 || device_info.st_rdev != device_id || !is_optical_device_node(device_info))
{
return false;
}
if (raw_device)
{
*raw_device = device_path;
}
return true;
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
// Here the path points to a mounted optical disc or to a mounted disc image (e.g. attached with "hdiutil"/"mdconfig"),
// so retrieve the device backing the filesystem it belongs to
struct ::stat file_info;
if (::stat(path.c_str(), &file_info) != 0)
{
return false;
}
// Skip a useless check to detect an optical raw device if the path is not the mount point of the disc, it means we are
// navigating on subfolders (on Windows, in the same way, only the drive root is accepted). There is no cheap way to
// discard a device here (unlike the major number on Linux), so this is the filter which must come first: it rejects
// any file without a single syscall and any other folder with just one more
if (!is_mount_point(path, file_info))
{
return false;
}
struct ::statfs mount_info;
if (::statfs(path.c_str(), &mount_info) != 0)
{
return false;
}
std::string device_path = mount_info.f_mntfromname; // e.g. "/dev/disk2"
if (!device_path.starts_with("/dev/"))
{
return false;
}
#ifdef __APPLE__
// Use the raw (unbuffered) device (e.g. "/dev/disk2" -> "/dev/rdisk2")
if (!device_path.starts_with("/dev/r"))
{
device_path.insert(5, 1, 'r');
}
#endif
// Ensure the resolved node really points to a raw device
struct ::stat device_info;
if (::stat(device_path.c_str(), &device_info) != 0 || !is_optical_device_node(device_info))
{
return false;
}
if (raw_device)
{
*raw_device = device_path;
}
return true;
#else
// Not supported on this platform
return false;
#endif
}
bool fs::statfs(const std::string& path, fs::device_stat& info)
@@ -1642,11 +1892,29 @@ bool fs::utime(const std::string& path, s64 atime, s64 mtime)
#endif
}
void fs::sync()
void fs::sync(const std::string& path)
{
#ifdef _WIN32
static_cast<void>(path);
fs::g_tls_error = fs::error::unknown;
#elif defined(__linux__)
// Only flush the filesystem containing the path: a global sync() also waits on every other mounted
// filesystem and may block indefinitely on a slow or stalled one (e.g. network or FUSE mounts)
const int fd = ::open(path.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1)
{
fs::g_tls_error = to_error(errno);
return;
}
const int res = ::syncfs(fd);
const int err = errno;
::close(fd);
fs::g_tls_error = res == 0 ? fs::error::ok : to_error(err);
#else
static_cast<void>(path);
::sync();
fs::g_tls_error = fs::error::ok;
#endif
@@ -1744,7 +2012,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
m_file = std::make_unique<windows_file>(handle, true);
m_file = std::make_unique<windows_file>(handle, static_cast<u64>(ensure(geometry.DiskSize.QuadPart)));
return;
}
@@ -1774,7 +2042,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
if ((mode & fs::write) && (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
{
CloseHandle(handle);
g_tls_error = fs::error::acces;
@@ -1790,6 +2058,9 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
m_file = std::make_unique<windows_file>(handle);
#else
// An optical raw device can only be read: any write related flag makes the opening fail (handled as any other error)
const bool raw_device = is_optical_raw_device(path);
int flags = O_CLOEXEC; // Ensures all files are closed on execl for auto updater
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
@@ -1805,7 +2076,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
if (mode & fs::write && mode & fs::unread)
{
if (!(mode & (fs::excl + fs::lock)) && mode & fs::trunc)
if (!raw_device && !(mode & (fs::excl + fs::lock)) && mode & fs::trunc)
{
// Alternative to truncation for "unread" flag (TODO)
if (mode & fs::create)
@@ -1838,6 +2109,23 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
ensure(::ftruncate(fd, 0) == 0);
}
// If path points to an optical raw device, complete the file opening
if (raw_device)
{
// Try to retrieve the size of the content. If it fails, no disc is probably mounted so abort the file opening
const u64 raw_device_size = get_raw_device_size(fd);
if (!raw_device_size)
{
::close(fd);
g_tls_error = fs::error::noent;
return;
}
m_file = std::make_unique<unix_file>(fd, raw_device_size);
return;
}
m_file = std::make_unique<unix_file>(fd);
if (mode & fs::isfile && !(mode & fs::write) && get_stat().is_directory)
@@ -2259,6 +2547,7 @@ const std::string& fs::get_config_dir([[maybe_unused]] bool get_config_subdirect
// Clear buffer on failure and notify user
MessageBoxA(nullptr, fmt::format("GetEnvironmentVariable(RPCS3_CONFIG_DIR) failed: error: %s", fmt::win_error{GetLastError(), nullptr}).c_str(), "fs::get_config_dir()", MB_ICONERROR);
buf.clear();
size = 0;
}
}
+18 -4
View File
@@ -198,6 +198,19 @@ namespace fs
// Return "path" plus an ending delimiter (if missing) if "path" is an existing directory. Otherwise, an empty string
std::string get_path_if_dir(const std::string& path);
// Check whether the path is the bare name of a drive (e.g. "E:"), that is a root path whose trailing delimiter was
// trimmed: such a path does not point to the root of the drive but to the current directory of that drive, so the
// delimiter must be restored before using or storing it.
// NOTE: always false on the other platforms, where a name ending with ':' is a regular path component
inline bool is_drive_name([[maybe_unused]] std::string_view path)
{
#ifdef _WIN32
return !path.empty() && path.back() == ':';
#else
return false;
#endif
}
// Get file information
bool get_stat(const std::string& path, stat_t& info);
@@ -213,10 +226,11 @@ namespace fs
// Check whether the path points to an existing symlink
bool is_symlink(const std::string& path);
// Check whether the path points to a raw device
// Check whether the path points to a raw device (e.g. "\\.\E:" on Windows, "/dev/sr0" on Linux, "/dev/rdisk2" on macOS)
bool is_optical_raw_device(const std::string& path);
// Check whether the path points to an optical drive. If so, provide the raw device in "raw_device" if requested
// Check whether the path points to an optical drive or to a mounted disc image (either the raw device itself or the
// mount point of the disc/image). If so, provide the raw device in "raw_device" if requested
bool get_optical_raw_device(const std::string& path, std::string* raw_device = nullptr);
// Get filesystem information
@@ -249,8 +263,8 @@ namespace fs
// Set file access/modification time
bool utime(const std::string& path, s64 atime, s64 mtime);
// Synchronize filesystems (TODO)
void sync();
// Synchronize the filesystem containing the given path (only that filesystem where supported)
void sync(const std::string& path);
class file final
{
+3 -1
View File
@@ -14,6 +14,8 @@
#if defined(__APPLE__)
#include <pthread.h>
#elif defined(ANDROID)
#include "util/cctype.hpp"
#endif
LOG_CHANNEL(jit_log, "JIT");
@@ -1039,7 +1041,7 @@ const char * fallback_cpu_detection()
return "cortex-a78";
}
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
std::transform(result.begin(), result.end(), result.begin(), utils::tolower<char>);
return result;
}();
+2 -1
View File
@@ -26,6 +26,7 @@
*/
#include "LUrlParser.h"
#include "util/cctype.hpp"
#include <algorithm>
#include <cstring>
@@ -36,7 +37,7 @@ static bool IsSchemeValid( const std::string& SchemeName )
{
return std::all_of(SchemeName.cbegin(), SchemeName.cend(), [](const auto& c)
{
return isalpha(c) || c == '+' || c == '-' || c == '.';
return utils::isalpha(c) || c == '+' || c == '-' || c == '.';
});
}
+7 -6
View File
@@ -3,6 +3,7 @@
#include "cfmt.h"
#include "util/endian.hpp"
#include "util/v128.hpp"
#include "util/cctype.hpp"
#include <locale>
#include <codecvt>
@@ -198,12 +199,12 @@ fmt::base57_result fmt::base57_result::from_string(std::string_view str)
{
auto to_val = [](u8 c) -> u64
{
if (std::isdigit(c))
if (utils::isdigit(c))
{
return c - '0';
}
if (std::isupper(c))
if (utils::isupper(c))
{
// Omitted characters
if (c == 'B' || c == 'D' || c == 'I' || c == 'O')
@@ -231,7 +232,7 @@ fmt::base57_result fmt::base57_result::from_string(std::string_view str)
return c - 'A' + 10;
}
if (std::islower(c))
if (utils::islower(c))
{
// Omitted characters
if (c == 'l')
@@ -611,7 +612,7 @@ void fmt_class_string<std::source_location>::format(std::string& out, u64 arg)
#ifdef _WIN32
if (DWORD error = GetLastError())
{
fmt::append(out, " (error=%s)", error, fmt::win_error_to_string(error));
fmt::append(out, " (error=%s)", fmt::win_error_to_string(error));
}
#else
if (int error = errno)
@@ -952,7 +953,7 @@ std::string fmt::to_upper(std::string_view string)
{
std::string result;
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::toupper);
std::transform(string.begin(), string.end(), result.begin(), utils::toupper<char>);
return result;
}
@@ -960,7 +961,7 @@ std::string fmt::to_lower(std::string_view string)
{
std::string result;
result.resize(string.size());
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
std::transform(string.begin(), string.end(), result.begin(), utils::tolower<char>);
return result;
}
+127 -10
View File
@@ -6,6 +6,7 @@
#include "Emu/Cell/lv2/sys_event.h"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/system_config.h"
#include "Thread.h"
#include "Utilities/JIT.h"
#include <cfenv>
@@ -96,6 +97,7 @@ DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDesc
#include "util/asm.hpp"
#include "util/v128.hpp"
#include "util/simd.hpp"
#include "util/cctype.hpp"
#include "util/sysinfo.hpp"
#include "Emu/Memory/vm_locking.h"
@@ -185,9 +187,9 @@ bool IsDebuggerPresent()
for (const char* cp = status.data() + found + 10; cp <= status.data() + num_read; ++cp)
{
if (!std::isspace(*cp))
if (!utils::isspace(*cp))
{
return std::isdigit(*cp) != 0 && *cp != '0';
return utils::isdigit(*cp) != 0 && *cp != '0';
}
}
@@ -384,7 +386,7 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, usz
case 0x67: // group 4
{
sig_log.error("decode_x64_reg_op(%016llxh): address-size override prefix found", code - out_length, prefix);
sig_log.error("decode_x64_reg_op(%016llxh): address-size override prefix found", code - out_length);
out_op = X64OP_NONE;
out_reg = X64_NOT_SET;
out_size = 0;
@@ -1964,8 +1966,15 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
// Hack: allocate memory in case the emulator is stopping
const auto hack_alloc = [&]()
{
const bool added_flag = cpu && !cpu->state.test_and_set(cpu_flag::wait);
if (vm::check_addr(addr, required_page_perms))
{
if (added_flag)
{
cpu->check_state();
}
return true;
}
@@ -1973,6 +1982,11 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
if (!area)
{
if (added_flag)
{
cpu->check_state();
}
return false;
}
@@ -1993,6 +2007,11 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
{
ppu_register_range(addr & -0x10000, 0x10000);
}
if (added_flag)
{
cpu->check_state();
}
g_tls_access_violation_recovered = addr;
return true;
@@ -2007,10 +2026,20 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
ppu_register_range(addr & -0x10000, 0x10000);
}
if (added_flag)
{
cpu->check_state();
}
g_tls_access_violation_recovered = addr;
return true;
}
if (added_flag)
{
cpu->check_state();
}
return false;
};
@@ -2247,12 +2276,12 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
if (Emu.IsStopped())
{
// Keep retrying until the page is mapped: the thread must be able to resume so it can observe cpu_flag::exit and terminate cleanly.
// Reporting the violation as unhandled here would escalate a guest crash into an unhandled host exception.
while (!hack_alloc())
{
thread_ctrl::wait_for(1000);
}
return false;
}
return true;
@@ -2301,10 +2330,11 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp) noexcept
is_exec = true;
addr = static_cast<u32>(exec64);
}
else if (const usz exec64 = (ptr - vm::g_exec_addr - vm::g_exec_addr_seg_offset); exec64 <= u32{umax})
else if (const usz seg_off = (ptr - vm::g_exec_addr - vm::g_exec_addr_seg_offset); seg_off <= u32{umax} / 2)
{
// Segment map holds one u16 per 4 bytes of guest code: ptr = g_exec_addr + g_exec_addr_seg_offset + (addr >> 1)
is_exec = true;
addr = static_cast<u32>(exec64);
addr = static_cast<u32>(seg_off * 2);
}
else
{
@@ -2549,7 +2579,8 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
#endif
const u64 exec64 = (reinterpret_cast<u64>(info->si_addr) - reinterpret_cast<u64>(vm::g_exec_addr)) / 2;
const u64 exec64_2 = (reinterpret_cast<u64>(info->si_addr) - reinterpret_cast<u64>(vm::g_exec_addr)) - vm::g_exec_addr_seg_offset;
// Segment map holds one u16 per 4 bytes of guest code: si_addr = g_exec_addr + g_exec_addr_seg_offset + (addr >> 1)
const u64 seg_off = (reinterpret_cast<u64>(info->si_addr) - reinterpret_cast<u64>(vm::g_exec_addr)) - vm::g_exec_addr_seg_offset;
const auto cause = is_executing ? "executing" : is_writing ? "writing" : "reading";
if (auto [addr, ok] = vm::try_get_addr(info->si_addr); ok && !is_executing)
@@ -2568,9 +2599,9 @@ static void signal_handler(int /*sig*/, siginfo_t* info, void* uct) noexcept
return;
}
}
else if (exec64_2 < 0x100000000ull && !is_executing)
else if (seg_off < 0x80000000ull && !is_executing)
{
if (thread_ctrl::get_current() && handle_access_violation(static_cast<u32>(exec64_2), is_writing, true, context))
if (thread_ctrl::get_current() && handle_access_violation(static_cast<u32>(seg_off * 2), is_writing, true, context))
{
return;
}
@@ -2780,6 +2811,14 @@ void thread_base::initialize(void (*error_cb)())
}
#endif
#if !defined(ANDROID) && (defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__))
// A new thread inherits its creator's affinity mask (e.g. compile workers spawned by a pinned PPU thread): reset it to the process mask
if (g_cfg.core.thread_scheduler != thread_scheduler_mode::os)
{
thread_ctrl::set_thread_affinity_mask(0);
}
#endif
// Initialize TLS variables
thread_ctrl::g_tls_this_thread = this;
@@ -4004,3 +4043,81 @@ bool thread_ctrl::is_main()
{
return get_tid() == utils::main_tid;
}
usz map_workload(std::string_view thread_name, usz thread_count, usz count, std::function<void(usz)>&& func)
{
ensure(!!func);
if (thread_count <= 1)
{
for (usz i = 0; i < count; i++)
{
func(i);
}
return 1;
}
atomic_t<u32> num_threads_succeeded {0}; // Check if any thread didn't finish. For example when hitting an exception.
atomic_t<usz> indexer = 0;
const auto iterate = [count, &func, &indexer]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
// Make sure indexer does not exceed count
const usz index = indexer.fetch_op([count](usz& v)
{
if (v < count)
{
v++;
return true;
}
return false;
}).first;
if (index >= count)
{
break;
}
func(index);
}
};
named_thread_group workers(thread_name, ::narrow<u32>(thread_count) - 1, [&iterate, &num_threads_succeeded]()
{
iterate();
num_threads_succeeded++;
});
iterate();
workers.join();
return num_threads_succeeded + 1;
}
usz map_workload(std::string_view thread_name, usz thread_count, std::function<void()>&& func)
{
ensure(!!func);
if (thread_count <= 1)
{
func();
return 1;
}
atomic_t<u32> num_threads_succeeded {0}; // Check if any thread didn't finish. For example when hitting an exception.
named_thread_group workers(thread_name, ::narrow<u32>(thread_count) - 1, [&func, &num_threads_succeeded]()
{
func();
num_threads_succeeded++;
});
func();
workers.join();
return num_threads_succeeded + 1;
}
+3
View File
@@ -905,3 +905,6 @@ public:
::operator delete(static_cast<void*>(m_threads), std::align_val_t{alignof(Thread)});
}
};
usz map_workload(std::string_view thread_name, usz thread_count, usz count, std::function<void(usz)>&& func);
usz map_workload(std::string_view thread_name, usz thread_count, std::function<void()>&& func);
+26 -9
View File
@@ -5,11 +5,13 @@
#include "version.h"
#include "Emu/IdManager.h"
#include "Emu/Memory/vm.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/VFS.h"
#include "util/types.hpp"
#include "util/asm.hpp"
#include "util/cctype.hpp"
#include <charconv>
#include <regex>
@@ -329,7 +331,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
is_valid = false;
continue;
}
else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(static_cast<unsigned char>(c)); }))
else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return utils::isalnum(c); }))
{
append_log_message(log_messages, fmt::format("Error: Serial '%s' invalid (patch: %s, key: %s, location: %s, file: %s)", serial, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;
@@ -962,7 +964,7 @@ static usz apply_modification(std::vector<u32>& applied, patch_engine::patch_inf
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
// Allocate map if needed, if allocated flags will indicate that bit 62 is set (unique identifier)
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::page_size_64k | (1ull << 62));
auto alloc_map = vm::reserve_map(vm::any, alloc_at & -0x10000, utils::align(alloc_size, 0x10000), vm::block_size_64k | (1ull << 62));
u64 flags = vm::alloc_unwritable;
@@ -1188,7 +1190,7 @@ static usz apply_modification(std::vector<u32>& applied, patch_engine::patch_inf
if (!addr)
{
patch_log.error("Failed to allocate 0x%x bytes for code (entry=0x%x)", alloc_size, addr, out_branch);
patch_log.error("Failed to allocate 0x%x bytes for code (entry=0x%x)", alloc_size, out_branch);
continue;
}
@@ -1386,7 +1388,7 @@ static usz apply_modification(std::vector<u32>& applied, patch_engine::patch_inf
if (exec_addr)
{
Emu.GetCallbacks().add_breakpoint(exec_addr);
g_emu_callbacks.add_breakpoint(exec_addr);
}
break;
@@ -1434,7 +1436,7 @@ static usz apply_modification(std::vector<u32>& applied, patch_engine::patch_inf
if (dest_path.empty())
{
patch_log.error("Failed to patch file path at '%s': destination is not mounted", original_vfs_path, dest_vfs_path);
patch_log.error("Failed to patch file path at '%s': destination is not mounted", original_vfs_path);
continue;
}
@@ -1803,8 +1805,9 @@ static void append_patches(patch_engine::patch_map& existing_patches, const patc
bool patch_engine::save_patches(const patch_map& patches, const std::string& path, std::stringstream* log_messages)
{
fs::file file(path, fs::rewrite);
if (!file)
fs::pending_file file(path);
if (!file.file)
{
append_log_message(log_messages, fmt::format("Failed to open patch file %s (%s)", path, fs::g_tls_error), &patch_log.fatal);
return false;
@@ -1904,7 +1907,17 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
out << YAML::Flow;
out << YAML::BeginSeq;
out << fmt::format("%s", data.type);
out << fmt::format("0x%.8x", data.offset);
if (patch_type_uses_hex_offset(data.type))
{
out << fmt::format("0x%.8x", data.offset);
}
else
{
// This element is a path for move_file and hide_file, not an address
out << data.original_offset;
}
out << data.original_value;
out << YAML::EndSeq;
}
@@ -1918,7 +1931,11 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
out << YAML::EndMap;
file.write(out.c_str(), out.size());
if (file.file.write(out.c_str(), out.size()) < out.size() || !file.commit())
{
append_log_message(log_messages, fmt::format("Failed to write patch file %s (%s)", path, fs::g_tls_error), &patch_log.fatal);
return false;
}
return true;
}
+22 -6
View File
@@ -146,25 +146,41 @@ public:
constexpr bool test_and_set(T bit)
{
bool r = (m_data & shift(bit)) != 0;
m_data |= shift(bit);
const under s = shift(bit);
const bool r = (m_data & s) != 0;
m_data |= s;
return r;
}
constexpr bool test_and_set(T bit, bool value)
{
const under s = shift(bit);
const bool r = (m_data & s) != 0;
m_data = (value ? (m_data | s) : (m_data & ~s));
return r;
}
constexpr bool test_and_reset(T bit)
{
bool r = (m_data & shift(bit)) != 0;
m_data &= ~shift(bit);
const under s = shift(bit);
const bool r = (m_data & s) != 0;
m_data &= ~s;
return r;
}
constexpr bool test_and_complement(T bit)
{
bool r = (m_data & shift(bit)) != 0;
m_data ^= shift(bit);
const under s = shift(bit);
const bool r = (m_data & s) != 0;
m_data ^= s;
return r;
}
constexpr bool test(bs_t arg) const
{
return (m_data & arg.m_data) != 0;
}
constexpr bool all_of(bs_t arg) const
{
return (m_data & arg.m_data) == arg.m_data;
+83
View File
@@ -91,6 +91,24 @@ usz cfmt_append(Dst& out, const Char* fmt, Src&& src)
}
};
const auto write_binary = [&](auto value, u64 min_num)
{
if constexpr (sizeof(value) == 16)
{
out.resize(out.size() + std::max<u64>(min_num, 128 - utils::clz128(value | 1)), '0');
}
else
{
out.resize(out.size() + std::max<u64>(min_num, 64 - std::countl_zero<u64>(value | 1)), '0');
}
// Write in reversed order
for (auto i = out.rbegin(); value; i++, value >>= 1)
{
*i = static_cast<usz>(value & 0b1) ? '1' : '0';
}
};
const auto write_decimal = [&](auto value, s64 min_size)
{
const usz start = out.size();
@@ -635,6 +653,71 @@ usz cfmt_append(Dst& out, const Char* fmt, Src&& src)
break;
}
case 'b':
case 'B':
{
if (!src.test(ctx.args)) [[unlikely]]
{
drop_sequence();
break;
}
const usz src_type = src.type(ctx.args);
if (!ctx.type || src_type > 8)
{
ctx.type = static_cast<u8>(src_type);
if (!ctx.type)
{
ctx.type = src.size_int;
}
}
const u64 mask =
ctx.type == 1 ? 0xff :
ctx.type == 2 ? 0xffff :
ctx.type == 4 ? 0xffff'ffffu :
0xffff'ffff'ffff'ffffu;
// Trunc sign-extended signed types
const u64 val = src.template get<u64>(ctx.args) & mask;
const usz start = out.size();
if (!ctx.dot || ctx.prec)
{
if (ctx.type >= 16)
{
u128 val2 = *reinterpret_cast<const u128*>(val);
write_binary(val2, ctx.prec);
}
else
{
write_binary(val, ctx.prec);
}
}
const usz size2 = out.size() - start;
if (size2 < ctx.width)
{
// Add padding if necessary
if (ctx.zeros && !ctx.left && !ctx.dot)
{
out.insert(out.begin() + start + (ctx.alter && val ? 2 : 0), ctx.width - size2, '0');
}
else
{
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
}
}
src.skip(ctx.args);
ctx = {0};
break;
}
case 'p':
{
if (!src.test(ctx.args) || ctx.type) [[unlikely]]
+1 -1
View File
@@ -47,7 +47,7 @@ namespace date_time
std::string parse_buf;
if constexpr(separator != 0)
if constexpr (separator != 0)
parse_buf = std::string("%Y") + separator + "%m" + separator + "%d" + separator + "%H" + separator + "%M" + separator + "%S";
else
parse_buf = "%Y%m%d%H%M%S";
+57 -3
View File
@@ -724,15 +724,24 @@ struct coord3_base
struct { T width, height, depth; };
};
constexpr coord3_base() : position{}, size{}
constexpr coord3_base()
: position{}, size{}
{
}
constexpr coord3_base(const position3_base<T>& position, const size3_base<T>& size) : position{ position }, size{ size }
constexpr coord3_base(const position3_base<T>& position, const size3_base<T>& size)
: position{ position }, size{ size }
{
}
constexpr coord3_base(T x, T y, T z, T width, T height, T depth) : x{ x }, y{ y }, z{ z }, width{ width }, height{ height }, depth{ depth }
constexpr coord3_base(T x, T y, T z, T width, T height, T depth)
: x{ x }, y{ y }, z{ z }, width{ width }, height{ height }, depth{ depth }
{
}
constexpr coord3_base(const area_base<T>& area, T z = 0, T depth = 1)
: x{ area.x1 }, y{ area.y1 }, z{ z }
, width{ area.x2 - area.x1 }, height{ area.y2 - area.y1 }, depth{ depth }
{
}
@@ -755,6 +764,51 @@ struct coord3_base
{
return{ static_cast<NT>(x), static_cast<NT>(y), static_cast<NT>(z), static_cast<NT>(width), static_cast<NT>(height), static_cast<NT>(depth) };
}
void flip_horizontal()
requires std::is_signed_v<T>
{
auto x2 = x + width;
x = x2;
width = -width;
}
void flip_vertical()
requires std::is_signed_v<T>
{
auto y2 = y + height;
y = y2;
height = -height;
}
bool is_flipped() const
requires std::is_signed_v<T>
{
return width < 0 || height < 0 || depth < 0;
}
area_base<T> to_area() const
{
return { x, y, x + width, y + height };
}
T abs_width() const
requires std::is_signed_v<T>
{
return width < 0 ? -width : width;
}
T abs_height() const
requires std::is_signed_v<T>
{
return height < 0 ? -height : height;
}
T abs_depth() const
requires std::is_signed_v<T>
{
return depth < 0 ? -depth : depth;
}
};
+5
View File
@@ -28,6 +28,11 @@ class shared_mutex final
public:
constexpr shared_mutex() = default;
const atomic_t<u32>& raw() const
{
return m_value;
}
bool try_lock_shared()
{
const u32 value = m_value.load();
+29
View File
@@ -13,6 +13,33 @@
namespace utils
{
void clean_backtrace(std::vector<std::string>& result)
{
for (usz it = 1, first_in_seq = 0, del_count = 0;;)
{
if (it >= result.size() || result[it] != result[first_in_seq])
{
if (del_count)
{
fmt::append(result[first_in_seq], " x%u", del_count + 1);
}
if (it < result.size())
{
first_in_seq = it;
it++;
del_count = 0;
continue;
}
break;
}
result.erase(result.begin() + it);
del_count++;
}
}
#ifdef _WIN32
std::string wstr_to_utf8(LPWSTR data, int str_len)
{
@@ -139,6 +166,7 @@ namespace utils
}
}
clean_backtrace(result);
return result;
}
#else
@@ -166,6 +194,7 @@ namespace utils
free(symbols);
#endif
clean_backtrace(result);
return result;
}
#endif
+2 -1
View File
@@ -55,7 +55,8 @@ namespace utils
int compare_versions(const std::string& v1, const std::string& v2, bool& ok)
{
// Check if both version strings are valid
ok = std::regex_match(v1, std::regex("[0-9.]*")) && std::regex_match(v2, std::regex("[0-9.]*"));
static const std::regex comp = std::regex("[0-9.]*");
ok = std::regex_match(v1, comp) && std::regex_match(v2, comp);
if (!ok)
{
+10
View File
@@ -208,6 +208,16 @@ QComboBox {
padding-bottom: 0.2em;
padding-left: 0.4em;
}
QComboBox QAbstractItemView {
background-color: #323232;
color: #fff;
border: 0.05em solid #1e1e1e;
}
QComboBox QAbstractItemView::item:selected,
QComboBox QAbstractItemView::item:hover {
background-color: #444444;
color: #fff;
}
QComboBox::disabled {
background-color: #828790;
color: #fff;
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
+4
View File
@@ -1,5 +1,9 @@
# Check and configure compiler options for RPCS3
if(USE_ARM_LSE2)
add_compile_definitions(ARM_FEATURE_LSE2=1)
endif()
if(MSVC)
add_compile_options(/Zc:throwingNew- /constexpr:steps16777216)
add_compile_definitions(
+17 -4
View File
@@ -3,9 +3,22 @@ if(USE_SYSTEM_ZLIB)
find_package(ZLIB)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
else()
add_library(ZLIB::ZLIB STATIC IMPORTED)
set_target_properties(ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libzlibstatic.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib")
if(NOT TARGET ZLIB::ZLIB)
add_library(ZLIB::ZLIB STATIC IMPORTED GLOBAL)
if(MSVC)
# On MSVC, zlib builds as zs.lib (Release) / zsd.lib (Debug) in per-config subdirectories
set_target_properties(ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION_RELEASE "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/Release/zs.lib"
IMPORTED_LOCATION_DEBUG "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/Debug/zsd.lib"
IMPORTED_LOCATION_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/RelWithDebInfo/zs.lib"
IMPORTED_LOCATION_MINSIZEREL "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/MinSizeRel/zs.lib")
else()
# On non-MSVC, zlib OUTPUT_NAME is "z" → libz.a
set_target_properties(ZLIB::ZLIB PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib/libz.a")
endif()
set_target_properties(ZLIB::ZLIB PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/zlib/zlib;${CMAKE_BINARY_DIR}/3rdparty/zlib/zlib")
endif()
set(ZLIB_FOUND TRUE)
endif()
+15 -4
View File
@@ -8,9 +8,9 @@
<LibraryPath>$(SolutionDir)build\lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
<IntDir>$(SolutionDir)build\tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
<GTestPath>$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.8\build\native\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.targets</GTestPath>
<GTestInstalled Condition="Exists('$(GTestPath)')">true</GTestInstalled>
<OpenCvBuildDir>$(SolutionDir)3rdparty\opencv\opencv\opencv413\build</OpenCvBuildDir>
<OpenCvWorld>opencv_world4130</OpenCvWorld>
<GTestInstalled Condition="Exists('$(GTestPath)') AND '$(SKIP_RPCS3_TEST)' == ''">true</GTestInstalled>
<OpenCvBuildDir>$(SolutionDir)3rdparty\opencv\opencv\opencv414\build</OpenCvBuildDir>
<OpenCvWorld>opencv_world4140</OpenCvWorld>
</PropertyGroup>
<ItemDefinitionGroup>
<Lib>
@@ -233,12 +233,23 @@
absl_time_zone.lib;
libutf8_validity.lib;
libutf8_range.lib;
libprotobuf-lite.lib;
</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)3rdparty\ffmpeg\lib\windows\x86_64</AdditionalLibraryDirectories>
<StackReserveSize>8388608</StackReserveSize>
<StackCommitSize>1048576</StackCommitSize>
</Link>
<Link Condition="'$(Configuration)'=='Release'">
<AdditionalDependencies>
libprotobuf-lite.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
</Link>
<Link Condition="'$(Configuration)'=='Debug'">
<AdditionalDependencies>
libprotobuf-lited.lib;
%(AdditionalDependencies)
</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup />
</Project>
+49
View File
@@ -0,0 +1,49 @@
# DualShock 3
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x054c";
match "product" "0x0268";
action "chmod 0666 /dev/$cdev";
};
# DualShock 4
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x054c";
match "product" "0x05c4";
action "chmod 0666 /dev/$cdev";
};
# DualShock 4 Wireless Adapter
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x054c";
match "product" "0x0ba0";
action "chmod 0666 /dev/$cdev";
};
# DualShock 4 Slim
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x054c";
match "product" "0x09cc";
action "chmod 0666 /dev/$cdev";
};
# DualSense
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x054c";
match "product" "0x0ce6";
action "chmod 0666 /dev/$cdev";
};
+26
View File
@@ -0,0 +1,26 @@
# DualShock 3 over USB
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0268", MODE="0666"
# DualShock 3 over Bluetooth
KERNEL=="hidraw*", KERNELS=="*054C:0268*", MODE="0666"
# DualShock 4 over USB
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE="0666"
# DualShock 4 Wireless Adapter over USB
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ba0", MODE="0666"
# DualShock 4 Slim over USB
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="09cc", MODE="0666"
# DualShock 4 over Bluetooth
KERNEL=="hidraw*", KERNELS=="*054C:05C4*", MODE="0666"
# DualShock 4 Slim over Bluetooth
KERNEL=="hidraw*", KERNELS=="*054C:09CC*", MODE="0666"
# PS5 DualSense controller over USB hidraw
KERNEL=="hidraw*", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0ce6", MODE="0666", TAG+="uaccess"
# PS5 DualSense controller over bluetooth hidraw
KERNEL=="hidraw*", KERNELS=="*054C:0CE6*", MODE="0666", TAG+="uaccess"
+22 -7
View File
@@ -182,17 +182,21 @@ if(BUILD_RPCS3_TESTS)
target_sources(rpcs3_test
PRIVATE
tests/test.cpp
tests/test_bit_set.cpp
tests/test_fmt.cpp
tests/test_pair.cpp
tests/test_tuple.cpp
tests/test_simple_array.cpp
tests/test_address_range.cpp
tests/test_sys_fs.cpp
tests/test_bit_set.cpp
tests/test_cctype.cpp
tests/test_dmux_pamf.cpp
tests/test_fmt.cpp
tests/test_iso_open.cpp
tests/test_iso_extent.cpp
tests/test_pair.cpp
tests/test_rsx_cfg.cpp
tests/test_rsx_fp_asm.cpp
tests/test_dmux_pamf.cpp
tests/test_rsx_mm_queue.cpp
tests/test_simple_array.cpp
tests/test_spu_analyser.cpp
tests/test_sys_fs.cpp
tests/test_tuple.cpp
tests/test_types_util.cpp
)
@@ -221,3 +225,14 @@ if(BUILD_RPCS3_TESTS)
)
endif()
endif()
# Udev rules, per platform
if (BUNDLE_UDEV_RULES)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
install(FILES ../config/udev/udev.rules
DESTINATION ${CMAKE_INSTALL_LIBDIR}/udev/rules.d/ RENAME 99-rpcs3.rules)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
install(FILES ../config/udev/devd.conf
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/devd/ RENAME rpcs3.conf)
endif()
endif()
+194 -156
View File
@@ -9,24 +9,28 @@
#include "Emu/system_utils.hpp"
#include "util/asm.hpp"
#include "util/cctype.hpp"
#include <algorithm>
#include <span>
LOG_CHANNEL(edat_log, "EDAT");
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
void generate_key(int crypto_mode, int version, u8* key_final, u8* iv_final, const u8* key, const u8* iv)
{
int mode = crypto_mode & 0xF0000000;
uchar temp_iv[16]{};
const int mode = crypto_mode & 0xF0000000;
switch (mode)
{
case 0x10000000:
{
// Encrypted ERK.
// Decrypt the key with EDAT_KEY + EDAT_IV and copy the original IV.
u8 temp_iv[16]{};
memcpy(temp_iv, EDAT_IV, 0x10);
aescbc128_decrypt(const_cast<u8*>(version ? EDAT_KEY_1 : EDAT_KEY_0), temp_iv, key, key_final, 0x10);
memcpy(iv_final, iv, 0x10);
break;
}
case 0x20000000:
// Default ERK.
// Use EDAT_KEY and EDAT_IV.
@@ -42,18 +46,20 @@ void generate_key(int crypto_mode, int version, unsigned char *key_final, unsign
}
}
void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
void generate_hash(int hash_mode, int version, u8* hash_final, const u8* hash)
{
int mode = hash_mode & 0xF0000000;
uchar temp_iv[16]{};
const int mode = hash_mode & 0xF0000000;
switch (mode)
{
case 0x10000000:
{
// Encrypted HASH.
// Decrypt the hash with EDAT_KEY + EDAT_IV.
u8 temp_iv[16]{};
memcpy(temp_iv, EDAT_IV, 0x10);
aescbc128_decrypt(const_cast<u8*>(version ? EDAT_KEY_1 : EDAT_KEY_0), temp_iv, hash, hash_final, 0x10);
break;
}
case 0x20000000:
// Default HASH.
// Use EDAT_HASH.
@@ -67,13 +73,13 @@ void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsign
};
}
bool decrypt(int hash_mode, int crypto_mode, int version, unsigned char *in, unsigned char *out, usz length, unsigned char *key, unsigned char *iv, unsigned char *hash, unsigned char *test_hash)
bool decrypt(int hash_mode, int crypto_mode, int version, const u8* in, u8* out, usz length, const u8* key, const u8* iv, const u8* hash, const u8* test_hash)
{
// Setup buffers for key, iv and hash.
unsigned char key_final[0x10] = {};
unsigned char iv_final[0x10] = {};
unsigned char hash_final_10[0x10] = {};
unsigned char hash_final_14[0x14] = {};
u8 key_final[0x10] = {};
u8 iv_final[0x10] = {};
u8 hash_final_10[0x10] = {};
u8 hash_final_14[0x14] = {};
// Generate crypto key and hash.
generate_key(crypto_mode, version, key_final, iv_final, key, iv);
@@ -116,25 +122,25 @@ bool decrypt(int hash_mode, int crypto_mode, int version, unsigned char *in, uns
}
// EDAT/SDAT functions.
std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
std::tuple<u64, s32, s32> dec_section(const u8* metadata)
{
std::array<u8, 0x10> dec;
dec[0x00] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
dec[0x01] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
dec[0x02] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
dec[0x03] = (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
dec[0x04] = (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
dec[0x05] = (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
dec[0x06] = (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
dec[0x07] = (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
dec[0x08] = (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
dec[0x09] = (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
dec[0x0A] = (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
dec[0x0B] = (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
dec[0x0C] = (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
dec[0x0D] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
dec[0x0E] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
dec[0x0F] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
dec[0x0] = (metadata[0xC] ^ metadata[0x8] ^ metadata[0x10]);
dec[0x1] = (metadata[0xD] ^ metadata[0x9] ^ metadata[0x11]);
dec[0x2] = (metadata[0xE] ^ metadata[0xA] ^ metadata[0x12]);
dec[0x3] = (metadata[0xF] ^ metadata[0xB] ^ metadata[0x13]);
dec[0x4] = (metadata[0x4] ^ metadata[0x8] ^ metadata[0x14]);
dec[0x5] = (metadata[0x5] ^ metadata[0x9] ^ metadata[0x15]);
dec[0x6] = (metadata[0x6] ^ metadata[0xA] ^ metadata[0x16]);
dec[0x7] = (metadata[0x7] ^ metadata[0xB] ^ metadata[0x17]);
dec[0x8] = (metadata[0xC] ^ metadata[0x0] ^ metadata[0x18]);
dec[0x9] = (metadata[0xD] ^ metadata[0x1] ^ metadata[0x19]);
dec[0xA] = (metadata[0xE] ^ metadata[0x2] ^ metadata[0x1A]);
dec[0xB] = (metadata[0xF] ^ metadata[0x3] ^ metadata[0x1B]);
dec[0xC] = (metadata[0x4] ^ metadata[0x0] ^ metadata[0x1C]);
dec[0xD] = (metadata[0x5] ^ metadata[0x1] ^ metadata[0x1D]);
dec[0xE] = (metadata[0x6] ^ metadata[0x2] ^ metadata[0x1E]);
dec[0xF] = (metadata[0x7] ^ metadata[0x3] ^ metadata[0x1F]);
u64 offset = read_from_ptr<be_t<u64>>(dec, 0);
s32 length = read_from_ptr<be_t<s32>>(dec, 8);
@@ -143,26 +149,26 @@ std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
return std::make_tuple(offset, length, compression_end);
}
u128 get_block_key(int block, NPD_HEADER *npd)
u128 get_block_key(int block, const NPD_HEADER& npd)
{
unsigned char empty_key[0x10] = {};
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
const u8 empty_key[0x10] = {};
const u8* src_key = (npd.version <= 1) ? empty_key : npd.dev_hash;
u128 dest_key{};
std::memcpy(&dest_key, src_key, 0xC);
s32 swappedBlock = std::bit_cast<be_t<s32>>(block);
std::memcpy(reinterpret_cast<uchar*>(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock));
const s32 swappedBlock = std::bit_cast<be_t<s32>>(block);
std::memcpy(reinterpret_cast<u8*>(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock));
return dest_key;
}
// for out data, allocate a buffer the size of 'edat->block_size'
// for out data, allocate a buffer the size of 'edat.block_size'
// Also, set 'in file' to the beginning of the encrypted data, which may be offset if inside another file, but normally just reset to beginning of file
// returns number of bytes written, -1 for error
s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *npd, u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left, bool is_out_buffer_aligned = false)
s64 decrypt_block(const fs::file& in, std::vector<u8>& out, const EDAT_HEADER& edat, const NPD_HEADER& npd, const u8* crypt_key, u32 block_num, u32 total_blocks, u64 size_left, bool is_out_buffer_aligned = false, bool verbose = true)
{
// Get metadata info and setup buffers.
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
const int metadata_offset = 0x100;
const u64 metadata_section_size = ((edat.flags & EDAT_COMPRESSED_FLAG) != 0 || (edat.flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
constexpr u64 metadata_offset = 0x100;
u8 hash[0x10] = { 0 };
u8 key_result[0x10] = { 0 };
@@ -172,20 +178,20 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
u64 metadata_sec_offset = 0;
u64 length = 0;
s32 compression_end = 0;
unsigned char empty_iv[0x10] = {};
u8 empty_iv[0x10] = {};
// Decrypt the metadata.
if ((edat->flags & EDAT_COMPRESSED_FLAG) != 0)
if ((edat.flags & EDAT_COMPRESSED_FLAG) != 0)
{
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
u8 metadata[0x20]{};
in->read_at(metadata_sec_offset, metadata, 0x20);
in.read_at(metadata_sec_offset, metadata, 0x20);
// If the data is compressed, decrypt the metadata.
// NOTE: For NPD version 1 the metadata is not encrypted.
if (npd->version <= 1)
if (npd.version <= 1)
{
offset = read_from_ptr<be_t<u64>>(metadata, 0x10);
length = read_from_ptr<be_t<s32>>(metadata, 0x18);
@@ -198,13 +204,13 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
std::memcpy(hash_result, metadata, 0x10);
}
else if ((edat->flags & EDAT_FLAG_0x20) != 0)
else if ((edat.flags & EDAT_FLAG_0x20) != 0)
{
// If FLAG 0x20, the metadata precedes each data block.
metadata_sec_offset = metadata_offset + u64{block_num} * (metadata_section_size + edat->block_size);
metadata_sec_offset = metadata_offset + u64{block_num} * (metadata_section_size + edat.block_size);
u8 metadata[0x20]{};
in->read_at(metadata_sec_offset, metadata, 0x20);
in.read_at(metadata_sec_offset, metadata, 0x20);
std::memcpy(hash_result, metadata, 0x14);
@@ -213,22 +219,22 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
hash_result[j] = metadata[j] ^ metadata[j + 0x10];
offset = metadata_sec_offset + 0x20;
length = edat->block_size;
length = edat.block_size;
if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size))
length = static_cast<s32>(edat->file_size % edat->block_size);
if ((block_num == (total_blocks - 1)) && (edat.file_size % edat.block_size))
length = static_cast<s32>(edat.file_size % edat.block_size);
}
else
{
metadata_sec_offset = metadata_offset + u64{block_num} * metadata_section_size;
in->read_at(metadata_sec_offset, hash_result, 0x10);
in.read_at(metadata_sec_offset, hash_result, 0x10);
offset = metadata_offset + u64{block_num} * edat->block_size + total_blocks * metadata_section_size;
length = edat->block_size;
offset = metadata_offset + u64{block_num} * edat.block_size + total_blocks * metadata_section_size;
length = edat.block_size;
if ((block_num == (total_blocks - 1)) && (edat->file_size % edat->block_size))
length = static_cast<s32>(edat->file_size % edat->block_size);
if ((block_num == (total_blocks - 1)) && (edat.file_size % edat.block_size))
length = static_cast<s32>(edat.file_size % edat.block_size);
}
// Locate the real data.
@@ -240,23 +246,24 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
std::vector<u8> dec_data_buf(length);
// Try to use out buffer for file reads if no padding is needed instead of a new buffer
u8* enc_data = enc_data_buf.empty() ? out : enc_data_buf.data();
std::vector<u8>* enc_data = enc_data_buf.empty() ? &out : &enc_data_buf;
// Variable to avoid copies when possible
u8* dec_data = dec_data_buf.data();
std::vector<u8>* dec_data = &dec_data_buf;
std::memset(hash, 0, 0x10);
std::memset(key_result, 0, 0x10);
in->read_at(offset, enc_data, length);
ensure(enc_data->size() >= length);
in.read_at(offset, enc_data->data(), length);
// Generate a key for the current block.
auto b_key = get_block_key(block_num, npd);
// Encrypt the block key with the crypto key.
aesecb128_encrypt(crypt_key, reinterpret_cast<uchar*>(&b_key), key_result);
aesecb128_encrypt(crypt_key, reinterpret_cast<u8*>(&b_key), key_result);
if ((edat->flags & EDAT_FLAG_0x10) != 0)
if ((edat.flags & EDAT_FLAG_0x10) != 0)
{
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
}
@@ -266,25 +273,25 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
}
// Setup the crypto and hashing mode based on the extra flags.
int crypto_mode = ((edat->flags & EDAT_FLAG_0x02) == 0) ? 0x2 : 0x1;
int crypto_mode = ((edat.flags & EDAT_FLAG_0x02) == 0) ? 0x2 : 0x1;
int hash_mode;
if ((edat->flags & EDAT_FLAG_0x10) == 0)
if ((edat.flags & EDAT_FLAG_0x10) == 0)
hash_mode = 0x02;
else if ((edat->flags & EDAT_FLAG_0x20) == 0)
else if ((edat.flags & EDAT_FLAG_0x20) == 0)
hash_mode = 0x04;
else
hash_mode = 0x01;
if ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
if ((edat.flags & EDAT_ENCRYPTED_KEY_FLAG) != 0)
{
crypto_mode |= 0x10000000;
hash_mode |= 0x10000000;
}
const bool should_decompress = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end;
const bool should_decompress = ((edat.flags & EDAT_COMPRESSED_FLAG) != 0) && compression_end;
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
if ((edat.flags & EDAT_DEBUG_DATA_FLAG) != 0)
{
// Reset the flags.
crypto_mode |= 0x01000000;
@@ -293,7 +300,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
// Simply copy the data without the header or the footer.
if (should_decompress)
{
std::memcpy(dec_data, enc_data, length);
std::memcpy(dec_data->data(), enc_data->data(), length);
}
else
{
@@ -304,12 +311,16 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
else
{
// IV is null if NPD version is 1 or 0.
u8* iv = (npd->version <= 1) ? empty_iv : npd->digest;
const u8* iv = (npd.version <= 1) ? empty_iv : npd.digest;
// Call main crypto routine on this data block.
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), enc_data, dec_data, length, key_result, iv, hash, hash_result))
if (!decrypt(hash_mode, crypto_mode, (npd.version == 4), enc_data->data(), dec_data->data(), length, key_result, iv, hash, hash_result))
{
edat_log.error("Block at offset 0x%llx has invalid hash!", offset);
if (verbose)
{
edat_log.error("Block at offset 0x%llx has invalid hash!", offset);
}
return -1;
}
}
@@ -317,7 +328,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
// Apply additional de-compression if needed and write the decrypted data.
if (should_decompress)
{
const int res = decompress(out, dec_data, edat->block_size);
const int res = decompress(out.data(), dec_data->data(), edat.block_size);
size_left -= res;
@@ -333,44 +344,46 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
return res;
}
if (dec_data != out)
if (dec_data != &out)
{
std::memcpy(out, dec_data, pad_length);
ensure(out.size() >= pad_length);
ensure(dec_data->size() >= pad_length);
std::memcpy(out.data(), dec_data->data(), pad_length);
}
return pad_length;
}
// set file offset to beginning before calling
bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f, bool verbose)
bool check_data(const u8* key, const EDAT_HEADER& edat, const NPD_HEADER& npd, const fs::file& f, bool verbose)
{
u8 header[0xA0] = { 0 };
u8 empty_header[0xA0] = { 0 };
u8 header_hash[0x10] = { 0 };
u8 metadata_hash[0x10] = { 0 };
const u64 file_offset = f->pos();
const u64 file_offset = f.pos();
// Check NPD version and flags.
if ((npd->version == 0) || (npd->version == 1))
if ((npd.version == 0) || (npd.version == 1))
{
if (edat->flags & 0x7EFFFFFE)
if (edat.flags & 0x7EFFFFFE)
{
edat_log.error("Bad header flags!");
return false;
}
}
else if (npd->version == 2)
else if (npd.version == 2)
{
if (edat->flags & 0x7EFFFFE0)
if (edat.flags & 0x7EFFFFE0)
{
edat_log.error("Bad header flags!");
return false;
}
}
else if ((npd->version == 3) || (npd->version == 4))
else if ((npd.version == 3) || (npd.version == 4))
{
if (edat->flags & 0x7EFFFFC0)
if (edat.flags & 0x7EFFFFC0)
{
edat_log.error("Bad header flags!");
return false;
@@ -383,17 +396,17 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
}
// Read in the file header.
f->read(header, 0xA0);
f.read(header, 0xA0);
// Read in the header and metadata section hashes.
f->seek(file_offset + 0x90);
f->read(metadata_hash, 0x10);
f->read(header_hash, 0x10);
f.seek(file_offset + 0x90);
f.read(metadata_hash, 0x10);
f.read(header_hash, 0x10);
// Setup the hashing mode and the crypto mode used in the file.
const int crypto_mode = 0x1;
int hash_mode = ((edat->flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != 0)
constexpr int crypto_mode = 0x1;
int hash_mode = ((edat.flags & EDAT_ENCRYPTED_KEY_FLAG) == 0) ? 0x00000002 : 0x10000002;
if ((edat.flags & EDAT_DEBUG_DATA_FLAG) != 0)
{
hash_mode |= 0x01000000;
@@ -402,17 +415,17 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
}
// Setup header key and iv buffers.
unsigned char header_key[0x10] = { 0 };
unsigned char header_iv[0x10] = { 0 };
u8 header_key[0x10] = { 0 };
u8 header_iv[0x10] = { 0 };
// Test the header hash (located at offset 0xA0).
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash))
if (!decrypt(hash_mode, crypto_mode, (npd.version == 4), header, empty_header, 0xA0, header_key, header_iv, key, header_hash))
{
if (verbose)
edat_log.warning("Header hash is invalid!");
// If the header hash test fails and the data is not DEBUG, then RAP/RIF/KLIC key is invalid.
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
if ((edat.flags & EDAT_DEBUG_DATA_FLAG) != EDAT_DEBUG_DATA_FLAG)
{
edat_log.error("RAP/RIF/KLIC key is invalid!");
return false;
@@ -420,24 +433,25 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
}
// Parse the metadata info.
const int metadata_section_size = ((edat->flags & EDAT_COMPRESSED_FLAG) != 0 || (edat->flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
if (((edat->flags & EDAT_COMPRESSED_FLAG) != 0))
if ((edat.flags & EDAT_COMPRESSED_FLAG) != 0)
{
if (verbose)
edat_log.warning("COMPRESSED data detected!");
}
if (!edat->block_size)
if (!edat.block_size)
{
return false;
}
const usz block_num = utils::aligned_div<u64>(edat->file_size, edat->block_size);
const u64 metadata_section_size = ((edat.flags & EDAT_COMPRESSED_FLAG) != 0 || (edat.flags & EDAT_FLAG_0x20) != 0) ? 0x20 : 0x10;
const usz block_num = utils::aligned_div<u64>(edat.file_size, edat.block_size);
constexpr usz metadata_offset = 0x100;
const usz metadata_size = utils::mul_saturate<u64>(metadata_section_size, block_num);
u64 metadata_section_offset = metadata_offset;
if (utils::add_saturate<u64>(utils::add_saturate<u64>(file_offset, metadata_section_offset), metadata_size) > f->size())
if (utils::add_saturate<u64>(utils::add_saturate<u64>(file_offset, metadata_section_offset), metadata_size) > f.size())
{
return false;
}
@@ -452,43 +466,43 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
const usz offset = file_offset + metadata_section_offset;
// Read in the metadata.
f->read_at(offset, metadata.get() + bytes_read, metadata_section_size);
f.read_at(offset, metadata.get() + bytes_read, metadata_section_size);
// Adjust sizes.
bytes_read += metadata_section_size;
if (((edat->flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block.
metadata_section_offset += (metadata_section_size + edat->block_size);
if (((edat.flags & EDAT_FLAG_0x20) != 0)) // Metadata block before each data block.
metadata_section_offset += (metadata_section_size + edat.block_size);
else
metadata_section_offset += metadata_section_size;
}
// Test the metadata section hash (located at offset 0x90).
if (!decrypt(hash_mode, crypto_mode, (npd->version == 4), metadata.get(), empty_metadata.get(), metadata_size, header_key, header_iv, key, metadata_hash))
if (!decrypt(hash_mode, crypto_mode, (npd.version == 4), metadata.get(), empty_metadata.get(), metadata_size, header_key, header_iv, key, metadata_hash))
{
if (verbose)
edat_log.warning("Metadata section hash is invalid!");
}
// Checking ECDSA signatures.
if ((edat->flags & EDAT_DEBUG_DATA_FLAG) == 0)
if ((edat.flags & EDAT_DEBUG_DATA_FLAG) == 0)
{
// Setup buffers.
unsigned char metadata_signature[0x28] = { 0 };
unsigned char header_signature[0x28] = { 0 };
unsigned char signature_hash[20] = { 0 };
unsigned char signature_r[0x15] = { 0 };
unsigned char signature_s[0x15] = { 0 };
unsigned char zero_buf[0x15] = { 0 };
u8 metadata_signature[0x28] = { 0 };
u8 header_signature[0x28] = { 0 };
u8 signature_hash[20] = { 0 };
u8 signature_r[0x15] = { 0 };
u8 signature_s[0x15] = { 0 };
u8 zero_buf[0x15] = { 0 };
// Setup ECDSA curve and public key.
ecdsa_set_curve(VSH_CURVE_P, VSH_CURVE_A, VSH_CURVE_B, VSH_CURVE_N, VSH_CURVE_GX, VSH_CURVE_GY);
ecdsa_set_pub(VSH_PUB);
// Read in the metadata and header signatures.
f->seek(0xB0);
f->read(metadata_signature, 0x28);
f->read(header_signature, 0x28);
f.seek(0xB0);
f.read(metadata_signature, 0x28);
f.read(header_signature, 0x28);
// Checking metadata signature.
// Setup signature r and s.
@@ -503,13 +517,13 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
else
{
// Setup signature hash.
if ((edat->flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
if ((edat.flags & EDAT_FLAG_0x20) != 0) //Sony failed again, they used buffer from 0x100 with half size of real metadata.
{
const usz metadata_buf_size = block_num * 0x10;
std::vector<u8> metadata_buf(metadata_buf_size);
f->read_at(file_offset + metadata_offset, metadata_buf.data(), metadata_buf_size);
f.read_at(file_offset + metadata_offset, metadata_buf.data(), metadata_buf_size);
sha1(metadata_buf.data(), metadata_buf_size, signature_hash);
}
@@ -519,7 +533,7 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
{
edat_log.warning("Metadata signature is invalid!");
if (((edat->block_size + 0ull) * block_num) > 0x100000000)
if (((edat.block_size + 0ull) * block_num) > 0x100000000)
edat_log.warning("*Due to large file size, metadata signature status may be incorrect!");
}
}
@@ -542,7 +556,7 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
u8 header_buf[0xD8]{};
f->read_at(file_offset, header_buf, 0xD8);
f.read_at(file_offset, header_buf, 0xD8);
sha1(header_buf, 0xD8, signature_hash);
if (!ecdsa_verify(signature_hash, signature_r, signature_s))
@@ -553,27 +567,27 @@ bool check_data(u8* key, EDAT_HEADER* edat, NPD_HEADER* npd, const fs::file* f,
return true;
}
bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
bool validate_dev_klic(const u8* klicensee, const NPD_HEADER& npd)
{
if ((npd->license & 0x3) != 0x3)
if ((npd.license & 0x3) != 0x3)
{
return true;
}
unsigned char dev[0x60]{};
u8 dev[0x60]{};
// Build the dev buffer (first 0x60 bytes of NPD header in big-endian).
std::memcpy(dev, npd, 0x60);
std::memcpy(dev, &npd, 0x60);
// Fix endianness.
s32 version = std::bit_cast<be_t<s32>>(npd->version);
s32 license = std::bit_cast<be_t<s32>>(npd->license);
s32 type = std::bit_cast<be_t<s32>>(npd->type);
s32 version = std::bit_cast<be_t<s32>>(npd.version);
s32 license = std::bit_cast<be_t<s32>>(npd.license);
s32 type = std::bit_cast<be_t<s32>>(npd.type);
std::memcpy(dev + 0x4, &version, 4);
std::memcpy(dev + 0x8, &license, 4);
std::memcpy(dev + 0xC, &type, 4);
// Check for an empty dev_hash (can't validate if devklic is NULL);
// Check for an empty dev_hash (can't validate if devklic is nullptr);
u128 klic;
std::memcpy(&klic, klicensee, sizeof(klic));
@@ -581,13 +595,13 @@ bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
u128 key = klic ^ std::bit_cast<u128>(NP_OMAC_KEY_2);
// Hash with generated key and compare with dev_hash.
return cmac_hash_compare(reinterpret_cast<uchar*>(&key), 0x10, dev, 0x60, npd->dev_hash, 0x10);
return cmac_hash_compare(reinterpret_cast<u8*>(&key), 0x10, dev, 0x60, npd.dev_hash, 0x10);
}
bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HEADER* npd, EDAT_HEADER* edat, bool verbose)
bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, const NPD_HEADER& npd, const EDAT_HEADER& edat, bool verbose)
{
// Ignore header validation in DEBUG data.
if (edat->flags & EDAT_DEBUG_DATA_FLAG)
if (edat.flags & EDAT_DEBUG_DATA_FLAG)
{
return true;
}
@@ -609,7 +623,7 @@ bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HE
std::unique_ptr<u8[]> buf_upper(new u8[buf_len]);
// Build the title buffer (content_id + file_name).
std::memcpy(buf.get(), npd->content_id, 0x30);
std::memcpy(buf.get(), npd.content_id, 0x30);
std::memcpy(buf.get() + 0x30, file_name.data(), file_name.size());
std::memcpy(buf_lower.get(), buf.get(), buf_len);
@@ -620,16 +634,16 @@ bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HE
for (usz i = std::distance(it, buf_span.rend()) - 1; i < buf_len; ++i)
{
const u8 c = buf[i];
buf_upper[i] = std::toupper(c);
buf_lower[i] = std::tolower(c);
buf_upper[i] = utils::toupper(c);
buf_lower[i] = utils::tolower(c);
}
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
// Try to ignore case sensivity with file extension
const bool title_hash_result =
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf.get(), buf_len, npd->title_hash, 0x10) ||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_lower.get(), buf_len, npd->title_hash, 0x10) ||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_upper.get(), buf_len, npd->title_hash, 0x10);
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf.get(), buf_len, npd.title_hash, 0x10) ||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_lower.get(), buf_len, npd.title_hash, 0x10) ||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_upper.get(), buf_len, npd.title_hash, 0x10);
if (verbose)
{
@@ -644,8 +658,8 @@ bool validate_npd_hashes(std::string_view file_name, const u8* klicensee, NPD_HE
void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT)
{
char npd_header[0x80]{};
char edat_header[0x10]{};
u8 npd_header[0x80]{};
u8 edat_header[0x10]{};
usz pos = input->pos();
pos += input->read_at(pos, npd_header, sizeof(npd_header));
@@ -663,7 +677,7 @@ void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& E
NPD.expire_time = read_from_ptr<be_t<s64>>(npd_header, 120);
EDAT.flags = read_from_ptr<be_t<s32>>(edat_header, 0);
EDAT.block_size = read_from_ptr<be_t<s32>>(edat_header, 4);
EDAT.block_size = read_from_ptr<be_t<u32>>(edat_header, 4);
EDAT.file_size = read_from_ptr<be_t<u64>>(edat_header, 8);
}
@@ -674,7 +688,7 @@ u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
rap_file.read<u128>(rapkey);
rap_to_rif(reinterpret_cast<const uchar*>(&rapkey), reinterpret_cast<uchar*>(&rifkey));
rap_to_rif(reinterpret_cast<const u8*>(&rapkey), reinterpret_cast<u8*>(&rifkey));
return rifkey;
}
@@ -703,7 +717,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, std::string_view input_
// Perform header validation (EDAT only).
char real_file_name[CRYPTO_MAX_PATH]{};
extract_file_name(input_file_name, real_file_name);
if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, &EDAT, false))
if (!validate_npd_hashes(real_file_name, custom_klic, NPD, EDAT, false))
{
edat_log.error("NPD hash validation failed!");
return false;
@@ -758,16 +772,16 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
memcpy(&devklic, NP_PSP_KEY_2, 0x10);
break;
case 8:
{
if (custom_klic)
memcpy(&devklic, custom_klic, 0x10);
else
{
if (custom_klic != NULL)
memcpy(&devklic, custom_klic, 0x10);
else
{
edat_log.error("Invalid custom klic!");
return fs::file{};
}
break;
edat_log.error("Invalid custom klic!");
return fs::file{};
}
break;
}
default:
edat_log.error("Invalid mode!");
return fs::file{};
@@ -787,7 +801,7 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
return output;
}
bool EDATADecrypter::ReadHeader()
bool EDATADecrypter::ReadHeader(bool verbose)
{
edata_file.seek(0);
@@ -808,14 +822,29 @@ bool EDATADecrypter::ReadHeader()
}
else
{
// extract key from RIF
char real_file_name[CRYPTO_MAX_PATH]{};
extract_file_name(m_file_name, real_file_name);
if (!validate_npd_hashes(real_file_name, reinterpret_cast<const u8*>(&dec_key), &npdHeader, &edatHeader, false))
// A klicensee that does not match the header decrypts nothing, so there is nothing to carry on with.
// DEBUG data carries no key at all, hence the exception
if (!(edatHeader.flags & EDAT_DEBUG_DATA_FLAG) && !validate_dev_klic(reinterpret_cast<const u8*>(&dec_key), npdHeader))
{
edat_log.error("NPD hash validation failed!");
return true;
if (verbose)
{
edat_log.error("Invalid klicensee for this file!");
}
return false;
}
// Beyond the key, this checks the title hash, taken over the name the file was packaged with: a file
// renamed since then decrypts all the same, so a mismatch is only worth a warning
if (!validate_npd_hashes(real_file_name, reinterpret_cast<const u8*>(&dec_key), npdHeader, edatHeader, false))
{
if (verbose)
{
edat_log.warning("NPD title hash is invalid!");
}
}
// Select EDAT key.
@@ -831,7 +860,7 @@ bool EDATADecrypter::ReadHeader()
// Type 2: Use key from RAP file (RIF key). (also used for type 1 at the moment)
else
{
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.content_id);
const std::string rap_path = rpcs3::utils::get_rap_file_path(npdHeader.get_content_id());
if (fs::file rap{rap_path}; rap && rap.size() >= sizeof(dec_key))
{
@@ -841,8 +870,9 @@ bool EDATADecrypter::ReadHeader()
// Make sure we don't have an empty RIF key.
if (!dec_key)
{
edat_log.error("A valid RAP file is needed for this EDAT file! (license=%d)", npdHeader.license);
return true;
edat_log.error("A valid RAP file is needed for this EDAT file! (content id '%s', license=%d, expected '%s')",
npdHeader.get_content_id(), npdHeader.license, rap_path);
return false;
}
edat_log.trace("RIFKEY: %s", std::bit_cast<be_t<u128>>(dec_key));
@@ -860,21 +890,25 @@ bool EDATADecrypter::ReadHeader()
//}
file_size = edatHeader.file_size;
total_blocks = ::narrow<u32>(utils::aligned_div(edatHeader.file_size, edatHeader.block_size));
total_blocks = (edatHeader.block_size == 0) ? 0 : ::narrow<u32>(utils::aligned_div(edatHeader.file_size, edatHeader.block_size));
// Try decrypting the first block instead
u8 data_sample[1];
if (file_size && !ReadData(0, data_sample, 1))
if (file_size && !ReadData(0, data_sample, 1, verbose))
{
edat_log.error("NPDRM ReadData() failed!");
if (verbose)
{
edat_log.error("NPDRM ReadData() failed!");
}
return false;
}
return true;
}
u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size, bool verbose)
{
size = std::min<u64>(size, pos > edatHeader.file_size ? 0 : edatHeader.file_size - pos);
@@ -898,11 +932,15 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
for (u32 i = starting_block; i < ending_block; i++)
{
u64 res = decrypt_block(&edata_file, data_buf.data(), &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size, true);
u64 res = decrypt_block(edata_file, data_buf, edatHeader, npdHeader, reinterpret_cast<u8*>(&dec_key), i, total_blocks, edatHeader.file_size, true, verbose);
if (res == umax)
{
edat_log.error("Error Decrypting data");
if (verbose)
{
edat_log.error("Error Decrypting data");
}
return 0;
}
+11 -4
View File
@@ -49,12 +49,18 @@ struct NPD_HEADER
u8 dev_hash[0x10];
s64 activate_time;
s64 expire_time;
std::string get_content_id() const
{
const std::string_view id{content_id, sizeof(content_id)};
return std::string{id.substr(0, id.find_first_of('\0'))};
}
};
struct EDAT_HEADER
{
s32 flags;
s32 block_size;
u32 block_size;
u64 file_size;
};
@@ -101,9 +107,10 @@ public:
{
}
// false if invalid
bool ReadHeader();
u64 ReadData(u64 pos, u8* data, u64 size);
// false if invalid. Clearing "verbose" mutes the log of a key that does not fit, which for a caller
// trying one candidate after another is an answer rather than a failure
bool ReadHeader(bool verbose = true);
u64 ReadData(u64 pos, u8* data, u64 size, bool verbose = true);
fs::stat_t get_stat() override
{
+217 -52
View File
@@ -2,6 +2,7 @@
#include "aes.h"
#include "sha1.h"
#include "key_vault.h"
#include "util/asm.hpp"
#include "util/logs.hpp"
#include "Utilities/StrUtil.h"
#include "Utilities/Thread.h"
@@ -16,6 +17,51 @@
LOG_CHANNEL(pkg_log, "PKG");
// Every path in here is UTF-8, and these two conversions never fail: on Windows a byte no sequence accounts for
// becomes U+FFFD, elsewhere a path is the bytes themselves
static std::filesystem::path fs_path_from_utf8(std::string_view utf8)
{
#ifdef _WIN32
return utf8_to_wchar(utf8);
#else
return std::filesystem::path(utf8);
#endif
}
static std::string fs_path_to_utf8(const std::filesystem::path& path)
{
#ifdef _WIN32
return wchar_to_utf8(path.native());
#else
return path.native();
#endif
}
// Whether a name read from the package could reach outside the directory it is joined to: a leading "/", or a "."
// or ".." component. "/" is the only separator that counts, vfs::escape writes any other one the host may know, and
// ":", as plain characters
static bool leaves_directory(std::string_view name)
{
if (name.starts_with('/'))
{
return true;
}
for (std::string_view rest = name; !rest.empty();)
{
const usz sep = rest.find('/');
const std::string_view component = rest.substr(0, sep);
rest = sep == umax ? std::string_view{} : rest.substr(sep + 1);
if (component == "." || component == "..")
{
return true;
}
}
return false;
}
package_reader::package_reader(const std::string& path, fs::file file)
: m_path(path)
, m_file(std::move(file))
@@ -120,9 +166,9 @@ bool package_reader::read_header()
return false;
}
if (u64{umax} / sizeof(PKGEntry) < u64(m_header.file_count))
if (m_header.file_count > PKG_MAX_FILE_COUNT || u64(m_header.file_count) > u64(m_header.data_size) / sizeof(PKGEntry))
{
pkg_log.error("PKG file count is too large! (0x%x)", m_header.file_count);
pkg_log.error("PKG file count is invalid! (count=0x%x, data_size=0x%llx)", m_header.file_count, m_header.data_size);
return false;
}
@@ -190,7 +236,8 @@ bool package_reader::read_header()
m_file = fs::make_gather(std::move(filelist));
}
if (m_header.data_size + m_header.data_offset > m_header.pkg_size)
if ((m_header.data_size + m_header.data_offset) > m_header.pkg_size ||
m_header.data_size > (u64{umax} - m_header.data_offset)) // Check for overflow
{
pkg_log.error("PKG data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", m_header.data_size, m_header.data_offset, m_header.pkg_size);
return false;
@@ -203,7 +250,7 @@ bool package_reader::read_metadata()
{
// Read title ID and use it as an installation directory
m_install_dir.resize(9);
archive_read_block(55, &m_install_dir.front(), m_install_dir.size());
archive_read_block(55, {reinterpret_cast<u8*>(m_install_dir.data()), m_install_dir.size()}, m_install_dir.size());
// Read package metadata
@@ -548,7 +595,7 @@ bool package_reader::read_entries(std::vector<PKGEntry>& entries)
entries.clear();
entries.resize(m_header.file_count + BUF_PADDING / sizeof(PKGEntry) + 1);
const usz read_size = decrypt(0, m_header.file_count * sizeof(PKGEntry), m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : m_dec_key.data(), entries.data());
const usz read_size = decrypt(0, m_header.file_count * sizeof(PKGEntry), m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : m_dec_key.data(), std::span<u8>{reinterpret_cast<u8*>(entries.data()), entries.size() * sizeof(PKGEntry)});
if (read_size < m_header.file_count * sizeof(PKGEntry))
{
@@ -620,7 +667,7 @@ bool package_reader::read_param_sfo()
std::string name_buf(entry.name_size + BUF_PADDING, '\0');
if (usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name_buf.data()); read_size < entry.name_size)
if (usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), std::span<u8>{reinterpret_cast<u8*>(name_buf.data()), name_buf.size()}); read_size < entry.name_size)
{
pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset);
continue;
@@ -643,7 +690,7 @@ bool package_reader::read_param_sfo()
data_buf.resize(block_size + BUF_PADDING);
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf.data()) != block_size)
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf) != block_size)
{
pkg_log.error("Failed to decrypt PARAM.SFO file");
return false;
@@ -853,7 +900,16 @@ bool package_reader::set_install_path()
// TODO: Verify whether other content types require appending title ID
// Append title ID depending on content type
if (m_metadata.content_type != PKG_CONTENT_TYPE_THEME && m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
dir += m_install_dir + '/';
{
// The name is read as it comes from the package
if (m_install_dir.empty() || leaves_directory(m_install_dir))
{
pkg_log.error("Invalid installation directory name '%s'", m_install_dir);
return false;
}
dir += vfs::escape(m_install_dir) + '/';
}
// If false, an existing directory is being overwritten: cannot cancel the operation
m_was_null = !fs::is_dir(dir);
@@ -875,6 +931,46 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
return false;
}
const auto canonicalize = [](std::string_view utf8_path)
{
std::error_code ec;
auto result = std::filesystem::weakly_canonical(fs_path_from_utf8(utf8_path), ec);
if (ec)
{
pkg_log.warning("Failed to canonicalize path '%s' (%s); falling back to lexical normalization.", utf8_path, ec.message());
result = fs_path_from_utf8(utf8_path).lexically_normal();
}
return result;
};
// weakly_canonical stats every prefix of a path that does not exist yet, and a package holds thousands of files in a
// few hundred directories: a directory is resolved once, and a file not on disk is that result plus its own name
std::map<std::string, std::filesystem::path, std::less<>> canonical_dirs;
// The installation directory is resolved without its closing "/", like every entry below it: on a file system that
// cannot resolve it, the lexical form would keep that separator as an empty last element no entry can match
std::string_view install_dir = m_install_path;
while (install_dir.ends_with('/'))
{
install_dir.remove_suffix(1);
}
const std::filesystem::path install_path = canonicalize(install_dir);
if (install_path.empty())
{
pkg_log.error("Failed to normalize installation path for '%s'", m_install_path);
return false;
}
const auto is_inside_install_path = [&install_path](const std::filesystem::path& path)
{
return std::mismatch(install_path.begin(), install_path.end(), path.begin(), path.end()).first == install_path.end();
};
m_install_entries.clear();
m_bootable_file_path.clear();
m_entry_indexer = 0;
@@ -903,7 +999,7 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u;
if (const usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), name_buf.data()); read_size < entry.name_size)
if (const usz read_size = decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), std::span<u8>{reinterpret_cast<u8*>(name_buf.data()), name_buf.size()}); read_size < entry.name_size)
{
num_failures++;
pkg_log.error("PKG name could not be read (size=0x%x, offset=0x%x)", entry.name_size, entry.name_offset);
@@ -912,8 +1008,53 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
std::string_view name = fmt::trim_back_sv(name_buf, "\0"sv);
if (leaves_directory(name))
{
num_failures++;
pkg_log.error("PKG entry path escapes installation directory: '%s'", name);
break;
}
std::string path = m_install_path + vfs::escape(name);
const u8 entry_type = entry.type & 0xff;
const bool is_folder = entry_type == PKG_FILE_ENTRY_FOLDER || entry_type == 0x12;
std::filesystem::path canonical_path;
// A path already on disk is resolved as a whole: it may be a link, or be spelt otherwise there
if (is_folder || fs::exists(path))
{
canonical_path = canonicalize(path);
if (is_folder)
{
canonical_dirs.emplace(path, canonical_path);
}
}
else
{
const usz slash = path.rfind('/');
const std::string_view dir(path.data(), slash);
auto found = canonical_dirs.find(dir);
if (found == canonical_dirs.end())
{
found = canonical_dirs.emplace(std::string(dir), canonicalize(dir)).first;
}
canonical_path = found->second / fs_path_from_utf8(std::string_view(path).substr(slash + 1));
}
if (canonical_path.empty() || !is_inside_install_path(canonical_path))
{
num_failures++;
pkg_log.error("PKG entry path escapes installation directory: '%s'", name);
break;
}
path = fs_path_to_utf8(canonical_path);
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);
@@ -923,8 +1064,6 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
pkg_log.notice("Entry: type=0x%08x, name='%s'", entry.type, name);
}
const u8 entry_type = entry.type & 0xff;
switch (entry_type)
{
case PKG_FILE_ENTRY_FOLDER:
@@ -949,16 +1088,8 @@ bool package_reader::fill_data(std::map<std::string, install_entry*>& all_instal
}
default:
{
// 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;
// "path" went through "vfs::escape", which also takes care of the bytes that are not valid UTF-8
auto map_ptr = &*all_install_entries.try_emplace(std::move(path)).first;
m_install_entries.push_back({
.weak_reference = map_ptr,
@@ -1082,7 +1213,7 @@ void package_reader::extract_worker()
const install_entry& m_entry;
usz m_pos;
explicit pkg_file_reader(std::function<u64(u64, void* buffer, u64)> read_func, const install_entry& entry) noexcept
explicit pkg_file_reader(std::function<u64(u64, void*, u64)> read_func, const install_entry& entry) noexcept
: m_read_func(std::move(read_func))
, m_entry(entry)
, m_pos(0)
@@ -1151,6 +1282,9 @@ void package_reader::extract_worker()
read_cache.clear();
// 16MB buffer
std::vector<u8> buffer(std::min<usz>(entry.file_size, 1u << 24) + BUF_PADDING);
auto reader = std::make_unique<pkg_file_reader>([&, cache_off = u64{umax}](usz pos, void* ptr, usz size) mutable -> u64
{
if (pos >= entry.file_size || !size)
@@ -1158,6 +1292,7 @@ void package_reader::extract_worker()
return 0;
}
const usz original_size = size;
size = std::min<u64>(entry.file_size - pos, size);
u64 size_cache_end = 0;
@@ -1192,7 +1327,7 @@ void package_reader::extract_worker()
read_cache.resize(block_size + BUF_PADDING);
cache_off = pos;
const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), read_cache.data());
const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), read_cache);
if (!advance_size)
{
@@ -1210,8 +1345,17 @@ void package_reader::extract_worker()
while (read_size < size)
{
const u64 block_size = std::min<u64>(BUF_SIZE, size - read_size);
u64 available_buffer_size = original_size - read_size;
const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), static_cast<u8*>(ptr) + read_size);
if (buffer.data() == ptr)
{
available_buffer_size = buffer.size() - read_size;
ensure(buffer.size() == original_size + BUF_PADDING);
}
ensure(available_buffer_size >= block_size);
const usz advance_size = decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), std::span<u8>{static_cast<u8*>(ptr) + read_size, available_buffer_size});
if (!advance_size)
{
@@ -1246,9 +1390,6 @@ void package_reader::extract_worker()
break;
}
// 16MB buffer
std::vector<u8> buffer(std::min<usz>(entry.file_size, 1u << 24) + BUF_PADDING);
while (usz read_size = final_data.read(buffer.data(), buffer.size() - BUF_PADDING))
{
out.write(buffer.data(), read_size);
@@ -1298,7 +1439,7 @@ void package_reader::extract_worker()
}
}
package_install_result package_reader::extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths)
package_install_result package_reader::extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths, bool from_optical_drive)
{
package_install_result::error_type error = package_install_result::error_type::no_error;
usz num_failures = 0;
@@ -1350,15 +1491,18 @@ package_install_result package_reader::extract_data(std::deque<package_reader>&
if (reader.m_num_failures == 0)
{
const usz thread_count = std::min<usz>(utils::get_thread_count(), reader.m_install_entries.size());
named_thread_group workers("PKG Installer "sv, std::max<u32>(::narrow<u32>(thread_count), 1) - 1, [&]()
// Disc archives don't like multithreaded file reads, so let's just use a single thread here
const usz thread_count = from_optical_drive ? 1 : std::min<usz>(utils::get_thread_count(), reader.m_install_entries.size());
const usz num_threads_succeeded = map_workload("PKG Installer "sv, thread_count, [&reader]()
{
reader.extract_worker();
});
reader.extract_worker();
workers.join();
if (thread_count != num_threads_succeeded)
{
pkg_log.error("%d thread(s) failed with an exception!", thread_count - num_threads_succeeded);
reader.m_num_failures++;
}
}
num_failures += reader.m_num_failures;
@@ -1426,14 +1570,16 @@ u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
return m_file ? m_file.read(data_ptr, num_bytes) : 0;
}
std::span<const char> package_reader::archive_read_block(u64 offset, void* data_ptr, u64 num_bytes)
std::span<const char> package_reader::archive_read_block(u64 offset, std::span<u8> dst, u64 num_bytes)
{
const usz read_n = m_file.read_at(offset, data_ptr, num_bytes);
ensure(dst.size() >= num_bytes);
return {static_cast<const char*>(data_ptr), read_n};
const usz read_n = m_file.read_at(offset, dst.data(), num_bytes);
return {reinterpret_cast<const char*>(dst.data()), read_n};
}
usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_buf)
usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, std::span<u8> local_buf)
{
if (!m_is_valid)
{
@@ -1445,15 +1591,26 @@ usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_
return 0;
}
ensure(local_buf.size() >= size);
// Read the data and set available size
const auto data_span = archive_read_block(m_header.data_offset + offset, local_buf, size);
ensure(data_span.data() == static_cast<void*>(local_buf));
ensure(data_span.data() == static_cast<void*>(local_buf.data()));
ensure(data_span.size() <= size);
// Get block count
const u64 blocks = (data_span.size() + 15) / 16;
const auto out_data = reinterpret_cast<u8*>(local_buf);
// Clear padding
if (data_span.size() < local_buf.size())
{
std::memset(&local_buf[data_span.size()], 0, local_buf.size() - data_span.size());
}
if (m_header.pkg_type == PKG_RELEASE_TYPE_DEBUG)
// Get block count. Round up.
const u64 blocks = utils::aligned_div<u64>(data_span.size(), sizeof(u128));
const u64 read_size = blocks * sizeof(u128);
switch (m_header.pkg_type)
{
case PKG_RELEASE_TYPE_DEBUG:
{
// Debug key
be_t<u64> input[8] =
@@ -1467,7 +1624,7 @@ usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_
for (u64 i = 0; i < blocks; i++)
{
// Initialize stream cipher for current position
input[7] = offset / 16 + i;
input[7] = offset / sizeof(u128) + i;
struct sha1_hash
{
@@ -1476,11 +1633,13 @@ usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_
sha1(reinterpret_cast<const u8*>(input), sizeof(input), hash.data);
const u128 v = read_from_ptr_unsafe<u128>(out_data, i * 16);
write_to_ptr_unsafe<u128>(out_data, i * 16, v ^ read_from_ptr<u128>(hash.data));
const u128 v = read_from_ptr<u128>(local_buf, i * sizeof(u128));
write_to_ptr<u128>(local_buf, i * sizeof(u128), v ^ read_from_ptr<u128>(hash.data));
}
break;
}
else if (m_header.pkg_type == PKG_RELEASE_TYPE_RELEASE)
case PKG_RELEASE_TYPE_RELEASE:
{
aes_context ctx;
@@ -1488,7 +1647,7 @@ usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_
aes_setkey_enc(&ctx, key, 128);
// Initialize stream cipher for start position
be_t<u128> input = m_header.klicensee.value() + offset / 16;
be_t<u128> input = m_header.klicensee.value() + offset / sizeof(u128);
// Increment stream position for every block
for (u64 i = 0; i < blocks; i++, input++)
@@ -1497,19 +1656,25 @@ usz package_reader::decrypt(u64 offset, u64 size, const uchar* key, void* local_
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const u8*>(&input), reinterpret_cast<u8*>(&key));
const u128 v = read_from_ptr_unsafe<u128>(out_data, i * 16);
write_to_ptr_unsafe<u128>(out_data, i * 16, v ^ key);
const u128 v = read_from_ptr<u128>(local_buf, i * sizeof(u128));
write_to_ptr<u128>(local_buf, i * sizeof(u128), v ^ key);
}
break;
}
else
default:
{
pkg_log.error("Unknown release type (0x%x)", m_header.pkg_type);
break;
}
}
if (blocks * 16 != size)
if (read_size > size)
{
// Put NTS and other zeroes on unaligned reads
std::memset(out_data + size, 0, blocks * 16 - size);
const u64 pad_size = read_size - size;
ensure(local_buf.size() >= (size + pad_size));
std::memset(&local_buf[size], 0, pad_size);
}
// Return the amount of data written in buf
+4 -3
View File
@@ -14,6 +14,7 @@ enum : u32
{
PKG_HEADER_SIZE = 0xC0, // sizeof(pkg_header) + sizeof(pkg_unk_checksum)
PKG_HEADER_SIZE2 = 0x280,
PKG_MAX_FILE_COUNT = 0x7FFF'FFFF,
PKG_MAX_FILENAME_SIZE = 256,
};
@@ -372,7 +373,7 @@ public:
const PKGHeader& get_header() const { return m_header; }
const PKGMetaData& get_metadata() const { return m_metadata; }
package_install_result check_target_app_version() const;
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths);
static package_install_result extract_data(std::deque<package_reader>& readers, std::deque<std::string>& bootable_paths, bool from_optical_drive);
const psf::registry& get_psf() const { return m_psf; }
result get_result() const { return m_result; };
@@ -395,8 +396,8 @@ private:
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);
usz decrypt(u64 offset, u64 size, const uchar* key, void* local_buf);
std::span<const char> archive_read_block(u64 offset, std::span<u8> dst, u64 num_bytes);
usz decrypt(u64 offset, u64 size, const uchar* key, std::span<u8> local_buf);
void extract_worker();
std::deque<install_entry> m_install_entries;
+129 -84
View File
@@ -90,7 +90,7 @@ inline void Write64(const fs::file& f, const be_t<u64> data)
f.write(&data, sizeof(data));
}
void WriteEhdr(const fs::file& f, Elf64_Ehdr& ehdr)
void WriteEhdr(const fs::file& f, const Elf64_Ehdr& ehdr)
{
Write32(f, ehdr.e_magic);
Write8(f, ehdr.e_class);
@@ -113,7 +113,7 @@ void WriteEhdr(const fs::file& f, Elf64_Ehdr& ehdr)
Write16(f, ehdr.e_shstrndx);
}
void WritePhdr(const fs::file& f, Elf64_Phdr& phdr)
void WritePhdr(const fs::file& f, const Elf64_Phdr& phdr)
{
Write32(f, phdr.p_type);
Write32(f, phdr.p_flags);
@@ -125,7 +125,7 @@ void WritePhdr(const fs::file& f, Elf64_Phdr& phdr)
Write64(f, phdr.p_align);
}
void WriteShdr(const fs::file& f, Elf64_Shdr& shdr)
void WriteShdr(const fs::file& f, const Elf64_Shdr& shdr)
{
Write32(f, shdr.sh_name);
Write32(f, shdr.sh_type);
@@ -139,7 +139,7 @@ void WriteShdr(const fs::file& f, Elf64_Shdr& shdr)
Write64(f, shdr.sh_entsize);
}
void WriteEhdr(const fs::file& f, Elf32_Ehdr& ehdr)
void WriteEhdr(const fs::file& f, const Elf32_Ehdr& ehdr)
{
Write32(f, ehdr.e_magic);
Write8(f, ehdr.e_class);
@@ -162,7 +162,7 @@ void WriteEhdr(const fs::file& f, Elf32_Ehdr& ehdr)
Write16(f, ehdr.e_shstrndx);
}
void WritePhdr(const fs::file& f, Elf32_Phdr& phdr)
void WritePhdr(const fs::file& f, const Elf32_Phdr& phdr)
{
Write32(f, phdr.p_type);
Write32(f, phdr.p_offset);
@@ -174,7 +174,7 @@ void WritePhdr(const fs::file& f, Elf32_Phdr& phdr)
Write32(f, phdr.p_align);
}
void WriteShdr(const fs::file& f, Elf32_Shdr& shdr)
void WriteShdr(const fs::file& f, const Elf32_Shdr& shdr)
{
Write32(f, shdr.sh_name);
Write32(f, shdr.sh_type);
@@ -323,7 +323,7 @@ void supplemental_header::Show() const
self_log.notice("Version: 0x%08x", PS3_npdrm_header.npd.version);
self_log.notice("License: 0x%08x", PS3_npdrm_header.npd.license);
self_log.notice("Type: 0x%08x", PS3_npdrm_header.npd.type);
self_log.notice("ContentID: %s", PS3_npdrm_header.npd.content_id);
self_log.notice("ContentID: %s", PS3_npdrm_header.npd.get_content_id());
self_log.notice("Digest: %s", PS3_npdrm_header.npd.digest);
self_log.notice("Inverse digest: %s", PS3_npdrm_header.npd.title_hash);
self_log.notice("XOR digest: %s", PS3_npdrm_header.npd.dev_hash);
@@ -627,7 +627,20 @@ bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
{
aes_context aes;
std::vector<u8> metadata_info(sizeof(meta_info));
std::vector<u8> metadata_headers(sce_hdr.se_hsize - (sizeof(sce_hdr) + sce_hdr.se_meta + sizeof(meta_info)));
constexpr usz sizeof_meta_and_header = sizeof(meta_info) + sizeof(sce_hdr);
const usz metadata_offset = static_cast<usz>(sce_hdr.se_meta);
if (metadata_offset > usz{umax} - sizeof_meta_and_header ||
sce_hdr.se_hsize < sizeof_meta_and_header + metadata_offset ||
sce_hdr.se_hsize - (sizeof_meta_and_header + metadata_offset) < sizeof(meta_hdr) ||
sce_hdr.se_hsize > sce_f.size())
{
self_log.error("Invalid SCE metadata header size!");
return false;
}
const usz metadata_headers_offset = sizeof_meta_and_header + metadata_offset;
std::vector<u8> metadata_headers(sce_hdr.se_hsize - metadata_headers_offset);
// Locate and read the encrypted metadata info.
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
@@ -674,7 +687,7 @@ bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
for (u32 i = 0; i < meta_hdr.section_count; i++)
{
const usz shdr_offset = sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i;
ensure(metadata_headers.size() > shdr_offset);
@@ -684,10 +697,11 @@ bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
}
// Copy the decrypted data keys.
data_keys.resize(meta_hdr.key_count * 0x10);
data_keys.resize(static_cast<u64>(meta_hdr.key_count) * 0x10);
const usz data_keys_offset = sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader);
ensure(metadata_headers.size() >= (data_keys_offset + data_keys.size()));
ensure(data_keys.size() <= (usz{umax} - data_keys_offset)); // Check for overflow
std::memcpy(data_keys.data(), metadata_headers.data() + data_keys_offset, data_keys.size());
return true;
@@ -699,9 +713,9 @@ bool SCEDecrypter::DecryptData()
usz data_buf_length = 0;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
for (const MetadataSectionHeader& hdr : meta_shdr)
{
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size);
data_buf_length += ::narrow<u32>(hdr.data_size);
}
// Allocate a buffer to store decrypted data.
@@ -710,52 +724,55 @@ bool SCEDecrypter::DecryptData()
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
usz ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Tmp buffer
std::vector<u8> buf;
u8 ctr_stream_block[0x10];
// Parse the metadata section headers to find the offsets of encrypted data.
for (const MetadataSectionHeader& hdr : meta_shdr)
{
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
if (hdr.encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
if ((hdr.key_idx < meta_hdr.key_count) && (hdr.iv_idx < meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
std::memcpy(data_key, data_keys.data() + meta_shdr[i].key_idx * 0x10, 0x10);
std::memcpy(data_iv, data_keys.data() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
const std::array<u8, 0x10> data_key = read_from_ptr<std::array<u8, 0x10>>(data_keys, static_cast<usz>(hdr.key_idx) * 0x10);
std::array<u8, 0x10> data_iv = read_from_ptr<std::array<u8, 0x10>>(data_keys, static_cast<usz>(hdr.iv_idx) * 0x10);
// Seek to the section data offset and read the encrypted data.
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
buf.resize(hdr.data_size);
sce_f.seek(hdr.data_offset);
sce_f.read(buf.data(), buf.size());
// Zero out our ctr nonce.
std::memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
usz ctr_nc_off = 0;
aes_setkey_enc(&aes, data_key.data(), 128);
aes_crypt_ctr(&aes, buf.size(), &ctr_nc_off, data_iv.data(), ctr_stream_block, buf.data(), buf.data());
// Copy the decrypted data.
std::memcpy(data_buf.data() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
ensure(data_buf.size() >= (buf.size() + data_buf_offset));
ensure(buf.size() <= (usz{umax} - static_cast<usz>(data_buf_offset))); // Check for overflow
std::memcpy(data_buf.data() + data_buf_offset, buf.data(), buf.size());
}
}
else
{
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
sce_f.seek(meta_shdr[i].data_offset);
sce_f.read(buf.get(), meta_shdr[i].data_size);
std::memcpy(data_buf.data() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
buf.resize(hdr.data_size);
sce_f.seek(hdr.data_offset);
sce_f.read(buf.data(), buf.size());
ensure(data_buf.size() >= (buf.size() + data_buf_offset));
ensure(buf.size() <= (usz{umax} - static_cast<usz>(data_buf_offset))); // Check for overflow
std::memcpy(data_buf.data() + data_buf_offset, buf.data(), buf.size());
}
// Advance the buffer's offset.
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
data_buf_offset += ::narrow<u32>(hdr.data_size);
}
return true;
@@ -770,9 +787,8 @@ std::vector<fs::file> SCEDecrypter::MakeFile()
u32 data_buf_offset = 0;
// Write data.
for (u32 i = 0; i < meta_hdr.section_count; i++)
for (const MetadataSectionHeader& hdr : meta_shdr)
{
const MetadataSectionHeader& hdr = meta_shdr[i];
const u8* src = data_buf.data() + data_buf_offset;
fs::file out_f = fs::make_stream<std::vector<u8>>();
@@ -851,13 +867,13 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
if (isElf32)
{
phdr32_arr.clear();
if(elf32_hdr.e_phoff == 0 && elf32_hdr.e_phnum)
if (elf32_hdr.e_phoff == 0 && elf32_hdr.e_phnum)
{
self_log.error("ELF program header offset is null!");
return false;
}
self_f.seek(m_ext_hdr.phdr_offset);
for(u32 i = 0; i < elf32_hdr.e_phnum; ++i)
for (u32 i = 0; i < elf32_hdr.e_phnum; ++i)
{
phdr32_arr.emplace_back();
phdr32_arr.back().Load(self_f);
@@ -886,7 +902,7 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
m_seg_ext_hdr.clear();
self_f.seek(m_ext_hdr.segment_ext_hdr_offset);
for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
for (u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
{
if (self_f.pos() >= self_size)
{
@@ -952,7 +968,7 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
self_f.seek(m_ext_hdr.shdr_offset);
for(u32 i = 0; i < elf32_hdr.e_shnum; ++i)
for (u32 i = 0; i < elf32_hdr.e_shnum; ++i)
{
shdr32_arr.emplace_back();
shdr32_arr.back().Load(self_f);
@@ -969,7 +985,7 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
self_f.seek(m_ext_hdr.shdr_offset);
for(u32 i = 0; i < elf64_hdr.e_shnum; ++i)
for (u32 i = 0; i < elf64_hdr.e_shnum; ++i)
{
shdr64_arr.emplace_back();
shdr64_arr.back().Load(self_f);
@@ -1004,13 +1020,21 @@ void SELFDecrypter::ShowHeaders(bool isElf32)
self_log.notice("----------------------------------------------------");
self_log.notice("ELF program headers");
self_log.notice("----------------------------------------------------");
for(unsigned int i = 0; i < ((isElf32) ? phdr32_arr.size() : phdr64_arr.size()); i++)
isElf32 ? phdr32_arr[i].Show() : phdr64_arr[i].Show();
if (isElf32)
{
for (const Elf32_Phdr& hdr : phdr32_arr)
hdr.Show();
}
else
{
for (const Elf64_Phdr& hdr : phdr64_arr)
hdr.Show();
}
self_log.notice("----------------------------------------------------");
self_log.notice("Section info");
self_log.notice("----------------------------------------------------");
for(unsigned int i = 0; i < m_seg_ext_hdr.size(); i++)
m_seg_ext_hdr[i].Show();
for (const segment_ext_header& hdr : m_seg_ext_hdr)
hdr.Show();
self_log.notice("----------------------------------------------------");
self_log.notice("SCE version info");
self_log.notice("----------------------------------------------------");
@@ -1018,13 +1042,21 @@ void SELFDecrypter::ShowHeaders(bool isElf32)
self_log.notice("----------------------------------------------------");
self_log.notice("Control info");
self_log.notice("----------------------------------------------------");
for(unsigned int i = 0; i < m_supplemental_hdr_arr.size(); i++)
m_supplemental_hdr_arr[i].Show();
for (const supplemental_header& hdr : m_supplemental_hdr_arr)
hdr.Show();
self_log.notice("----------------------------------------------------");
self_log.notice("ELF section headers");
self_log.notice("----------------------------------------------------");
for(unsigned int i = 0; i < ((isElf32) ? shdr32_arr.size() : shdr64_arr.size()); i++)
isElf32 ? shdr32_arr[i].Show() : shdr64_arr[i].Show();
if (isElf32)
{
for (const Elf32_Shdr& hdr : shdr32_arr)
hdr.Show();
}
else
{
for (const Elf64_Shdr& hdr : shdr64_arr)
hdr.Show();
}
self_log.notice("----------------------------------------------------");
}
@@ -1046,7 +1078,7 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
if (npd->license == 1) // Network license.
{
// Try to find a RAP file to get the key.
if (!GetKeyFromRap(npd->content_id, npdrm_key))
if (!GetKeyFromRap(npd->get_content_id(), npdrm_key))
{
self_log.error("Can't decrypt network NPDRM!");
return false;
@@ -1055,7 +1087,7 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
else if (npd->license == 2) // Local license.
{
// Try to find a RAP file to get the key.
if (!GetKeyFromRap(npd->content_id, npdrm_key))
if (!GetKeyFromRap(npd->get_content_id(), npdrm_key))
{
self_log.error("Can't find RAP file for NPDRM decryption!");
return false;
@@ -1104,7 +1136,20 @@ bool SELFDecrypter::LoadMetadata(const u8* klic_key)
{
aes_context aes;
std::vector<u8> metadata_info(sizeof(meta_info));
std::vector<u8> metadata_headers(sce_hdr.se_hsize - (sizeof(sce_hdr) + sce_hdr.se_meta + sizeof(meta_info)));
constexpr usz sizeof_meta_and_header = sizeof(meta_info) + sizeof(sce_hdr);
const usz metadata_offset = static_cast<usz>(sce_hdr.se_meta);
if (metadata_offset > usz{umax} - sizeof_meta_and_header ||
sce_hdr.se_hsize < sizeof_meta_and_header + metadata_offset ||
sce_hdr.se_hsize - (sizeof_meta_and_header + metadata_offset) < sizeof(meta_hdr) ||
sce_hdr.se_hsize > self_f.size())
{
self_log.error("Invalid SELF metadata header size!");
return false;
}
const usz metadata_headers_offset = sizeof_meta_and_header + metadata_offset;
std::vector<u8> metadata_headers(sce_hdr.se_hsize - metadata_headers_offset);
// Locate and read the encrypted metadata info.
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
@@ -1162,7 +1207,7 @@ bool SELFDecrypter::LoadMetadata(const u8* klic_key)
// Load the metadata section headers.
meta_shdr.clear();
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
for (u32 i = 0; i < meta_hdr.section_count; i++)
{
const usz shdr_offset = sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i;
ensure(metadata_headers.size() > shdr_offset);
@@ -1172,10 +1217,11 @@ bool SELFDecrypter::LoadMetadata(const u8* klic_key)
}
// Copy the decrypted data keys.
data_keys.resize(meta_hdr.key_count * 0x10);
data_keys.resize(static_cast<u64>(meta_hdr.key_count) * 0x10);
const usz data_keys_offset = sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader);
ensure(metadata_headers.size() >= (data_keys_offset + data_keys.size()));
ensure(data_keys.size() <= (usz{umax} - data_keys_offset)); // Check for overflow
std::memcpy(data_keys.data(), metadata_headers.data() + data_keys_offset, data_keys.size());
return true;
@@ -1187,12 +1233,12 @@ bool SELFDecrypter::DecryptData()
usz data_buf_length = 0;
// Calculate the total data size.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
for (const MetadataSectionHeader& hdr : meta_shdr)
{
if (meta_shdr[i].encrypted == 3)
if (hdr.encrypted == 3)
{
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size);
if ((hdr.key_idx < meta_hdr.key_count) && (hdr.iv_idx < meta_hdr.key_count))
data_buf_length += ::narrow<u32>(hdr.data_size);
}
}
@@ -1202,43 +1248,43 @@ bool SELFDecrypter::DecryptData()
// Set initial offset.
u32 data_buf_offset = 0;
// Parse the metadata section headers to find the offsets of encrypted data.
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
{
usz ctr_nc_off = 0;
u8 ctr_stream_block[0x10];
u8 data_key[0x10];
u8 data_iv[0x10];
// Tmp buffer
std::vector<u8> buf;
u8 ctr_stream_block[0x10];
// Parse the metadata section headers to find the offsets of encrypted data.
for (const MetadataSectionHeader& hdr : meta_shdr)
{
// Check if this is an encrypted section.
if (meta_shdr[i].encrypted == 3)
if (hdr.encrypted == 3)
{
// Make sure the key and iv are not out of boundaries.
if((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
if ((hdr.key_idx < meta_hdr.key_count) && (hdr.iv_idx < meta_hdr.key_count))
{
// Get the key and iv from the previously stored key buffer.
std::memcpy(data_key, data_keys.data() + meta_shdr[i].key_idx * 0x10, 0x10);
std::memcpy(data_iv, data_keys.data() + meta_shdr[i].iv_idx * 0x10, 0x10);
// Allocate a buffer to hold the data.
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
const std::array<u8, 0x10> data_key = read_from_ptr<std::array<u8, 0x10>>(data_keys, static_cast<usz>(hdr.key_idx) * 0x10);
std::array<u8, 0x10> data_iv = read_from_ptr<std::array<u8, 0x10>>(data_keys, static_cast<usz>(hdr.iv_idx) * 0x10);
// Seek to the section data offset and read the encrypted data.
self_f.seek(meta_shdr[i].data_offset);
self_f.read(buf.get(), meta_shdr[i].data_size);
buf.resize(hdr.data_size);
self_f.seek(hdr.data_offset);
self_f.read(buf.data(), buf.size());
// Zero out our ctr nonce.
std::memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
// Perform AES-CTR encryption on the data blocks.
aes_setkey_enc(&aes, data_key, 128);
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
usz ctr_nc_off = 0;
aes_setkey_enc(&aes, data_key.data(), 128);
aes_crypt_ctr(&aes, buf.size(), &ctr_nc_off, data_iv.data(), ctr_stream_block, buf.data(), buf.data());
// Copy the decrypted data.
std::memcpy(data_buf.data() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
ensure(data_buf.size() >= (buf.size() + data_buf_offset));
ensure(buf.size() <= (usz{umax} - static_cast<usz>(data_buf_offset))); // Check for overflow
std::memcpy(data_buf.data() + data_buf_offset, buf.data(), buf.size());
// Advance the buffer's offset.
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
data_buf_offset += ::narrow<u32>(hdr.data_size);
}
}
}
@@ -1263,14 +1309,13 @@ fs::file SELFDecrypter::MakeElf(bool isElf32)
return e;
}
bool SELFDecrypter::GetKeyFromRap(const char* content_id, u8* npdrm_key)
bool SELFDecrypter::GetKeyFromRap(std::string_view content_id, u8* npdrm_key)
{
// Set empty RAP key.
std::array<u8, 0x10> rap_key {};
// Try to find a matching RAP file under exdata folder.
const std::string ci_str = content_id;
const std::string rap_path = rpcs3::utils::get_rap_file_path(ci_str);
const std::string rap_path = rpcs3::utils::get_rap_file_path(content_id);
// Open the RAP file and read the key.
const fs::file rap_file(rap_path);
@@ -1283,7 +1328,7 @@ bool SELFDecrypter::GetKeyFromRap(const char* content_id, u8* npdrm_key)
return false;
}
self_log.notice("Loading RAP file %s.rap", ci_str);
self_log.notice("Loading RAP file %s.rap", content_id);
if (rap_file.read(rap_key.data(), rap_key.size()) != rap_key.size())
{
+38 -24
View File
@@ -478,11 +478,11 @@ public:
bool DecryptData();
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
const NPD_HEADER* GetNPDHeader() const;
static bool GetKeyFromRap(const char *content_id, u8 *npdrm_key);
static bool GetKeyFromRap(std::string_view content_id, u8 *npdrm_key);
private:
template<typename EHdr, typename SHdr, typename PHdr>
void WriteElf(fs::file& e, EHdr ehdr, SHdr shdr, PHdr phdr)
void WriteElf(fs::file& e, EHdr ehdr, const std::vector<SHdr>& shdrs, const std::vector<PHdr>& phdrs)
{
// Set initial offset.
u32 data_buf_offset = 0;
@@ -491,32 +491,35 @@ private:
WriteEhdr(e, ehdr);
// Write program headers.
for (u32 i = 0; i < ehdr.e_phnum; ++i)
for (const PHdr& phdr : phdrs)
{
WritePhdr(e, phdr[i]);
WritePhdr(e, phdr);
}
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
// Tmp buffer
std::vector<u8> decomp_buf;
for (const MetadataSectionHeader& hdr : meta_shdr)
{
// PHDR type.
if (meta_shdr[i].type == 2)
if (hdr.type == 2)
{
// Decompress if necessary.
if (meta_shdr[i].compressed == 2)
{
const auto filesz = phdr[meta_shdr[i].program_idx].p_filesz;
const PHdr& phdr = ::at32(phdrs, hdr.program_idx);
// Create a pointer to a buffer for decompression.
std::unique_ptr<u8[]> decomp_buf(new u8[filesz]);
// Decompress if necessary.
if (hdr.compressed == 2)
{
const auto filesz = phdr.p_filesz;
decomp_buf.resize(filesz);
// Create a buffer separate from data_buf to uncompress.
std::vector<u8> zlib_buf = data_buf;
uLongf decomp_buf_length = ::narrow<uLongf>(filesz);
ensure(data_buf.size() > data_buf_offset);
const std::vector<u8> zlib_buf(data_buf.cbegin() + data_buf_offset, data_buf.cend());
// Use zlib uncompress on the new buffer.
// decomp_buf_length changes inside the call to uncompress
const int rv = uncompress(decomp_buf.get(), &decomp_buf_length, zlib_buf.data() + data_buf_offset, ::size32(zlib_buf));
uLongf decomp_buf_length = ::narrow<uLongf>(filesz);
const int rv = uncompress(decomp_buf.data(), &decomp_buf_length, zlib_buf.data(), ::size32(zlib_buf));
// Check for errors (TODO: Probably safe to remove this once these changes have passed testing.)
switch (rv)
@@ -527,19 +530,30 @@ private:
default: break;
}
ensure(decomp_buf_length <= decomp_buf.size());
// Warn if the final data is smaller than expected
if (decomp_buf_length < decomp_buf.size())
{
self_log.warning("zlib uncompress returned a buffer of less size than expected (size=%d, expected=%d)", decomp_buf_length, decomp_buf.size());
}
// Seek to the program header data offset and write the data.
e.seek(phdr[meta_shdr[i].program_idx].p_offset);
e.write(decomp_buf.get(), filesz);
e.seek(phdr.p_offset);
e.write(decomp_buf.data(), decomp_buf_length);
}
else
{
// Seek to the program header data offset and write the data.
e.seek(phdr[meta_shdr[i].program_idx].p_offset);
e.write(data_buf.data() + data_buf_offset, meta_shdr[i].data_size);
ensure(data_buf.size() >= (hdr.data_size + data_buf_offset));
ensure(hdr.data_size <= (u64{umax} - static_cast<u64>(data_buf_offset))); // Check for overflow
e.seek(phdr.p_offset);
e.write(data_buf.data() + data_buf_offset, hdr.data_size);
}
// Advance the data buffer offset by data size.
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
data_buf_offset += ::narrow<u32>(hdr.data_size);
}
}
@@ -548,9 +562,9 @@ private:
{
e.seek(ehdr.e_shoff);
for (u32 i = 0; i < ehdr.e_shnum; ++i)
for (const SHdr& shdr : shdrs)
{
WriteShdr(e, shdr[i]);
WriteShdr(e, shdr);
}
}
}
-1
View File
@@ -4,7 +4,6 @@
#include "stdafx.h"
#include "FAudioBackend.h"
#include "Emu/System.h"
#include "Emu/Audio/audio_device_enumerator.h"
#include "Utilities/StrUtil.h"
@@ -4,7 +4,6 @@
#include <algorithm>
#include "util/logs.hpp"
#include "Emu/System.h"
#include "Emu/Audio/audio_device_enumerator.h"
#include "Utilities/StrUtil.h"
+3 -3
View File
@@ -1,7 +1,7 @@
#include "stdafx.h"
#include "audio_utils.h"
#include "Emu/system_config.h"
#include "Emu/System.h"
#include "Emu/emu_callbacks.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/Overlays/overlay_message.h"
#include <cmath>
@@ -17,7 +17,7 @@ namespace audio
{
audio_fxo& fxo = g_fxo->get<audio_fxo>();
fxo.audio_muted = !fxo.audio_muted;
Emu.GetCallbacks().update_emu_settings();
g_emu_callbacks.update_emu_settings();
rsx::overlays::queue_message(fxo.audio_muted ? localized_string_id::AUDIO_MUTED : localized_string_id::AUDIO_UNMUTED, 3'000'000);
}
@@ -50,7 +50,7 @@ namespace audio
return;
g_cfg.audio.volume.set(std::clamp<s32>(new_volume, g_cfg.audio.volume.min, g_cfg.audio.volume.max));
Emu.GetCallbacks().update_emu_settings();
g_emu_callbacks.update_emu_settings();
rsx::overlays::queue_message(localized_string(localized_string_id::AUDIO_CHANGED, "%d%%", g_cfg.audio.volume.get()), 3'000'000, {}, rsx::overlays::message_pin_location::top_left, {}, true, true);
}
+15 -1
View File
@@ -19,6 +19,7 @@ add_library(rpcs3_emu STATIC
perf_monitor.cpp
IPC_config.cpp
IPC_socket.cpp
Io/SkylanderPortalIPC_config.cpp
)
if(USE_LTO)
@@ -137,6 +138,8 @@ target_sources(rpcs3_emu PRIVATE
../Loader/PSF.cpp
../Loader/PUP.cpp
../Loader/TAR.cpp
../Loader/IRD.cpp
../Loader/IRD_validation.cpp
../Loader/ISO.cpp
../Loader/iso_cache.cpp
../Loader/content_validation.cpp
@@ -198,6 +201,7 @@ target_sources(rpcs3_emu PRIVATE
Cell/SPUThread.cpp
Cell/lv2/lv2.cpp
Cell/lv2/sys_bdemu.cpp
Cell/lv2/sys_bluetooth.cpp
Cell/lv2/sys_btsetting.cpp
Cell/lv2/sys_cond.cpp
Cell/lv2/sys_console.cpp
@@ -425,6 +429,7 @@ target_sources(rpcs3_emu PRIVATE
Io/GHLtar.cpp
Io/GunCon3.cpp
Io/Infinity.cpp
Io/wiimote_config.cpp
Io/interception.cpp
Io/KamenRider.cpp
Io/KeyboardHandler.cpp
@@ -442,6 +447,7 @@ target_sources(rpcs3_emu PRIVATE
Io/RB3MidiKeyboard.cpp
Io/recording_config.cpp
Io/Skylander.cpp
Io/SkylanderPortalIPC.cpp
Io/TopShotElite.cpp
Io/TopShotFearmaster.cpp
Io/Turntable.cpp
@@ -518,6 +524,12 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_settings.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_savestate.cpp
RSX/Overlays/HomeMenu/overlay_home_menu_sidebar_page.cpp
RSX/Overlays/BigPicture/overlay_big_picture.cpp
RSX/Overlays/BigPicture/overlay_big_picture_game_details.cpp
RSX/Overlays/BigPicture/overlay_big_picture_game_grid.cpp
RSX/Overlays/BigPicture/overlay_big_picture_game_info.cpp
RSX/Overlays/BigPicture/overlay_big_picture_main_menu.cpp
RSX/Overlays/Network/overlay_recvmessage_dialog.cpp
RSX/Overlays/Network/overlay_sendmessage_dialog.cpp
RSX/Overlays/Trophies/overlay_trophy_list_dialog.cpp
@@ -574,8 +586,8 @@ target_sources(rpcs3_emu PRIVATE
RSX/RSXThread.cpp
RSX/RSXZCULL.cpp
RSX/rsx_methods.cpp
RSX/rsx_utils.cpp
RSX/rsx_vertex_data.cpp
RSX/Utils/rsx_utils.cpp
)
if(NOT ANDROID AND NOT APPLE)
@@ -621,6 +633,7 @@ if(TARGET 3rdparty_vulkan)
RSX/VK/vkutils/commands.cpp
RSX/VK/vkutils/ex.cpp
RSX/VK/vkutils/data_heap.cpp
RSX/VK/vkutils/debug_print.cpp
RSX/VK/vkutils/descriptors.cpp
RSX/VK/vkutils/image.cpp
RSX/VK/vkutils/image_helpers.cpp
@@ -686,6 +699,7 @@ target_link_libraries(rpcs3_emu
3rdparty::vulkan
3rdparty::glew
3rdparty::libusb
3rdparty::hidapi
3rdparty::wolfssl
3rdparty::openal
3rdparty::cubeb
+3 -15
View File
@@ -691,7 +691,6 @@ void cpu_thread::operator()()
static thread_local struct thread_cleanup_t
{
cpu_thread* _this = nullptr;
std::string name;
std::string(*log_prefix)() = nullptr;
void cleanup()
@@ -728,22 +727,11 @@ void cpu_thread::operator()()
~thread_cleanup_t()
{
if (_this)
{
cleanup();
auto log_thread = named_thread("CPU Thread Cleanup Logger", [name = name]()
{
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
});
log_thread();
}
cleanup();
}
} cleanup;
cleanup._this = this;
cleanup.name = thread_ctrl::get_name();
cleanup.log_prefix = old_prefix;
// Check thread status
@@ -1282,8 +1270,8 @@ u32 cpu_thread::get_pc() const
}
case thread_class::rsx:
{
const auto ctrl = static_cast<const rsx::thread*>(this)->ctrl;
return ctrl ? ctrl->get.load() : umax;
const auto& ctrl = static_cast<const rsx::thread*>(this)->fifo_ctrl;
return ctrl ? ctrl->get_pos() : umax;
}
default: break;
}
+25
View File
@@ -58,7 +58,11 @@ cpu_translator::cpu_translator(llvm::Module* _module, bool is_be)
const auto prev = ci->getParent();
const auto next = prev->splitBasicBlock(ci->getNextNode());
#if LLVM_VERSION_MAJOR >= 23
llvm::cast<llvm::UncondBrInst>(m_ir->GetInsertBlock()->getTerminator())->setSuccessor(loop);
#else
llvm::cast<llvm::BranchInst>(m_ir->GetInsertBlock()->getTerminator())->setOperand(0, loop);
#endif
llvm::Value* result;
//m_ir->CreateBr(loop);
@@ -341,6 +345,27 @@ std::pair<bool, v128> cpu_translator::get_const_vector<v128>(llvm::Value* c, u32
fmt::throw_exception("[0x%x, %u] Bad vector size: i%ux%u", _pos, _line, v->getScalarSizeInBits(), v->getNumElements());
}
// Vector splats may be represented as ConstantInt/ConstantFP with a vector type (default for ConstantFP since LLVM 23)
const auto splat = [&](const llvm::APInt& elem) -> std::pair<bool, v128>
{
const auto cv = llvm::APInt::getSplat(128, elem);
result._u64[0] = cv.extractBitsAsZExtValue(64, 0);
result._u64[1] = cv.extractBitsAsZExtValue(64, 64);
return {true, result};
};
if (const auto ci = llvm::dyn_cast<llvm::ConstantInt>(c))
{
return splat(ci->getValue());
}
if (const auto cfp = llvm::dyn_cast<llvm::ConstantFP>(c))
{
return splat(cfp->getValueAPF().bitcastToAPInt());
}
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
if (!cv)
+96 -7
View File
@@ -8,6 +8,10 @@
#include "Utilities/JIT.h"
#include "util/v128.hpp"
#ifdef ARCH_X64
#include <immintrin.h>
#endif
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
@@ -21,6 +25,7 @@
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#pragma GCC diagnostic ignored "-Wredundant-decls"
#endif
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/IR/IRBuilder.h"
@@ -43,6 +48,13 @@
#pragma GCC diagnostic pop
#endif
// MSVC can use intrinsics without compiling for its target feature
#if defined(_MSC_VER) || !defined(ARCH_X64)
#define GNUC_X64_TARGET(x)
#else
#define GNUC_X64_TARGET(x) [[gnu::target(x)]]
#endif
#include <functional>
#include <unordered_map>
#include <source_location>
@@ -50,6 +62,26 @@
// Helper function
llvm::Value* peek_through_bitcasts(llvm::Value*);
inline bool llvm_has_terminator(const llvm::BasicBlock* bb)
{
#if LLVM_VERSION_MAJOR >= 23
return bb->hasTerminator();
#else
return bb->getTerminator() != nullptr;
#endif
}
// Constant fold the result of IRBuilder::CreateIntrinsic (LLVM 23+ IRBuilder may already return a folded constant)
inline llvm::Constant* llvm_const_fold(llvm::Value* v)
{
if (auto c = llvm::dyn_cast<llvm::Constant>(v))
{
return c;
}
return llvm::ConstantFoldInstruction(llvm::cast<llvm::Instruction>(v), llvm::DataLayout(""));
}
enum class i2 : char
{
};
@@ -2757,7 +2789,7 @@ struct llvm_ctlz
if (llvm::isa<llvm::Constant>(v))
{
return llvm::ConstantFoldInstruction(ir->CreateIntrinsic(llvm::Intrinsic::ctlz, {v->getType()}, {v, ir->getFalse()}), llvm::DataLayout(""));
return llvm_const_fold(ir->CreateIntrinsic(llvm::Intrinsic::ctlz, {v->getType()}, {v, ir->getFalse()}));
}
return ir->CreateIntrinsic(llvm::Intrinsic::ctlz, {v->getType()}, {v, ir->getFalse()});
@@ -2802,7 +2834,7 @@ struct llvm_ctpop
if (llvm::isa<llvm::Constant>(v))
{
return llvm::ConstantFoldInstruction(ir->CreateUnaryIntrinsic(llvm::Intrinsic::ctpop, v), llvm::DataLayout(""));
return llvm_const_fold(ir->CreateUnaryIntrinsic(llvm::Intrinsic::ctpop, v));
}
return ir->CreateUnaryIntrinsic(llvm::Intrinsic::ctpop, v);
@@ -2923,7 +2955,7 @@ struct llvm_fsqrt
if (llvm::isa<llvm::Constant>(v))
{
if (auto c = llvm::ConstantFoldInstruction(ir->CreateUnaryIntrinsic(llvm::Intrinsic::sqrt, v), llvm::DataLayout("")))
if (auto c = llvm_const_fold(ir->CreateUnaryIntrinsic(llvm::Intrinsic::sqrt, v)))
{
// Will fail in some cases (such as negative constant)
return c;
@@ -2969,7 +3001,7 @@ struct llvm_fabs
if (llvm::isa<llvm::Constant>(v))
{
return llvm::ConstantFoldInstruction(ir->CreateUnaryIntrinsic(llvm::Intrinsic::fabs, v), llvm::DataLayout(""));
return llvm_const_fold(ir->CreateUnaryIntrinsic(llvm::Intrinsic::fabs, v));
}
return ir->CreateUnaryIntrinsic(llvm::Intrinsic::fabs, v);
@@ -3016,7 +3048,7 @@ struct llvm_fmuladd
if (llvm::isa<llvm::Constant>(v1) && llvm::isa<llvm::Constant>(v2) && llvm::isa<llvm::Constant>(v3))
{
return llvm::ConstantFoldInstruction(ir->CreateIntrinsic(llvm::Intrinsic::fma, {v1->getType()}, {v1, v2, v3}), llvm::DataLayout(""));
return llvm_const_fold(ir->CreateIntrinsic(llvm::Intrinsic::fma, {v1->getType()}, {v1, v2, v3}));
}
return ir->CreateIntrinsic(strict_fma ? llvm::Intrinsic::fma : llvm::Intrinsic::fmuladd, {v1->getType()}, {v1, v2, v3});
@@ -3807,13 +3839,25 @@ public:
}
template <typename T1, typename T2>
value_t<u8[16]> gf2p8affineqb(T1 a, T2 b, u8 c)
GNUC_X64_TARGET("gfni") value_t<u8[16]> gf2p8affineqb(T1 a, T2 b, u8 c)
{
value_t<u8[16]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
#ifdef ARCH_X64
const auto [a_is_const, a_data] = get_const_vector(data0, -1);
const auto [b_is_const, b_data] = get_const_vector(data1, -1);
if (a_is_const && b_is_const)
{
const auto affine = _mm_xor_si128(_mm_gf2p8affine_epi64_epi8(a_data, b_data, 0), _mm_set1_epi8(c));
result.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(static_cast<v128>(affine)._u8.m_data, 16));
return result;
}
#endif
const auto immediate = (llvm_const_int<u8>{c});
const auto imm8 = immediate.eval(m_ir);
@@ -3829,6 +3873,24 @@ public:
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
#ifdef ARCH_X64
const auto [a_is_const, a_data] = get_const_vector(data0, -1);
const auto [b_is_const, b_data] = get_const_vector(data1, -1);
const auto [c_is_const, c_data] = get_const_vector(data2, -1);
if (a_is_const && b_is_const && c_is_const)
{
__m128i dpbusd;
if (utils::has_avx512_icl())
dpbusd = _mm_wrapper_dpbusd_avx512vnni(a_data, b_data, c_data);
else
dpbusd = _mm_wrapper_dpbusd_avxvnni(a_data, b_data, c_data);
result.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(static_cast<v128>(dpbusd)._u32.m_data, 4));
return result;
}
#endif
result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128),
{data0, m_ir->CreateBitCast(data1, get_type<u8[16]>()), m_ir->CreateBitCast(data2, get_type<u8[16]>())});
@@ -4197,13 +4259,27 @@ template <typename T1, typename T2, typename T3>
}
template <typename T1, typename T2, typename T3>
value_t<f32[4]> vfixupimmps(T1 a, T2 b, T3 c, u8 d, u8 e)
GNUC_X64_TARGET("avx512vl") value_t<f32[4]> vfixupimmps(T1 a, T2 b, T3 c, u8 d, u8 e)
{
value_t<f32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
#ifdef ARCH_X64
const auto [a_is_const, a_data] = get_const_vector(data0, -1);
const auto [b_is_const, b_data] = get_const_vector(data1, -1);
const auto [c_is_const, c_data] = get_const_vector(data2, -1);
if (a_is_const && b_is_const && c_is_const)
{
const auto vfixup = _mm_mask_fixupimm_ps(a_data, e, b_data, c_data, 0); // flag reporting doesn't matter for constants
result.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(static_cast<v128>(vfixup)._f.m_data, 4));
return result;
}
#endif
const auto immediate = (llvm_const_int<u32>{d});
const auto imm32 = immediate.eval(m_ir);
const auto immediate2 = (llvm_const_int<u8>{e});
@@ -4314,6 +4390,19 @@ private:
// Custom intrinsic table
std::unordered_map<std::string_view, std::function<llvm::Value*(llvm::CallInst*)>> m_intrinsics;
#ifdef ARCH_X64
// LLVM uses the same intrinsic despite different encodings
GNUC_X64_TARGET("avx512vnni,avx512vl") __m128i _mm_wrapper_dpbusd_avx512vnni(__m128i a, __m128i b, __m128i c)
{
return _mm_dpbusd_epi32(a, b, c);
}
GNUC_X64_TARGET("avxvnni") __m128i _mm_wrapper_dpbusd_avxvnni(__m128i a, __m128i b, __m128i c)
{
return _mm_dpbusd_avx_epi32(a, b, c);
}
#endif
public:
// Call custom intrinsic by name
template <typename RT, typename... Args>
+151
View File
@@ -199,3 +199,154 @@ inline struct ppu_frsqrte_lut_t
}
}
} ppu_frqrte_lut;
constexpr u32 ppu_vrefp_segments[32][2] =
{
{0x7ff800, 0x3e1}, {0x783800, 0x3a7}, {0x70ea00, 0x371}, {0x6a0800, 0x340},
{0x638800, 0x313}, {0x5d6200, 0x2ea}, {0x579000, 0x2c4}, {0x520800, 0x2a0},
{0x4cc800, 0x27f}, {0x47ca00, 0x261}, {0x430800, 0x245}, {0x3e8000, 0x22a},
{0x3a2c00, 0x212}, {0x360800, 0x1fb}, {0x321400, 0x1e5}, {0x2e4a00, 0x1d1},
{0x2aa800, 0x1be}, {0x272c00, 0x1ac}, {0x23d600, 0x19b}, {0x209e00, 0x18b},
{0x1d8800, 0x17c}, {0x1a9000, 0x16e}, {0x17ae00, 0x15b}, {0x14f800, 0x15b},
{0x124400, 0x143}, {0x0fbe00, 0x143}, {0x0d3800, 0x12d}, {0x0ade00, 0x12d},
{0x088400, 0x11a}, {0x065000, 0x11a}, {0x041c00, 0x108}, {0x020c00, 0x106},
};
constexpr u32 ppu_vrsqrtefp_segments[32][3] =
{
{0x34fd00, 0x568, 0x00}, {0x2f9700, 0x4f3, 0x26}, {0x2aa500, 0x48d, 0xc8}, {0x261800, 0x435, 0xc8},
{0x21e400, 0x3e7, 0x26}, {0x1dfe00, 0x3a2, 0x88}, {0x1a5c00, 0x365, 0xc8}, {0x16f800, 0x32e, 0x22},
{0x13ca00, 0x2fc, 0x00}, {0x10ce00, 0x2d0, 0x00}, {0x0dfe00, 0x2a8, 0x00}, {0x0b5700, 0x283, 0x26},
{0x08d400, 0x261, 0xc8}, {0x067300, 0x243, 0x26}, {0x043100, 0x226, 0x22}, {0x020b00, 0x20b, 0x26},
{0x7ff400, 0x7a4, 0x00}, {0x785200, 0x700, 0x00}, {0x715400, 0x670, 0x00}, {0x6ae400, 0x5f2, 0x88},
{0x64f200, 0x584, 0x00}, {0x5f6e00, 0x524, 0x00}, {0x5a4c00, 0x4cc, 0x00}, {0x558000, 0x47e, 0x22},
{0x510200, 0x43a, 0x88}, {0x4cca00, 0x3fa, 0x88}, {0x48d000, 0x3c2, 0x88}, {0x450e00, 0x38e, 0x22},
{0x418200, 0x35e, 0x22}, {0x3e2400, 0x332, 0x88}, {0x3af200, 0x30a, 0x88}, {0x37e800, 0x2e6, 0x22},
};
constexpr u16 ppu_vlogefp_segments[16][2] =
{
{0x0000, 12}, {0x0600, 10}, {0x0b00, 10}, {0x1000, 9},
{0x1480, 9}, {0x1900, 9}, {0x1d80, 8}, {0x2180, 8},
{0x2580, 8}, {0x297f, 7}, {0x2cff, 7}, {0x307f, 7},
{0x33fe, 6}, {0x36fe, 6}, {0x39fe, 6}, {0x3cfe, 6},
};
constexpr u16 ppu_vexptefp_segments[16][2] =
{
{0x0006, 6}, {0x0306, 6}, {0x0606, 6}, {0x0906, 6},
{0x0c07, 7}, {0x0f87, 7}, {0x1307, 7}, {0x1680, 8},
{0x1a80, 8}, {0x1e80, 8}, {0x2288, 9}, {0x2708, 9},
{0x2b88, 9}, {0x3008, 10}, {0x3508, 10}, {0x3a08, 12},
};
constexpr void ppu_vnormalize(s32& e, u32& m)
{
const u32 shift = std::countl_zero(m) - 8;
m = (m << shift) & 0x7fffff;
e = 1 - static_cast<s32>(shift);
}
constexpr u32 ppu_vrefp(u32 x, bool nj)
{
const u32 s = x & 0x80000000;
s32 e = (x >> 23) & 0xff;
u32 m = x & 0x7fffff;
if (e == 0xff)
return m ? x | 0x400000 : s;
if (e == 0 && (nj || m == 0))
return s | 0x7f800000;
if (e == 0)
ppu_vnormalize(e, m);
const auto& seg = ppu_vrefp_segments[m >> 18];
const u32 mant = seg[0] - seg[1] * ((m >> 9) & 0x1ff);
const s32 exp = 253 - e;
if (exp >= 0xff)
return s | 0x7f800000;
if (exp > 0)
return s | exp << 23 | mant;
if (nj)
return s;
const u32 full = mant | 0x800000, shift = 1 - exp, half = 1u << (shift - 1);
const u32 rest = full & ((half << 1) - 1);
return s | ((full >> shift) + (rest > half || (rest == half && (full >> shift) & 1)));
}
constexpr u32 ppu_vrsqrtefp(u32 x, bool nj)
{
const u32 s = x & 0x80000000;
s32 e = (x >> 23) & 0xff;
u32 m = x & 0x7fffff;
if (e == 0xff)
return m ? x | 0x400000 : s ? 0x7fc00000 : 0;
if (e == 0 && (nj || m == 0))
return s | 0x7f800000;
if (s)
return 0x7fc00000;
if (e == 0)
ppu_vnormalize(e, m);
const auto& seg = ppu_vrsqrtefp_segments[(e & 1) << 4 | m >> 19];
const u32 key = (m >> 9) & 0x3ff;
return (380 - e) >> 1 << 23 | (seg[0] - ((seg[1] * key) >> 2) - ((seg[2] >> (key & 7)) & 1));
}
constexpr u32 ppu_vlogefp(u32 x, bool nj)
{
const u32 s = x & 0x80000000;
s32 e = (x >> 23) & 0xff;
u32 m = x & 0x7fffff;
if (e == 0xff)
return m ? x | 0x400000 : s ? 0x7fc00000 : x;
if (e == 0 && (nj || m == 0))
return 0xff800000;
if (s)
return 0x7fc00000;
if (e == 0)
ppu_vnormalize(e, m);
const u32 key = m >> 12;
const auto& seg = ppu_vlogefp_segments[key >> 7];
const s32 fixed = (e - 127) * 2048 + ((seg[0] + seg[1] * (key & 0x7f)) >> 3);
return std::bit_cast<u32>(static_cast<f32>(fixed) / 2048);
}
constexpr u32 ppu_vexptefp(u32 x, bool)
{
const u32 s = x & 0x80000000, e = (x >> 23) & 0xff, m = x & 0x7fffff;
if (e == 0xff)
return m ? x | 0x400000 : s ? 0 : x;
if (e > 150)
return s ? 0 : 0x7f800000;
const s64 mag = e >= 135 ? s64{m | 0x800000} << (e - 135) : e > 111 ? (m | 0x800000) >> (135 - e) : 0;
const s64 fixed = (s ? -mag : mag) >> 4;
const s64 exp = 127 + (fixed >> 11);
if (exp >= 0xff)
return 0x7f800000;
if (exp <= 0)
return 0;
const auto& seg = ppu_vexptefp_segments[(fixed >> 7) & 0xf];
return static_cast<u32>(exp) << 23 | ((seg[0] + seg[1] * (fixed & 0x7f)) >> 3) << 12;
}
File diff suppressed because it is too large Load Diff
+62 -9
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Utilities/Thread.h"
#include "Utilities/simple_ringbuf.h"
#include "Emu/Memory/vm.h"
@@ -10,6 +11,8 @@
#include "Emu/system_config_types.h"
struct lv2_event_queue;
struct lv2_memory;
class ppu_thread;
// Error codes
enum CellAudioError : u32
@@ -122,6 +125,24 @@ enum : u32
PORT_BUFFER_TAG_LAST_8CH = AUDIO_BLOCK_SIZE_8CH - 1,
PORT_BUFFER_TAG_DELTA_8CH = PORT_BUFFER_TAG_LAST_8CH / (PORT_BUFFER_TAG_COUNT - 1),
PORT_BUFFER_TAG_FIRST_8CH = PORT_BUFFER_TAG_LAST_8CH % (PORT_BUFFER_TAG_COUNT - 1),
// The tags above are spread over the block diagonally, so on an 8 channel port they all land on channels
// 2 to 7. A game may legitimately never write those: one that was told the output is stereo fills only
// front left/right of an 8 channel port, and cellAudioAdd2chData does the same. Such a port keeps every
// tag at its initial value and looks untouched forever, which silences it while buffering is enabled.
// These extra marks sit on the front right channel, which any audio content writes, and are only used to
// tell "the game wrote nothing" apart from "the game wrote the front channels only". They carry no state:
// a mark counts as written when it no longer is the negative zero that tag() put there. On a two channel
// port they coincide with the tags, so only wider ports carry their own.
PORT_BUFFER_MARK_CHANNEL = 1,
PORT_BUFFER_MARK_DELTA_SAMPLE = (AUDIO_BUFFER_SAMPLES - 1) / (PORT_BUFFER_TAG_COUNT - 1),
// How many periods a port must go without moving a single tag before the marks are believed and it is
// treated as one that only ever fills its front channels. A game writing genuine surround moves a tag
// every period, so it never gets anywhere near this; one that fills the buffer one channel at a time
// and stalls mid-pass is covered by the same margin. Below the threshold the tags alone decide, so the
// marks are a fallback for a case the tags cannot see rather than a rule of their own.
PORT_FRONT_ONLY_SETTLE_PERIODS = 8,
};
enum class audio_port_state : u32
@@ -136,6 +157,8 @@ struct audio_port
atomic_t<audio_port_state> state = audio_port_state::closed;
u32 number = 0;
u32 server_index = 0;
bool mapped = false;
vm::ptr<char> addr{};
vm::ptr<u64> index{};
@@ -159,7 +182,7 @@ struct audio_port
u32 block_size() const
{
return num_channels * AUDIO_BUFFER_SAMPLES;
return std::max(num_channels, 1u) * AUDIO_BUFFER_SAMPLES;
}
u32 buf_size() const
@@ -173,14 +196,10 @@ struct audio_port
return (cur_pos + ofs) % num_blocks;
}
u32 buf_addr(s32 offset = 0) const
be_t<f32>* get_vm_ptr() const
{
return addr.addr() + position(offset) * buf_size();
}
be_t<f32>* get_vm_ptr(s32 offset = 0) const
{
return vm::_ptr<f32>(buf_addr(offset));
const u32 block = static_cast<u16>(*index) & (num_blocks - 1);
return vm::_ptr<f32>(addr.addr() + block * buf_size());
}
@@ -188,7 +207,12 @@ struct audio_port
u32 prev_touched_tag_nr = 0;
f32 last_tag_value[PORT_BUFFER_TAG_COUNT] = { 0 };
void tag(s32 offset = 0);
u32 mark_position(u32 tag_nr) const
{
return tag_nr * PORT_BUFFER_MARK_DELTA_SAMPLE * num_channels + PORT_BUFFER_MARK_CHANNEL;
}
void tag(be_t<f32>* port_buf);
audio_port() = default;
@@ -370,6 +394,7 @@ class cell_audio_thread
{
private:
std::unique_ptr<audio_ringbuffer> ringbuffer{};
be_t<f32>* get_buffer(const audio_port& port, s32 offset = 0) const;
void reset_ports(s32 offset = 0);
void advance(u64 timestamp);
@@ -388,6 +413,14 @@ public:
shared_mutex mutex{};
atomic_t<u8> init = 0;
u32 shared_area = 0;
u32 shared_address = 0;
u32 shared_refs = 0;
shared_ptr<lv2_memory> shared_memory;
std::array<shared_ptr<lv2_memory>, AUDIO_PORT_COUNT> port_memories;
u32 free_port_count = 0;
std::array<u32, AUDIO_PORT_COUNT> free_ports{};
std::array<u32, AUDIO_PORT_COUNT> free_indices{};
u32 key_count = 0;
u8 event_period = 0;
@@ -414,6 +447,23 @@ public:
bool m_backend_failed = false;
bool m_audio_should_restart = false;
// Whether we already reported that a port carries front channel audio only. Diagnostics for a condition
// that is otherwise indistinguishable from a game that simply went silent, so it is worth one line per
// port. Deliberately not serialized: a savestate that reloads into the same condition may report it
// again, which is harmless and arguably useful.
std::array<bool, AUDIO_PORT_COUNT> m_front_only_reported{};
// Periods since each port last moved one of its buffer tags, saturating. Tells a port that fills only
// its front channels apart from one that is merely between two passes of a channel by channel write,
// see PORT_FRONT_ONLY_SETTLE_PERIODS. Deliberately not serialized: it starts at zero, which is the
// conservative end, and settles again within a few periods.
std::array<u8, AUDIO_PORT_COUNT> m_periods_without_tag{};
// Whether the marks have shown that a port only ever fills its front channels; cleared when a tag moves.
// They only describe the current block, so without this a port fed in bursts would look untouched
// between its writes and stall the thread on a timeout. Not serialized: it settles again in a few periods.
std::array<bool, AUDIO_PORT_COUNT> m_front_only_port{};
void operator()();
SAVESTATE_INIT_POS(9);
@@ -423,6 +473,9 @@ public:
void save(utils::serial& ar);
audio_port* open_port();
error_code allocate_port(ppu_thread& ppu, audio_port& port);
void close_port(ppu_thread& ppu, audio_port& port);
void release_shared_memory(ppu_thread& ppu);
static constexpr auto thread_name = "cellAudio Thread"sv;
};
+2
View File
@@ -25,6 +25,8 @@ enum CellAudioInDeviceMode
enum CellAudioInPortType
{
CELL_AUDIO_IN_PORT_EYETOY1 = 1, // speculative, EyeToy
CELL_AUDIO_IN_PORT_EYETOY2 = 2, // speculative, PS EYE
CELL_AUDIO_IN_PORT_USB = 3,
CELL_AUDIO_IN_PORT_BLUETOOTH = 4,
};
+46 -2
View File
@@ -2,7 +2,7 @@
#include "Emu/system_config.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
#include "Emu/RSX/rsx_utils.h"
#include "Emu/RSX/Utils/rsx_utils.h"
#include "Utilities/StrUtil.h"
#include "cellMic.h"
@@ -89,6 +89,50 @@ avconf_manager::avconf_manager()
}
break;
}
case microphone_handler::eye_toy:
{
for (u32 index = 0; index < mic_list.size(); index++)
{
device_info device {};
device.info.portType = CELL_AUDIO_IN_PORT_EYETOY1;
device.info.availableModeCount = 1;
device.info.state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
device.info.deviceId = 0xE11CC0DE + curindex;
device.info.type = 0xC0DEE11C;
device.info.availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
device.info.availableModes[0].channel = CELL_AUDIO_IN_CHNUM_2;
device.info.availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
device.info.deviceNumber = curindex;
device.full_device_name = mic_list[index];
strcpy_trunc(device.info.name, device.full_device_name);
devices.push_back(std::move(device));
curindex++;
}
break;
}
case microphone_handler::ps_eye:
{
for (u32 index = 0; index < mic_list.size(); index++)
{
device_info device {};
device.info.portType = CELL_AUDIO_IN_PORT_EYETOY2;
device.info.availableModeCount = 1;
device.info.state = CELL_AUDIO_IN_DEVICE_STATE_AVAILABLE;
device.info.deviceId = 0xE11CC0DE + curindex;
device.info.type = 0xC0DEE11C;
device.info.availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM;
device.info.availableModes[0].channel = CELL_AUDIO_IN_CHNUM_2;
device.info.availableModes[0].fs = CELL_AUDIO_IN_FS_8KHZ | CELL_AUDIO_IN_FS_12KHZ | CELL_AUDIO_IN_FS_16KHZ | CELL_AUDIO_IN_FS_24KHZ | CELL_AUDIO_IN_FS_32KHZ | CELL_AUDIO_IN_FS_48KHZ;
device.info.deviceNumber = curindex;
device.full_device_name = mic_list[index];
strcpy_trunc(device.info.name, device.full_device_name);
devices.push_back(std::move(device));
curindex++;
}
break;
}
case microphone_handler::real_singstar:
case microphone_handler::singstar:
{
@@ -230,7 +274,7 @@ error_code cellAudioInGetDeviceInfo(u32 deviceNumber, u32 deviceIndex, vm::ptr<C
std::lock_guard lock(av_manager.mutex);
if (deviceNumber >= av_manager.devices.size())
return CELL_AUDIO_OUT_ERROR_DEVICE_NOT_FOUND;
return CELL_AUDIO_IN_ERROR_DEVICE_NOT_FOUND;
av_manager.copy_device_info(deviceNumber, info);
+25 -8
View File
@@ -1,6 +1,7 @@
#include "stdafx.h"
#include "cellCamera.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include "Emu/Cell/PPUModule.h"
@@ -504,8 +505,10 @@ error_code cellCameraInit()
return CELL_OK;
}
error_code cellCameraEnd()
error_code cellCameraEnd(ppu_thread& ppu)
{
ppu.state += cpu_flag::wait;
cellCamera.todo("cellCameraEnd()");
auto& g_camera = g_fxo->get<camera_thread>();
@@ -570,7 +573,7 @@ error_code cellCameraOpenAsync()
return CELL_OK;
}
error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
error_code cellCameraOpenEx(ppu_thread& ppu, s32 dev_num, vm::ptr<CellCameraInfoEx> info)
{
cellCamera.todo("cellCameraOpenEx(dev_num=%d, info=*0x%x)", dev_num, info);
@@ -621,6 +624,8 @@ error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
const auto vbuf_size = get_video_buffer_size(*info);
ppu.state += cpu_flag::wait;
std::lock_guard lock(g_camera.mutex);
// TODO: find out if the buffers are also checked for nullptr
@@ -666,8 +671,10 @@ error_code cellCameraOpenPost()
return CELL_OK;
}
error_code cellCameraClose(s32 dev_num)
error_code cellCameraClose(ppu_thread& ppu, s32 dev_num)
{
ppu.state += cpu_flag::wait;
cellCamera.notice("cellCameraClose(dev_num=%d)", dev_num);
if (error_code error = check_init_and_open(dev_num))
@@ -694,14 +701,19 @@ error_code cellCameraClose(s32 dev_num)
if (g_camera.info.buffer)
{
vm::dealloc(g_camera.info.buffer.addr(), vm::main);
g_camera.info.buffer = vm::null;
}
if (g_camera.info.pbuf[0])
{
vm::dealloc(g_camera.info.pbuf[0].addr(), vm::main);
g_camera.info.pbuf[0] = vm::null;
}
if (g_camera.info.pbuf[1])
{
vm::dealloc(g_camera.info.pbuf[1].addr(), vm::main);
g_camera.info.pbuf[1] = vm::null;
}
g_camera.close_camera();
@@ -1407,7 +1419,7 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
u64 frame_number{};
u64 bytes_read{};
if (!g_camera.get_camera_frame(g_camera.info.buffer.get_ptr(), width, height, frame_number, bytes_read))
if (!g_camera.get_camera_frame(g_camera.info.buffer ? g_camera.info.buffer.get_ptr() : nullptr, width, height, frame_number, bytes_read))
{
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
}
@@ -1705,20 +1717,20 @@ void camera_context::operator()()
{
if (auto queue = lv2_event_queue::find(key))
{
constexpr u64 camera_id = 0;
u64 data2 = 0;
u64 data3 = 0;
if (read_mode.load() == CELL_CAMERA_READ_DIRECT)
{
const u64 image_data_size = static_cast<u64>(info.bytesize);
const u64 camera_id = 0;
data2 = image_data_size << 32 | buffer_number << 16 | camera_id;
data3 = get_guest_system_time() - start_timestamp_us; // timestamp
}
else // CELL_CAMERA_READ_FUNCCALL, also default
{
data2 = 0; // device id (always 0)
data2 = camera_id;
data3 = 0; // unused
}
@@ -1759,7 +1771,7 @@ bool camera_context::open_camera()
Emu.BlockingCallFromMainThread([this]()
{
handler.reset();
handler = Emu.GetCallbacks().get_camera_handler();
handler = g_emu_callbacks.get_camera_handler();
if (handler)
{
handler->open_camera();
@@ -1841,14 +1853,19 @@ void camera_context::reset_state()
if (info.buffer)
{
vm::dealloc(info.buffer.addr(), vm::main);
info.buffer = vm::null;
}
if (info.pbuf[0])
{
vm::dealloc(info.pbuf[0].addr(), vm::main);
info.pbuf[0] = vm::null;
}
if (info.pbuf[1])
{
vm::dealloc(info.pbuf[1].addr(), vm::main);
info.pbuf[1] = vm::null;
}
std::scoped_lock lock(mutex_notify_data_map);
@@ -1981,7 +1998,7 @@ bool camera_context::on_handler_state(camera_handler_base::camera_handler_state
{
if (!is_attached)
{
cellCamera.warning("Camera handler not attached. Sending attach event...", static_cast<int>(state));
cellCamera.warning("Camera handler not attached (state=%d). Sending attach event...", static_cast<int>(state));
is_attached = true;
is_attached_dirty = true;
}
+49 -23
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Emu/localized_string.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/system_utils.hpp"
#include "Emu/VFS.h"
@@ -18,6 +19,7 @@
#include "Utilities/StrUtil.h"
#include "util/init_mutex.hpp"
#include "util/asm.hpp"
#include "util/cctype.hpp"
#include "Crypto/utils.h"
#include <span>
@@ -186,6 +188,18 @@ struct content_permission final
}
};
// Content directory of the last cellGameDataCheckCreate2() call, measured by cellGameDataGetSizeKB()
// A game may check several different directories: Rock Band 2 checks its own game data and then Rock Band's
// in order to import its songs, so the size always refers to the dirName of the check which asked for it
struct game_data_check final
{
shared_mutex mutex; // Checks may be issued from more than one thread of the game
std::string dir; // "/dev_hdd0/game/" + dirName, empty until the first check
void set_dir(std::string_view path) { std::lock_guard lock(mutex); dir = path; }
std::string get_dir() { reader_lock lock(mutex); return dir; }
};
template<>
void fmt_class_string<content_permission::check_mode>::format(std::string& out, u64 arg)
{
@@ -228,26 +242,26 @@ static bool check_system_ver(vm::cptr<char> systemVersion)
return (
systemVersion &&
std::strlen(systemVersion.get_ptr()) == 7 &&
std::isdigit(systemVersion[0]) &&
std::isdigit(systemVersion[1]) &&
utils::isdigit(systemVersion[0]) &&
utils::isdigit(systemVersion[1]) &&
systemVersion[2] == '.' &&
std::isdigit(systemVersion[3]) &&
std::isdigit(systemVersion[4]) &&
std::isdigit(systemVersion[5]) &&
std::isdigit(systemVersion[6])
utils::isdigit(systemVersion[3]) &&
utils::isdigit(systemVersion[4]) &&
utils::isdigit(systemVersion[5]) &&
utils::isdigit(systemVersion[6])
);
}
disc_change_manager::disc_change_manager()
{
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
g_emu_callbacks.enable_disc_eject(false);
g_emu_callbacks.enable_disc_insert(false);
}
disc_change_manager::~disc_change_manager()
{
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
g_emu_callbacks.enable_disc_eject(false);
g_emu_callbacks.enable_disc_insert(false);
}
error_code disc_change_manager::register_callbacks(vm::ptr<CellGameDiscEjectCallback> func_eject, vm::ptr<CellGameDiscInsertCallback> func_insert)
@@ -264,8 +278,8 @@ error_code disc_change_manager::register_callbacks(vm::ptr<CellGameDiscEjectCall
state = is_disc_mounted ? eject_state::inserted : eject_state::ejected;
}
Emu.GetCallbacks().enable_disc_eject(!!func_eject && is_disc_mounted);
Emu.GetCallbacks().enable_disc_insert(!!func_insert && !is_disc_mounted);
g_emu_callbacks.enable_disc_eject(!!func_eject && is_disc_mounted);
g_emu_callbacks.enable_disc_insert(!!func_insert && !is_disc_mounted);
return CELL_OK;
}
@@ -277,8 +291,8 @@ error_code disc_change_manager::unregister_callbacks()
eject_callback = vm::null;
insert_callback = vm::null;
Emu.GetCallbacks().enable_disc_eject(false);
Emu.GetCallbacks().enable_disc_insert(false);
g_emu_callbacks.enable_disc_eject(false);
g_emu_callbacks.enable_disc_insert(false);
};
if (is_inserting)
@@ -309,7 +323,7 @@ void disc_change_manager::eject_disc()
}
state = eject_state::busy;
Emu.GetCallbacks().enable_disc_eject(false);
g_emu_callbacks.enable_disc_eject(false);
ensure(eject_callback);
@@ -326,7 +340,7 @@ void disc_change_manager::eject_disc()
dcm.state = eject_state::ejected;
// Re-enable disc insertion only if the callback is still registered
Emu.GetCallbacks().enable_disc_insert(!!dcm.insert_callback);
g_emu_callbacks.enable_disc_insert(!!dcm.insert_callback);
return CELL_OK;
});
@@ -345,7 +359,7 @@ void disc_change_manager::insert_disc(u32 disc_type, std::string title_id)
}
state = eject_state::busy;
Emu.GetCallbacks().enable_disc_insert(false);
g_emu_callbacks.enable_disc_insert(false);
ensure(insert_callback);
@@ -371,7 +385,7 @@ void disc_change_manager::insert_disc(u32 disc_type, std::string title_id)
dcm.state = eject_state::inserted;
// Re-enable disc ejection only if the callback is still registered
Emu.GetCallbacks().enable_disc_eject(!!dcm.eject_callback);
g_emu_callbacks.enable_disc_eject(!!dcm.eject_callback);
dcm.is_inserting = false;
@@ -691,11 +705,14 @@ error_code cellGameDataGetSizeKB(ppu_thread& ppu, vm::ptr<u32> size)
return CELL_GAMEDATA_ERROR_PARAM;
}
const std::string content_dir = g_fxo->get<game_data_check>().get_dir();
lv2_obj::sleep(ppu);
const u64 start_sleep = ppu.start_time;
const std::string local_dir = vfs::get(Emu.GetDir());
// Until the first check there is no dirName to go by: the booted directory is the game data itself for an HDD game
const std::string local_dir = vfs::get(content_dir.empty() ? Emu.GetDir() : content_dir);
const auto dirsz = fs::get_dir_size(local_dir, 1024);
@@ -707,11 +724,15 @@ error_code cellGameDataGetSizeKB(ppu_thread& ppu, vm::ptr<u32> size)
{
const auto error = fs::g_tls_error;
if (fs::exists(local_dir))
if (!fs::exists(local_dir))
{
cellGame.error("cellGameDataGetSizeKB(): Unknown failure on calculating directory '%s' size (%s)", local_dir, error);
// The content directory is only created once the status callback has returned
ppu.check_state();
*size = 0;
return CELL_OK;
}
cellGame.error("cellGameDataGetSizeKB(): Unknown failure on calculating directory '%s' size (%s)", local_dir, error);
return CELL_GAMEDATA_ERROR_FAILURE;
}
@@ -1109,6 +1130,9 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
strcpy_trunc(cbGet->getParam.titleLang[i], psf::get_string(sfo, fmt::format("TITLE_%02d", i)));
}
// The status callback may ask for the size of this directory through cellGameDataGetSizeKB()
g_fxo->get<game_data_check>().set_dir(dir);
lv2_sleep(5000, &ppu);
funcStat(ppu, cbResult, cbGet, cbSet);
@@ -1313,7 +1337,7 @@ error_code cellGameDeleteGameData(vm::cptr<char> dirName)
{
cellGame.warning("cellGameDeleteGameData(dirName=%s)", dirName);
if (!dirName)
if (!dirName || sysutil_check_name_string(dirName.get_ptr(), 1, CELL_GAME_DIRNAME_SIZE) != 0)
{
return CELL_GAME_ERROR_PARAM;
}
@@ -1325,7 +1349,9 @@ error_code cellGameDeleteGameData(vm::cptr<char> dirName)
auto remove_gd = [&]() -> error_code
{
if (Emu.GetCat() == "GD" && Emu.GetDir().substr(Emu.GetDir().find_last_of('/') + 1) == vfs::escape(name))
const std::string_view boot_dir = fmt::trim_back_sv(Emu.GetDir(), fs::delim);
if (Emu.GetCat() == "GD" && boot_dir.substr(boot_dir.find_last_of(fs::delim) + 1) == vfs::escape(name))
{
// Boot patch cannot delete its own directory
return CELL_GAME_ERROR_NOTSUPPORTED;
+4 -1
View File
@@ -3,6 +3,8 @@
#include "Emu/IdManager.h"
#include "Emu/System.h"
#include "Utilities/StrUtil.h"
#include "cellGame.h"
LOG_CHANNEL(cellGameExec);
@@ -108,7 +110,8 @@ error_code cellGameGetBootGameInfo(vm::ptr<u32> type, vm::ptr<char> dirName, vm:
if (source_type == CELL_GAME_GAMETYPE_HDD)
{
const std::string dir_name = Emu.GetDir();
const std::string_view boot_dir = fmt::trim_back_sv(Emu.GetDir(), fs::delim);
const std::string dir_name{boot_dir.substr(boot_dir.find_last_of(fs::delim) + 1)};
if (dir_name.size() >= CELL_GAME_DIRNAME_SIZE)
{
+3 -1
View File
@@ -389,6 +389,8 @@ u32 g_defaultCommandBufferBegin, g_defaultCommandBufferFragmentCount;
// Called by cellGcmInit
error_code _cellGcmInitBody(ppu_thread& ppu, vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSize, u32 ioAddress)
{
ppu.state += cpu_flag::wait;
cellGcmSys.warning("_cellGcmInitBody(context=**0x%x, cmdSize=0x%x, ioSize=0x%x, ioAddress=0x%x)", context, cmdSize, ioSize, ioAddress);
auto& gcm_cfg = g_fxo->get<gcm_config>();
@@ -1103,7 +1105,7 @@ error_code cellGcmMapMainMemory(ppu_thread& ppu, u32 ea, u32 size, vm::ptr<u32>
std::lock_guard lock(gcm_cfg.gcmio_mutex);
// Use the offset table to find the next free io address
for (u32 io = 0, end = (rsx::get_current_renderer()->main_mem_size - gcm_cfg.reserved_size) >> 20, unmap_count = 1; io < end; unmap_count++)
for (u32 io = 0, end = (rsx::get_current_renderer()->main_mem_size - gcm_cfg.reserved_size) >> 20, unmap_count = 1; unmap_count + io <= end; unmap_count++)
{
if (gcm_cfg.offsetTable.eaAddress[io + unmap_count - 1] > 0xBFF)
{
+102 -81
View File
@@ -323,7 +323,7 @@ public:
{
const auto& pad = ::at32(handler->GetPads(), pad_num(i));
const bool connected = pad && pad->is_connected() && !pad->is_copilot() && i < attribute.max_connect;
const bool is_real_move = g_cfg.io.move != move_handler::real || pad->m_pad_handler == pad_handler::move;
const bool is_real_move = g_cfg.io.move != move_handler::real || (pad && pad->m_pad_handler == pad_handler::move);
update_connection(i, connected && is_real_move);
}
@@ -1572,18 +1572,24 @@ public:
return true;
}
bool set_image(u32 addr)
bool set_image(vm::cptr<void> buf)
{
if (!addr)
if (!buf)
return false;
auto& g_camera = g_fxo->get<camera_thread>();
std::lock_guard lock(g_camera.mutex);
m_camera_info = g_camera.info;
if (m_camera_info.buffer.addr() != addr && m_camera_info.pbuf[0].addr() != addr && m_camera_info.pbuf[1].addr() != addr)
if (m_camera_info.buffer.addr() != buf.addr() && m_camera_info.pbuf[0].addr() != buf.addr() && m_camera_info.pbuf[1].addr() != buf.addr())
{
cellGem.error("gem_tracker: unexpected image address: addr=0x%x, expected one of: 0x%x, 0x%x, 0x%x", addr, m_camera_info.buffer.addr(), m_camera_info.pbuf[0].addr(), m_camera_info.pbuf[1].addr());
// The game passes its own buffer. Let's just ignore this and log a warning. We use our own camera buffer for tracking for now anyway.
cellGem.warning("gem_tracker: unexpected image address: addr=0x%x, expected one of: 0x%x, 0x%x, 0x%x", buf.addr(), m_camera_info.buffer.addr(), m_camera_info.pbuf[0].addr(), m_camera_info.pbuf[1].addr());
}
if (!m_camera_info.buffer)
{
cellGem.error("gem_tracker: camer buffer not set");
return false;
}
@@ -1770,10 +1776,10 @@ static bool check_gem_num(u32 gem_num)
return gem_num < CELL_GEM_MAX_NUM;
}
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller&, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller&, f32 x_pos, f32 y_pos)
{
const s16 x = static_cast<s16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
const s16 y = static_cast<s16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
const s16 x = static_cast<s16>(x_pos * rsx::overlays::overlay::virtual_width);
const s16 y = static_cast<s16>(y_pos * rsx::overlays::overlay::virtual_height);
// Note: We shouldn't use sphere_rgb here. The game will set it to black in many cases.
const gem_config_data::gem_color& rgb = gem_config_data::gem_color::get_default_color(gem_num);
@@ -1782,30 +1788,24 @@ static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_contro
rsx::overlays::set_cursor(rsx::overlays::cursor_offset::cell_gem + gem_num, x, y, color, 2'000'000, false);
}
static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemImageState>& gem_image_state, f32 x_pos, f32 y_pos)
{
const auto& shared_data = g_fxo->get<gem_camera_shared>();
if (x_max <= 0) x_max = shared_data.width;
if (y_max <= 0) y_max = shared_data.height;
// Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this)
if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10;
if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10;
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
if (x_pos <= 0) x_pos -= 0.1f; else if (x_pos >= 1.0f) x_pos += 0.1f;
if (y_pos <= 0) y_pos -= 0.1f; else if (y_pos >= 1.0f) y_pos += 0.1f;
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
const f32 image_x = x_pos * shared_data.width;
const f32 image_y = y_pos * shared_data.height;
// Centered image coordinates in pixels
const f32 centered_x = image_x - (shared_data.width / 2.f);
const f32 centered_y = (shared_data.height / 2.f) - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
@@ -1821,12 +1821,12 @@ static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controlle
if (g_cfg.io.move != move_handler::real)
{
// Let's say the sphere is not visible if the position is at the edge of the screen
controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max;
controller.radius_valid = x_pos > 0.0f && x_pos < 1.0f && y_pos > 0.0f && y_pos < 1.0f;
}
if (g_cfg.io.show_move_cursor)
{
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
draw_overlay_cursor(gem_num, controller, x_pos, y_pos);
}
if (g_cfg.io.paint_move_spheres)
@@ -1835,24 +1835,17 @@ static inline void pos_to_gem_image_state(u32 gem_num, gem_config::gem_controlle
}
}
static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max, ps_move_data& move_data)
static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& controller, vm::ptr<CellGemState>& gem_state, f32 x_pos, f32 y_pos, ps_move_data& move_data)
{
const auto& shared_data = g_fxo->get<gem_camera_shared>();
if (x_max <= 0) x_max = shared_data.width;
if (y_max <= 0) y_max = shared_data.height;
// Move the cursor out of the screen if we're at the screen border (Time Crisis 4 needs this)
if (x_pos <= 0) x_pos -= x_max / 10; else if (x_pos >= x_max) x_pos += x_max / 10;
if (y_pos <= 0) y_pos -= y_max / 10; else if (y_pos >= y_max) y_pos += y_max / 10;
const f32 scaling_width = x_max / static_cast<f32>(shared_data.width);
const f32 scaling_height = y_max / static_cast<f32>(shared_data.height);
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
if (x_pos <= 0.0f) x_pos -= 0.1f; else if (x_pos >= 1.0f) x_pos += 0.1f;
if (y_pos <= 0.0f) y_pos -= 0.1f; else if (y_pos >= 1.0f) y_pos += 0.1f;
// Image coordinates in pixels
const f32 image_x = static_cast<f32>(x_pos) / scaling_width;
const f32 image_y = static_cast<f32>(y_pos) / scaling_height;
const f32 image_x = x_pos * shared_data.width;
const f32 image_y = y_pos * shared_data.height;
// Half of the camera image
const f32 half_width = shared_data.width / 2.f;
@@ -1863,6 +1856,7 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
const f32 centered_y = half_height - image_y; // Image coordinates increase downwards, so we have to invert this
// Camera coordinates in mm (centered, so it's the same as world coordinates)
const f32 mmPerPixel = controller.radius <= 0.0f ? 0.0f : (CELL_GEM_SPHERE_RADIUS_MM / controller.radius);
const f32 camera_x = centered_x * mmPerPixel;
const f32 camera_y = centered_y * mmPerPixel;
@@ -1901,7 +1895,7 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
gem_state->quat[3] = quat.w();
// Calculate handle position based on our world coordinate and the current orientation
constexpr ps_move_data::vect<3> offset_local_mm({0.f, 0.f, -45.f}); // handle is ~45 mm below sphere
constexpr ps_move_data::vect<3> offset_local_mm({0.f, 0.f, 45.f}); // handle is ~45 mm below sphere
const ps_move_data::vect<3> offset_world = ps_move_data::rotate_vector(quat, offset_local_mm);
gem_state->handle_pos[0] = gem_state->pos[0] - offset_world.x(); // Flip x offset
@@ -1929,12 +1923,12 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
if (g_cfg.io.move != move_handler::real)
{
// Let's say the sphere is not visible if the position is at the edge of the screen
controller.radius_valid = x_pos > 0 && x_pos < x_max && y_pos > 0 && y_pos < y_max;
controller.radius_valid = x_pos > 0.0f && x_pos < 1.0f && y_pos > 0.0f && y_pos < 1.0f;
}
if (g_cfg.io.show_move_cursor)
{
draw_overlay_cursor(gem_num, controller, x_pos, y_pos, x_max, y_max);
draw_overlay_cursor(gem_num, controller, x_pos, y_pos);
}
if (g_cfg.io.paint_move_spheres)
@@ -2020,13 +2014,10 @@ static void ds3_input_to_pad(const u32 gem_num, be_t<u16>& digital_buttons, be_t
}
}
constexpr u16 ds3_max_x = 255;
constexpr u16 ds3_max_y = 255;
static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>& pad, s32& x_pos, s32& y_pos)
static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>& pad, f32& x_pos, f32& y_pos)
{
x_pos = 0;
y_pos = 0;
x_pos = 0.0f;
y_pos = 0.0f;
const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num);
cfg->handle_input(pad, true, [&](const auto& value, bool& /*abort*/)
@@ -2036,8 +2027,8 @@ static inline void ds3_get_stick_values(u32 gem_num, const std::shared_ptr<Pad>&
switch (value.btn)
{
case gem_btn::x_axis: x_pos = value.value; break;
case gem_btn::y_axis: y_pos = value.value; break;
case gem_btn::x_axis: x_pos = value.value / 255.0f; break;
case gem_btn::y_axis: y_pos = value.value / 255.0f; break;
default: break;
}
});
@@ -2061,16 +2052,16 @@ static void ds3_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& contro
return;
}
s32 ds3_pos_x, ds3_pos_y;
f32 ds3_pos_x, ds3_pos_y;
ds3_get_stick_values(gem_num, pad, ds3_pos_x, ds3_pos_y);
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
{
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y, pad->move_data);
pos_to_gem_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, pad->move_data);
}
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
{
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y, ds3_max_x, ds3_max_y);
pos_to_gem_image_state(gem_num, controller, gem_state, ds3_pos_x, ds3_pos_y);
}
}
@@ -2110,11 +2101,11 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
gem_state->angaccel[i] = pad->move_data.angaccel_world[i];
}
pos_to_gem_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, info.x_max, info.y_max, pad->move_data);
pos_to_gem_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, pad->move_data);
}
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
{
pos_to_gem_image_state(gem_num, controller, gem_state, info.x_pos, info.y_pos, info.x_max, info.y_max);
pos_to_gem_image_state(gem_num, controller, gem_state, info.x_pos, info.y_pos);
}
}
@@ -2160,12 +2151,12 @@ static const std::unordered_map<gem_btn, u16> ext_btn_indices =
{ gem_btn::racing_wheel_l2, 1 },
{ gem_btn::racing_wheel_r2, 2 },
{ gem_btn::racing_wheel_paddle_l, 3 },
{ gem_btn::racing_wheel_paddle_r, 4 },
{ gem_btn::racing_wheel_paddle_r, 3 },
{ gem_btn::combo_racing_wheel_throttle, 0 },
{ gem_btn::combo_racing_wheel_l2, 1 },
{ gem_btn::combo_racing_wheel_r2, 2 },
{ gem_btn::combo_racing_wheel_paddle_l, 3 },
{ gem_btn::combo_racing_wheel_paddle_r, 4 },
{ gem_btn::combo_racing_wheel_paddle_r, 3 },
};
static u32 ext_device_id(gem_ext_id id)
@@ -2276,23 +2267,23 @@ static void input_to_ext(u32 external_device_id, CellGemExtPortData& ext, std::s
{
switch (value.btn)
{
case gem_btn::combo_racing_wheel_throttle:
case gem_btn::combo_racing_wheel_l2:
case gem_btn::combo_racing_wheel_r2:
case gem_btn::racing_wheel_throttle:
case gem_btn::racing_wheel_l2:
case gem_btn::racing_wheel_r2:
ext.custom[::at32(ext_btn_indices, value.btn)] = static_cast<u8>(value.value);
break;
case gem_btn::combo_racing_wheel_paddle_l:
case gem_btn::combo_racing_wheel_paddle_r:
case gem_btn::racing_wheel_paddle_l:
case gem_btn::racing_wheel_paddle_r:
ext.custom[::at32(ext_btn_indices, value.btn)] |= ::at32(ext_btn_map, value.btn);
break;
case gem_btn::combo_racing_wheel_d_pad_up:
case gem_btn::combo_racing_wheel_d_pad_right:
case gem_btn::combo_racing_wheel_d_pad_down:
case gem_btn::combo_racing_wheel_d_pad_left:
case gem_btn::racing_wheel_d_pad_up:
case gem_btn::racing_wheel_d_pad_right:
case gem_btn::racing_wheel_d_pad_down:
case gem_btn::racing_wheel_d_pad_left:
ext.digital1 |= ::at32(ext_btn_map, value.btn);
break;
case gem_btn::combo_racing_wheel_l1:
case gem_btn::combo_racing_wheel_r1:
case gem_btn::racing_wheel_l1:
case gem_btn::racing_wheel_r1:
ext.digital2 |= ::at32(ext_btn_map, value.btn);
break;
default:
@@ -2646,11 +2637,11 @@ static void mouse_pos_to_gem_state(u32 mouse_no, gem_config::gem_controller& con
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
{
ps_move_data& move_data = ::at32(g_fxo->get<gem_config>().fake_move_data, mouse_no);
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max, move_data);
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, move_data);
}
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
{
pos_to_gem_image_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
pos_to_gem_image_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos);
}
}
@@ -2701,25 +2692,23 @@ static void gun_pos_to_gem_state(u32 gem_no, gem_config::gem_controller& control
if (!gem_state || !is_input_allowed())
return;
int x_pos, y_pos, x_max, y_max;
f32 x_pos, y_pos;
{
gun_thread& gun = g_fxo->get<gun_thread>();
std::scoped_lock lock(gun.handler.mutex);
x_pos = gun.handler.get_axis_x(gem_no);
y_pos = gun.handler.get_axis_y(gem_no);
x_max = gun.handler.get_axis_x_max(gem_no);
y_max = gun.handler.get_axis_y_max(gem_no);
}
if constexpr (std::is_same_v<T, vm::ptr<CellGemState>>)
{
ps_move_data& move_data = ::at32(g_fxo->get<gem_config>().fake_move_data, gem_no);
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max, move_data);
pos_to_gem_state(gem_no, controller, gem_state, x_pos, y_pos, move_data);
}
else if constexpr (std::is_same_v<T, vm::ptr<CellGemImageState>>)
{
pos_to_gem_image_state(gem_no, controller, gem_state, x_pos, y_pos, x_max, y_max);
pos_to_gem_image_state(gem_no, controller, gem_state, x_pos, y_pos);
}
}
#endif
@@ -3079,7 +3068,7 @@ error_code cellGemGetAccelerometerPositionInDevice(u32 gem_num, vm::ptr<f32> pos
error_code cellGemGetAllTrackableHues(vm::ptr<u8> hues)
{
cellGem.todo("cellGemGetAllTrackableHues(hues=*0x%x)");
cellGem.todo("cellGemGetAllTrackableHues(hues=*0x%x)", hues);
auto& gem = g_fxo->get<gem_config>();
@@ -3587,12 +3576,19 @@ error_code cellGemHSVtoRGB(f32 h, f32 s, f32 v, vm::ptr<f32> r, vm::ptr<f32> g,
{
cellGem.warning("cellGemHSVtoRGB(h=%f, s=%f, v=%f, r=*0x%x, g=*0x%x, b=*0x%x)", h, s, v, r, g, b);
if (s < 0.0f || s > 1.0f || v < 0.0f || v > 1.0f || !r || !g || !b)
if (!r || !g || !b)
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
h = std::clamp(h, 0.0f, 360.0f);
h = std::fmod(h, 360.0f);
s = std::clamp(s, 0.0f, 1.0f);
v = std::clamp(v, 0.0f, 1.0f);
if (h < 0.0)
{
h += 360.0f;
}
const f32 c = v * s;
const f32 x = c * (1.0f - fabs(fmod(h / 60.0f, 2.0f) - 1.0f));
@@ -3633,9 +3629,9 @@ error_code cellGemHSVtoRGB(f32 h, f32 s, f32 v, vm::ptr<f32> r, vm::ptr<f32> g,
b_tmp = x;
}
*r = (r_tmp + m) * 255.0f;
*g = (g_tmp + m) * 255.0f;
*b = (b_tmp + m) * 255.0f;
*r = std::clamp(r_tmp + m, 0.0f, 1.0f);
*g = std::clamp(g_tmp + m, 0.0f, 1.0f);
*b = std::clamp(b_tmp + m, 0.0f, 1.0f);
return CELL_OK;
}
@@ -3997,9 +3993,15 @@ error_code cellGemSetRumble(u32 gem_num, u8 rumble)
return CELL_OK;
}
error_code cellGemSetYaw(u32 gem_num, vm::ptr<f32> z_direction)
error_code cellGemSetYaw(u32 gem_num, v128 z_direction)
{
cellGem.todo("cellGemSetYaw(gem_num=%d, z_direction=*0x%x)", gem_num, z_direction);
// Unpack vector argument
const f32 z_direction_x = z_direction.fr[0];
const f32 z_direction_y = z_direction.fr[1];
const f32 z_direction_z = z_direction.fr[2];
const f32 z_direction_w = z_direction.fr[3];
cellGem.warning("cellGemSetYaw(gem_num=%d, z_direction_x=%f, z_direction_y=%f, z_direction_z=%f, z_direction_w=%f)", gem_num, z_direction_x, z_direction_y, z_direction_z, z_direction_w);
auto& gem = g_fxo->get<gem_config>();
@@ -4010,12 +4012,31 @@ error_code cellGemSetYaw(u32 gem_num, vm::ptr<f32> z_direction)
return CELL_GEM_ERROR_UNINITIALIZED;
}
if (!z_direction || !check_gem_num(gem_num))
if (!check_gem_num(gem_num))
{
return CELL_GEM_ERROR_INVALID_PARAMETER;
}
// TODO
if (g_cfg.io.move != move_handler::real)
{
return CELL_OK;
}
std::lock_guard pad_lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (pad && pad->m_pad_handler == pad_handler::move && !pad->is_copilot())
{
// z_direction is the direction of the controller's z axis (sphere -> handle) in world coordinates.
// This function is usually used when the game wants to re-orient the ps move towards the camera, e.g. during calibration or as a surrogate for it.
// So far we've seen:
// A: (0,0,1,0): The controller points straight at the camera along the camera axis. This is our default orientation.
// B: The current sphere position (CellGemState.pos) while pointing at the camera.
// This is equal to A if the controller is on the camera axis. Otherwise the correct yaw would be atan2(x, z).
// For now we simply reset the orientation (including pitch and roll) instead of only adjusting the yaw.
pad->move_data.orientation_reset_requested = true;
}
return CELL_OK;
}
@@ -4176,7 +4197,7 @@ error_code cellGemUpdateStart(vm::cptr<void> camera_frame, u64 timestamp)
gem.camera_frame = camera_frame.addr();
const bool image_set = tracker.set_image(gem.camera_frame);
const bool image_set = tracker.set_image(camera_frame);
tracker.wake_up_tracker();
+10 -9
View File
@@ -502,8 +502,8 @@ error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStre
}
//Decode GIF file. (TODO: Is there any faster alternative? Can we do it without external libraries?)
int width, height, actual_components;
auto image = std::unique_ptr<unsigned char,decltype(&::free)>
int width = 0, height = 0, actual_components = 0;
auto image = std::unique_ptr<u8, decltype(&::free)>
(
stbi_load_from_memory(gif.get(), ::narrow<int>(fileSize), &width, &height, &actual_components, 4),
&::free
@@ -515,6 +515,7 @@ error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStre
const int bytesPerLine = static_cast<int>(dataCtrlParam->outputBytesPerLine);
constexpr char nComponents = 4;
const u32 image_size = width * height * nComponents;
u8* src = image.get();
switch(current_outParam.outputColorSpace)
{
@@ -527,12 +528,12 @@ error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStre
{
const int dstOffset = i * bytesPerLine;
const int srcOffset = width * nComponents * i;
memcpy(&data[dstOffset], &image.get()[srcOffset], linesize);
std::memcpy(&data[dstOffset], &src[srcOffset], linesize);
}
}
else
{
memcpy(data.get_ptr(), image.get(), image_size);
std::memcpy(data.get_ptr(), src, image_size);
}
break;
}
@@ -549,10 +550,10 @@ error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStre
const int srcOffset = width * nComponents * i;
for (int j = 0; j < linesize; j += nComponents)
{
output[j + 0] = image.get()[srcOffset + j + 3];
output[j + 1] = image.get()[srcOffset + j + 0];
output[j + 2] = image.get()[srcOffset + j + 1];
output[j + 3] = image.get()[srcOffset + j + 2];
output[j + 0] = src[srcOffset + j + 3];
output[j + 1] = src[srcOffset + j + 0];
output[j + 2] = src[srcOffset + j + 1];
output[j + 3] = src[srcOffset + j + 2];
}
std::memcpy(&data[dstOffset], output.get(), linesize);
}
@@ -560,7 +561,7 @@ error_code cellGifDecDecodeData(vm::ptr<GifDecoder> mainHandle, vm::cptr<GifStre
else
{
const auto img = std::make_unique<uint[]>(image_size);
uint* source_current = reinterpret_cast<uint*>(image.get());
uint* source_current = reinterpret_cast<uint*>(src);
uint* dest_current = img.get();
for (uint i = 0; i < image_size / nComponents; i++)
{
+1 -1
View File
@@ -3702,7 +3702,7 @@ error_code cellHttpEndExternalCache()
error_code cellHttpInitExternalCache(vm::ptr<void> buf, u32 size)
{
cellHttp.todo("cellHttpInitExternalCache(buf=*0x%x, size=0x%x)", size);
cellHttp.todo("cellHttpInitExternalCache(buf=*0x%x, size=0x%x)", buf, size);
auto& man = g_fxo->get<http_manager>();
std::lock_guard lock(man.mtx);
+3 -3
View File
@@ -507,14 +507,14 @@ error_code cellHttpUtilEscapeUri(vm::ptr<char> out, u32 outSize, vm::cptr<u8> in
u32 size_needed = 0;
u32 out_pos = 0;
s32 rindex = 0;
u32 in_size = 0;
if (const u32 end = in.addr() + inSize; end && end >= in.addr())
{
rindex = inSize;
in_size = inSize;
}
for (u32 pos = 0; rindex >= 0; rindex--, pos++)
for (u32 pos = 0; pos < in_size; pos++)
{
const char c1 = in[pos];
+2 -3
View File
@@ -1158,11 +1158,10 @@ static error_code cellImeJpGetCandidateList(CellImeJpHandle hImeJpHandle, vm::pt
for (const ime_jp_manager::candidate& can : list)
{
// Copy the candidate
for (u32 i = pos; i < can.text.length(); i++)
for (u32 i = 0; i < can.text.length(); i++)
{
pCandidateString[i] = can.text[i];
pCandidateString[pos++] = can.text[i];
}
pos += ::narrow<u32>(can.text.length());
// Add null terminator
pCandidateString[pos++] = 0;
+2 -2
View File
@@ -2,8 +2,8 @@
#include "Emu/IdManager.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Io/KeyboardHandler.h"
#include "util/cctype.hpp"
#include "cellKb.h"
error_code sys_config_start(ppu_thread& ppu);
@@ -198,7 +198,7 @@ u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode)
const auto get_ascii = [&](u16 raw, u16 shifted = 0, u16 altered = 0)
{
// Usually caps lock only applies uppercase to letters, but some layouts treat it as shift lock for all keys.
if ((is_shift || (is_caps_lock && (is_shift_lock || std::isalpha(raw)))) && shifted)
if ((is_shift || (is_caps_lock && (is_shift_lock || utils::isalpha(raw)))) && shifted)
{
return shifted;
}
+15 -1
View File
@@ -208,6 +208,8 @@ u32 mic_context::register_device(const std::string& device_name)
case microphone_handler::standard:
case microphone_handler::real_singstar:
case microphone_handler::rocksmith:
case microphone_handler::eye_toy:
case microphone_handler::ps_eye:
{
microphone_device& device = ::at32(mic_list, index);
device = microphone_device(g_cfg.audio.microphone_type.get());
@@ -328,6 +330,8 @@ error_code microphone_device::open_microphone(const u8 type, const u32 dsp_r, co
switch (device_type)
{
case microphone_handler::standard:
case microphone_handler::eye_toy:
case microphone_handler::ps_eye:
break;
case microphone_handler::singstar:
case microphone_handler::real_singstar:
@@ -729,6 +733,8 @@ void microphone_device::get_data(const u32 num_samples)
}
case microphone_handler::standard:
case microphone_handler::rocksmith:
case microphone_handler::eye_toy:
case microphone_handler::ps_eye:
{
constexpr u8 channel_size = bit_resolution / 8;
const usz bufsize = num_samples * sample_size;
@@ -1458,7 +1464,15 @@ error_code cellMicGetType(s32 dev_num, vm::ptr<s32> ptr_type)
return CELL_MICIN_ERROR_NOT_INIT;
// TODO: get proper type (log message is trace because of massive spam)
*ptr_type = CELLMIC_TYPE_USBAUDIO; // Needed for Guitar Hero: Warriors of Rock (BLUS30487)
if (dev_num < static_cast<s32>(mic_thr.mic_list.size()))
{
const microphone_device& device = ::at32(mic_thr.mic_list, dev_num);
*ptr_type = device.get_mic_type();
}
else
{
*ptr_type = CELLMIC_TYPE_UNDEF;
}
return CELL_OK;
}
+12 -2
View File
@@ -2,6 +2,7 @@
#include "Utilities/Thread.h"
#include "Utilities/mutex.h"
#include "Emu/system_config_types.h"
#include "alc.h"
@@ -113,8 +114,8 @@ enum CellMicType : s32
{
CELLMIC_TYPE_UNDEF = -1,
CELLMIC_TYPE_UNKNOWN = 0,
CELLMIC_TYPE_EYETOY1 = 1,
CELLMIC_TYPE_EYETOY2 = 2,
CELLMIC_TYPE_EYETOY1 = 1, // EyeToy
CELLMIC_TYPE_EYETOY2 = 2, // PS EYE
CELLMIC_TYPE_USBAUDIO = 3,
CELLMIC_TYPE_BLUETOOTH = 4,
CELLMIC_TYPE_A2DP = 5,
@@ -310,6 +311,15 @@ public:
return 1; // BE
}
}
s32 get_mic_type() const
{
switch(device_type)
{
case microphone_handler::eye_toy: return CELLMIC_TYPE_EYETOY1;
case microphone_handler::ps_eye: return CELLMIC_TYPE_EYETOY2;
default: return CELLMIC_TYPE_USBAUDIO; // Needed for Guitar Hero: Warriors of Rock (BLUS30487)
}
}
u32 read_raw(u8* buf, u32 size) { return rbuf_raw.read_bytes(buf, size); }
u32 read_dsp(u8* buf, u32 size) { return rbuf_dsp.read_bytes(buf, size); }
+2 -1
View File
@@ -1,4 +1,5 @@
#include "stdafx.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/PPUThread.h"
@@ -75,7 +76,7 @@ struct msg_info
return nullptr;
}
dlg = Emu.GetCallbacks().get_msg_dialog();
dlg = g_emu_callbacks.get_msg_dialog();
return dlg;
}
+2 -2
View File
@@ -4,7 +4,7 @@
#include "Emu/Cell/lv2/sys_lwcond.h"
#include "Emu/Cell/lv2/sys_spu.h"
#include "Emu/Io/music_handler_base.h"
#include "Emu/System.h"
#include "Emu/emu_callbacks.h"
#include "Emu/VFS.h"
#include "Emu/RSX/Overlays/overlay_media_list_dialog.h"
#include "cellSearch.h"
@@ -79,7 +79,7 @@ public:
music_state()
{
handler = Emu.GetCallbacks().get_music_handler();
handler = g_emu_callbacks.get_music_handler();
handler->set_event_status_callback([this](u32 status)
{
if (!func)
+50 -67
View File
@@ -188,76 +188,70 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
auto& dec = g_fxo->get<Music_Decode>();
std::lock_guard lock(dec.mutex);
std::scoped_lock slock(dec.decoder.m_mtx);
if (dec.decoder.has_error)
if (dec.decoder.has_error())
{
return CELL_MUSIC_DECODE_ERROR_DECODE_FAILURE;
}
if (dec.decoder.m_size == 0)
{
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "m_size == 0" };
}
std::scoped_lock data_lock(dec.decoder.data_mutex());
const u64 size = dec.decoder.size();
ensure(dec.decoder.m_size >= dec.read_pos);
const u64 size_left = dec.decoder.m_size - dec.read_pos;
if (dec.read_pos == 0)
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_START, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
*position = CELL_MUSIC_DECODE_POSITION_START;
}
else if (!dec.decoder.track_fully_decoded || size_left > reqSize) // track_fully_decoded is not guarded by a mutex, but since it is set to true after the decode, it should be fine.
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_MID, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
*position = CELL_MUSIC_DECODE_POSITION_MID;
}
else
{
if (dec.decoder.set_next_index(true) == umax)
if (size == 0)
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END_LIST_END, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "size == 0" };
}
ensure(size >= dec.read_pos);
const u64 size_left = size - dec.read_pos;
if (dec.read_pos == 0)
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_START, read_pos=%d, reqSize=%d, size=%d", dec.read_pos, reqSize, size);
*position = CELL_MUSIC_DECODE_POSITION_START;
}
else if (!dec.decoder.track_fully_decoded() || size_left > reqSize) // track_fully_decoded is not guarded by a mutex, but since it is set to true after the decode, it should be fine.
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_MID, read_pos=%d, reqSize=%d, size=%d", dec.read_pos, reqSize, size);
*position = CELL_MUSIC_DECODE_POSITION_MID;
}
else
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
*position = CELL_MUSIC_DECODE_POSITION_END;
if (dec.decoder.set_next_index(true) == umax)
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END_LIST_END, read_pos=%d, reqSize=%d, size=%d", dec.read_pos, reqSize, size);
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
}
else
{
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END, read_pos=%d, reqSize=%d, size=%d", dec.read_pos, reqSize, size);
*position = CELL_MUSIC_DECODE_POSITION_END;
}
}
}
const u64 size_to_read = std::min(reqSize, size_left);
*readSize = size_to_read;
const u64 read_size = dec.decoder.read(buf.get_ptr(), dec.read_pos, reqSize);
*readSize = read_size;
if (size_to_read == 0)
{
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "size_to_read == 0" }; // TODO: speculative
}
std::memcpy(buf.get_ptr(), &::at32(dec.decoder.data, dec.read_pos), size_to_read);
if (size_to_read < reqSize)
{
// Set the rest of the buffer to zero to prevent loud pops at the end of the stream if the game ignores the readSize.
std::memset(vm::static_ptr_cast<u8>(buf).get_ptr() + size_to_read, 0, reqSize - size_to_read);
}
dec.read_pos += size_to_read;
s64 start_time_ms = 0;
if (!dec.decoder.timestamps_ms.empty())
{
start_time_ms = dec.decoder.timestamps_ms.front().second;
while (dec.decoder.timestamps_ms.size() > 1 && dec.read_pos >= ::at32(dec.decoder.timestamps_ms, 1).first)
if (read_size == 0)
{
dec.decoder.timestamps_ms.pop_front();
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "read_size == 0" }; // TODO: speculative
}
}
*startTime = static_cast<u32>(start_time_ms); // startTime is milliseconds
if (read_size < reqSize)
{
// Set the rest of the buffer to zero to prevent loud pops at the end of the stream if the game ignores the readSize.
std::memset(vm::static_ptr_cast<u8>(buf).get_ptr() + read_size, 0, reqSize - read_size);
}
dec.read_pos += read_size;
const s64 start_time_ms = dec.decoder.get_start_time_ms(dec.read_pos);
*startTime = static_cast<u32>(start_time_ms); // startTime is milliseconds
cellMusicDecode.trace("cell_music_decode_read(read_size=%d, samples=%d, start_time_ms=%d)", read_size, read_size / sizeof(u64), start_time_ms);
}
switch (*position)
{
@@ -272,8 +266,7 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
{
dec.read_pos = 0;
dec.decoder.clear();
dec.decoder.track_fully_consumed = 1;
dec.decoder.track_fully_consumed.notify_one();
dec.decoder.wake_up();
break;
}
default:
@@ -282,8 +275,6 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
}
}
cellMusicDecode.trace("cell_music_decode_read(size_to_read=%d, samples=%d, start_time_ms=%d)", size_to_read, size_to_read / sizeof(u64), start_time_ms);
return CELL_OK;
}
@@ -375,11 +366,7 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
error_code result = CELL_OK;
{
std::scoped_lock slock(dec.decoder.m_mtx);
result = dec.set_decode_command(command);
}
const error_code result = dec.set_decode_command(command);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
@@ -566,11 +553,7 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
error_code result = CELL_OK;
{
std::scoped_lock slock(dec.decoder.m_mtx);
result = dec.set_decode_command(command);
}
const error_code result = dec.set_decode_command(command);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
@@ -17,7 +17,22 @@ bool music_selection_context::set(const CellMusicSelectionContext& in)
}
constexpr u32 pos = sizeof(magic);
hash = &in.data[pos];
const std::string_view data{&in.data[pos], sizeof(in.data) - pos};
const std::string_view new_hash = data.substr(0, data.find_first_of('\0'));
const bool is_valid_hash = !new_hash.empty() && std::all_of(new_hash.begin(), new_hash.end(), [](char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_';
});
if (!is_valid_hash)
{
cellMusicSelectionContext.error("Invalid music selection context hash, context = %s", context_to_hex(in));
return false;
}
hash = std::string{new_hash};
return load_playlist();
}
@@ -35,7 +50,7 @@ CellMusicSelectionContext music_selection_context::get() const
std::memset(out.data, 0, CELL_MUSIC_SELECTION_CONTEXT_SIZE);
std::memcpy(out.data, magic, sizeof(magic));
pos += sizeof(magic);
std::memcpy(&out.data[pos], hash.c_str(), hash.size());
std::memcpy(&out.data[pos], hash.data(), hash.size());
return out;
}
-1
View File
@@ -1,5 +1,4 @@
#include "stdafx.h"
#include "Emu/system_config.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
+5 -3
View File
@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/Modules/sysPrxForUser.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/Keyboard.h"
#include "Emu/IdManager.h"
@@ -140,7 +142,7 @@ std::shared_ptr<OskDialogBase> _get_osk_dialog(bool create)
}
else
{
osk.dlg = Emu.GetCallbacks().get_osk_dialog();
osk.dlg = g_emu_callbacks.get_osk_dialog();
}
return osk.dlg;
@@ -310,7 +312,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
sysutil_register_cb([&, length = i, string_to_send = std::move(string_to_send)](ppu_thread& cb_ppu) -> s32
{
vm::var<u16[], vm::page_allocator<>> string_var(CELL_OSKDIALOG_STRING_SIZE, string_to_send.data());
vm::var<u16[], vm::hle_malloc_allocator> string_var(CELL_OSKDIALOG_STRING_SIZE, string_to_send.data());
const u32 return_value = ccb(cb_ppu, string_var.begin(), static_cast<s32>(length));
cellOskDialog.warning("osk_confirm_callback return_value=%d", return_value);
@@ -505,7 +507,7 @@ error_code cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dia
// 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(::narrow<u32>(string_to_send.size()), string_to_send.data());
vm::var<u16[], vm::hle_malloc_allocator> pActionInfo(::narrow<u32>(string_to_send.size()), string_to_send.data());
// Create helpers for logging
std::u16string utf16_string(reinterpret_cast<const char16_t*>(string_to_send.data()), string_to_send.size());
+2 -2
View File
@@ -1,7 +1,7 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/VFS.h"
#include "Emu/System.h"
#include "Emu/emu_callbacks.h"
#include "cellSysutil.h"
LOG_CHANNEL(cellPhotoDecode);
@@ -160,7 +160,7 @@ error_code cellPhotoDecodeFromFile(vm::cptr<char> srcHddDir, vm::cptr<char> srcH
s32 width{};
s32 height{};
if (!Emu.GetCallbacks().get_scaled_image(path, set_param->width, set_param->height, width, height, static_cast<u8*>(set_param->dstBuffer.get_ptr()), false))
if (!g_emu_callbacks.get_scaled_image(path, set_param->width, set_param->height, width, height, static_cast<u8*>(set_param->dstBuffer.get_ptr()), false))
{
cellPhotoDecode.error("Failed to decode '%s'", path);
return CELL_PHOTO_DECODE_ERROR_DECODE;
+2 -1
View File
@@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/emu_callbacks.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/VFS.h"
@@ -117,7 +118,7 @@ std::string get_available_photo_path(std::string_view filename)
extension = filename.substr(extension_start);
}
return Emu.GetCallbacks().get_photo_path(fmt::format("%s%s", Emu.GetTitle(), extension));
return g_emu_callbacks.get_photo_path(fmt::format("%s%s", Emu.GetTitle(), extension));
}
+3 -3
View File
@@ -1333,13 +1333,13 @@ error_code cellRecClose(s32 isDiscard)
rec.sink->stop(true);
}
if (fs::is_file(rec.param.filename))
if (const std::string local_path = vfs::get(rec.param.filename); fs::is_file(local_path))
{
cellRec.warning("cellRecClose: removing discarded recording '%s'", rec.param.filename);
if (!fs::remove_file(rec.param.filename))
if (!fs::remove_file(local_path))
{
cellRec.error("cellRecClose: failed to remove recording '%s'", rec.param.filename);
cellRec.error("cellRecClose: failed to remove recording '%s' (%s)", rec.param.filename, fs::g_tls_error);
}
}
}
+41 -37
View File
@@ -6,6 +6,7 @@
#include "Emu/Cell/lv2/sys_time.h"
#include "Emu/Cell/lv2/sys_memory.h"
#include "Emu/Cell/lv2/sys_ss.h"
#include "util/cctype.hpp"
LOG_CHANNEL(cellRtc);
@@ -197,7 +198,7 @@ error_code cellRtcFormatRfc2822(ppu_thread& ppu, vm::ptr<char> pszDateTime, vm::
s32 weekdayIdx = cellRtcGetDayOfWeek(date_time->year, date_time->month, date_time->day);
// Day name
pszDateTime[0] = std::toupper(WEEKDAY_NAMES[weekdayIdx][0]);
pszDateTime[0] = utils::toupper(WEEKDAY_NAMES[weekdayIdx][0]);
pszDateTime[1] = WEEKDAY_NAMES[weekdayIdx][1];
pszDateTime[2] = WEEKDAY_NAMES[weekdayIdx][2];
pszDateTime[3] = ',';
@@ -208,7 +209,7 @@ error_code cellRtcFormatRfc2822(ppu_thread& ppu, vm::ptr<char> pszDateTime, vm::
pszDateTime[7] = ' ';
// month name
pszDateTime[8] = std::toupper(MONTH_NAMES[date_time->month - 1][0]);
pszDateTime[8] = utils::toupper(MONTH_NAMES[date_time->month - 1][0]);
pszDateTime[9] = MONTH_NAMES[date_time->month - 1][1];
pszDateTime[10] = MONTH_NAMES[date_time->month - 1][2];
pszDateTime[0xb] = ' ';
@@ -433,7 +434,7 @@ u16 rtcParseComponent(vm::cptr<char> pszDateTime, u32& pos, char delimiter, cons
pos++;
}
if (!std::isdigit(pszDateTime[pos]))
if (!utils::isdigit(pszDateTime[pos]))
{
cellRtc.error("rtcParseComponent(): failed to parse %s: ASCII value 0x%x at position %d is not a digit", component_name, pszDateTime[pos + 1], pos);
return umax;
@@ -443,7 +444,7 @@ u16 rtcParseComponent(vm::cptr<char> pszDateTime, u32& pos, char delimiter, cons
pos++;
if (std::isdigit(pszDateTime[pos]))
if (utils::isdigit(pszDateTime[pos]))
{
ret = ret * 10 + digit(pszDateTime[pos]);
@@ -462,7 +463,7 @@ u8 rtcParseName(vm::cptr<char> pszDateTime, u32& pos, const std::array<std::stri
u32 ch_idx = 0;
while (ch_idx < name_length && std::tolower(pszDateTime[pos + ch_idx]) == names[name_idx][ch_idx]) // Not case sensitive
while (ch_idx < name_length && utils::tolower(pszDateTime[pos + ch_idx]) == names[name_idx][ch_idx]) // Not case sensitive
{
ch_idx++;
}
@@ -520,15 +521,15 @@ error_code rtcParseRfc2822(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::cptr<
// Year: "XX" or "XXXX"
u16 year = 0;
if (!std::isdigit(pszDateTime[pos]) ||
!std::isdigit(pszDateTime[pos + 1]))
if (!utils::isdigit(pszDateTime[pos]) ||
!utils::isdigit(pszDateTime[pos + 1]))
{
return { CELL_RTC_ERROR_BAD_PARSE, "rtcParseRfc2822(): failed to parse year: one of the first two ASCII values 0x%x, 0x%x at position %d is not a digit",
pszDateTime[pos], pszDateTime[pos + 1], pos };
}
if (!std::isdigit(pszDateTime[pos + 2]) ||
!std::isdigit(pszDateTime[pos + 3]))
if (!utils::isdigit(pszDateTime[pos + 2]) ||
!utils::isdigit(pszDateTime[pos + 3]))
{
year = digit(pszDateTime[pos]) * 10 + digit(pszDateTime[pos + 1]);
year += (year < 50) ? 2000 : 1900;
@@ -591,10 +592,10 @@ error_code rtcParseRfc2822(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::cptr<
{
// "±hhmm"
if (std::isdigit(pszDateTime[pos + 1]) &&
std::isdigit(pszDateTime[pos + 2]) &&
std::isdigit(pszDateTime[pos + 3]) &&
std::isdigit(pszDateTime[pos + 4]))
if (utils::isdigit(pszDateTime[pos + 1]) &&
utils::isdigit(pszDateTime[pos + 2]) &&
utils::isdigit(pszDateTime[pos + 3]) &&
utils::isdigit(pszDateTime[pos + 4]))
{
const s32 time_zone_hhmm = digit(pszDateTime[pos + 1]) * 1000 + digit(pszDateTime[pos + 2]) * 100 + digit(pszDateTime[pos + 3]) * 10 + digit(pszDateTime[pos + 4]);
@@ -627,7 +628,7 @@ error_code rtcParseRfc2822(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::cptr<
// "A", "B", "C", ..., not case sensitive
// These are all off by one ("A" should be UTC+01:00, "B" should be UTC+02:00, etc.)
const char letter = std::toupper(pszDateTime[pos]);
const char letter = utils::toupper(pszDateTime[pos]);
if (letter >= 'A' && letter <= 'M' && letter != 'J')
{
@@ -671,15 +672,15 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::
u32 pos = 0;
while (std::isblank(pszDateTime[pos]))
while (utils::isblank(pszDateTime[pos]))
{
pos++;
}
if (std::isdigit(pszDateTime[pos]) &&
std::isdigit(pszDateTime[pos + 1]) &&
std::isdigit(pszDateTime[pos + 2]) &&
std::isdigit(pszDateTime[pos + 3]))
if (utils::isdigit(pszDateTime[pos]) &&
utils::isdigit(pszDateTime[pos + 1]) &&
utils::isdigit(pszDateTime[pos + 2]) &&
utils::isdigit(pszDateTime[pos + 3]))
{
return cellRtcParseRfc3339(ppu, pUtc, pszDateTime + pos);
}
@@ -697,7 +698,7 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::
}
// Skip spaces and tabs
while (std::isblank(pszDateTime[pos]))
while (utils::isblank(pszDateTime[pos]))
{
pos++;
}
@@ -742,13 +743,13 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::
pos++;
}
else if (std::isdigit(pszDateTime[pos]))
else if (utils::isdigit(pszDateTime[pos]))
{
day = digit(pszDateTime[pos]);
pos++;
if (std::isdigit(pszDateTime[pos]))
if (utils::isdigit(pszDateTime[pos]))
{
day = day * 10 + digit(pszDateTime[pos]);
@@ -793,10 +794,10 @@ error_code cellRtcParseDateTime(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::
pos++;
// Year: XXXX
if (!std::isdigit(pszDateTime[pos]) ||
!std::isdigit(pszDateTime[pos + 1]) ||
!std::isdigit(pszDateTime[pos + 2]) ||
!std::isdigit(pszDateTime[pos + 3]))
if (!utils::isdigit(pszDateTime[pos]) ||
!utils::isdigit(pszDateTime[pos + 1]) ||
!utils::isdigit(pszDateTime[pos + 2]) ||
!utils::isdigit(pszDateTime[pos + 3]))
{
return { CELL_RTC_ERROR_BAD_PARSE, "cellRtcParseDateTime(): failed to parse year: one of the ASCII values 0x%x, 0x%x, 0x%x, or 0x%x is not a digit",
pszDateTime[pos], pszDateTime[pos + 1], pszDateTime[pos + 2], pszDateTime[pos + 3] };
@@ -831,7 +832,10 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
vm::var<CellRtcDateTime> date_time;
// Year: XXXX-12-03T13:23:00.00Z
if (std::isdigit(pszDateTime[0]) && std::isdigit(pszDateTime[1]) && std::isdigit(pszDateTime[2]) && std::isdigit(pszDateTime[3]))
if (utils::isdigit(pszDateTime[0]) &&
utils::isdigit(pszDateTime[1]) &&
utils::isdigit(pszDateTime[2]) &&
utils::isdigit(pszDateTime[3]))
{
date_time->year = digit(pszDateTime[0]) * 1000 + digit(pszDateTime[1]) * 100 + digit(pszDateTime[2]) * 10 + digit(pszDateTime[3]);
}
@@ -846,7 +850,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
}
// Month: 1995-XX-03T13:23:00.00Z
if (std::isdigit(pszDateTime[5]) && std::isdigit(pszDateTime[6]))
if (utils::isdigit(pszDateTime[5]) && utils::isdigit(pszDateTime[6]))
{
date_time->month = digit(pszDateTime[5]) * 10 + digit(pszDateTime[6]);
}
@@ -861,7 +865,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
}
// Day: 1995-12-XXT13:23:00.00Z
if (std::isdigit(pszDateTime[8]) && std::isdigit(pszDateTime[9]))
if (utils::isdigit(pszDateTime[8]) && utils::isdigit(pszDateTime[9]))
{
date_time->day = digit(pszDateTime[8]) * 10 + digit(pszDateTime[9]);
}
@@ -876,7 +880,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
}
// Hour: 1995-12-03TXX:23:00.00Z
if (std::isdigit(pszDateTime[11]) && std::isdigit(pszDateTime[12]))
if (utils::isdigit(pszDateTime[11]) && utils::isdigit(pszDateTime[12]))
{
date_time->hour = digit(pszDateTime[11]) * 10 + digit(pszDateTime[12]);
}
@@ -891,7 +895,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
}
// Minute: 1995-12-03T13:XX:00.00Z
if (std::isdigit(pszDateTime[14]) && std::isdigit(pszDateTime[15]))
if (utils::isdigit(pszDateTime[14]) && utils::isdigit(pszDateTime[15]))
{
date_time->minute = digit(pszDateTime[14]) * 10 + digit(pszDateTime[15]);
}
@@ -906,7 +910,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
}
// Second: 1995-12-03T13:23:XX.00Z
if (std::isdigit(pszDateTime[17]) && std::isdigit(pszDateTime[18]))
if (utils::isdigit(pszDateTime[17]) && utils::isdigit(pszDateTime[18]))
{
date_time->second = digit(pszDateTime[17]) * 10 + digit(pszDateTime[18]);
}
@@ -923,7 +927,7 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
{
u32 mul = 100000;
for (char c = pszDateTime[++pos]; std::isdigit(c); c = pszDateTime[++pos])
for (char c = pszDateTime[++pos]; utils::isdigit(c); c = pszDateTime[++pos])
{
date_time->microsecond += digit(c) * mul;
mul /= 10;
@@ -942,11 +946,11 @@ error_code cellRtcParseRfc3339(ppu_thread& ppu, vm::ptr<CellRtcTick> pUtc, vm::c
// Time offset: 1995-12-03T13:23:00.00+02:30
if (sign == '+' || sign == '-')
{
if (!std::isdigit(pszDateTime[pos + 1]) ||
!std::isdigit(pszDateTime[pos + 2]) ||
if (!utils::isdigit(pszDateTime[pos + 1]) ||
!utils::isdigit(pszDateTime[pos + 2]) ||
pszDateTime[pos + 3] != ':' ||
!std::isdigit(pszDateTime[pos + 4]) ||
!std::isdigit(pszDateTime[pos + 5]))
!utils::isdigit(pszDateTime[pos + 4]) ||
!utils::isdigit(pszDateTime[pos + 5]))
{
return CELL_RTC_ERROR_BAD_PARSE;
}

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