Compare commits

..

320 Commits

Author SHA1 Message Date
Elad f6ea921e0d Merge branch 'master' into hot-cake 2026-06-07 19:19:33 +03:00
kd-11 339877ea7d rsx: Handle mipmap sampling edge case when SNORM emulation is active 2026-06-07 18:56:15 +03:00
Elad ba9464c547 cellAudio: Fix _mxr000 event queue hack 2026-06-07 17:57:40 +03:00
Malcolm e7165cbe99 RSX: Use one-shot cacheline wait in semaphore acquire 2026-06-07 15:55:28 +02:00
Malcolm d85f1c7155 RSX: Add hardware spin_wait helper, use it in flush requests 2026-06-07 15:55:28 +02:00
Rad0van 9e2c41bd48 cellAudio: fix _mxr000 surmixer event-queue key constant
The "Dirty hack for sound" in AudioSetNotifyEventQueue() is gated on a
named constant c_mxr000 that is supposed to equal the IPC key of the
_mxr000 event queue created by the game's _cellsurMixerMain thread
(0x8000cafe02460300, e.g. Virtua Fighter 5 FS / SEGA AM2 CRI titles).

When the inline literal was refactored into the named constant in
730badd37 ("cellAudio: Move and partially fix _mxr000 hack", 2023) a
trailing hex digit was dropped: 0x8000cafe0246030 instead of
0x8000cafe02460300 (the value used by the original 2018 hack in
cc0d7c598). As a result `key == c_mxr000` never matches and
lv2_event_queue::find(c_mxr000) looks up the wrong key, so the
surmixer-queue serialization path is effectively dead for every
_mxr000 CRI game.

Restore the correct 16-digit key so the gate and lookup match the
queue the game actually creates.

Note: this corrects a real latent bug but does not by itself resolve
the cellSurMixer null-deref in VF5FS (#17066), which is a separate,
flaky CRI channel-strip init divergence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 15:05:29 +02:00
Whatcookie 2d1be09180 SPU LLVM: Use TBL for AArch64 ROTQBY shuffles 2026-06-06 18:51:35 +02:00
Rad0van d25972e196 JIT/AArch64: advertise +i8mm to the LLVM target machine
The PPU/SPU recompilers emit i8mm intrinsics (ummla/smmla, used by the
SPU GBB/GBH gather paths) gated on utils::has_i8mm(). The JIT's MAttrs
list mirrored dotprod/sha3/sve from HWCAP but never added i8mm, and the
resolved -mcpu on Apple silicon is the cortex-a78 fallback (no i8mm), so
the backend aborted with "Cannot select: intrinsic %llvm.aarch64.neon.ummla"
on every game. Mirror i8mm into MAttrs like the other features.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
(cherry picked from commit 52d121fee79cc569c9ac273852edd0e493ae51fc)
2026-06-06 18:34:38 +03:00
Megamouse 62d32ab45e Update SDL to 3.4.10 2026-06-06 01:23:02 +02:00
Megamouse 1ca995cc96 Update FAudio to 26.06 2026-06-06 01:23:02 +02:00
Malcolm 3164d44952 SPU LLVM: Use I8MM for GBH and GBB 2026-06-05 23:19:35 +02:00
Sanjay Govind 546f3082a2 usb_device: present wii instruments as PS3 instruments (#18796)
The instruments work exactly the same but the IDs are different, so
doing this allows them to work in games
2026-06-05 21:08:46 +02:00
Nico Franke 8d9abd396b Make FFB direction encoding for emulated G27 configurable (#18776)
This adds a dropdown in the emulated G27 settings dialog ("Force
feedback direction encoding") with three options: Steering Axis
(default), Cartesian, Polar. Default is Steering Axis so existing users
see no change.

RPCS3 right now hardcodes SDL_HAPTIC_STEERING_AXIS for the spring and
constant-force effects it sends to the host wheel. That's SDL's
recommended encoding for wheels and works on the hardware the code was
written against. Some Windows DirectInput drivers (like my MOZA R3) do
not react to these affects at all. This now allows changing it to an
encoding that works.
2026-06-05 20:09:35 +02:00
Megamouse 8112c7cc19 Initialize rtmidi async
On my system this opening the settings takes 10 seconds on a cold boot.
Letting it initialize async during boot reduces this delay drastically.
2026-06-05 16:28:33 +02:00
kd-11 bf85a3fdd9 rsx/vk: Suppress the pool growth signal a bit
- Still allows for growth when needed but avoids creating larger sets unless there really is demand for it
2026-06-05 15:42:32 +02:00
kd-11 8e370c2cf1 rsx/vk: Implement auto-scaling of descriptor pool sizes 2026-06-05 15:42:32 +02:00
Malcolm 6349ea2ee9 SPU LLVM: SVE multiply optimizations
- Reduces most SPU multiply instructions to a single operation
- Add SVE length detection, only use SVE instructions for 128b sve for
  now
2026-06-05 14:58:24 +02:00
Whatcookie 2f2ac69d60 SPU LLVM: Idiomatic FSM implementation
- Compiles down to just 2 instructions on Neon, instead of falling back to scalar instructions
- Remove this workaround when LLVM fixes this issue upstream: https://github.com/llvm/llvm-project/issues/200325
2026-06-05 09:48:16 +02:00
Malcolm 4a92d96cf4 SPU LLVM: Use 128-bit SVE2 for FMS 2026-06-05 08:41:17 +02:00
Windsurf7 9a2243bfcb Add "Enable Mouse-based Gyro" setting to I/O config (#18835)
This is a continuation of my work on #18113.

Addresses issue #18197.

Adds a new checkbox in the I/O settings tab that enables mouse-based
gyro emulation at game startup, making the feature more discoverable to
users.
2026-06-04 14:49:48 +02:00
digant73 97704e35e0 Remove duplicated code 2026-06-04 12:06:42 +03:00
Ani 40e9ee5af0 rpcs3_version: Bump to 0.0.41 2026-06-04 02:58:57 +02:00
Megamouse 3fa1241ac0 Initialize render_creator in headless mode 2026-06-02 21:49:09 +02:00
Megamouse 62747bb40d Set OpenGl as default renderer if vulkan is not supported 2026-06-02 21:49:09 +02:00
Megamouse 44b33f1962 Force renderer to default renderer if not supported 2026-06-02 21:49:09 +02:00
Megamouse cc999d4210 Force disable some settings in headless mode 2026-06-02 21:49:09 +02:00
Megamouse 7c2bd56776 cellMusic: don't set current_track to umax on prev
There's no reason to do that and it might cause OOB access elsewhere.
2026-06-02 21:49:09 +02:00
Megamouse 5fc7450865 Fix uninitialized members in queue_submit_t 2026-06-02 16:10:21 +02:00
Megamouse 4ca0f0b11d cpu: fix prefetch list 2026-06-02 16:10:21 +02:00
Megamouse 66de4022b1 Fix ARCH_X64 define for hypervisor_context_t 2026-06-02 16:10:21 +02:00
Megamouse 2b9368abf5 Fix potential OOB access in try_to_enum_value 2026-06-02 16:10:21 +02:00
Megamouse f8a5a6ad93 cellMusicDecode: fix track index on next if already on last track
Fixes an OOB exception
2026-06-01 08:58:52 +02:00
Megamouse fa1aa9dcf3 cellMusicDecode: Fix track selection on prev/next track 2026-06-01 08:58:52 +02:00
Megamouse 883a6889e0 cellMusicDecode: Fix read position on prev/next track 2026-06-01 08:58:52 +02:00
Megamouse 1b5a895346 cellMusic: fix log level of "Comparing track" message 2026-06-01 08:58:52 +02:00
Megamouse 2187f8ae51 cellMusic: Fix OOB playlist access
current_track can be umax
2026-06-01 08:58:52 +02:00
Megamouse 17f1fc31bc cellMusic: fix occasional deadlock during auto-play in playlist mode 2026-06-01 08:58:52 +02:00
Elad ecf77ecef0 Add type-safe idm::last_id() overload 2026-05-31 19:54:10 +03:00
Elad db474c292f utils: Make utils::serial::operator T() explicit 2026-05-31 19:54:10 +03:00
Elad b08e805020 PPU Analyser: Fix possible infinite loop
Because of possible overflow.
2026-05-31 19:54:10 +03:00
Megamouse 7b540cdb20 cellMusic: don't send status callback if we just keep playing automatically 2026-05-31 15:49:32 +02:00
Megamouse 059e70069d cellMusic: improve music list playback 2026-05-31 15:49:32 +02:00
Megamouse 9c719a5857 trophies: fix return value of GetUnlockedPlatinumID with invalid file 2026-05-31 15:49:32 +02:00
Megamouse 375eb21c07 Fix some format and logger typos 2026-05-31 15:49:32 +02:00
Megamouse 1bd246d2d3 Fix some gcc warnings 2026-05-30 23:58:51 +02:00
Megamouse 8c82ce8bed Fix slicing warning in tar_object::save_directory 2026-05-30 19:31:16 +02:00
Megamouse eafa6a9a25 Gracefully exit after headless installation. 2026-05-30 19:31:16 +02:00
Megamouse b9d027a76c Implement fake ps move external devices 2026-05-30 15:48:56 +02:00
Megamouse 0a45c84e16 Update OpenCv to 4.13.0 2026-05-30 13:14:25 +02:00
Megamouse a88951baf3 Qt: clarify package installation during boot 2026-05-30 10:51:59 +02:00
Megamouse 66349fcb5b Qt: Move code to class 2026-05-30 10:51:59 +02:00
Megamouse c6602f673b cli: allow headless installs 2026-05-30 10:51:59 +02:00
Megamouse 7c6b3dcdb9 Update ffmpeg to 8.1.1 2026-05-29 10:23:19 +02:00
Malcolm c11979d124 Revert "SPU LLVM: Remove dead interpreter ret fallback"
This reverts commit 3058a71d72.
2026-05-29 02:24:05 +02:00
Malcolm 3058a71d72 SPU LLVM: Remove dead interpreter ret fallback 2026-05-28 14:10:51 +03:00
Arsh Kumar Singh b41b10a031 RawSPU: bound ELF loads; PPUInterpreter: fix modulo 127->128 (#18797)
Two small fixes that stop the emulator from writing past buffer
boundaries.

RawSPU: when loading a program onto an SPU processor, it now checks that
the program actually fits in 256 KB before copying it. Before this, a
corrupted file could write past the buffer into random host memory.

PPUInterpreter: a one-character typo fix. A modulo operation used % 127
instead of % 128 when computing cross-boundary data sizes. This caused
reservation checks to compare the wrong number of bytes on every load
that crossed a cache line. This fix was pointed out by @AniLeo in the
spam PR #18795 (Discovered by Opus 4.7)


The PS3 hardware enforces these limits:
- Each SPU has exactly 256 KB of memory (from Cell Broadband Engine
Handbook v1.1)
- The PPE cache line is 128 bytes — all reservation logic uses this
granularity

Neither fix should affect the usual games. They only applying to
corrupted or malformed files. Will highly appreciate feedback and
suggestions for this PR

Tested on CI: CI passes on my fork 9 of 10 platforms (Mac Intel failed
downloading a dependency).

Title and Desc written by Codex
2026-05-27 15:27:58 +00:00
Malcolm a87d175295 SPU LLVM: Retry ARM64 TBL2 register scavenger failures
- Some SPU programs inexplicably fail to compile when TBL2/TBX2 are used.
- As an insane workaround, first try to compile with TBL2/TBX2, if LLVM crashes while compiling, try to compile the same program without TBL2/TBX2.
2026-05-27 02:46:11 +03:00
Malcolm dff29a7864 PPU/SPU LLVM: Use arm shuffles in recompilers instead of emulating x86 pshufb
> - SHUFB from 9 instructions down to 5
> - Though it should be 4 if LLVM would just emit BCAX...
2026-05-27 02:46:11 +03:00
Megamouse dd81c92a02 Fix compilation on VS 2026 2026-05-24 21:05:29 +02:00
Megamouse 64ba0dd5e6 Update windows runners to visual studio 2026
Maybe this fixes current build issues...
2026-05-24 21:05:29 +02:00
Windsurf7 0a5b88ac38 Fix non-numerical numpad keys input binding (#18761) 2026-05-22 14:13:37 +02:00
Megamouse f63a34dcc2 Try to fix libusb includes 2026-05-22 09:14:16 +03:00
Megamouse d8710c431d Fix named_thread_group constructor
m_count - 1 could underflow
2026-05-22 09:14:16 +03:00
Megamouse 67464f97df Qt: only allow removal of firmware cache while emulator is stopped 2026-05-20 22:26:08 +02:00
Megamouse 190378bc33 Qt: fix batch progress bar value
It was always one too early
2026-05-20 22:26:08 +02:00
Megamouse da599f7286 Fix localization of anaglyph_settings_dialog 2026-05-20 22:26:08 +02:00
Megamouse 73c8c270a6 Fix USIO log spam 2026-05-20 22:26:08 +02:00
Elad d7da6a713b RawSPU: Implement 16-bit and 8-bit read MMIO 2026-05-20 20:44:24 +03:00
Megamouse 6a2ad0a0aa sys_usbd: enable windows hotplug 2026-05-20 18:10:01 +02:00
Megamouse 84b61e3850 Update libusb to 1.0.30 2026-05-20 18:10:01 +02:00
Malcolm 7e436f9bf1 SPU LLVM: Optimize SPU multiplies for ARM
- Saves 2 instructions in MPY, 1 instruction in MPYU, 2 instructions in MPYS, 2 instructions in MPYA, 1 instruction in MPYI, and 2 instructions in MPYUI
2026-05-20 10:54:13 +03:00
Radu13 2613d7eee7 Do not append install dir for content type theme PKGs (#18765) 2026-05-19 20:11:44 +02:00
Megamouse 55e17ccd3b Update Qt to 6.11.1 2026-05-18 22:05:33 +02:00
Megamouse f368a56c66 Update openal to 1.25.2 2026-05-18 22:05:33 +02:00
RipleyTom 79cb35daa6 Fix ethernet addr in offline mode (#18756) 2026-05-18 14:14:41 +03:00
qurious-pixel b0c1791a0c Restore .DirIcon 2026-05-18 08:29:34 +02:00
Malcolm 61a2604824 SPU LLVM: Inline reading/writing the decrementer for ARM too
- Try switching to llvm's readycyclecounter, which should map to rdtsc on x86 too.
2026-05-17 02:26:42 +03:00
Gustavo Graziano 08c926622d Visual improvements to Windows 11 Themes (#18745) 2026-05-16 18:04:15 +02:00
Megamouse b533a560e6 input: move keyboard stick value buffers to pad
This should improve stick input when using the keyboard for 2 players.
2026-05-15 07:10:40 +02:00
Megamouse e4a49cd425 input: only copy back necessary values in keyboard_pad_handler::process
Profiling has shown that copying both containers took longest.
2026-05-15 07:10:40 +02:00
Megamouse fdce82fc24 input: Use smaller external structs for button and stick access outside of the pad_thread
This needs less memory and hides unwanted members from client code.
2026-05-15 07:10:40 +02:00
Megamouse 5f00b87a44 Move bit_set to new file 2026-05-14 17:12:25 +02:00
Megamouse 53180b8141 Add bit_set unit test 2026-05-14 17:12:25 +02:00
Megamouse 4cc0e4c7fc spu_recompiler: Initialize members 2026-05-14 17:12:25 +02:00
Megamouse 46364856ae Remove unused variable 2026-05-14 17:12:25 +02:00
Megamouse f5c420994d Add unsafe bit_set access for obvious cases 2026-05-14 17:12:25 +02:00
Megamouse c5ee48f54a Harden bitset access 2026-05-14 17:12:25 +02:00
Gustavo Graziano 6f50f0854f I made some improvements to my theme to make it better (#18732) 2026-05-14 14:05:39 +02:00
Elad 53d76db753 SPU: More UB fixes 2026-05-14 13:21:23 +03:00
Elad c860aa2107 SPU Analyzer: Fix initiate_patterns function 2026-05-14 10:42:20 +03:00
Elad a545fe92a0 SPU: Log Mega SPU programs properly 2026-05-14 10:42:20 +03:00
Elad ee436307cf SPU Update 2026-05-14 10:42:20 +03:00
Elad 33016742f1 SPU: Fix CPU usage of spu_channel on BE configuration 2026-05-14 10:42:20 +03:00
Malcolm 320e8d634a SPU LLVM: Workaround bad LLVM codegen for FCGT on AARCH64
- LLVM was emitting a nasty sequence for select instead of just using BSL, so let's try using inline assembly.
2026-05-13 15:20:39 +03:00
kd-11 e52b6ecbbf rsx: Typo fix for pipeline reload hint
- Fragment state just reloads some constant buffers.
- Fragment program state recalculates the program.
- This is overkill for a variant reload, but that can be optimized later.
2026-05-13 11:43:32 +03:00
kd-11 a4712a283f C++ things 2026-05-13 11:43:32 +03:00
kd-11 6da3021158 rsx: Make emulated Z operations compatible with MSAA 2026-05-13 11:43:32 +03:00
kd-11 0064ecb85b gl: Implement emulated Z comparison support 2026-05-13 11:43:32 +03:00
kd-11 1d71854c7f rsx/qt: Add depth compare emulation to the advanced tab 2026-05-13 11:43:32 +03:00
kd-11 a9be80da6f rsx/vk: Implement software Z-buffer comparison
- Useful for scenarios where desktop precision is too high
2026-05-13 11:43:32 +03:00
capriots 2387af0854 cellDmux: fix state check 2026-05-13 07:19:09 +03:00
Phil Coulson 021f16f775 rsx: Fix swapped width/height in NV309E_SET_FORMAT decoder 2026-05-12 19:39:05 +03:00
schm1dtmac fb1c1eeaef [System] Fix restart-loops upon restart-then-quit (#18723)
I observed under macOS (although the bug seems platform agnostic to me)
that when restarting a game, then subsequently quitting it, I'd get
stuck in a 'restart loop' where the game would restart upon completely
shutting down, and this would repeat every time I tried to close the
game until I gave up & force quit RPCS3. Seems like for whatever reason,
the use of std::move didn't actually guarantee that after_kill_callback
got nuked if at all, so all I've done is take a hammer to the fucker and
manually set it to nullptr after being called.

Co-authored-by: Elad <18193363+elad335@users.noreply.github.com>
2026-05-11 17:00:45 +00:00
Edwin Jarvis 6fa7efbcc3 cellMusic: Fix shuffle always producing the same order
The shuffle in step_track() used std::default_random_engine with a
default (fixed) seed, causing the playlist to be 'shuffled' into the
same deterministic order every time. Use std::random_device to seed the
engine so each shuffle produces a genuinely random order.

Fixes #18672
2026-05-11 12:16:03 +03:00
Edwin Jarvis c534da86c3 gui: Remove close button from Game List dock widget
Users frequently close the game list accidentally and then can't
figure out how to restore it. Remove the DockWidgetClosable feature
so the close button is not shown. The game list visibility can still
be toggled via View > View Game List menu action.

Fixes #18518
2026-05-11 11:26:40 +03:00
Antonino Di Guardo 6b5a2f781a ISO: Fix missing read of remaining chunk of data on next extent, if present (#18706) 2026-05-10 16:59:15 +00:00
Arsh Kumar Singh cd7cb1cc11 Qt: include territory in language menu labels (#18704)
## Summary
Fixes the GUI language dropdown to show territory variants when
translation files include country codes.

**Before:** "Portuguese" for both `pt_BR` and `pt_PT`
**After:** "Portuguese (Brazil)" and "Portuguese (Portugal)"

Also fixes the same ambiguity for Chinese and any other language with
country variants.

## Implementation
Uses `QLocale::territoryToString()` alongside the existing
`QLocale::languageToString()` to construct display labels. When no
territory exists (e.g., `en`, `ja`), the label is unchanged.

## Edge cases
- No-territory locales (`en`, `ja`): unchanged —
`QLocale::territoryToString(AnyTerritory)` returns empty, guard
preserves plain language name
- Territory names are locale-translated (not hardcoded English) — they
follow the current UI language, same as `languageToString()`
- `zh_CN` → "Chinese (China)" / `zh_TW` → "Chinese (Taiwan)"
(territory-based, unlike the PS3 system language dropdown which uses
Simplified/Traditional script names — this is appropriate for the GUI
translator selector context)

## Test Plan
- [ ] Language menu shows "Portuguese (Brazil)" when `rpcs3_pt_BR.qm` is
present
- [ ] Language menu shows "Chinese (China)" when `rpcs3_zh_CN.qm` is
present
- [ ] Plain languages (`en`, `ja`) continue to show without parens

---

<details>
<summary>Review note: territory strings in UI locale</summary>

Territory names render in the current UI language (not forced English).
This matches `QLocale::languageToString()` behavior and is consistent
with how the rest of the menu renders. If hardcoded English labels are
preferred, that can be added after review.
</details>

Fixes #18215
2026-05-10 17:25:00 +02:00
kd-11 c0b358003f Add basic guidance on AI usage for contributors 2026-05-10 16:27:18 +03:00
brian218 29b4577fdb USIO: Implemented BanaPassport (バナパスポート) card reader emulation 2026-05-10 09:48:53 +02:00
schm1dtmac b34eba2fb3 Revert PPUThread changes
Causes GOW3 segfaults at the end of PPU linking
2026-05-09 17:56:04 +03:00
AurisDSP 6850e2e5a8 Apple Silicon: consolidate JIT W^X handling with RAII guards
Address review feedback on #18701 (cc @elad335): combine the SPU
worker fix from #18701, the SPRX Loader fix from #18703, and three
additional similar W^X leaks discovered while auditing the codebase
for the same pattern. Use Allman-style braces to match RPCS3 coding
style.

Background:
On AArch64 Apple Silicon, MAP_JIT pages enforce W^X per-thread.
pthread_jit_write_protect_np(false) enables write mode and
pthread_jit_write_protect_np(true) restores execute mode. When code
takes an early return or throws between these calls, the thread is
left in write mode, which can cause segfaults on subsequent code
fetches or inconsistent state at thread teardown.

Fixes applied (all gated on __APPLE__):

1. Emu/Cell/SPUCommonRecompiler.cpp - SPU cache worker thread
   Add RAII guard so execute mode is restored on worker exit.

2. Emu/System.cpp - SPRX Loader thread
   Enter write mode (was missing entirely) so ppu_initialize() and
   ppu_precompile() can write to MAP_JIT pages, and pair with an
   RAII guard. Reproducer: Red Dead Redemption (BLUS30418) crashes
   ~12s into boot at 0x300010000 without this fix.

3. Emu/Cell/SPULLVMRecompiler.cpp - SPU LLVM compile path
   The compile function enters write mode, then has an early
   "return nullptr" path on rebuild_ubertrampoline failure that
   skipped the explicit restore. Add RAII guard so execute mode
   is restored on every exit path. The existing explicit restore
   before the cache-flush asm directives is preserved.

4. Emu/Cell/PPUThread.cpp - PPU LLVM worker thread (operator())
   Worker entered write mode but never restored it on operator()
   return. Add RAII guard.

5. Emu/Cell/PPUThread.cpp - ppu_initialize() main path
   This scope alternates write/execute mode and contains an early
   "return compiled_new" at the empty-jits check plus a final
   return that both leak write mode. Add RAII guard so execute
   mode is always restored on exit. Intermediate explicit
   transitions for the symbol-resolver invocation are preserved.

No behavioral change on x86_64 or non-Apple ARM64 (all changes are
inside #ifdef __APPLE__ / #if defined(__APPLE__)).

Supersedes #18703.
2026-05-09 15:07:22 +03:00
RipleyTom 3c2815e89c Add option to derive MAC from PSID 2026-05-09 09:27:49 +03:00
Megamouse 4312221498 Add anaglyph settings 2026-05-08 21:42:29 +02:00
Gustavo Graziano c102d1ec0d Add missing hover effect in ‎GustavoGraziano's theme 2026-05-08 14:24:54 +00:00
Arsh Kumar Singh 98e9b3cf30 Qt: Disambiguate offset direction labels from DPad/Stick labels (#18695)
## Summary

The "Left" and "Right" strings in `pad_settings_dialog` were shared
between DPad/Stick direction labels (which have ample space) and offset
value labels — Squircle Values, Stick Multipliers, and Stick
Interpolation (where space is tight). Translators couldn't abbreviate
the offset version without also affecting the direction binding labels.

## Change

Added disambiguation via `comment="Offset direction"` in the `.ui`
file's `<string>` elements for the six offset labels:

- `label_squircle_left`
- `label_squircle_right`
- `label_stick_multi_left`
- `label_stick_multi_right`
- `left_stick_lerp_label`
- `right_stick_lerp_label`

Qt now generates separate translation entries, allowing translators to
use shorter abbreviations in the tight offset areas while keeping full
forms for direction bindings.

**English UI is unchanged** — no visual difference for English users.

Fixes #18691
2026-05-08 15:52:05 +02:00
Vishrut Sachan f9ffce76f2 game_list: Fix icon display for multi-game collection ISOs 2026-05-08 14:32:43 +02:00
Florin9doi e2e1cf02f4 USB: Stop sending USB replies after prx_unload 2026-05-08 13:05:08 +03:00
Antonino Di Guardo 026297334f Some fixes (#18686)
### FIXES + MINOR IMPROVEMENTS:
- Fixed issue18685: Not a real issue. PR18648 added some logging that made
evident a missing check on the validity of the paths in `games.yml`
before trying to manage the path as a possible ISO file. That missing
check was already present but simply invisible due to missing log
- Fixed `bytes_to_hex()` function used to store file hash hex string: In
some cases the integrity check wrongly reported the check as failed
- Optimized `fs::get_optical_raw_device()` function: Moved under
`_WIN32` block a check valid only on `Windows` and simplified the logic
to detect a CDROM / BD as raw device
- Used `iso_file` objects instead of `fs:file` in `ISO.cpp`: It allows
to use proper `bool()` operator on some checks
2026-05-07 06:38:57 +03:00
kd-11 4f47fee360 gl: Dirty tracking of interpreter sampler handles 2026-05-06 02:10:14 +03:00
kd-11 9c143d3d45 gl: Use bindless textures for the shader interpreter 2026-05-06 02:10:14 +03:00
kd-11 58eb7d2c04 gl: Implement bindless texture support 2026-05-06 02:10:14 +03:00
kd-11 2bb533274c gl: Wait for shader compilation to complete server-side before attaching to a program 2026-05-06 02:10:14 +03:00
kd-11 3fc41b6d66 gl: Fix context race condition when preparing optimized interpreter variants
- The main thread may bind the program before the compiler context has published the binding instructions
- Once bound, late bindings are not updated so the program remains with uninitialized bindings and rendering breaks
2026-05-06 02:10:14 +03:00
kd-11 c350c02592 rsx: Fix build and silence warnings 2026-05-06 02:10:14 +03:00
kd-11 9dde2bf04e vk: Skip shader cache load if we're running interpreter only 2026-05-06 02:10:14 +03:00
kd-11 4e160320ef vk/interpreter: Precompile more variants to avoid ingame stutters
- Also temporarily logs a message to make stutters more obvious
2026-05-06 02:10:14 +03:00
kd-11 ca762d5b84 vk: Add extra renderpass API entry to allow generating keys with no images 2026-05-06 02:10:14 +03:00
kd-11 d93f5f9f84 vk: Implement variant recompilation for performance 2026-05-06 02:10:14 +03:00
kd-11 ac28c391f6 vk/interpreter: Implement base pipeline propagation 2026-05-06 02:10:14 +03:00
kd-11 d519571e18 vk: Implement async interpreter builds 2026-05-06 02:10:14 +03:00
kd-11 99e6b19025 gl: C++ things 2026-05-06 02:10:14 +03:00
kd-11 3d6773dd25 vk/pipe-compiler: Allow external definition of graphics properties during async compilation 2026-05-06 02:10:14 +03:00
kd-11 d36a4bd858 vk/interpreter: Separable shader cache for the interpreter 2026-05-06 02:10:14 +03:00
kd-11 26db44b3b3 gl/interpreter: Make use of r-w locking mechanisms 2026-05-06 02:10:14 +03:00
kd-11 a6c3ef350a vk: Interpreter bringup to restore previous functionality
- Get basics working again along with some prep-work for enhancements
2026-05-06 02:10:14 +03:00
kd-11 f3d0c3ba28 gl: Implement async optimized interpreter build 2026-05-06 02:10:14 +03:00
kd-11 97c7d069ac gl: Disable hard requirement for bindless texture to use the interpreter
- I never got around to porting the implementation to use bindless correctly
2026-05-06 02:10:14 +03:00
kd-11 af7ae45888 rsx/gl: Upgrade interpreter model to use a minimal set of reusable pipelines as a base
- We generate optimized variants in the background if we have a partial miss
- A full miss is impossible
2026-05-06 02:10:14 +03:00
kd-11 5c4b0a165b gl/interpreter: Separable shader objects
- We use a real cache for separate VS and FS objects
2026-05-06 02:10:14 +03:00
kd-11 af2af23306 gl/interpreter: Implement async variant generation 2026-05-06 02:10:14 +03:00
kd-11 59f54caf08 gl: Strengthen RAII guarantees for some more objects 2026-05-06 02:10:14 +03:00
kd-11 b3ac8127ea vk: Prebuild base interpreter variants 2026-05-06 02:10:14 +03:00
kd-11 90ea92d93b gl: Fix crash when closing shader loading dialog 2026-05-06 02:10:14 +03:00
kd-11 f694000454 rsx: Fix shader interpreter variants generation 2026-05-06 02:10:14 +03:00
kd-11 b2daaff29f rsx/gl: Implement shader interpreter variant precompilation 2026-05-06 02:10:14 +03:00
Vishrut Sachan d93d9b2c5a game_list: Fix ISO cache bypass in is_from_yml branch for multi-game ISOs (#18683)
Fixes regression from #18546 and #18679.

## Problem
The is_from_yml ISO branch constructed iso_archive unconditionally,
bypassing the cache check inside add_game, making the cache write-only
for yml-sourced ISOs.

## Fix
Added a lightweight index cache entry (iso_path + "//index") storing the
subdir list + mtime. On hit, skips archive construction entirely. On
miss, walks as before and writes the index
2026-05-05 11:14:16 +02:00
MarkosTh09 4f23f5505a fix minor issues with rpath logic 2026-05-05 09:47:20 +09:00
MarkosTh09 69bc124930 add /opt/homebrew to prefix path instead
Append to `CMAKE_PREFIX_PATH` and `CMAKE_SYSTEM_PREFIX` instead of using `include_directories()` and `link_directories()`.

This avoids include/link path
pollution and reduces clashes with vendored dependencies. This means that conflicting homebrew packages no longer have to be unlinked to build rpcs3, as system paths are searched last.
2026-05-05 09:47:20 +09:00
MarkosTh09 056a8ba1b1 macos: hack to fix homebrew rpath issues 2026-05-05 09:47:20 +09:00
Megamouse 350e7b09f5 Update SDL to 3.4.8 2026-05-04 18:18:32 +02:00
Megamouse 1899b61fa5 Update FAudio to 26.05 2026-05-04 18:18:32 +02:00
xperia64 773808169e Move multi-game ISO parsing code path to proper location 2026-05-04 06:57:12 +02:00
Haxy a6fb0c8931 Restore 0.85 compatibility for PS3 Binary Decryption
0.85 elfs break here due to them not having a version header offset.

Very unlikely these elfs will ever load but its useful to be able to decrypt them at least.

Co-authored-by: Elad <18193363+elad335@users.noreply.github.com>
2026-05-03 14:27:10 +03:00
schm1dtmac 155883ea2a Update MoltenVK and restore Game Mode support (as the relevant MVK regression has been fixed) 2026-05-03 12:38:42 +02:00
Vishrut Sachan ca87d103fd game_list: Add multi-game collection support for ISO format discs 2026-05-03 10:14:44 +03:00
Elad 5dd7792c3d Remove vm_ref.h 2026-05-03 08:03:28 +03:00
Megamouse e8cd6f4ef6 Win: Update ffmpeg to 8.1 2026-05-02 08:24:33 +02:00
schm1dtmac b6ed4201df Correct the MVK build config 2026-05-01 05:38:31 +02:00
Megamouse c25e3e33ac Update curl to 8.20.0 2026-04-30 20:41:35 +02:00
Megamouse a8dd053593 Fix some warnings 2026-04-30 20:41:35 +02:00
Megamouse 66d2bc3dd5 Qt: Add timeouts and abort logic to downloader 2026-04-30 20:41:35 +02:00
kd-11 d773a3f94e ci: Bump docker version and use rpcs3 dropbox URL for vulkan SDK 2026-04-30 18:58:08 +03:00
kd-11 4e0995744a vk: Fix shader interpreter 2026-04-30 18:58:08 +03:00
kd-11 80c1c5dd6d vk: Use bulk allocator for FP and VP constant blocks to reduce wastage 2026-04-30 18:58:08 +03:00
kd-11 91a8c56c37 vk: Use UBO for basic transform constants
- Instancing still uses SSBOs, but that's ok. 95% of draw calls are uninstanced anyway.
2026-04-30 18:58:08 +03:00
kd-11 5144a999b3 vk: Use UBO for vertex environment block 2026-04-30 18:58:08 +03:00
kd-11 3b1abec405 rsx/vk: Implement bulk aligned allocator
- Avoids wasting space and allows use of natural arrays in shaders
2026-04-30 18:58:08 +03:00
kd-11 8b02f46e67 rsx: Handle edge case where window size falls outside memory range 2026-04-30 18:58:08 +03:00
kd-11 41e6cbbb5a vk: Fix build on linux CI using outdated SDK 2026-04-30 18:58:08 +03:00
kd-11 ed7f1aa17c vk: Fix UBO alignment mismatch on fragment constants binding
- Fragment constants are of dynamic size, so this isn't totally unexpected.
- NVIDIA GPUs have terrible UBO alignment requirements
2026-04-30 18:58:08 +03:00
kd-11 5839ad9c70 vk: Use UBOs for critical per-fragment data 2026-04-30 18:58:08 +03:00
kd-11 9c725935dc CI: use dropbox for vulkan SDK installer 2026-04-30 18:58:08 +03:00
kd-11 108276c076 vk/data-heap: Implement a caching sliding window buffer view system 2026-04-30 18:58:08 +03:00
kd-11 82a4ff051e vk: Upgrade Vulkan SDK to 1.4.341 2026-04-30 18:58:08 +03:00
kd-11 d1c8073106 rsx: Upgrade SPIRV generation to SPIRV 1.5 for vulkan. 2026-04-30 18:58:08 +03:00
kd-11 3829802699 3rdparty: Update glslang to release 16.2.0 2026-04-30 18:58:08 +03:00
kd-11 46bbbd2056 vk: Fix missing TRANSFER->FRAGMENT barrier for shadowed blocks 2026-04-30 18:58:08 +03:00
kd-11 cbbf52878a vk: Simplify manual heap flush for shadowed blocks 2026-04-30 18:58:08 +03:00
kd-11 278daddd2a rsx/util: Add a simple wrapper for simple_array<T>::for_each
- Just calls std::for_each behind the scenes but it's a lot more convenient
2026-04-30 18:58:08 +03:00
kd-11 48e2d808c9 rsx/util: Add simple_array<T>::filter 2026-04-30 18:58:08 +03:00
kd-11 28ab991302 vk: Support more heap types
- Support for GPU shadow heap (manual flush)
- Support for Fixed-Length data heaps
2026-04-30 18:58:08 +03:00
kd-11 d40a4ee04e vk: Add iGPU detection 2026-04-30 18:58:08 +03:00
Megamouse 7ea0a96d02 Only set db_config if we boot with a title_id 2026-04-30 14:15:29 +02:00
Megamouse 985703058b ISO: Improve path logging 2026-04-30 14:15:29 +02:00
Megamouse 14ca11fdf4 Fix uninizialized member warning 2026-04-30 14:15:29 +02:00
Megamouse 7bc08d05c6 Add some more logging for use of database config 2026-04-30 14:15:29 +02:00
digant73 79d0e0eb3c reduce check for raw device 2026-04-30 10:39:10 +03:00
digant73 570b2bddca Avoid to open bd drive if no content is present 2026-04-30 10:39:10 +03:00
schm1dtmac f3cf1da7b7 Revert "Fix race condition in memory_decommit/memory_reset on Apple ARM64"
This reverts commit 4cac76caad.
2026-04-29 20:26:49 +03:00
schm1dtmac 3c9a4417f2 Revert "Remove redundant Apple ARM64 ifdef blocks"
This reverts commit e1734b51c3.
2026-04-29 20:26:49 +03:00
Antonino Di Guardo b212935c70 Add support to play from a BD Drive (currently only on Windows) (#18648)
Follow up of #18345 to add support (currently only on `Windows`; see
notes below) for playing a PS3 disc game directly from a Blu-Ray Disc
Drive.

### HOW IT WORKS:
- The BD drive can be added as any other game so from `VFS games` or
from `Add Games` menu. In case it is selected from `VFS games`, any
attempt to write files is discarded, e.g. file `Disc Games Can Be Put
Here For Automatic Detection.txt`
- It scans the default redump keys folder `<rpcs3>/data/redump` (it
currently needs to be manually created due it is not yet provided by
rpcs3 installation) to find a matching decryption key

### NOTES:
- Support is currently provided on `Windows` where I can fully test it.
I cannot test under other OS. However, the additions needed for the
other OS are limited only on `fs::file.h/cpp`. In particular inside the
following new functions:
  - `bool is_optical_raw_device(const std::string& path);`
- `bool get_optical_raw_device(const std::string& path, std::string*
raw_device = nullptr);`
- Icons etc. are always refreshed (ISO cache cannot be used due `mtime`
on raw device is not available and any cache check would always fail)
- Code in `ISO.h/cpp` needed some rework to properly manage a read on a
raw device (alignment on offset, size and memory is mandatory). The BD
drive needs to be detected as a file, not as a folder

### MINOR FIXES:
- Fixed wrong specifier used in logging on `ISO.h/cpp`
2026-04-29 05:42:49 +03:00
shinra-electric e05d359721 Bump setup-nuget to v4 2026-04-28 19:34:31 +02:00
Megamouse 74bcbd9816 Update 7zip to 26.01 2026-04-28 12:33:45 +02:00
Megamouse b14c76c1c5 Qt: fix memory leak in confg_database 2026-04-28 10:00:00 +02:00
Luca Silva 6d058586c4 USB: remove zero-length IN URB fake-completion workaround
The passthrough path was fake-completing zero-length bulk/interrupt IN
URBs to mirror the emulated path, so games that drain-poll between
transfers wouldn't stall the libusb worker thread. This baked an
emulator-side assumption about device behaviour into the host stack.

Real PS3 USIO devices issue a ZLP on the read endpoint after any
transfer that would otherwise leave the host's drain URB outstanding
(notably after a CMD_WRITE). Devices that follow the same protocol
(e.g. ITAIKO firmware) are expected to do the same; faking it here
masks firmware bugs and diverges from real hardware behaviour.

Drop the workaround and let libusb submit the URB normally. The IN
URB now completes when the device sends its ZLP, matching real PS3.

Refs: https://github.com/RPCS3/rpcs3/pull/18636#discussion_r3143290987
2026-04-28 08:55:06 +02:00
Luca Silva 771837ee55 USB: use libusb enums for endpoint bitmask checks
Address review: replace raw 0x80 / 0x03 / 0x02 with LIBUSB_ENDPOINT_IN,
LIBUSB_TRANSFER_TYPE_MASK, and LIBUSB_TRANSFER_TYPE_BULK.
2026-04-28 08:55:06 +02:00
Luca Silva d2fc5a5b81 USB: fix passthrough of bulk endpoints on Linux
The PS3 USB stack routes both bulk and interrupt requests through
usb_device_passthrough::interrupt_transfer. The old code unconditionally
called libusb_fill_interrupt_transfer regardless of the endpoint type,
which on Linux's usbfs backend is rejected with EINVAL whenever the URB
type doesn't match the endpoint's bmAttributes. Reads against bulk IN
endpoints therefore never completed and the worker thread stalled.

Look up the endpoint descriptor and dispatch to libusb_fill_bulk_transfer
when bmAttributes indicates bulk. A new usb_device::find_endpoint helper
walks the descriptor tree once per submission.

Confirmed broken on Linux and working on Windows pre-fix; macOS
untested. WinUSB silently accepts the type mismatch, which is why this
went unnoticed. Reproduces with any passthrough device that has a bulk
IN endpoint, e.g. a Bandai Namco USIO (0b9a:0910) under Taiko no Tatsujin
S111 [SCEEXE000]: the game's boot-time the I/O board check runs and then hangs.

Two related defensive fixes uncovered while debugging the above:

- Zero-length bulk/interrupt IN URBs hang in libusb until the device
  sends a ZLP. The emulated path fake-completes them immediately; mirror
  that here so drain-polls between transfers don't stall the worker.

- An unexpected libusb_submit_transfer error used to leave
  UsbTransfer::busy = true forever. Mark it as a fake completion with
  EHCI_CC_HALTED so the USB manager processes the failure cleanly
  instead of deadlocking the request.
2026-04-28 08:55:06 +02:00
Megamouse 3b21833b8e Don't use Qt event loop in headless mode 2026-04-27 23:13:11 +02:00
Mack Core ed02f3a2ce RSX: workaround for color/depth aliasing heurestic edge case failures (#18644)
The color/depth alias collapse heuristic at get_framebuffer_layout()
picks depth whenever depth_test_enabled or stencil_test_enabled is set.
But test means read, not write - so deferred renderers that run a
Z-prepass and then a G-buffer pass with depth-test ON, depth-write OFF,
color-write ON get classified as a depth pass even though they're
writing color. The color writes get silently dropped.

In Starhawk this killed every lit surface - characters, skybox, anything
that goes through the deferred lighting path rendered black or
invisible. Terrain, particles, and emissive geometry kept working
because they don't go through that same pipeline.

Fix is to check writes instead of tests:

    if (zeta_write_enabled && !color_write_enabled)
        keep_as_depth();  // Z-prepass, shadow gen
    else
        keep_as_color();  // G-buffer / lit pass

For the both-writes case I went with color since losing color is much
more obvious visually than losing depth (the engine's Z-prepass usually
still has the depth around).

Tested with Starhawk [BCUS98181] in menu and gameplay - before: missing
characters and sky. After: matches PS3 reference. Logged every aliasing
event during a couple minutes of gameplay and they were all the G-buffer
pattern (depth-test ON, depth-write OFF, color-write ON), all handled
right. The depth-keep branch wasn't actually exercised in Starhawk, but
it's there for games that do use a Z-prepass to an aliased buffer.

Fixes #11877.
2026-04-27 21:59:45 +02:00
score3229 7028e85fac RSX Debugger: populate Captured Draw Calls list
The constructor inserted rows into m_list_captured_frame for the command queue but never did the same for m_list_captured_draw_calls. GetMemory()'s setItem() loop for draw calls then no-op'd on missing rows, so the Captured Draw Calls tab always appeared empty after a frame capture.
2026-04-27 07:16:29 +03:00
capriots 96f73f4497 cellVdec: fixup 2026-04-25 19:51:09 +03:00
capriots 3ff4f188f4 cellVdec: fixup 2026-04-25 18:52:40 +03:00
capriots 61e1c0f1fb cellVdec: reimplement cellVdecQueryAttr() 2026-04-25 15:48:50 +03:00
capriots 631e7ef979 Stub video decoder modules 2026-04-25 15:48:50 +03:00
Elad 7dce197ec4 sys_spu: Fix sys_isolated_spu_create 2026-04-25 08:16:32 +03:00
Haxy 9e0824112f Fix checks for flash device aliases 2026-04-25 07:43:17 +03:00
Daniel Risto e1734b51c3 Remove redundant Apple ARM64 ifdef blocks
The Apple ARM64 code paths are now identical to the generic case,
so the ifdef blocks are unnecessary.
2026-04-24 20:15:41 +03:00
Daniel Risto 4cac76caad Fix race condition in memory_decommit/memory_reset on Apple ARM64
The previous approach used munmap followed by mmap without MAP_FIXED
(since Apple rejects MAP_FIXED | MAP_JIT). Between the two calls,
another thread could claim the unmapped address range, causing mmap
to return a different address and triggering a fatal verification error.

Under concurrent load (e.g. PPU LLVM compilation with many worker threads),
this race manifests reliably as "Verification failed (object: 0x0)" crashes
across all PPUW threads in memory_decommit.

Fix: Use MAP_FIXED without MAP_JIT instead. This atomically replaces the
mapping without any window for other threads to interfere. The MAP_JIT
attribute is lost on the replaced pages, but the application's code signing
entitlements (allow-unsigned-executable-memory, disable-executable-page-protection)
permit executable mappings without it.

Applied the same fix to memory_reset which had the identical pattern.
2026-04-24 20:15:41 +03:00
Kravickas 077f2a73e1 Add clickable PR links in update changelog (#18465)
Replaces the plain text "Show Details" changelog in the update dialog
with a QTextBrowser that converts PR references like (#1234) into
clickable GitHub links.

**Why? :** Easy to check PRs you are interested in beyond the title.
2026-04-24 16:37:17 +00:00
kd-11 a60734e27b gl: Fix crash when renderdoc compatibility is enabled on some hardware 2026-04-24 15:00:27 +03:00
kd-11 3574677b66 rsx: Fix deswizzle of wide texel formats 2026-04-24 15:00:27 +03:00
zeph b3cc01387f Clans: PPU thread sleeping while making API calls
Signed-off-by: zeph <zephyrzefa15@gmail.com>
2026-04-23 13:31:01 +03:00
Antonino Di Guardo e26c80c129 Add ISO integrity check 2026-04-22 15:06:02 +02:00
Elad 80b6faef10 SPU Analyzer: Add some logging 2026-04-22 09:59:14 +03:00
Elad 7d0df300ea SPU LLVM: Fix RCHCNT write channel looping 2026-04-22 09:59:14 +03:00
Elad 1ca8ab393a sys_fs: Fix O_CREATE and O_TRUNC for BDVD 2026-04-21 15:43:41 +03:00
Caleb 3cca094d2d Enhance FAudio backend error handling and initialization processes (#18614) 2026-04-21 08:32:58 +00:00
digant73 a088b18e95 Add missing default setting after settings rework 2026-04-21 00:50:05 +02:00
digant73 e5af69d7b2 Add recursive scan support 2026-04-20 22:04:54 +03:00
Megamouse 814821d760 SPU LLVM: Fix unused variable warnings
- Remove unused block_optimization_exit_early
- Pass mod_val instead of value
2026-04-20 14:12:18 +00:00
Megamouse 7a8c88056a Qt: make emu_settings_type cfg_location dynamic 2026-04-19 22:51:13 +02:00
Megamouse b6b043b23d qt: remove unused emu_settings_type 2026-04-19 22:51:13 +02:00
Megamouse c5db9899e4 cfg: remove redundant prefix from variable names 2026-04-19 22:51:13 +02:00
qurious-pixel 081c094c5a Improve Mac Translation download (#18598)
Switch from API to direct download.
Fixes 403 error code.

jammy-aarch64 needs `unzip` from universe in the Dockerfile.

eg:
```
add-apt-repository universe
apt update -qq
apt-get install -y unzip
```

Fixes https://github.com/RPCS3/rpcs3/issues/18560
2026-04-19 20:42:31 +02:00
digant73 b6c8374aa5 Fix region range check on ISO file 2026-04-19 19:23:37 +03:00
Megamouse 06a6880c6c Qt: Allow to compare configurations in gamelist context menu 2026-04-19 16:21:41 +02:00
Megamouse bd5c10fd48 Fix database config application in continuous mode 2026-04-19 15:25:57 +02:00
Megamouse d3804de243 Loader: try to retrieve database config if not booted through the UI 2026-04-19 15:25:57 +02:00
Megamouse 36cd81fb5f Log url of downloader 2026-04-19 15:25:57 +02:00
Megamouse 7a4d7aa936 Improve quit logging 2026-04-19 15:25:57 +02:00
Megamouse aff082e0c1 version_check: fix formating of result output 2026-04-19 15:25:57 +02:00
Megamouse 2b144101ab Update libpng to 1.6.58 2026-04-19 15:25:57 +02:00
digant73 d4b7c31ec8 Allow to skip optional packages installation
minor cleanup

minor cleanup
2026-04-19 14:38:48 +02:00
oltolm 6c9445ff28 cmake: do not disable "WOLFSSL_HARDEN" and fix build with latest WolfSSL 2026-04-19 13:10:25 +02:00
Megamouse 2d6ca912fe Qt: fix regular boot without database config 2026-04-18 17:57:11 +02:00
digant73 3b9cc0bc3a fix wrong folder creation 2026-04-17 00:48:27 +02:00
Megamouse 88175aa84f Check for unknown keys during config validation 2026-04-16 21:28:34 +02:00
Elad a7c606c8ac sys_fs: Add unit tests 2026-04-16 11:04:37 +03:00
Elad 48acbbe4f5 sys_fs: Reimplement path analysis 2026-04-16 11:04:37 +03:00
schm1dtmac b729772041 Try building MVK 1.4.2 privapi instead of DLing 1.4.1 privapi prebuilt 2026-04-16 03:08:31 +02:00
Megamouse ea0d8a0d78 Qt/input: fix mouse button names
I tested the last PR with an older commit and thought it worked.
2026-04-16 02:10:41 +02:00
Megamouse e79f56bfdc Qt: put Custom Config with Database Settings first 2026-04-16 01:18:05 +02:00
Megamouse a543f38704 linux/macOs: update compat_db and config_db on update 2026-04-16 01:18:05 +02:00
Megamouse 1cdc401cc5 Use database per default, remove global 2026-04-16 01:18:05 +02:00
Megamouse 50d6396f99 implement config db 2026-04-16 01:18:05 +02:00
Megamouse 7d41bbdd2b Fix Disk Usage thread 2026-04-15 13:05:54 +02:00
Megamouse d53a6a87f6 Update wolfssl to 5.9.1 2026-04-15 13:05:54 +02:00
Megamouse 1e63385dfc Update libpng to 1.6.57 2026-04-15 13:05:54 +02:00
schm1dtmac 4ffeee0344 Opt out of Game Mode on macOS due to throttling 2026-04-15 03:02:23 +03:00
Megamouse 0b9c53e254 Qt: Add missing thread name 2026-04-14 11:53:41 +02:00
Megamouse 27f857a8fe Remove unused variable in find_dialog 2026-04-14 11:53:41 +02:00
Megamouse c340eb2f17 Remove nop loop 2026-04-14 11:53:41 +02:00
Megamouse e43435e152 Remove unused should_have_argument_increment 2026-04-14 11:53:41 +02:00
Megamouse ac3d14cf5c Add missing fmt case for cpu_flag::req_exit 2026-04-14 11:53:41 +02:00
Megamouse 426f3c445b Remove some unused variables 2026-04-14 11:53:41 +02:00
Megamouse bbeb7c4cd7 Remove sneaky pragma optimize 2026-04-14 11:53:41 +02:00
Megamouse 6c87413f48 SPURecompiler: add missing default 2026-04-14 11:53:41 +02:00
Megamouse b10c742f10 iso stuff 2026-04-14 09:37:18 +02:00
Megamouse 9e3e01d8f6 input: fix mouse+kb combos 2026-04-14 08:44:20 +02:00
luci4 bcd9663349 Thread.cpp: Added stack trace and register logging to exception filter (#18564) 2026-04-13 15:47:44 +03:00
Megamouse 72fa4098dc Add 3D screen size setting 2026-04-12 17:59:01 +02:00
luci4 6dc95dd078 sysinfo.cpp: Remove registry helpers (#18557)
The "caveat" is that said API bypasses compatibility shims so it will
always show the true Windows version.
2026-04-12 13:53:00 +03:00
Vishrut Sachan 11050a7032 ISO: Add metadata cache to speed up game list scanning (#18546)
Every launch constructs a fresh iso_archive for each ISO game, which
calls iso_form_hierarchy() and walks the full directory tree. On top of
that, qt_utils opens a second iso_archive just for icon loading, so
every ISO game ends up doing two full directory tree walks on every
launch.
This adds a metadata cache keyed by ISO path + mtime stored under
fs::get_config_dir()/iso_cache/. Each entry stores the raw SFO binary,
resolved icon/movie/audio paths and raw icon bytes.

- On cache hit, iso_archive construction is skipped entirely for both
game list scanning and icon loading
- On cache miss archive is scanned as before and the result is persisted
to disk
- Cache is automatically invalidated when the ISO file's mtime changes

Tested with a decrypted PS3 disc ISO (God of War III):

- First launch writes cache files correctly to iso_cache/
- Second launch reads from cache with correct title and icon
- touch game.iso correctly invalidates the cache and triggers a rescan
2026-04-12 09:07:25 +00:00
Ani f826f95c70 gui: Group CPU accuracy settings together 2026-04-11 22:08:19 +02:00
luci4 934bc34685 sysinfo.cpp: Replaced PEB read with ntdll function (OS version read) 2026-04-11 22:28:11 +03:00
oltolm f463c128e3 Fix ASan fatal-report access during emulator teardown 2026-04-11 14:01:38 +03:00
oltolm 7168cd566a Fix ASan logger shutdown use-after-free 2026-04-11 14:01:38 +03:00
digant73 110c786d80 Add support to encrypted ISO
fix compile errors and suppress minor warnings

strip minor bug fixes and cleanup

minor cleanup

minor cleanup

Fixed crash at boot parsing an ISO with a empty directory entry at file head

applied suggested changes

added missing break in switch

Update rpcs3/Loader/ISO.cpp

Co-authored-by: Megamouse <studienricky89@googlemail.com>

use hex_to_bytes() instead of self made functions

minor cleanup

rewrite nested if()

add explicit support to .key

minor cleanup

optimize partial sector read

minor optimization

fix conflict
2026-04-10 22:41:05 +02:00
oltolm fb194241d5 fix LLVM assert in use_begin 2026-04-10 19:29:23 +03:00
kd-11 09554c43ba rsx: Allow DXT texture decoding to gracefully fall back to unaligned memory addresses 2026-04-10 13:06:52 +02:00
kd-11 1b1143094e rsx: Improve handling of aligned memory 2026-04-10 13:06:52 +02:00
Ani 8121bd443c ppu: Enable vector NaN fixup by default 2026-04-10 02:26:42 +02:00
oltolm a1a140db91 CPUThread: fix ASAN use-after-free 2026-04-09 23:20:56 +03:00
Megamouse e9fb3572f9 Qt: Show error if any package is corrupt before installation 2026-04-09 12:09:06 +02:00
Katalin Rebhan 1d85de6236 Include <pthread.h> for pthread_self
Fixes compilation on Gentoo Linux with clang/libc++ 21.
2026-04-09 03:27:19 +03:00
Megamouse 6981e308a0 Update docker to 1.11
Update SDL to 3.4.4
Update ffmpeg to 8.1
Update opencv to 4.13
2026-04-08 12:19:58 +02:00
Megamouse 8e80ddd99f ISO: optimize some file reads (#18511)
- Batch some file reads in iso_read_directory_entry (speeds up indexing
by ~41% on my test iso)
- Fix some warnings
2026-04-08 10:52:03 +02:00
Ani beac01d5d1 SPU: Remove RCHCNT loop handling of SPU_WrOutIntrMbox 2026-04-07 15:19:48 +03:00
Ani d7f8e25cca SPU: Remove RCHCNT loop handling of SPU_WrOutMbox
Fixes freezing in Half-Life 2

Fixes #17958
2026-04-07 15:19:48 +03:00
kd-11 e0c3df5328 vk: Fix crash when running CPU detiler path 2026-04-07 04:11:25 +02:00
kd-11 59468f1e1e vk: Handle WAW and RAW hazards when performing "flush" operations 2026-04-07 04:11:25 +02:00
kd-11 8ab0ceaa67 vk: Wrap scratch buffer access with proper memory barriers 2026-04-07 04:11:25 +02:00
kd-11 b9f05ba71b vk: Insert a all_commands->transfer barrier before copying query results to scratch 2026-04-07 04:11:25 +02:00
kd-11 5311f004d0 vk: Insert barriers on the scratch buffer when detiling memory 2026-04-07 04:11:25 +02:00
kd-11 cb276f0da7 vk: Insert transfer->transfer barriers before creating aggregates 2026-04-07 04:11:25 +02:00
kd-11 34c26eff68 rsx: Extend cubemap_unwrap decode to handle cubemaps with mipmaps 2026-04-06 14:21:40 +02:00
kd-11 b700a7abd9 gl: Extend mipcount support for reconstructed images 2026-04-06 14:21:40 +02:00
kd-11 c279583f1f vk: Allow cubemap unwrap to generate more than 1 mipmap level 2026-04-06 14:21:40 +02:00
kd-11 ce12c29441 rsx: Allow negative src and dst pitch in nv0039
- Confirmed with hardware tests
2026-04-05 18:18:44 +02:00
capriots e95a980651 cellDmux: process PPU state after lv2 syscalls 2026-04-05 17:42:45 +03:00
capriots 87fc45d661 cellDmuxPamf: logging fix 2026-04-05 17:42:45 +03:00
capriots 93a2f1edc8 cellPamf: remove unused code 2026-04-05 17:42:45 +03:00
capriots 22fe8648ef cellDmux implementation 2026-04-05 17:42:45 +03:00
Daniel Risto f42b09d1fc Fix SPRX Loader segfault on Apple ARM64 by excluding MAP_JIT from memory mapping regions
On Apple ARM64, memory_reserve unconditionally applies MAP_JIT to all
reservations. However, regions marked as is_memory_mapping are later
replaced by file-backed MAP_FIXED mappings via shm::map/map_critical.
Overlaying a file-backed MAP_FIXED mapping onto a MAP_JIT region causes
the resulting pages to be inaccessible, leading to a segfault when the
SPRX Loader attempts to write module data into PS3 memory (g_sudo_addr).

Fix: Only apply MAP_JIT for non-mapping regions. Memory mapping regions
don't need JIT capability since they use shared memory for the PS3
address space, not executable JIT code.

Tested on Apple M3 Max, macOS 26.3.1. The SPRX Loader now successfully
loads all modules and emulation proceeds past the loading stage.
2026-04-05 14:47:24 +03:00
Megamouse b8e6785789 Update soundtouch to 2.4.1 2026-04-05 11:14:11 +02:00
Megamouse afdb57bee9 Update FAudio to 26.04 2026-04-05 11:14:11 +02:00
Sanjay Govind b22d0a0b4a Bump SDL to 3.4.4 2026-04-05 10:07:49 +02:00
Vishrut Sachan ec989781a3 game_list_table: Allow reordering game list columns 2026-04-04 18:47:12 +02:00
oltolm 110ef818b3 PPU LLVM: fix compilation for PPU Debug option 2026-04-04 11:57:50 +00:00
Elad aa841ac332 LLVM: Add source location for bitcast error 2026-04-04 01:08:05 +03:00
Elad ceb2168375 SPU LLVM: Remove debug code 2026-04-04 01:08:05 +03:00
Elad 02eb549208 SPU LLVM: Fix register updates in second block of Reduced Loop 2026-04-03 18:33:07 +03:00
Elad 13de8233b0 SPU Analyzer: Fix register origin for Reduced Loop 2026-04-03 18:33:07 +03:00
Elad ff4444b18e Silence cellNetCtlGetInfo 2026-04-03 18:33:07 +03:00
Elad 555ace0955 rsx: Silence Unknown render mode error 2026-04-03 18:33:07 +03:00
Elad a03a78dbd2 SPU: Verify SPU Reduced loop completability 2026-04-03 18:33:07 +03:00
BehroozRezvani f63b1b5dc1 Fixes PS button wrong input path check 2026-04-02 19:42:15 +03:00
Elad ac104a519e SPU Analyzer: More fix 2026-03-31 21:56:30 +02:00
Elad bd95c728f0 sys_fs: Implement ENOTDIR 2026-03-31 21:56:30 +02:00
kd-11 122ccca50e rsx: Propagate texture flags when MSAA sampling is required 2026-03-31 02:51:03 +02:00
kd-11 83bfbc1399 rsx/fp: Fix lane mask computation for varying registers 2026-03-31 02:51:03 +02:00
Elad 80f972cd38 SPU Analyzer: Acknowledge unknown targets 2026-03-31 02:12:40 +02:00
Elad 615f416af3 SPU Anlyzer: Verify bitset index 2026-03-31 02:12:40 +02:00
kayforbe 1c37f64a58 Add missing <set> include for MSVC compatibility (#18486)
Added missing #include <set> in shortcut_utils.h to fix MSVC build
errors (C2059)
2026-03-30 17:47:52 +00:00
Megamouse 7c9261a461 overlays: add toggle for boot sequence music 2026-03-30 18:40:34 +02:00
Megamouse cb3a83cba7 overlays: add setting for fatal error hint (disabled by default) 2026-03-30 18:40:34 +02:00
Megamouse b0dbac9a07 Qt: hide log frame by default 2026-03-30 18:40:34 +02:00
Megamouse f92677fa7e rsx: Add missing break 2026-03-30 14:26:50 +02:00
Megamouse 789bab97be Qt: add some sorting options to the screenshot manager 2026-03-30 14:26:50 +02:00
Megamouse 3e60bd2aa6 steam: disable steam input for shortcuts 2026-03-30 12:41:19 +02:00
397 changed files with 17181 additions and 5882 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ cmake .. \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SDL=ON \
-DUSE_SYSTEM_SDL=ON \
-DUSE_SYSTEM_FFMPEG=OFF \
-DUSE_SYSTEM_FFMPEG=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
+1 -1
View File
@@ -46,7 +46,7 @@ cmake .. \
-DUSE_SYSTEM_CURL=ON \
-DUSE_SDL=ON \
-DUSE_SYSTEM_SDL=ON \
-DUSE_SYSTEM_FFMPEG=OFF \
-DUSE_SYSTEM_FFMPEG=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_DISCORD_RPC=ON \
-DOpenGL_GL_PREFERENCE=LEGACY \
+3
View File
@@ -19,6 +19,9 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt
# Restore App Icon
ln -sr ./AppDir/rpcs3.svg ./AppDir/.DirIcon
# Remove libwayland-client because it has platform-dependent exports and breaks other OSes
rm -f ./AppDir/usr/lib/libwayland-client.so*
+39 -21
View File
@@ -4,11 +4,16 @@
cd build || exit 1
cd bin
git clone --revision=a075e5e417f87675ea3137b7365f3e5a99608d72 https://github.com/KhronosGroup/MoltenVK.git
cd MoltenVK
./fetchDependencies --macos
sudo xcode-select -switch /Applications/Xcode_16.2.app/Contents/Developer
make macos MVK_USE_METAL_PRIVATE_API=1
cd ../
mkdir -p "rpcs3.app/Contents/Resources/vulkan/icd.d" || true
wget https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/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"
cp "MoltenVK/Package/Release/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib" "rpcs3.app/Contents/Frameworks/libMoltenVK.dylib"
cp "MoltenVK/Package/Release/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"
@@ -27,21 +32,18 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
# Download translations
mkdir -p "rpcs3.app/Contents/translations"
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
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" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \
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
echo "Successfully downloaded translations."
if unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1; then
rm -f translations.zip
else
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
rm -f translations.zip
fi
else
echo "Warning: Failed to download translations. Skipping..."
fi
# Copy Qt translations manually
@@ -55,9 +57,25 @@ rm -f rpcs3.app/Contents/translations/qt_help_*.qm || true
mv rpcs3.app RPCS3_.app
mv RPCS3_.app RPCS3.app
# Hack
install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || true
install_name_tool -delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 || true
# 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
+1
View File
@@ -24,6 +24,7 @@ mkdir ./bin/config
mkdir ./bin/config/input_configs
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -f ISO-8859-1 -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -f ISO-8859-1 -t UTF-8 1> ./bin/GuiConfigs/config_database.dat
# Download translations
mkdir -p ./bin/share/qt6/translations
+1
View File
@@ -14,6 +14,7 @@ mkdir ./bin/config
mkdir ./bin/config/input_configs
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
curl -fsSL 'https://api.rpcs3.net/config/?api=v1' | iconv -t UTF-8 1> ./bin/GuiConfigs/config_database.dat
# Download translations
mkdir -p ./bin/qt6/translations
+2 -2
View File
@@ -16,7 +16,7 @@ QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia$
QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}"
LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/olpvqk4346ig7i8btadk5/vulkansdk-windows-X64-${VULKAN_VER}.exe?rlkey=huvssch6sy904qkg8ti9p65br&st=pztptdin&dl=1"
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip"
DEP_URLS=" \
@@ -88,7 +88,7 @@ for url in $DEP_URLS; do
*qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;;
*llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./build/lib_ext/Release-x64" ;;
*ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;;
*Vulkan*)
*vulkansdk*)
# Vulkan setup needs to be run in batch environment
# Need to subshell this or else it doesn't wait
download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName"
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
Windows_Build:
if: github.event_name == 'workflow_dispatch'
name: LLVM Windows (MSVC)
runs-on: windows-2025
runs-on: windows-2025-vs2026
env:
COMPILER: msvc
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
+11 -11
View File
@@ -30,23 +30,23 @@ jobs:
matrix:
include:
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.10"
docker_img: "rpcs3/rpcs3-ci-jammy:1.14"
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:1.10"
docker_img: "rpcs3/rpcs3-ci-jammy:1.14"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.10"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.14"
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:1.10"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.14"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
@@ -134,7 +134,7 @@ jobs:
runs-on: macos-14
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.11.0'
QT_VER: '6.11.1'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
@@ -209,16 +209,16 @@ jobs:
# Only run push event on master branch of main repo, but run all PRs
if: github.event_name != 'push' || (github.repository == 'RPCS3/rpcs3' && github.ref_name == 'master')
name: RPCS3 Windows
runs-on: windows-2025
runs-on: windows-2025-vs2026
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.11.0'
QT_VER: '6.11.1'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202603180535'
QT_DATE: '202605090529'
LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'
VULKAN_VER: '1.4.341.1'
VULKAN_SDK_SHA: 'bcf2d75aa9556889ab974858666e20b3655b6055a0db704ccb47279ff33b5bfe'
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
CCACHE_BIN_DIR: 'C:\ccache_bin'
CCACHE_DIR: 'C:\ccache'
@@ -235,7 +235,7 @@ jobs:
fetch-depth: 0
- name: Setup NuGet
uses: nuget/setup-nuget@v2
uses: nuget/setup-nuget@v4
- name: Restore NuGet packages
run: nuget restore rpcs3.sln
+15 -15
View File
@@ -7,24 +7,24 @@ if(USE_SYSTEM_CURL)
target_link_libraries(3rdparty_libcurl INTERFACE CURL::libcurl)
else()
message(STATUS "RPCS3: building libcurl + wolfssl submodules")
set(BUILD_CURL_EXE OFF CACHE BOOL "Set to ON to build curl executable.")
set(BUILD_STATIC_CURL OFF CACHE BOOL "Set to ON to build curl executable with static libcurl.")
set(BUILD_STATIC_LIBS ON CACHE BOOL "Set to ON to build static libcurl.")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Set to ON to build shared libcurl.")
set(BUILD_CURL_EXE OFF CACHE INTERNAL "")
set(BUILD_STATIC_CURL OFF CACHE INTERNAL "")
set(BUILD_STATIC_LIBS ON CACHE INTERNAL "")
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "")
find_package(WolfSSL REQUIRED)
set(CURL_USE_WOLFSSL ON CACHE BOOL "enable wolfSSL for SSL/TLS")
set(CURL_USE_OPENSSL OFF CACHE BOOL "Use OpenSSL code. Experimental")
set(HTTP_ONLY ON CACHE BOOL "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)")
set(USE_LIBIDN2 OFF CACHE BOOL "Use libidn2 for IDN support") # Disabled because MacOS CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
option(CURL_DISABLE_INSTALL "Disable installation targets" ON)
set(CURL_USE_WOLFSSL ON CACHE INTERNAL "")
set(CURL_USE_OPENSSL OFF CACHE INTERNAL "")
set(HTTP_ONLY ON CACHE INTERNAL "")
set(USE_LIBIDN2 OFF CACHE INTERNAL "") # Disabled because MacOS CI doesn't work otherwise
set(CURL_CA_PATH "none" CACHE INTERNAL "")
set(CURL_DISABLE_INSTALL ON CACHE INTERNAL "")
if(WIN32)
set(ENABLE_UNICODE ON CACHE BOOL "enable Unicode")
set(ENABLE_UNICODE ON CACHE INTERNAL "")
endif()
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2")
set(CURL_USE_LIBPSL OFF CACHE BOOL "Use libPSL")
option(BUILD_TESTING "Build tests" OFF)
option(BUILD_EXAMPLES "Build libcurl examples" OFF)
set(CURL_USE_LIBSSH2 OFF CACHE INTERNAL "")
set(CURL_USE_LIBPSL OFF CACHE INTERNAL "")
set(BUILD_TESTING OFF CACHE INTERNAL "")
set(BUILD_EXAMPLES OFF CACHE INTERNAL "")
add_subdirectory(curl EXCLUDE_FROM_ALL)
+11 -3
View File
@@ -66,6 +66,7 @@
<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" />
@@ -107,7 +108,6 @@
<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_rtmp.c" />
<ClCompile Include="curl\lib\curl_sasl.c" />
<ClCompile Include="curl\lib\curl_sha512_256.c" />
<ClCompile Include="curl\lib\curl_share.c" />
@@ -118,6 +118,7 @@
<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" />
@@ -172,6 +173,7 @@
<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\psl.c" />
<ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\ratelimit.c" />
@@ -195,6 +197,8 @@
<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" />
@@ -255,6 +259,7 @@
<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" />
@@ -275,7 +280,6 @@
<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\curlx.h" />
<ClInclude Include="curl\lib\curlx\dynbuf.h" />
<ClInclude Include="curl\lib\curlx\fopen.h" />
<ClInclude Include="curl\lib\curlx\inet_ntop.h" />
@@ -309,7 +313,6 @@
<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_rtmp.h" />
<ClInclude Include="curl\lib\curl_sasl.h" />
<ClInclude Include="curl\lib\curl_setup.h" />
<ClInclude Include="curl\lib\curl_setup_once.h" />
@@ -322,6 +325,7 @@
<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" />
@@ -332,6 +336,7 @@
<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" />
@@ -368,6 +373,7 @@
<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\psl.h" />
<ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\ratelimit.h" />
@@ -395,6 +401,8 @@
<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" />
+33 -9
View File
@@ -66,9 +66,6 @@
<ClCompile Include="curl\lib\curl_range.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curl_rtmp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curl_sasl.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -552,6 +549,21 @@
<ClCompile Include="curl\lib\curlx\strdup.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\cf-dns.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\dnscache.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\protocol.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\thrdpool.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\thrdqueue.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="curl\include\curl\curl.h">
@@ -668,9 +680,6 @@
<ClInclude Include="curl\lib\curl_range.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_rtmp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_sasl.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -1040,9 +1049,6 @@
<ClInclude Include="curl\lib\curlx\base64.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\curlx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\dynbuf.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -1121,6 +1127,24 @@
<ClInclude Include="curl\lib\curlx\strdup.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\cf-dns.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\dnscache.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\ftp-int.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\protocol.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\thrdpool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\thrdqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="curl\lib\libcurl.rc">
+5
View File
@@ -54,6 +54,9 @@
<ClCompile Condition="'$(Configuration)'=='Release'">
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'">
<PreprocessorDefinitions>LIBUSB_WINDOWS_HOTPLUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Lib>
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
</Lib>
@@ -64,6 +67,7 @@
<ClCompile Include="libusb\libusb\hotplug.c" />
<ClCompile Include="libusb\libusb\io.c" />
<ClCompile Include="libusb\libusb\os\events_windows.c" />
<ClCompile Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'" Include="libusb\libusb\os\windows_hotplug.c" />
<ClCompile Include="libusb\libusb\strerror.c" />
<ClCompile Include="libusb\libusb\sync.c" />
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
@@ -72,6 +76,7 @@
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Condition="'$(EnableLibUsbWindowsHotplug)' == 'true'" Include="libusb\libusb\os\windows_hotplug.h" />
<ClInclude Include="libusb\msvc\config.h" />
<ClInclude Include="libusb\libusb\hotplug.h" />
<ClInclude Include="libusb\libusb\libusb.h" />
+6 -3
View File
@@ -119,7 +119,8 @@ echo -e "\n\nResult:\n"
# Find the max length of the paths (before '->')
max_len=0
while IFS='->' read -r left _; do
while read -r line; do
left="${line%%->*}"
len=$(echo -n "$left" | wc -c)
if (( len > max_len )); then
max_len=$len
@@ -127,8 +128,10 @@ while IFS='->' read -r left _; do
done < "$resultfile"
# Print with padding so '->' lines up
while IFS='->' read -r left right; do
right=$(echo "$right" | sed 's/^[[:space:]]*>*[[:space:]]*//')
while read -r line; do
left="${line%%->*}"
right="${line#*->}"
right=$(echo "$right" | sed 's/^[[:space:]]*//')
printf "%-${max_len}s -> %s\n" "$left" "$right"
done < "$resultfile"
+15 -16
View File
@@ -4,23 +4,22 @@ if(USE_SYSTEM_WOLFSSL)
add_library(wolfssl INTERFACE)
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
else()
option(WOLFSSL_TLS13 "Enable wolfSSL TLS v1.3 (default: enabled)" OFF)
set(WOLFSSL_SHA3 ON CACHE STRING "Enable wolfSSL SHA-3 support (default: enabled on x86_64/aarch64)")
set(WOLFSSL_SHAKE256 ON CACHE STRING "Enable wolfSSL SHAKE256 support (default: enabled on x86_64/aarch64)")
option(WOLFSSL_BASE64_ENCODE "Enable Base64 encoding (default: enabled on x86_64)" OFF)
option(WOLFSSL_DES3 "Enable DES3 (default: disabled)" ON)
option(WOLFSSL_PWDBASED "Enable PWDBASED (default: disabled)" ON)
option(WOLFSSL_FAST_MATH "Enable fast math ops (default: disabled)" ON)
option(WOLFSSL_EXAMPLES "Enable examples (default: enabled)" OFF)
option(WOLFSSL_CRYPT_TESTS "Enable Crypt Bench/Test (default: enabled)" OFF)
option(WOLFSSL_ASYNC_THREADS "Enable Asynchronous Threading (default: enabled)" OFF)
option(WOLFSSL_BUILD_OUT_OF_TREE "Don't generate files in the source tree (default: yes)" ON)
option(WOLFSSL_SNI "Enable SNI (default: disabled)" ON)
option(WOLFSSL_OPENSSLEXTRA "Enable extra OpenSSL API, size+ (default: disabled)" ON)
option(WOLFSSL_HARDEN "Enable Hardened build, Enables Timing Resistance and Blinding (default: enabled)" OFF)
option(WOLFSSL_ALT_CERT_CHAINS "Enable support for Alternate certification chains (default: disabled)" ON)
set(WOLFSSL_TLS13 OFF CACHE INTERNAL "")
set(WOLFSSL_SHA3 ON CACHE INTERNAL "")
set(WOLFSSL_SHAKE256 ON CACHE INTERNAL "")
set(WOLFSSL_BASE64_ENCODE OFF CACHE INTERNAL "")
set(WOLFSSL_DES3 ON CACHE INTERNAL "")
set(WOLFSSL_PWDBASED ON CACHE INTERNAL "")
set(WOLFSSL_FAST_MATH ON CACHE INTERNAL "")
set(WOLFSSL_EXAMPLES OFF CACHE INTERNAL "")
set(WOLFSSL_CRYPT_TESTS OFF CACHE INTERNAL "")
set(WOLFSSL_ASYNC_THREADS OFF CACHE INTERNAL "")
set(WOLFSSL_BUILD_OUT_OF_TREE ON CACHE INTERNAL "")
set(WOLFSSL_SNI ON CACHE INTERNAL "")
set(WOLFSSL_OPENSSLEXTRA ON CACHE INTERNAL "")
set(WOLFSSL_ALT_CERT_CHAINS ON CACHE INTERNAL "")
add_subdirectory(wolfssl EXCLUDE_FROM_ALL)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_NO_OPTIONS_H)
target_compile_definitions(wolfssl PUBLIC WOLFSSL_DES_ECB HAVE_WRITE_DUP FP_MAX_BITS=8192 WOLFSSL_USE_OPTIONS_H)
endif()
+7 -7
View File
@@ -20,27 +20,27 @@ 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.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.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [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)
- [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.0\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.11.1\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.0\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.11.1\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.0](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [Qt 6.11.1](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)
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
@@ -123,7 +123,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.0\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.11.1\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**)
+2 -2
View File
@@ -94,8 +94,8 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
endif()
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
include_directories(/opt/homebrew/include)
link_directories(/opt/homebrew/lib)
list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew")
list(APPEND CMAKE_SYSTEM_PREFIX_PATH "/opt/homebrew")
endif()
if(MSVC)
+10
View File
@@ -23,6 +23,16 @@ If you want to contribute as a developer, please take a look at the following pa
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
### AI Use
Use of AI tools for research and reverse engineering purposes is permitted. However, contributors are expected to fully own and understand all code they submit. Any communication with the team — including code, code comments, and GitHub comments — must come from the human contributor, not an AI agent acting autonomously.
We have unfortunately seen a rise in untested and unverified AI-generated slop being submitted to this project. This wastes maintainer time and, in worse cases, such changes get merged and break functionality for all users. Repeated violations will result in a ban from the repository. Please be respectful of everyone's time.
**Pull requests opened by AI agents or automated tools must include a disclosure in the PR description** stating the scope of AI involvement — which parts were AI-generated and what human testing or review was performed prior to submission. PRs that omit this disclosure may be closed without review.
If you are unsure about your work, open a discussion issue to talk it through with the team, or reach out to a maintainer on [Discord](https://discord.gg/RPCS3).
## Building
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
+118 -50
View File
@@ -40,7 +40,7 @@ namespace cfg
owner->m_nodes.emplace_back(this);
}
bool _base::from_string(std::string_view, bool)
bool _base::from_string(std::string_view /*value*/, bool /*dynamic*/)
{
cfg_log.fatal("cfg::_base::from_string() purecall");
return false;
@@ -68,7 +68,7 @@ namespace cfg
// Incrementally load config entries from YAML::Node.
// The config value is preserved if the corresponding YAML node doesn't exist.
static void decode(const YAML::Node& data, class _base& rhs, bool dynamic = false);
[[nodiscard]] static bool decode(const YAML::Node& data, class _base& rhs, bool dynamic, bool strict);
}
std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
@@ -76,11 +76,11 @@ std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
return {std::to_string(min), std::to_string(max)};
}
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max, std::string_view name)
{
if (value.empty())
{
if (out) cfg_log.error("cfg::try_to_int64(): called with an empty string");
if (out) cfg_log.error("cfg::try_to_int64('%s'): called with an empty string", name);
return false;
}
@@ -107,7 +107,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
if (ret.ec != std::errc() || ret.ptr != end || (start[0] == '-' && sign < 0))
{
if (out) cfg_log.error("cfg::try_to_int64('%s'): invalid integer", value);
if (out) cfg_log.error("cfg::try_to_int64('%s', '%s'): invalid integer", value, name);
return false;
}
@@ -115,7 +115,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_int64('%s'): out of bounds (val=%d, min=%d, max=%d)", value, result, min, max);
if (out) cfg_log.error("cfg::try_to_int64('%s', '%s'): out of bounds (val=%d, min=%d, max=%d)", value, name, result, min, max);
return false;
}
@@ -128,11 +128,11 @@ std::vector<std::string> cfg::make_uint_range(u64 min, u64 max)
return {std::to_string(min), std::to_string(max)};
}
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max, std::string_view name)
{
if (value.empty())
{
if (out) cfg_log.error("cfg::try_to_uint64(): called with an empty string");
if (out) cfg_log.error("cfg::try_to_uint64('%s'): called with an empty string", name);
return false;
}
@@ -152,13 +152,13 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
if (ret.ec != std::errc() || ret.ptr != end)
{
if (out) cfg_log.error("cfg::try_to_uint64('%s'): invalid integer", value);
if (out) cfg_log.error("cfg::try_to_uint64('%s', '%s'): invalid integer", value, name);
return false;
}
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (val=%u, min=%u, max=%u)", value, result, min, max);
if (out) cfg_log.error("cfg::try_to_uint64('%s', '%s'): out of bounds (val=%u, min=%u, max=%u)", value, name, result, min, max);
return false;
}
@@ -166,11 +166,11 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
return true;
}
bool try_to_uint128(u128* out, std::string_view value)
bool try_to_uint128(u128* out, std::string_view value, std::string_view name)
{
if (value.empty())
{
if (out) cfg_log.error("cfg::try_to_uint128(): called with an empty string");
if (out) cfg_log.error("cfg::try_to_uint128('%s'): called with an empty string", name);
return false;
}
@@ -193,7 +193,7 @@ bool try_to_uint128(u128* out, std::string_view value)
if (ret.ec != std::errc() || ret.ptr != end)
{
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
if (out) cfg_log.error("cfg::try_to_uint128('%s', '%s'): invalid integer", value, name);
return false;
}
@@ -207,7 +207,7 @@ bool try_to_uint128(u128* out, std::string_view value)
if (ret.ec != std::errc() || ret.ptr != start_low64)
{
if (out) cfg_log.error("cfg::try_to_uint128('%s'): invalid integer", value);
if (out) cfg_log.error("cfg::try_to_uint128('%s', '%s'): invalid integer", value, name);
return false;
}
@@ -220,11 +220,11 @@ std::vector<std::string> cfg::make_float_range(f64 min, f64 max)
return {std::to_string(min), std::to_string(max)};
}
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max, std::string_view name)
{
if (value.empty())
{
if (out) cfg_log.error("cfg::try_to_float(): called with an empty string");
if (out) cfg_log.error("cfg::try_to_float('%s'): called with an empty string", name);
return false;
}
@@ -237,13 +237,13 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
if (end_check != str.data() + str.size())
{
if (out) cfg_log.error("cfg::try_to_float('%s'): invalid float", value);
if (out) cfg_log.error("cfg::try_to_float('%s', '%s'): invalid float", value, name);
return false;
}
if (result < min || result > max)
{
if (out) cfg_log.error("cfg::try_to_float('%s'): out of bounds (val=%f, min=%f, max=%f)", value, result, min, max);
if (out) cfg_log.error("cfg::try_to_float('%s', '%s'): out of bounds (val=%f, min=%f, max=%f)", value, name, result, min, max);
return false;
}
@@ -251,12 +251,8 @@ bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
return true;
}
bool try_to_string(std::string* out, const f64& value)
bool try_to_string(std::string* out, f64 value, std::string_view name)
{
#ifdef __APPLE__
if (out) *out = std::to_string(value);
return true;
#else
std::array<char, 32> str{};
if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), value, std::chars_format::fixed); ec == std::errc())
@@ -266,14 +262,15 @@ bool try_to_string(std::string* out, const f64& value)
}
else
{
if (out) cfg_log.error("cfg::try_to_string(): could not convert value '%f' to string. error='%s'", value, std::make_error_code(ec).message());
if (out) cfg_log.error("cfg::try_to_string('%s'): could not convert value '%f' to string. error='%s'", name, value, std::make_error_code(ec).message());
return false;
}
#endif
}
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value)
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value, std::string_view name)
{
ensure(func);
u64 max = umax;
for (u64 i = 0;; i++)
@@ -302,7 +299,7 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
const char* end = start + value.size();
int base = 10;
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
if (value.size() >= 2 && start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
{
// Limited hex support
base = 16;
@@ -313,13 +310,13 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
if (ret.ec != std::errc() || ret.ptr != end)
{
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): invalid enum or integer", value);
if (out) cfg_log.error("cfg::try_to_enum_value('%s', '%s'): invalid enum or integer", value, name);
return false;
}
if (result > max)
{
if (out) cfg_log.error("cfg::try_to_enum_value('%s'): out of bounds(val=%u, min=0, max=%u)", value, result, max);
if (out) cfg_log.error("cfg::try_to_enum_value('%s', '%s'): out of bounds(val=%u, min=0, max=%u)", value, name, result, max);
return false;
}
@@ -468,37 +465,50 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
}
}
void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
bool cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic, bool strict)
{
if (dynamic && !rhs.get_is_dynamic())
{
return;
return true;
}
switch (rhs.get_type())
{
case type::node:
{
if (data.IsScalar() || data.IsSequence())
if (!data.IsMap())
{
return; // ???
cfg_log.error("node node is not a map");
return false;
}
bool success = true;
for (const auto& pair : data)
{
if (!pair.first.IsScalar()) continue;
// Find the key among existing nodes
for (const auto& node : static_cast<node&>(rhs).get_nodes())
const auto& nodes = static_cast<node&>(rhs).get_nodes();
const auto it = std::find_if(nodes.cbegin(), nodes.cend(), [&pair](const auto& node) { return ensure(node)->get_name() == pair.first.Scalar(); });
if (it == nodes.cend())
{
if (node->get_name() == pair.first.Scalar())
if (strict)
{
decode(pair.second, *node, dynamic);
cfg_log.error("Unknown key found: '%s'", pair.first.Scalar());
success = false;
}
continue;
}
if (!decode(pair.second, *ensure(*it), dynamic, strict) && strict)
{
success = false;
}
}
break;
return success;
}
case type::set:
{
@@ -506,7 +516,10 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
if (YAML::convert<decltype(values)>::decode(data, values))
{
rhs.from_list(std::move(values));
if (!rhs.from_list(std::move(values)) && strict)
{
return false;
}
}
break;
@@ -516,7 +529,8 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
{
if (!data.IsMap())
{
return;
cfg_log.error("map node is not a map");
return false;
}
map_of_type<std::string> values;
@@ -533,22 +547,36 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
}
case type::log:
{
if (data.IsScalar() || data.IsSequence())
if (!data.IsMap())
{
return; // ???
cfg_log.error("log node is not a map");
return false;
}
map_of_type<logs::level> values;
for (const auto& pair : data)
{
if (!pair.first.IsScalar() || !pair.second.IsScalar()) continue;
if (!pair.first.IsScalar() || !pair.second.IsScalar())
{
if (strict)
{
if (!pair.first.IsScalar())
cfg_log.error("Key in map is not a scalar");
else
cfg_log.error("Value in map is not a scalar. key='%s'", pair.first.Scalar());
return false;
}
continue;
}
u64 value;
if (cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar()))
if (!cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar(), pair.first.Scalar()) && strict)
{
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
return false;
}
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
}
static_cast<log_entry&>(rhs).set_map(std::move(values));
@@ -558,20 +586,43 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
{
if (!data.IsMap())
{
return; // ???
cfg_log.error("device node is not a map");
return false;
}
map_of_type<device_info> values;
for (const auto& pair : data)
{
if (!pair.first.IsScalar() || !pair.second.IsMap()) continue;
if (!pair.first.IsScalar() || !pair.second.IsMap())
{
if (strict)
{
if (!pair.first.IsScalar())
cfg_log.error("Key in device map is not a scalar");
else
cfg_log.error("Value in device map is not a map. key='%s'", pair.first.Scalar());
return false;
}
continue;
}
device_info info{};
for (const auto& key_value : pair.second)
{
if (!key_value.first.IsScalar() || !key_value.second.IsScalar()) continue;
if (!key_value.first.IsScalar() || !key_value.second.IsScalar())
{
if (strict)
{
if (!key_value.first.IsScalar())
cfg_log.error("Key in device info map is not a scalar");
else
cfg_log.error("Value in device map is not a scalar. key='%s'", key_value.first.Scalar());
return false;
}
continue;
}
if (key_value.first.Scalar() == "Path")
info.path = key_value.second.Scalar();
@@ -598,12 +649,17 @@ void cfg::decode(const YAML::Node& data, cfg::_base& rhs, bool dynamic)
if (YAML::convert<std::string>::decode(data, value))
{
rhs.from_string(value, dynamic);
if (!rhs.from_string(value, dynamic) && strict)
{
return false;
}
}
break; // ???
}
}
return true;
}
std::string cfg::node::to_string() const
@@ -620,8 +676,7 @@ bool cfg::node::from_string(std::string_view value, bool dynamic)
if (error.empty())
{
cfg::decode(result, *this, dynamic);
return true;
return cfg::decode(result, *this, dynamic, false);
}
cfg_log.error("Failed to load node: %s", error);
@@ -644,6 +699,19 @@ void cfg::node::restore_defaults()
}
}
bool cfg::node::validate(std::string_view value)
{
auto [result, error] = yaml_load(std::string(value));
if (error.empty())
{
return cfg::decode(result, *this, false, true);
}
cfg_log.error("Failed to load node: %s", error);
return false;
}
std::string cfg::map_entry::get_value(std::string_view key)
{
if (auto it = m_map.find(key); it != m_map.end())
+17 -9
View File
@@ -25,7 +25,7 @@ namespace cfg
std::vector<std::string> make_float_range(f64 min, f64 max);
// Internal hack
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view);
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value, std::string_view name = {});
// Internal hack
std::vector<std::string> try_to_enum_list(decltype(&fmt_class_string<int>::format) func);
@@ -110,7 +110,7 @@ namespace cfg
}
// Try to convert from string (optional)
virtual bool from_string(std::string_view, bool /*dynamic*/ = false);
virtual bool from_string(std::string_view value, bool dynamic = false);
// Get string list (optional)
virtual std::vector<std::string> to_list() const
@@ -161,6 +161,9 @@ namespace cfg
// Restore default members
void restore_defaults() override;
// Try to convert from string and validate
bool validate(std::string_view value);
};
class _bool final : public _base
@@ -301,7 +304,7 @@ namespace cfg
{
u64 result;
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value))
if (try_to_enum_value(&result, &fmt_class_string<T>::format, value, m_name))
{
// No narrowing check, it's hard to do right there
m_value = static_cast<T>(static_cast<std::underlying_type_t<T>>(result));
@@ -382,7 +385,7 @@ namespace cfg
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
s64 result;
if (try_to_int64(&result, value, Min, Max))
if (try_to_int64(&result, value, Min, Max, m_name))
{
m_value = static_cast<int_type>(result);
return true;
@@ -451,7 +454,7 @@ namespace cfg
std::string to_string() const override
{
std::string result;
if (try_to_string(&result, m_value))
if (try_to_string(&result, m_value, m_name))
{
return result;
}
@@ -462,7 +465,7 @@ namespace cfg
std::string def_to_string() const override
{
std::string result;
if (try_to_string(&result, def))
if (try_to_string(&result, def, m_name))
{
return result;
}
@@ -473,7 +476,7 @@ namespace cfg
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
f64 result;
if (try_to_float(&result, value, Min, Max))
if (try_to_float(&result, value, Min, Max, m_name))
{
m_value = static_cast<float_type>(result);
return true;
@@ -560,7 +563,7 @@ namespace cfg
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
u64 result;
if (try_to_uint64(&result, value, Min, Max))
if (try_to_uint64(&result, value, Min, Max, m_name))
{
m_value = static_cast<int_type>(result);
return true;
@@ -646,7 +649,7 @@ namespace cfg
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
{
u128 result;
if (try_to_uint128(&result, value))
if (try_to_uint128(&result, value, m_name))
{
m_value = result;
return true;
@@ -714,6 +717,11 @@ namespace cfg
m_value = std::string(value);
return true;
}
void set(std::string_view value)
{
m_value = std::string(value);
}
};
// Simple set entry (TODO: template for various types)
+99 -7
View File
@@ -38,6 +38,17 @@ static std::unique_ptr<wchar_t[]> to_wchar(std::string_view source)
// Buffer for max possible output length
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size + 8 + 32768]);
// If path points to an optical raw device, copy it AS IS
if (fs::is_optical_raw_device(std::string(source)))
{
ensure(MultiByteToWideChar(CP_UTF8, 0, source.data(), size, buffer.get() + 32768, size)); // "to_wchar"
// Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc)
ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar"
return buffer;
}
// Prepend wide path prefix (4 characters)
std::memcpy(buffer.get() + 32768, L"\\\\\?\\", 4 * sizeof(wchar_t));
@@ -166,6 +177,7 @@ static fs::error to_error(int e)
case ENOTEMPTY: return fs::error::notempty;
case EROFS: return fs::error::readonly;
case EISDIR: return fs::error::isdir;
case ENOTDIR: return fs::error::notdir;
case ENOSPC: return fs::error::nospace;
case EXDEV: return fs::error::xdev;
default: return fs::error::unknown;
@@ -399,11 +411,12 @@ namespace fs
class windows_file final : public file_base
{
HANDLE m_handle;
bool m_raw_device;
atomic_t<u64> m_pos {0};
public:
windows_file(HANDLE handle)
: m_handle(handle)
windows_file(HANDLE handle, bool raw_device = false)
: m_handle(handle), m_raw_device(raw_device)
{
}
@@ -563,11 +576,20 @@ namespace fs
u64 size() override
{
// NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
LARGE_INTEGER size;
ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
if (!m_raw_device)
{
// NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
LARGE_INTEGER size;
return size.QuadPart;
ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
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;
}
native_handle get_handle() override
@@ -1090,6 +1112,55 @@ bool fs::is_symlink(const std::string& path)
return true;
}
bool fs::is_optical_raw_device([[maybe_unused]] const std::string& path)
{
#ifdef _WIN32
if (path.starts_with("\\\\.\\"))
{
return true;
}
#endif
return false;
}
bool fs::get_optical_raw_device(const std::string& path, std::string* raw_device)
{
if (fs::is_optical_raw_device(path))
{
if (raw_device)
{
*raw_device = path;
}
return true;
}
#ifdef _WIN32
// Skip a useless check to detect an optical raw device if navigating on subfolders (e.g. C:\subfolder_1\subfolder_2\),
// it means we are on a HDD/SSD. A path for an optical drive should include only the drive letter (e.g. E:\)
const size_t drive_delim_pos = path.find_first_of(":");
if (drive_delim_pos != 1 || drive_delim_pos != path.find_last_not_of(delim))
{
return false;
}
const std::string drive_letter = path.substr(0, drive_delim_pos + 1); // e.g. "E:"
const std::string drive_path = drive_letter + "\\"; // e.g. "E:\"
if (GetDriveTypeA(drive_path.c_str()) == DRIVE_CDROM)
{
if (raw_device)
{
*raw_device = "\\\\.\\" + drive_letter;
}
return true;
}
#endif
return false;
}
bool fs::statfs(const std::string& path, fs::device_stat& info)
{
if (auto device = get_virtual_device(path))
@@ -1657,9 +1728,29 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
// If path points to an optical raw device, complete the file opening
// (the following GetFileInformationByHandle() would always fail on a raw device).
if (is_optical_raw_device(path))
{
DISK_GEOMETRY_EX geometry;
// Try to retrieve information on content. If it fails, no disc is probably mounted so abort the file opening
if (!DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, &geometry, sizeof(geometry), nullptr, nullptr))
{
const DWORD last_error = GetLastError();
CloseHandle(handle);
g_tls_error = to_error(last_error);
return;
}
m_file = std::make_unique<windows_file>(handle, true);
return;
}
// Check if the handle is actually valid.
// This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION).
BY_HANDLE_FILE_INFORMATION info{};
if (!GetFileInformationByHandle(handle, &info))
{
const DWORD last_error = GetLastError();
@@ -1670,7 +1761,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
g_tls_error = fs::error::isdir;
return;
}
g_tls_error = to_error(last_error);
return;
}
@@ -2833,6 +2924,7 @@ void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
case fs::error::notempty: return "Not empty";
case fs::error::readonly: return "Read only";
case fs::error::isdir: return "Is a directory";
case fs::error::notdir: return "Not a directory";
case fs::error::toolong: return "Path too long";
case fs::error::nospace: return "Not enough space on the device";
case fs::error::xdev: return "Device mismatch";
+7
View File
@@ -213,6 +213,12 @@ 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
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
bool get_optical_raw_device(const std::string& path, std::string* raw_device = nullptr);
// Get filesystem information
bool statfs(const std::string& path, device_stat& info);
@@ -683,6 +689,7 @@ namespace fs
notempty,
readonly,
isdir,
notdir,
toolong,
nospace,
xdev,
+13
View File
@@ -493,6 +493,10 @@ inline FT build_function_asm(std::string_view name, F&& builder, ::jit_runtime*
return reinterpret_cast<FT>(uptr(result));
}
#if defined(__INTELLISENSE__) && !defined(LLVM_AVAILABLE)
#define LLVM_AVAILABLE
#endif
#ifdef LLVM_AVAILABLE
namespace llvm
@@ -539,9 +543,15 @@ public:
// Add module (path to obj cache dir)
void add(std::unique_ptr<llvm::Module> _module, const std::string& path);
// Returns false after LLVM fatal recovery. The compiler must be discarded.
bool try_add(std::unique_ptr<llvm::Module> _module, const std::string& path, std::string& error);
// Add module (not cached)
void add(std::unique_ptr<llvm::Module> _module);
// Returns false after LLVM fatal recovery. The compiler must be discarded.
bool try_add(std::unique_ptr<llvm::Module> _module, std::string& error);
// Add object (path to obj file)
bool add(const std::string& path);
@@ -554,6 +564,9 @@ public:
// Finalize
void fin();
// Returns false after LLVM fatal recovery. The compiler must be discarded.
bool try_fin(std::string& error);
// Get compiled function address
u64 get(const std::string& name);
+115
View File
@@ -12,6 +12,10 @@
#include <charconv>
#if defined(__APPLE__)
#include <pthread.h>
#endif
LOG_CHANNEL(jit_log, "JIT");
#ifdef LLVM_AVAILABLE
@@ -50,6 +54,44 @@ LOG_CHANNEL(jit_log, "JIT");
#include "Emu/CPU/Backends/AArch64/AArch64Common.h"
#endif
namespace
{
thread_local std::string* g_llvm_fatal_message = nullptr;
template <typename F>
bool run_recoverable_llvm(F&& func, std::string& error)
{
error.clear();
// Run LLVM codegen in a disposable thread. If LLVM invokes the fatal
// handler, only this helper thread exits.
named_thread worker("LLVM JIT", [&]()
{
#if defined(__APPLE__)
pthread_jit_write_protect_np(false);
#endif
g_llvm_fatal_message = &error;
std::forward<F>(func)();
g_llvm_fatal_message = nullptr;
#if defined(__APPLE__)
pthread_jit_write_protect_np(true);
#endif
});
worker();
const bool result = static_cast<thread_state>(worker) == thread_state::finished;
if (!result && error.empty())
{
error = "LLVM crash recovery invoked";
}
return result;
}
}
const bool jit_initialize = []() -> bool
{
llvm::InitializeNativeTarget();
@@ -649,6 +691,13 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
llvm::install_fatal_error_handler([](void*, const char* msg, bool)
{
const std::string_view out = msg ? msg : "";
if (g_llvm_fatal_message)
{
*g_llvm_fatal_message = out;
thread_ctrl::silent_exit();
}
fmt::throw_exception("LLVM Emergency Exit Invoked: '%s'", out);
}, nullptr);
@@ -701,6 +750,15 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
else
attributes.push_back("-dotprod");
// The recompilers emit i8mm intrinsics (e.g. ummla) gated on utils::has_i8mm().
// The JIT target features must advertise i8mm too, otherwise the backend fails
// with "Cannot select: intrinsic %llvm.aarch64.neon.ummla" whenever the resolved
// -mcpu does not already imply it (e.g. the cortex-a78 fallback on Apple silicon).
if (utils::has_i8mm())
attributes.push_back("+i8mm");
else
attributes.push_back("-i8mm");
if (utils::has_sve())
attributes.push_back("+sve");
else
@@ -788,6 +846,33 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> _module, const std::string&
}
}
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _module, const std::string& path, std::string& error)
{
ObjectCache cache{path, this};
m_engine->setObjectCache(&cache);
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
if (!run_recoverable_llvm([&]()
{
m_engine->generateCodeForModule(ptr);
}, error))
{
return false;
}
m_engine->setObjectCache(nullptr);
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
return true;
}
void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
{
const auto ptr = _module.get();
@@ -801,6 +886,28 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> _module)
}
}
bool jit_compiler::try_add(std::unique_ptr<llvm::Module> _module, std::string& error)
{
const auto ptr = _module.get();
m_engine->addModule(std::move(_module));
if (!run_recoverable_llvm([&]()
{
m_engine->generateCodeForModule(ptr);
}, error))
{
return false;
}
for (auto& func : ptr->functions())
{
// Delete IR to lower memory consumption
func.deleteBody();
}
return true;
}
bool jit_compiler::add(const std::string& path)
{
auto cache = ObjectCache::load(path);
@@ -852,6 +959,14 @@ void jit_compiler::fin()
m_engine->finalizeObject();
}
bool jit_compiler::try_fin(std::string& error)
{
return run_recoverable_llvm([&]()
{
m_engine->finalizeObject();
}, error);
}
u64 jit_compiler::get(const std::string& name)
{
return m_engine->getGlobalValueAddress(name);
+5 -5
View File
@@ -23,19 +23,19 @@ inline void strcpy_trunc(D&& dst, const T& src)
}
// Convert string to signed integer
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max, std::string_view name = {});
// Convert string to unsigned integer
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max, std::string_view name = {});
// Convert string to unsigned int128_t
bool try_to_uint128(u128* out, std::string_view value);
bool try_to_uint128(u128* out, std::string_view value, std::string_view name = {});
// Convert string to float
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max);
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max, std::string_view name = {});
// Convert float to string locale independent
bool try_to_string(std::string* out, const f64& value);
bool try_to_string(std::string* out, f64 value, std::string_view name = {});
// Get the file extension of a file path ("png", "jpg", etc.)
std::string get_file_extension(const std::string& file_path);
+131 -11
View File
@@ -25,6 +25,7 @@
#include <process.h>
#include <sysinfoapi.h>
#include "stack_trace.h"
#include "util/dyn_lib.hpp"
DYNAMIC_IMPORT_RENAME("Kernel32.dll", SetThreadDescriptionImport, "SetThreadDescription", HRESULT(HANDLE hThread, PCWSTR lpThreadDescription));
@@ -1383,11 +1384,7 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
return false;
}
if (a_size != 4)
{
// Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
break;
}
bool handled = true;
switch (op)
{
@@ -1397,14 +1394,37 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_LOAD_TEST:
{
u32 value;
if (is_writing || !thread->read_reg(addr, value))
const u32 addr_aligned = addr & -4;
if (addr % 4 + a_size > 4)
{
handled = false;
break;
}
if (is_writing || !thread->read_reg(addr_aligned, value))
{
return false;
}
// Adjust value for 8-bit and 16-bit reads
value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
if (op != X64OP_LOAD_BE)
{
value = stx::se_storage<u32>::swap(value);
if (a_size == 4)
{
value = stx::se_storage<u32>::swap(value);
}
else if (a_size == 2)
{
value = stx::se_storage<u16>::swap(value);
}
else
{
ensure(a_size == 1);
}
}
if (op == X64OP_LOAD_CMP)
@@ -1439,12 +1459,35 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_BEXTR:
{
u32 value;
if (is_writing || !thread->read_reg(addr, value))
const u32 addr_aligned = addr & -4;
if (addr % 4 + a_size > 4)
{
handled = false;
break;
}
if (is_writing || !thread->read_reg(addr_aligned, value))
{
return false;
}
value = stx::se_storage<u32>::swap(value);
// Adjust value for 8-bit and 16-bit reads
value >>= ((4 - a_size) * 8) - ((addr % 4) * 8);
value &= a_size == 4 ? u32{umax} : ((1u << (a_size * 8)) - 1);
if (a_size == 4)
{
value = stx::se_storage<u32>::swap(value);
}
else if (a_size == 2)
{
value = stx::se_storage<u16>::swap(value);
}
else
{
ensure(a_size == 1);
}
u64 ctrl;
if (!get_x64_reg_value(context, s_tls_reg3, d_size, i_size, ctrl))
@@ -1470,6 +1513,13 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STORE:
case X64OP_STORE_BE:
{
if (a_size != 4)
{
// Might be unimplemented, such as writing MFC proxy EAL+EAH using 64-bit store
handled = false;
break;
}
u64 reg_value;
if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value))
{
@@ -1488,12 +1538,19 @@ bool handle_access_violation(u32 addr, bool is_writing, bool is_exec, ucontext_t
case X64OP_STOS:
default:
{
sig_log.error("Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, i_size=%lld)", +op, +reg, d_size, i_size);
sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
report_opcode();
return false;
}
}
if (!handled)
{
sig_log.error("Invalid or unsupported operation (op=%d, addr=0x%x, reg=%d, d_size=%lld, i_size=%lld, a_size=%d)", +op, addr, +reg, d_size, i_size, a_size);
report_opcode();
break;
}
// skip processed instruction
RIP(context) += i_size;
g_tls_fault_spu++;
@@ -1981,9 +2038,39 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp) noexcept
}
fmt::append(msg, "RPCS3 image base: %p.\n", GetModuleHandle(NULL));
#if defined(ARCH_X64)
fmt::append(msg, "RAX: %016llX RBX: %016llX\n", pExp->ContextRecord->Rax, pExp->ContextRecord->Rbx);
fmt::append(msg, "RCX: %016llX RDX: %016llX\n", pExp->ContextRecord->Rcx, pExp->ContextRecord->Rdx);
fmt::append(msg, "RSI: %016llX RDI: %016llX\n", pExp->ContextRecord->Rsi, pExp->ContextRecord->Rdi);
fmt::append(msg, "RBP: %016llX RSP: %016llX\n", pExp->ContextRecord->Rbp, pExp->ContextRecord->Rsp);
fmt::append(msg, "R8: %016llX R9: %016llX\n", pExp->ContextRecord->R8, pExp->ContextRecord->R9);
fmt::append(msg, "R10: %016llX R11: %016llX\n", pExp->ContextRecord->R10, pExp->ContextRecord->R11);
fmt::append(msg, "R12: %016llX R13: %016llX\n", pExp->ContextRecord->R12, pExp->ContextRecord->R13);
fmt::append(msg, "R14: %016llX R15: %016llX\n", pExp->ContextRecord->R14, pExp->ContextRecord->R15);
fmt::append(msg, "RFLAGS: %08X\n", pExp->ContextRecord->EFlags);
#elif defined(ARCH_ARM64)
for (int i = 0; i < 29; i += 2)
{
if (i + 1 < 29)
fmt::append(msg, "X%-2d: %016llX X%-2d: %016llX\n", i, pExp->ContextRecord->X[i], i + 1, pExp->ContextRecord->X[i + 1]);
else
fmt::append(msg, "X%-2d: %016llX\n", i, pExp->ContextRecord->X[i]);
}
fmt::append(msg, "SP: %016llX FP: %016llX LR: %016llX\n", pExp->ContextRecord->Sp, pExp->ContextRecord->Fp, pExp->ContextRecord->Lr);
fmt::append(msg, "CPSR: %08X\n", pExp->ContextRecord->Cpsr);
#endif
// TODO: print registers and the callstack
const auto stack_trace = utils::get_backtrace(64, pExp->ContextRecord);
const auto stack_symbols = utils::get_backtrace_symbols(stack_trace);
msg += "Stack Trace:\n";
for (const auto& symbol : stack_symbols)
{
fmt::append(msg, "%s\n", symbol);
}
sys_log.fatal("\n%s", msg);
logs::listener::sync_all();
@@ -2852,6 +2939,13 @@ void thread_base::exec()
}
}
void thread_ctrl::set_name(std::string name)
{
ensure(g_tls_this_thread);
g_tls_this_thread->m_tname.store(make_single<std::string>(name));
g_tls_this_thread->set_name(std::move(name));
}
[[noreturn]] void thread_ctrl::emergency_exit(std::string_view reason)
{
// Print stacktrace
@@ -2950,6 +3044,32 @@ void thread_base::exec()
report_fatal_error(reason);
}
void thread_ctrl::silent_exit() noexcept
{
if (const auto _this = g_tls_this_thread)
{
g_tls_error_callback();
u64 _self = _this->finalize(thread_state::errored);
if (_self == umax)
{
// Unused, detached thread support remnant
delete _this;
}
thread_base::finalize(umax);
}
#ifdef _WIN32
_endthreadex(0);
#else
pthread_exit(nullptr);
#endif
std::abort();
}
void thread_ctrl::detect_cpu_layout()
{
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
+10 -12
View File
@@ -129,7 +129,7 @@ public:
const native_entry entry_point;
// Set name for debugger
static void set_name(std::string);
static void set_name(std::string name);
private:
// Thread handle (platform-specific)
@@ -232,11 +232,7 @@ public:
}
// Set current thread name (not recommended)
static void set_name(std::string name)
{
g_tls_this_thread->m_tname.store(make_single<std::string>(name));
g_tls_this_thread->set_name(std::move(name));
}
static void set_name(std::string name);
// Set thread name (not recommended)
template <typename T>
@@ -319,6 +315,9 @@ public:
// Exit.
[[noreturn]] static void emergency_exit(std::string_view reason);
// Exit the current named thread as errored without reporting a fatal error.
[[noreturn]] static void silent_exit() noexcept;
// Get current thread (may be nullptr)
static thread_base* get_current()
{
@@ -801,31 +800,30 @@ public:
m_count = 0;
// Create all threads
for (u32 i = 0; i < count - 1; i++)
for (; m_count < count - 1; m_count++)
{
// Copy the context
std::remove_cvref_t<Context> context(static_cast<const Context&>(f));
// Perform the check and additional preparations for each context
if (!std::invoke(std::forward<CheckAndPrepare>(check), i, context))
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count, context))
{
return;
}
m_count++;
new (static_cast<void*>(m_threads + i)) Thread(std::string(name) + std::to_string(i + 1), std::move(context));
new (static_cast<void*>(m_threads + m_count)) Thread(std::string(name) + std::to_string(m_count + 1), std::move(context));
}
// Move the context (if movable)
std::remove_cvref_t<Context> context(std::forward<Context>(f));
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count - 1, context))
if (!std::invoke(std::forward<CheckAndPrepare>(check), m_count, context))
{
return;
}
new (static_cast<void*>(m_threads + m_count)) Thread(std::string(name) + std::to_string(m_count + 1), std::move(context));
m_count++;
new (static_cast<void*>(m_threads + m_count - 1)) Thread(std::string(name) + std::to_string(m_count - 1), std::move(context));
}
// Default constructor
+4
View File
@@ -1,5 +1,6 @@
#pragma once
#include <array>
#include <cmath>
#include <type_traits>
@@ -1027,3 +1028,6 @@ using color1u = color1_base<unsigned int>;
using color1i = color1_base<int>;
using color1f = color1_base<float>;
using color1d = color1_base<double>;
using mat3f = color3_base<float>[3];
static_assert(sizeof(mat3f) == sizeof(float) * 3 * 3);
+13
View File
@@ -10,6 +10,19 @@ rXmlNode::rXmlNode(const pugi::xml_node& node)
handle = node;
}
std::shared_ptr<rXmlNode> rXmlNode::GetChild(std::string_view name)
{
if (handle)
{
if (const pugi::xml_node child = handle.child(name))
{
return std::make_shared<rXmlNode>(child);
}
}
return nullptr;
}
std::shared_ptr<rXmlNode> rXmlNode::GetChildren()
{
if (handle)
+1
View File
@@ -20,6 +20,7 @@ struct rXmlNode
{
rXmlNode();
rXmlNode(const pugi::xml_node& node);
std::shared_ptr<rXmlNode> GetChild(std::string_view name);
std::shared_ptr<rXmlNode> GetChildren();
std::shared_ptr<rXmlNode> GetNext();
std::string GetName();
+31 -12
View File
@@ -30,42 +30,61 @@ namespace utils
return out.data();
}
std::vector<void*> get_backtrace(int max_depth)
std::vector<void*> get_backtrace(int max_depth, PCONTEXT ctx)
{
static struct sym_initer_t
{
sym_initer_t() noexcept
{
SymInitialize(GetCurrentProcess(), NULL, TRUE);
}
~sym_initer_t() noexcept
{
SymCleanup(GetCurrentProcess());
}
} s_initer{};
std::vector<void*> result = {};
const auto hProcess = ::GetCurrentProcess();
const auto hThread = ::GetCurrentThread();
CONTEXT context{};
RtlCaptureContext(&context);
if (ctx)
context = *ctx;
else
RtlCaptureContext(&context);
STACKFRAME64 stack = {};
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Mode = AddrModeFlat;
#if defined(ARCH_X64)
const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
stack.AddrPC.Offset = context.Rip;
stack.AddrStack.Offset = context.Rsp;
stack.AddrFrame.Offset = context.Rbp;
#elif defined(ARCH_ARM64)
const DWORD machineType = IMAGE_FILE_MACHINE_ARM64;
stack.AddrPC.Offset = context.Pc;
stack.AddrStack.Offset = context.Sp;
stack.AddrFrame.Offset = context.Fp;
#else
#error "Unsupported architecture"
#endif
while (max_depth--)
{
if (!StackWalk64(
IMAGE_FILE_MACHINE_AMD64,
hProcess,
hThread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
machineType,
hProcess,
hThread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
break;
}
@@ -116,7 +135,7 @@ namespace utils
}
else
{
result.push_back(fmt::format("rpcs3@0xp", pointer));
result.push_back(fmt::format("rpcs3@0x%p", pointer));
}
}
+10
View File
@@ -2,6 +2,11 @@
#include <util/types.hpp>
#include <util/logs.hpp>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#endif
namespace utils
{
namespace stack_trace
@@ -30,7 +35,12 @@ namespace utils
};
}
#ifdef _WIN32
std::vector<void*> get_backtrace(int max_depth = 255, PCONTEXT ctx = nullptr);
#else
std::vector<void*> get_backtrace(int max_depth = 255);
#endif
std::vector<std::string> get_backtrace_symbols(const std::vector<void*>& stack);
FORCE_INLINE void print_trace(stack_trace::Logger auto& logger, int max_depth = 255)
+159
View File
@@ -0,0 +1,159 @@
#include "stdafx.h"
#include "stereo_config.h"
#include "Emu/system_config.h"
stereo_config::stereo_matrices::stereo_matrices(const std::array<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r)
{
for (usz i = 0; i < 3; i++)
{
left[i] = l[i];
right[i] = r[i];
}
}
atomic_t<bool> stereo_config::s_live_preview_enabled = false;
stereo_config::stereo_matrices stereo_config::m_live_matrices = {};
const std::unordered_map<stereo_render_mode_options, stereo_config::stereo_matrices> stereo_config::m_matrices =
{
{stereo_render_mode_options::anaglyph_red_cyan, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_red_green, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
}
)},
{stereo_render_mode_options::anaglyph_red_blue, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_magenta_cyan, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 0.5f)
},
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0.5f)
}
)},
{stereo_render_mode_options::anaglyph_trioscopic, stereo_matrices(
{
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 0, 0),
color3_base<f32>(0, 0, 1)
}
)},
{stereo_render_mode_options::anaglyph_amber_blue, stereo_matrices(
{
color3_base<f32>(1, 0, 0),
color3_base<f32>(0, 1, 0),
color3_base<f32>(0, 0, 0)
},
{
color3_base<f32>(0, 0, 1.0f / 3.0f),
color3_base<f32>(0, 0, 1.0f / 3.0f),
color3_base<f32>(0, 0, 1.0f / 3.0f)
}
)},
};
void stereo_config::update_from_config(bool stereo_enabled)
{
m_stereo_mode = stereo_enabled ? g_cfg.video.stereo_render_mode.get() : stereo_render_mode_options::disabled;
if (m_stereo_mode == stereo_render_mode_options::anaglyph_custom)
{
stereo_config::convert_matrix(m_custom_matrices.left, g_cfg.video.custom_anaglyph_matrices.left.get_map());
stereo_config::convert_matrix(m_custom_matrices.right, g_cfg.video.custom_anaglyph_matrices.right.get_map());
}
}
const stereo_config::stereo_matrices& stereo_config::matrices() const
{
if (m_in_emulation && s_live_preview_enabled)
{
return m_live_matrices;
}
switch (m_stereo_mode)
{
case stereo_render_mode_options::disabled:
case stereo_render_mode_options::side_by_side:
case stereo_render_mode_options::over_under:
case stereo_render_mode_options::interlaced:
break;
case stereo_render_mode_options::anaglyph_red_green:
case stereo_render_mode_options::anaglyph_red_blue:
case stereo_render_mode_options::anaglyph_red_cyan:
case stereo_render_mode_options::anaglyph_magenta_cyan:
case stereo_render_mode_options::anaglyph_trioscopic:
case stereo_render_mode_options::anaglyph_amber_blue:
return ::at32(m_matrices, m_stereo_mode);
case stereo_render_mode_options::anaglyph_custom:
return m_custom_matrices;
}
static const stereo_matrices s_left_only_matrices = stereo_matrices(
{
color3_base<float>(1, 0, 0),
color3_base<float>(0, 1, 0),
color3_base<float>(0, 0, 1)
},
{
color3_base<float>(0, 0, 0),
color3_base<float>(0, 0, 0),
color3_base<float>(0, 0, 0)
});
return s_left_only_matrices;
}
std::map<std::string, std::string> stereo_config::get_custom_matrix(bool is_left) const
{
return convert_matrix(is_left ? m_custom_matrices.left : m_custom_matrices.right);
}
std::map<std::string, std::string> stereo_config::convert_matrix(const mat3f& mat)
{
std::map<std::string, std::string> values {};
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
values[fmt::format("%d%d", i, j)] = fmt::format("%f", mat[i].rgb[j]);
}
}
return values;
}
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include "geometry.h"
#include "Emu/system_config_types.h"
#include "util/atomic.hpp"
#include "Utilities/StrFmt.h"
#include "Utilities/StrUtil.h"
#include <map>
#include <mutex>
struct stereo_config
{
public:
struct stereo_matrices
{
stereo_matrices() = default;
stereo_matrices(const std::array<color3_base<f32>, 3>& l, const std::array<color3_base<f32>, 3>& r);
mat3f left {};
mat3f right {};
};
stereo_config(bool in_emulation) : m_in_emulation(in_emulation) {}
void set_stereo_mode(stereo_render_mode_options mode) { m_stereo_mode = mode; }
stereo_render_mode_options stereo_mode() const { return m_stereo_mode; }
const stereo_matrices& matrices() const;
void update_from_config(bool stereo_enabled);
void set_custom_matrices(const stereo_matrices& matrices) { m_custom_matrices = matrices; }
static void set_live_matrices(const stereo_matrices& matrices) { m_live_matrices = matrices; }
std::map<std::string, std::string> get_custom_matrix(bool is_left) const;
template <typename T>
static void convert_matrix(mat3f& mat, const T& values)
{
mat[0] = {};
mat[1] = {};
mat[2] = {};
if (values.empty())
{
return;
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
const std::string key = fmt::format("%d%d", i, j);
const auto it = values.find(key);
if (it == values.cend()) continue;
const std::string& val_s = it->second;
if (val_s.empty()) continue;
if (f64 val = 0.0f; try_to_float(&val, val_s, -10.0f, 10.0f))
{
mat[i].rgb[j] = static_cast<f32>(val);
}
}
}
}
static std::map<std::string, std::string> convert_matrix(const mat3f& mat);
static atomic_t<bool> s_live_preview_enabled;
private:
stereo_matrices m_custom_matrices {};
stereo_render_mode_options m_stereo_mode = stereo_render_mode_options::disabled;
bool m_in_emulation = false;
static stereo_matrices m_live_matrices;
static const std::unordered_map<stereo_render_mode_options, stereo_matrices> m_matrices;
};
+1 -1
View File
@@ -74,7 +74,7 @@ enum
};
#endif
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, [[maybe_unused]] uint mask = 0)
{
#ifdef __linux__
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
@@ -349,24 +349,24 @@ QPushButton:disabled {
}
QDialog QDialogButtonBox QPushButton {
QDialog QDialogButtonBox QPushButton:default {
color: #000000;
background-color: #4CC2FF;
border-color: #5AC7FF;
}
QDialog QDialogButtonBox QPushButton:hover {
QDialog QDialogButtonBox QPushButton:default:hover {
background-color: #48B2E9;
border-color: #56B8EB;
}
QDialog QDialogButtonBox QPushButton:pressed {
QDialog QDialogButtonBox QPushButton:default:pressed {
color: #22526A;
background-color: #45A4D5;
border-color: #45A4D5;
}
QDialog QDialogButtonBox QPushButton:disabled {
QDialog QDialogButtonBox QPushButton:default:disabled {
color: #ABABAB;
background-color: #4C4C4C;
border-color: #4C4C4C;
@@ -614,10 +614,15 @@ QComboBox QAbstractItemView {
QComboBox QAbstractItemView::item {
color: #FFFFFF;
padding: 4px 6px;
margin: 2px 0px;
background-color: transparent;
border-radius: 4px;
}
QComboBox QAbstractItemView::item:hover {
background-color: #383838;
}
QComboBox QAbstractItemView::item:selected {
background-color: #383838;
}
@@ -642,7 +647,7 @@ QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
border-radius: 4px;
width: 8px;
height: 8px;
padding: 3px 4px;
padding: 3px 4px;
}
QSpinBox::up-button:hover, QSpinBox::down-button:hover,
@@ -709,6 +714,16 @@ QSlider::handle:horizontal:disabled {
image: url("GuiConfigs/dark/slider-handle-disabled.svg");
}
QSlider::sub-page:horizontal {
background-color: #4CC2FF;
border-radius: 2px;
height: 4px;
}
QSlider::sub-page:horizontal:disabled {
background-color: #767A7D;
}
/* LINE EDIT */
QLineEdit {
@@ -791,7 +806,7 @@ QMenuBar::item:pressed {
QMenu {
background-color: #2C2C2C;
border: 1px solid #131313;
border: 1px solid #3E3E3E;
padding: 4px 0px;
}
@@ -799,12 +814,12 @@ QMenu::item {
color: #FFFFFF;
background-color: transparent;
border-radius: 4px;
padding: 8px 20px;
padding: 4px 20px;
margin: 0px 4px;
}
QMenu::item:selected {
background-color: #383838;
background-color: #353535;
}
QMenu::item:disabled {
@@ -814,8 +829,8 @@ QMenu::item:disabled {
QMenu::separator {
height: 1px;
background: #131313;
margin: 4px 0px;
background: #3E3E3E;
margin: 4px 8px;
}
QMenu::indicator, QMenu::right-arrow {
@@ -349,24 +349,24 @@ QPushButton:disabled {
}
QDialog QDialogButtonBox QPushButton {
QDialog QDialogButtonBox QPushButton:default {
color: #FFFFFF;
background-color: #0067C0;
border-color: #1473C5;
}
QDialog QDialogButtonBox QPushButton:hover {
QDialog QDialogButtonBox QPushButton:default:hover {
background-color: #1A76C6;
border-color: #2C80CA;
}
QDialog QDialogButtonBox QPushButton:pressed {
QDialog QDialogButtonBox QPushButton:default:pressed {
color: #C2DAEF;
background-color: #3284CB;
border-color: #3284CB;
}
QDialog QDialogButtonBox QPushButton:disabled {
QDialog QDialogButtonBox QPushButton:default:disabled {
color: #FFFFFF;
background-color: #C5C5C5;
border-color: #C5C5C5;
@@ -614,10 +614,15 @@ QComboBox QAbstractItemView {
QComboBox QAbstractItemView::item {
color: #1B1B1B;
padding: 4px 6px;
margin: 2px 0px;
background-color: transparent;
border-radius: 4px;
}
QComboBox QAbstractItemView::item:hover {
background-color: #F0F0F0;
}
QComboBox QAbstractItemView::item:selected {
background-color: #F0F0F0;
}
@@ -642,7 +647,7 @@ QDoubleSpinBox::up-button, QDoubleSpinBox::down-button {
border-radius: 4px;
width: 8px;
height: 8px;
padding: 3px 4px;
padding: 3px 4px;
}
QSpinBox::up-button:hover, QSpinBox::down-button:hover,
@@ -709,6 +714,16 @@ QSlider::handle:horizontal:disabled {
image: url("GuiConfigs/light/slider-handle-disabled.svg");
}
QSlider::sub-page:horizontal {
background-color: #1A76C6;
border-radius: 2px;
height: 4px;
}
QSlider::sub-page:horizontal:disabled {
background-color: #868788;
}
/* LINE EDIT */
QLineEdit {
@@ -791,7 +806,7 @@ QMenuBar::item:pressed {
QMenu {
background-color: #F9F9F9;
border: 1px solid #EEEEEE;
border: 1px solid #E5E5E5;
padding: 4px 0px;
}
@@ -799,7 +814,7 @@ QMenu::item {
color: #1B1B1B;
background-color: transparent;
border-radius: 4px;
padding: 8px 20px;
padding: 4px 20px;
margin: 0px 4px;
}
@@ -814,8 +829,8 @@ QMenu::item:disabled {
QMenu::separator {
height: 1px;
background: #EEEEEE;
margin: 4px 0px;
background: #E5E5E5;
margin: 4px 8px;
}
QMenu::indicator, QMenu::right-arrow {
+3
View File
@@ -3,6 +3,9 @@
<!-- Common default props for defining properties without macros. Import right before Microsoft.Cpp.Default.props -->
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<EnableLibUsbWindowsHotplug>true</EnableLibUsbWindowsHotplug>
</PropertyGroup>
<PropertyGroup Label="Globals">
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
+2
View File
@@ -9,6 +9,8 @@
<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>
</PropertyGroup>
<ItemDefinitionGroup>
<Lib>
-1
View File
@@ -2,7 +2,6 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup>
<ClCompile>
<Optimization>Full</Optimization>
+2
View File
@@ -182,11 +182,13 @@ 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_rsx_cfg.cpp
tests/test_rsx_fp_asm.cpp
tests/test_dmux_pamf.cpp
+2 -1
View File
@@ -807,7 +807,8 @@ bool package_reader::set_install_path()
}
// TODO: Verify whether other content types require appending title ID
if (m_metadata.content_type != PKG_CONTENT_TYPE_LICENSE)
// 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 + '/';
// If false, an existing directory is being overwritten: cannot cancel the operation
+2 -2
View File
@@ -126,7 +126,7 @@ struct PKGEntry
struct PKGMetaData
{
private:
static std::string to_hex_string(u8 buf[], usz size)
static std::string to_hex_string(const u8* buf, usz size)
{
std::stringstream sstream;
for (usz i = 0; i < size; i++)
@@ -135,7 +135,7 @@ private:
}
return sstream.str();
}
static std::string to_hex_string(u8 buf[], usz size, usz dotpos)
static std::string to_hex_string(const u8* buf, usz size, usz dotpos)
{
std::string result = to_hex_string(buf, size);
if (result.size() > dotpos)
+19 -7
View File
@@ -807,6 +807,8 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
self_f.seek(0);
sce_hdr.Load(self_f);
const usz self_size = self_f.size();
if (out_info)
{
*out_info = {};
@@ -880,8 +882,9 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
for(u32 i = 0; i < (isElf32 ? elf32_hdr.e_phnum : elf64_hdr.e_phnum); ++i)
{
if (self_f.pos() >= self_f.size())
if (self_f.pos() >= self_size)
{
// Read out of bounds (file is truncated or corrupted)
return false;
}
@@ -889,15 +892,23 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
m_seg_ext_hdr.back().Load(self_f);
}
if (m_ext_hdr.version_hdr_offset == 0 || utils::add_saturate<u64>(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_f.size())
if (m_ext_hdr.version_hdr_offset == 0)
{
// 0.85 Selfs have version_hdr_offset set to 0
m_version_hdr = {};
}
else if (utils::add_saturate<u64>(m_ext_hdr.version_hdr_offset, sizeof(version_header)) > self_size)
{
// Read out of bounds (file is truncated or corrupted)
return false;
}
else
{
// Read SCE version info.
self_f.seek(m_ext_hdr.version_hdr_offset);
// Read SCE version info.
self_f.seek(m_ext_hdr.version_hdr_offset);
m_version_hdr.Load(self_f);
m_version_hdr.Load(self_f);
}
// Read control info.
m_supplemental_hdr_arr.clear();
@@ -905,8 +916,9 @@ bool SELFDecrypter::LoadHeaders(bool isElf32, SelfAdditionalInfo* out_info)
for (u64 i = 0; i < m_ext_hdr.supplemental_hdr_size;)
{
if (self_f.pos() >= self_f.size())
if (self_f.pos() >= self_size)
{
// Read out of bounds (file is truncated or corrupted)
return false;
}
+26 -1
View File
@@ -23,7 +23,32 @@
// Auxiliary functions (endian swap, xor).
// Hex string conversion auxiliary functions.
// Bytes conversion auxiliary function.
void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length)
{
size_t str_length = data_length * 2;
hex_str.resize(str_length);
for (size_t i = 0; i < str_length; i += 2)
{
const auto [ptr, err] = std::to_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16);
if (err != std::errc())
{
fmt::throw_exception("Failed to read bytes: %s", std::make_error_code(err).message());
}
// Padding handling for values < 0x10 (e.g. 0x05 becomes "5" instead of "05")
// If to_chars only writes 1 character, we move to the right and put '0'
if (ptr == &hex_str[i] + 1)
{
hex_str[i + 1] = hex_str[i];
hex_str[i] = '0';
}
}
}
// Hex string conversion auxiliary function.
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length)
{
const auto strn_length = (str_length > 0) ? str_length : hex_str.size();
+4 -1
View File
@@ -15,7 +15,10 @@ char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PA
std::string sha256_get_hash(const char* data, usz size, bool lower_case);
// Hex string conversion auxiliary functions.
// Bytes conversion auxiliary function.
void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length);
// Hex string conversion auxiliary function.
void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length);
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
+218 -78
View File
@@ -13,82 +13,112 @@ LOG_CHANNEL(FAudio_, "FAudio");
FAudioBackend::FAudioBackend()
: AudioBackend()
{
FAudio *instance;
FAudio *instance = nullptr;
if (u32 res = FAudioCreate(&instance, 0, FAUDIO_DEFAULT_PROCESSOR))
const u32 res = FAudioCreate(&instance, 0, FAUDIO_DEFAULT_PROCESSOR);
if (res != 0)
{
FAudio_.error("FAudioCreate() failed(0x%08x)", res);
FAudio_.error("FAudioCreate() failed with error code 0x%08x", res);
return;
}
if (!instance)
{
FAudio_.error("FAudioCreate() succeeded but returned null instance");
return;
}
// Initialize callbacks
OnProcessingPassStart = nullptr;
OnProcessingPassEnd = nullptr;
OnCriticalError = OnCriticalError_func;
if (u32 res = FAudio_RegisterForCallbacks(instance, this))
const u32 callback_res = FAudio_RegisterForCallbacks(instance, this);
if (callback_res != 0)
{
// Some error recovery functionality will be lost, but otherwise backend is operational
FAudio_.error("FAudio_RegisterForCallbacks() failed(0x%08x)", res);
FAudio_.warning("FAudio_RegisterForCallbacks() failed with error code 0x%08x - error recovery may be limited", callback_res);
}
// All succeeded, "commit"
// All succeeded, commit the instance
m_instance = instance;
FAudio_.success("FAudio backend initialized successfully");
}
FAudioBackend::~FAudioBackend()
{
Close();
if (m_instance != nullptr)
if (m_instance)
{
// Unregister callbacks before stopping engine
FAudio_UnregisterForCallbacks(m_instance, this);
FAudio_StopEngine(m_instance);
FAudio_Release(m_instance);
m_instance = nullptr;
}
FAudio_.notice("FAudio backend destroyed successfully");
}
void FAudioBackend::Play()
{
if (m_source_voice == nullptr)
if (!m_source_voice)
{
FAudio_.error("Play() called uninitialized");
FAudio_.error("Play() called with uninitialized source voice");
return;
}
if (m_playing) return;
std::lock_guard lock(m_cb_mutex);
m_playing = true;
if (!m_playing)
{
m_playing = true;
FAudio_.trace("Audio playback started");
}
}
void FAudioBackend::Pause()
{
if (m_source_voice == nullptr)
if (!m_source_voice)
{
FAudio_.error("Pause() called uninitialized");
FAudio_.error("Pause() called with uninitialized source voice");
return;
}
if (!m_playing) return;
bool was_playing = false;
{
std::lock_guard lock(m_cb_mutex);
m_playing = false;
m_last_sample.fill(0);
was_playing = m_playing;
if (m_playing)
{
m_playing = false;
m_last_sample.fill(0);
}
}
if (u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice))
if (was_playing)
{
FAudio_.error("FAudioSourceVoice_FlushSourceBuffers() failed(0x%08x)", res);
const u32 res = FAudioSourceVoice_FlushSourceBuffers(m_source_voice);
if (res != 0)
{
FAudio_.error("FAudioSourceVoice_FlushSourceBuffers() failed with error code 0x%08x", res);
}
else
{
FAudio_.trace("Audio playback paused and buffers flushed");
}
}
}
void FAudioBackend::CloseUnlocked()
{
if (m_source_voice != nullptr)
if (m_source_voice)
{
if (u32 res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW))
const u32 stop_res = FAudioSourceVoice_Stop(m_source_voice, 0, FAUDIO_COMMIT_NOW);
if (stop_res != 0)
{
FAudio_.error("FAudioSourceVoice_Stop() failed(0x%08x)", res);
FAudio_.error("FAudioSourceVoice_Stop() failed with error code 0x%08x", stop_res);
}
FAudioVoice_DestroyVoice(m_source_voice);
@@ -103,6 +133,10 @@ void FAudioBackend::CloseUnlocked()
m_playing = false;
m_last_sample.fill(0);
m_data_buf.clear();
m_data_buf.shrink_to_fit(); // Free memory
FAudio_.trace("FAudio voices and resources cleaned up");
}
void FAudioBackend::Close()
@@ -118,34 +152,41 @@ bool FAudioBackend::Initialized()
bool FAudioBackend::Operational()
{
return m_source_voice != nullptr && !m_reset_req.observe();
return m_source_voice != nullptr && m_instance != nullptr && !m_reset_req.observe();
}
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
{
if (!Initialized())
{
FAudio_.error("Open() called uninitialized");
FAudio_.error("Open() called on uninitialized backend");
return false;
}
FAudio_.notice("Opening FAudio device: dev_id='%s', freq=%u, sample_size=%u, channels=%u",
dev_id.empty() ? "default" : std::string(dev_id).c_str(),
static_cast<u32>(freq), static_cast<u32>(sample_size), static_cast<u32>(ch_cnt));
std::lock_guard lock(m_cb_mutex);
CloseUnlocked();
const bool use_default_dev = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
u64 devid{};
u64 devid = 0;
if (!use_default_dev)
{
if (!try_to_uint64(&devid, dev_id, 0, UINT32_MAX))
{
FAudio_.error("Invalid device id - %s", dev_id);
FAudio_.error("Invalid device ID format: '%s'", dev_id);
return false;
}
}
if (u32 res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr))
const u32 master_res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice,
FAUDIO_DEFAULT_CHANNELS, FAUDIO_DEFAULT_SAMPLERATE, 0, static_cast<u32>(devid), nullptr);
if (master_res != 0)
{
FAudio_.error("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
FAudio_.error("FAudio_CreateMasteringVoice() failed with error code 0x%08x", master_res);
m_master_voice = nullptr;
return false;
}
@@ -155,19 +196,22 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
if (vd.InputChannels == 0)
{
FAudio_.error("Channel count of 0 is invalid");
FAudio_.error("Master voice reports invalid channel count of 0");
CloseUnlocked();
return false;
}
FAudio_.notice("Channel count is %d", vd.InputChannels);
FAudio_.notice("Master voice created with %u input channels", vd.InputChannels);
// Store audio parameters
m_sampling_rate = freq;
m_sample_size = sample_size;
// Setup channel layout with validation
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, FAudio_);
FAudioWaveFormatEx waveformatex;
// Configure wave format
FAudioWaveFormatEx waveformatex{};
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
waveformatex.nChannels = get_channels();
waveformatex.nSamplesPerSec = get_sampling_rate();
@@ -176,6 +220,11 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
waveformatex.wBitsPerSample = get_sample_size() * 8;
waveformatex.cbSize = 0;
FAudio_.trace("Wave format: %s, %u channels, %u Hz, %u bits per sample",
get_convert_to_s16() ? "PCM" : "IEEE_FLOAT",
waveformatex.nChannels, waveformatex.nSamplesPerSec, waveformatex.wBitsPerSample);
// Initialize voice callbacks
OnVoiceProcessingPassStart = OnVoiceProcessingPassStart_func;
OnVoiceProcessingPassEnd = nullptr;
OnStreamEnd = nullptr;
@@ -184,92 +233,183 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
OnLoopEnd = nullptr;
OnVoiceError = nullptr;
if (u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))
const u32 source_res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex,
0, FAUDIO_DEFAULT_FREQ_RATIO, this, nullptr, nullptr);
if (source_res != 0)
{
FAudio_.error("FAudio_CreateSourceVoice() failed(0x%08x)", res);
FAudio_.error("FAudio_CreateSourceVoice() failed with error code 0x%08x", source_res);
CloseUnlocked();
return false;
}
if (u32 res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW))
const u32 start_res = FAudioSourceVoice_Start(m_source_voice, 0, FAUDIO_COMMIT_NOW);
if (start_res != 0)
{
FAudio_.error("FAudioSourceVoice_Start() failed(0x%08x)", res);
FAudio_.error("FAudioSourceVoice_Start() failed with error code 0x%08x", start_res);
CloseUnlocked();
return false;
}
if (u32 res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW))
const u32 volume_res = FAudioVoice_SetVolume(m_source_voice, 1.0f, FAUDIO_COMMIT_NOW);
if (volume_res != 0)
{
FAudio_.error("FAudioVoice_SetVolume() failed(0x%08x)", res);
FAudio_.warning("FAudioVoice_SetVolume() failed with error code 0x%08x", volume_res);
}
m_data_buf.resize(get_sampling_rate() * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000);
// Pre-allocate audio buffer with some safety margin
const usz buffer_size = static_cast<usz>(get_sampling_rate()) * get_sample_size() * get_channels() * INTERNAL_BUF_SIZE_MS / 1000;
m_data_buf.reserve(buffer_size + 1024); // Extra safety margin
m_data_buf.resize(buffer_size);
FAudio_.success("FAudio device opened successfully - %u Hz, %u channels, %u-bit %s",
get_sampling_rate(), get_channels(), get_sample_size() * 8,
get_convert_to_s16() ? "PCM" : "float");
return true;
}
f64 FAudioBackend::GetCallbackFrameLen()
{
constexpr f64 _10ms = 0.01;
constexpr f64 fallback_10ms = 0.01;
if (m_instance == nullptr)
if (!m_instance)
{
FAudio_.error("GetCallbackFrameLen() called uninitialized");
return _10ms;
FAudio_.error("GetCallbackFrameLen() called on uninitialized instance");
return fallback_10ms;
}
f64 min_latency{};
u32 samples_per_quantum = 0;
u32 frequency = 0;
FAudio_GetProcessingQuantum(m_instance, &samples_per_quantum, &frequency);
u32 samples_per_q = 0, freq = 0;
FAudio_GetProcessingQuantum(m_instance, &samples_per_q, &freq);
if (freq)
if (frequency == 0 || samples_per_quantum == 0)
{
min_latency = static_cast<f64>(samples_per_q) / freq;
FAudio_.warning("Invalid processing quantum values: samples=%u, freq=%u", samples_per_quantum, frequency);
return fallback_10ms;
}
return std::max<f64>(min_latency, _10ms);
const f64 calculated_latency = static_cast<f64>(samples_per_quantum) / frequency;
const f64 final_latency = std::max(calculated_latency, fallback_10ms);
FAudio_.trace("Calculated frame length: %.3f ms (quantum: %u samples @ %u Hz)",
final_latency * 1000.0, samples_per_quantum, frequency);
return final_latency;
}
void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj, u32 BytesRequired)
{
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && faudio->m_write_callback && faudio->m_playing)
if (!cb_obj || BytesRequired == 0)
{
ensure(BytesRequired <= faudio->m_data_buf.size(), "FAudio internal buffer is too small. Report to developers!");
const u32 sample_size = faudio->get_sample_size() * faudio->get_channels();
u32 written = std::min(faudio->m_write_callback(BytesRequired, faudio->m_data_buf.data()), BytesRequired);
written -= written % sample_size;
if (written >= sample_size)
{
memcpy(faudio->m_last_sample.data(), faudio->m_data_buf.data() + written - sample_size, sample_size);
}
for (u32 i = written; i < BytesRequired; i += sample_size)
{
memcpy(faudio->m_data_buf.data() + i, faudio->m_last_sample.data(), sample_size);
}
FAudioBuffer buffer{};
buffer.AudioBytes = BytesRequired;
buffer.pAudioData = static_cast<const u8*>(faudio->m_data_buf.data());
// Avoid logging in callback and assume that this always succeeds, all errors are caught by error callback anyway
FAudioSourceVoice_SubmitSourceBuffer(faudio->m_source_voice, &buffer, nullptr);
return; // Nothing to do
}
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
// Validate backend state
if (!faudio->m_source_voice || faudio->m_reset_req.observe())
{
return; // Backend is in invalid state
}
// Use a shorter timeout to avoid blocking the audio thread
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
if (!lock.try_lock_for(std::chrono::microseconds{100}))
{
return; // Could not acquire lock in reasonable time
}
// Double-check state after acquiring lock
if (!faudio->m_write_callback || !faudio->m_playing)
{
return; // No callback or not playing
}
// Validate buffer size
if (BytesRequired > faudio->m_data_buf.size())
{
// This should never happen with proper initialization, but let's be safe
FAudio_.error("FAudio internal buffer is too small (%u requested, %zu available). Report to developers!",
BytesRequired, faudio->m_data_buf.size());
return;
}
const u32 sample_size = faudio->get_sample_size() * faudio->get_channels();
if (sample_size == 0)
{
return; // Invalid configuration
}
// Get audio data from callback
u32 written = 0;
if (faudio->m_write_callback)
{
written = faudio->m_write_callback(BytesRequired, faudio->m_data_buf.data());
written = std::min(written, BytesRequired);
written -= written % sample_size; // Align to sample boundary
}
// Update last sample for padding if we got any data
if (written >= sample_size)
{
const usz last_sample_offset = written - sample_size;
if (last_sample_offset + sample_size <= faudio->m_data_buf.size())
{
std::memcpy(faudio->m_last_sample.data(),
faudio->m_data_buf.data() + last_sample_offset,
std::min(sample_size, static_cast<u32>(faudio->m_last_sample.size())));
}
}
// Pad remaining buffer with last sample to avoid audio glitches
for (u32 i = written; i < BytesRequired; i += sample_size)
{
const u32 copy_size = std::min(sample_size, BytesRequired - i);
if (i + copy_size <= faudio->m_data_buf.size())
{
std::memcpy(faudio->m_data_buf.data() + i, faudio->m_last_sample.data(), copy_size);
}
}
// Submit buffer to FAudio
FAudioBuffer buffer{};
buffer.AudioBytes = BytesRequired;
buffer.pAudioData = static_cast<const u8*>(faudio->m_data_buf.data());
buffer.Flags = 0;
buffer.PlayBegin = 0;
buffer.PlayLength = 0;
buffer.LoopBegin = 0;
buffer.LoopLength = 0;
buffer.LoopCount = 0;
buffer.pContext = nullptr;
// Submit buffer - errors are handled by the error callback
FAudioSourceVoice_SubmitSourceBuffer(faudio->m_source_voice, &buffer, nullptr);
}
void FAudioBackend::OnCriticalError_func(FAudioEngineCallback *cb_obj, u32 Error)
{
FAudio_.error("OnCriticalError() failed(0x%08x)", Error);
// Log the critical error
FAudio_.error("OnCriticalError() - FAudio engine encountered critical error 0x%08x", Error);
if (!cb_obj)
{
FAudio_.error("OnCriticalError() called with null callback object");
return;
}
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
// Use try_lock to avoid potential deadlock in error conditions
std::unique_lock lock(faudio->m_state_cb_mutex, std::try_to_lock);
if (!lock.owns_lock())
{
FAudio_.warning("Could not acquire state callback mutex during critical error handling");
return;
}
std::lock_guard lock(faudio->m_state_cb_mutex);
// Set reset request flag and notify application if we haven't already
if (!faudio->m_reset_req.test_and_set() && faudio->m_state_callback)
{
faudio->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
+14 -1
View File
@@ -77,6 +77,7 @@ target_sources(rpcs3_emu PRIVATE
../../Utilities/sema.cpp
../../Utilities/simple_ringbuf.cpp
../../Utilities/stack_trace.cpp
../../Utilities/stereo_config.cpp
../../Utilities/StrFmt.cpp
../../Utilities/Thread.cpp
../../Utilities/version.cpp
@@ -126,6 +127,8 @@ target_sources(rpcs3_emu PRIVATE
../Loader/PUP.cpp
../Loader/TAR.cpp
../Loader/ISO.cpp
../Loader/iso_cache.cpp
../Loader/iso_validation.cpp
../Loader/TROPUSR.cpp
../Loader/TRP.cpp
)
@@ -160,7 +163,7 @@ if(WIN32)
Audio/XAudio2/xaudio2_enumerator.cpp
)
target_compile_definitions(rpcs3_emu PRIVATE UNICODE _UNICODE _WIN32_WINNT=0x0A00)
target_link_libraries(rpcs3_emu PRIVATE pdh bcrypt)
target_link_libraries(rpcs3_emu PRIVATE pdh bcrypt dbghelp)
endif()
# Cell
@@ -333,9 +336,17 @@ target_sources(rpcs3_emu PRIVATE
Cell/Modules/HLE_PATCHES.cpp
Cell/Modules/libad_async.cpp
Cell/Modules/libad_core.cpp
Cell/Modules/libavcdec.cpp
Cell/Modules/libdivx311dec.cpp
Cell/Modules/libdivxdec.cpp
Cell/Modules/libmedi.cpp
Cell/Modules/libmixer.cpp
Cell/Modules/libmvcdec.cpp
Cell/Modules/libsjvtd.cpp
Cell/Modules/libsmvd2.cpp
Cell/Modules/libsmvd4.cpp
Cell/Modules/libsnd3.cpp
Cell/Modules/libsvc1d.cpp
Cell/Modules/libsynth2.cpp
Cell/Modules/sceNp2.cpp
Cell/Modules/sceNpClans.cpp
@@ -399,6 +410,7 @@ target_sources(rpcs3_emu PRIVATE
Io/Dimensions.cpp
Io/evdev_gun_handler.cpp
Io/GameTablet.cpp
Io/gem_config.cpp
Io/GHLtar.cpp
Io/GunCon3.cpp
Io/Infinity.cpp
@@ -540,6 +552,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Program/GLSLCommon.cpp
RSX/Program/ProgramStateCache.cpp
RSX/Program/program_util.cpp
RSX/Program/ShaderInterpreter.cpp
RSX/Program/SPIRVCommon.cpp
RSX/Program/VertexProgramDecompiler.cpp
RSX/GSFrameBase.cpp
@@ -39,7 +39,7 @@ namespace aarch64
if (head->magic == ESR_CTX_MAGIC)
{
return reinterpret_cast<const aarch64_esr_ctx*>(head);
return utils::bless<const aarch64_esr_ctx>(head);
}
offset += head->size;
+9 -2
View File
@@ -61,6 +61,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::notify: return "ntf";
case cpu_flag::yield: return "y";
case cpu_flag::preempt: return "PREEMPT";
case cpu_flag::req_exit: return "REQ-EXIT";
case cpu_flag::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
@@ -729,8 +730,14 @@ void cpu_thread::operator()()
{
if (_this)
{
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
cleanup();
auto log_thread = named_thread("CPU Thread Cleanup Logger", [name = name]()
{
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
});
log_thread();
}
}
} cleanup;
@@ -1502,7 +1509,7 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept
{
for (u32 i = 0; i < work->prf_size; i++)
{
utils::prefetch_write(work->prf_list[0]);
utils::prefetch_write(work->prf_list[i]);
}
}
+64 -26
View File
@@ -207,10 +207,25 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
{
m_use_dotprod = true;
}
if (utils::has_i8mm())
{
m_use_i8mm = true;
}
if (utils::has_sve() && utils::sve_length() == 128)
{
m_use_sve_128 = true;
}
if (utils::has_sve2() && utils::sve_length() == 128)
{
m_use_sve2_128 = true;
}
#endif
}
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc) const
{
uint s1 = type->getScalarSizeInBits();
uint s2 = val->getType()->getScalarSizeInBits();
@@ -222,7 +237,7 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
if (s1 != s2)
{
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)", s1, s2);
fmt::throw_exception("cpu_translator::bitcast(): incompatible type sizes (%u vs %u)\nCalled from: %s", s1, s2, src_loc);
}
if (val->getType() == type)
@@ -244,36 +259,48 @@ llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const
}
}
for (auto it = source_val->use_begin(); it != source_val->use_end(); ++it)
// Skip use iteration for values that don't have use lists
#if LLVM_VERSION_MAJOR >= 21
if (source_val->hasUseList())
#endif
{
llvm::Value* it_val = *it;
if (!it_val)
for (llvm::Value* it_val : source_val->uses())
{
continue;
}
llvm::CastInst* bci = llvm::dyn_cast_or_null<llvm::CastInst>(it_val);
// Walk through bitcasts
while (bci && bci->getOpcode() == llvm::Instruction::BitCast)
{
if (bci->getParent() != m_ir->GetInsertBlock())
if (!it_val)
{
break;
continue;
}
if (bci->getType() == type)
{
return bci;
}
llvm::CastInst* bci = llvm::dyn_cast_or_null<llvm::CastInst>(it_val);
if (bci->use_begin() == bci->use_end())
// Walk through bitcasts
while (bci && bci->getOpcode() == llvm::Instruction::BitCast)
{
break;
}
if (bci->getParent() != m_ir->GetInsertBlock())
{
break;
}
bci = llvm::dyn_cast_or_null<llvm::CastInst>(*bci->use_begin());
if (bci->getType() == type)
{
return bci;
}
// Check if bci has use list before accessing use_begin()
#if LLVM_VERSION_MAJOR >= 21
if (!bci->hasUseList())
{
break;
}
#endif
if (bci->use_begin() == bci->use_end())
{
break;
}
bci = llvm::dyn_cast_or_null<llvm::CastInst>(*bci->use_begin());
}
}
}
@@ -553,14 +580,25 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
{
for (auto v : args)
{
for (auto it = v->use_begin(); it != v->use_end(); ++it)
// Skip use iteration for values that don't have use lists
#if LLVM_VERSION_MAJOR >= 21
if (!v->hasUseList())
continue;
#endif
for (llvm::Value* i : v->uses())
{
llvm::Value* i = *it;
llvm::CastInst* bci = nullptr;
// Walk through bitcasts
while (i && (bci = llvm::dyn_cast<llvm::CastInst>(i)) && bci->getOpcode() == llvm::Instruction::BitCast)
{
// Check if bci has use list before accessing use_begin()
#if LLVM_VERSION_MAJOR >= 21
if (!bci->hasUseList())
break;
#endif
i = *bci->use_begin();
}
+301 -3
View File
@@ -43,6 +43,7 @@
#include <functional>
#include <unordered_map>
#include <source_location>
// Helper function
llvm::Value* peek_through_bitcasts(llvm::Value*);
@@ -3119,6 +3120,16 @@ protected:
// ARMv8 SDOT/UDOT
bool m_use_dotprod = false;
// ARMv8.6 SMMLA/UMMLA
bool m_use_i8mm = false;
// Allow direct TBL2/TBX2 emission.
bool m_use_tbl2 = true;
bool m_use_sve_128 = false;
bool m_use_sve2_128 = false;
#else
// Allow FMA
bool m_use_fma = false;
@@ -3239,7 +3250,7 @@ public:
}
// Bitcast with immediate constant folding
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type) const;
llvm::Value* bitcast(llvm::Value* val, llvm::Type* type, std::source_location src_loc = std::source_location::current()) const;
template <typename T>
llvm::Value* bitcast(llvm::Value* val)
@@ -3719,8 +3730,170 @@ template <typename T1, typename T2, typename T3>
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_sdot), {data0, data1, data2});
return result;
}
template <typename T1, typename T2>
template <typename T1, typename T2, typename T3>
value_t<u32[4]> ummla(T1 a, T2 b, T3 c)
{
value_t<u32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_ummla), {data0, data1, data2});
return result;
}
template <typename T1, typename T2, typename T3>
value_t<u32[4]> smmla(T1 a, T2 b, T3 c)
{
value_t<u32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_smmla), {data0, data1, data2});
return result;
}
template <typename T1, typename T2>
value_t<s32[4]> smull(T1 a, T2 b)
{
value_t<s32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<s32[4]>(llvm::Intrinsic::aarch64_neon_smull), {data0, data1});
return result;
}
template <typename T1, typename T2>
value_t<u32[4]> umull(T1 a, T2 b)
{
value_t<u32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u32[4]>(llvm::Intrinsic::aarch64_neon_umull), {data0, data1});
return result;
}
llvm::Value* to_sve_vector(llvm::Value* value)
{
if (llvm::isa<llvm::ScalableVectorType>(value->getType()))
{
return value;
}
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(value->getType());
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
return m_ir->CreateInsertVector(scalable_type, llvm::UndefValue::get(scalable_type), value, m_ir->getInt64(0));
}
llvm::Value* from_sve_vector(llvm::Value* value, llvm::FixedVectorType* fixed_type)
{
if (value->getType() == fixed_type)
{
return value;
}
return m_ir->CreateExtractVector(fixed_type, value, m_ir->getInt64(0));
}
llvm::Value* sve_ptrue(llvm::FixedVectorType* fixed_type)
{
const auto pred_type = llvm::ScalableVectorType::get(m_ir->getInt1Ty(), fixed_type->getNumElements());
return m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_ptrue, {pred_type}, {m_ir->getInt32(31)});
}
llvm::Value* sve_fnmls(llvm::Value* acc, llvm::Value* lhs, llvm::Value* rhs)
{
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(acc->getType());
const auto vacc = to_sve_vector(acc);
const auto vlhs = to_sve_vector(lhs);
const auto vrhs = to_sve_vector(rhs);
const auto result = m_ir->CreateIntrinsic(llvm::Intrinsic::aarch64_sve_fnmls, {vacc->getType()}, {sve_ptrue(fixed_type), vacc, vlhs, vrhs});
return from_sve_vector(result, fixed_type);
}
template <typename T, typename T1, typename T2>
value_t<T> sve_mull(llvm::Intrinsic::ID id, T1 a, T2 b)
{
value_t<T> result;
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
const auto data0 = to_sve_vector(a.eval(m_ir));
const auto data1 = to_sve_vector(b.eval(m_ir));
const std::array<llvm::Type*, 1> types{scalable_type};
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1}), fixed_type);
return result;
}
template <typename T, typename T0, typename T1, typename T2>
value_t<T> sve_mlal(llvm::Intrinsic::ID id, T0 acc, T1 a, T2 b)
{
value_t<T> result;
const auto fixed_type = llvm::cast<llvm::FixedVectorType>(get_type<T>());
const auto scalable_type = llvm::ScalableVectorType::get(fixed_type->getElementType(), fixed_type->getNumElements());
const auto data0 = to_sve_vector(acc.eval(m_ir));
const auto data1 = to_sve_vector(a.eval(m_ir));
const auto data2 = to_sve_vector(b.eval(m_ir));
const std::array<llvm::Type*, 1> types{scalable_type};
result.value = from_sve_vector(m_ir->CreateIntrinsic(id, types, {data0, data1, data2}), fixed_type);
return result;
}
template <typename T1, typename T2>
value_t<s32[4]> sve_smullb(T1 a, T2 b)
{
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullb, a, b);
}
template <typename T1, typename T2>
value_t<s32[4]> sve_smullt(T1 a, T2 b)
{
return sve_mull<s32[4]>(llvm::Intrinsic::aarch64_sve_smullt, a, b);
}
template <typename T1, typename T2>
value_t<u32[4]> sve_umullb(T1 a, T2 b)
{
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullb, a, b);
}
template <typename T1, typename T2>
value_t<u32[4]> sve_umullt(T1 a, T2 b)
{
return sve_mull<u32[4]>(llvm::Intrinsic::aarch64_sve_umullt, a, b);
}
template <typename T0, typename T1, typename T2>
value_t<s32[4]> sve_smlalb(T0 acc, T1 a, T2 b)
{
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalb, acc, a, b);
}
template <typename T0, typename T1, typename T2>
value_t<s32[4]> sve_smlalt(T0 acc, T1 a, T2 b)
{
return sve_mlal<s32[4]>(llvm::Intrinsic::aarch64_sve_smlalt, acc, a, b);
}
template <typename T0, typename T1, typename T2>
value_t<u32[4]> sve_umlalt(T0 acc, T1 a, T2 b)
{
return sve_mlal<u32[4]>(llvm::Intrinsic::aarch64_sve_umlalt, acc, a, b);
}
template <typename T1, typename T2>
auto addp(T1 a, T2 b)
{
using T_vector = typename is_llvm_expr<T1>::type;
@@ -4029,6 +4202,131 @@ public:
});
}
#ifdef ARCH_ARM64
template <typename T1, typename T2>
value_t<u8[16]> tbl(T1 a, T2 b)
{
value_t<u8[16]> result;
const auto data0 = a.eval(m_ir);
const auto index = b.eval(m_ir);
const auto zeros = llvm::ConstantAggregateZero::get(get_type<u8[16]>());
if (auto c = llvm::dyn_cast<llvm::Constant>(index))
{
v128 mask{};
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
if (cv)
{
for (u32 i = 0; i < 16; i++)
{
const u64 b_val = cv->getElementAsInteger(i);
mask._u8[i] = (b_val < 16) ? static_cast<u8>(b_val) : static_cast<u8>(16);
}
}
if (cv || llvm::isa<llvm::ConstantAggregateZero>(c))
{
result.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&mask), 16));
result.value = m_ir->CreateZExt(result.value, get_type<u32[16]>());
result.value = m_ir->CreateShuffleVector(data0, zeros, result.value);
return result;
}
}
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data0, index });
return result;
}
template <typename T1, typename T2, typename T3>
value_t<u8[16]> tbl2(T1 a, T2 b, T3 indices)
{
value_t<u8[16]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto index = indices.eval(m_ir);
if (m_use_tbl2)
{
if (auto c = llvm::dyn_cast<llvm::Constant>(index))
{
v128 mask{};
v128 bitmask{};
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(c);
if (cv)
{
for (u32 i = 0; i < 16; i++)
{
const u64 b_val = cv->getElementAsInteger(i);
mask._u8[i] = (b_val < 32) ? static_cast<u8>(b_val) : static_cast<u8>(0);
bitmask._u8[i] = (b_val < 32) ? static_cast<u8>(0xFF) : static_cast<u8>(0x00);
}
}
else if (llvm::isa<llvm::ConstantAggregateZero>(c))
{
bitmask = v128::from8p(0xFF);
}
if (cv || llvm::isa<llvm::ConstantAggregateZero>(c))
{
auto m_val = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&mask), 16));
auto m_ext = m_ir->CreateZExt(m_val, get_type<u32[16]>());
auto lookup = m_ir->CreateShuffleVector(data0, data1, m_ext);
auto z_mask = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(reinterpret_cast<const u8*>(&bitmask), 16));
result.value = m_ir->CreateAnd(lookup, z_mask);
return result;
}
}
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl2), { data0, data1, index });
return result;
}
const auto data0_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data0, index });
const auto data1_index = m_ir->CreateSub(index, llvm::ConstantInt::get(get_type<u8[16]>(), 16));
const auto data1_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbl1), { data1, data1_index });
result.value = m_ir->CreateOr(data0_lookup, data1_lookup);
return result;
}
template <typename T1, typename T2, typename T3>
value_t<u8[16]> tbx(T1 fallback, T2 a, T3 indices)
{
value_t<u8[16]> result;
const auto v_fallback = fallback.eval(m_ir);
const auto data0 = a.eval(m_ir);
const auto index = indices.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { v_fallback, data0, index });
return result;
}
template <typename T1, typename T2, typename T3, typename T4>
value_t<u8[16]> tbx2(T1 fallback, T2 a, T3 b, T4 indices)
{
value_t<u8[16]> result;
const auto v_fallback = fallback.eval(m_ir);
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto index = indices.eval(m_ir);
if (m_use_tbl2)
{
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx2), { v_fallback, data0, data1, index });
return result;
}
const auto first_lookup = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { v_fallback, data0, index });
const auto data1_index = m_ir->CreateSub(index, llvm::ConstantInt::get(get_type<u8[16]>(), 16));
result.value = m_ir->CreateCall(get_intrinsic<u8[16]>(llvm::Intrinsic::aarch64_neon_tbx1), { first_lookup, data1, data1_index });
return result;
}
#endif
// (m << 3) >= 0 ? a : b
template <typename T, typename U, typename V>
static auto select_by_bit4(T&& m, U&& a, V&& b)
+1 -1
View File
@@ -4,7 +4,7 @@
namespace rpcs3
{
#if defined(ARCH_x64)
#if defined(ARCH_X64)
union hypervisor_context_t
{
u64 regs[1];
+2
View File
@@ -81,6 +81,8 @@
#if defined(__GNUC__) || defined(__clang__)
#pragma push_macro("FORCE_INLINE")
#pragma push_macro("ALIGN_STRUCT")
#undef FORCE_INLINE
#undef ALIGN_STRUCT
#define FORCE_INLINE static inline __attribute__((always_inline))
#define ALIGN_STRUCT(x) __attribute__((aligned(x)))
#define _sse2neon_likely(x) __builtin_expect(!!(x), 1)
+27 -27
View File
@@ -398,7 +398,7 @@ cell_audio_thread::cell_audio_thread(utils::serial& ar)
ar(key_count, event_period);
keys.resize(ar);
keys.resize(ar.pop<u64>());
for (key_info& k : keys)
{
@@ -421,7 +421,7 @@ void cell_audio_thread::save(utils::serial& ar)
USING_SERIALIZATION_VERSION(cellAudio);
ar(key_count, event_period);
ar(keys.size());
ar(static_cast<u64>(keys.size()));
for (const key_info& k : keys)
{
@@ -1662,36 +1662,35 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags)
lv2_sleep(20, &ppu);
// Dirty hack for sound: confirm the creation of _mxr000 event queue by _cellsurMixerMain thread
constexpr u64 c_mxr000 = 0x8000cafe0246030;
constexpr u64 c_mxr000 = 0x8000cafe02460300;
if (key == c_mxr000 || key == 0)
{
bool has_sur_mixer_thread = false;
for (usz count = 0; !lv2_event_queue::find(c_mxr000) && count < 100; count++)
const bool has_sur_mixer_thread = idm::select<named_thread<ppu_thread>>([&](u32 id, named_thread<ppu_thread>& test_ppu)
{
if (has_sur_mixer_thread || idm::select<named_thread<ppu_thread>>([&](u32 id, named_thread<ppu_thread>& test_ppu)
// Confirm thread existence
if (id == ppu.id)
{
// Confirm thread existence
if (id == ppu.id)
{
return false;
}
const auto ptr = test_ppu.ppu_tname.load();
if (!ptr)
{
return false;
}
return *ptr == "_cellsurMixerMain"sv;
}).ret)
{
has_sur_mixer_thread = true;
return false;
}
else
const auto ptr = test_ppu.ppu_tname.load();
if (!ptr)
{
return false;
}
return *ptr == "_cellsurMixerMain"sv;
}).ret;
bool was_mxr000_queue_found = false;
for (usz count = 0; has_sur_mixer_thread && count < 100; count++)
{
if (lv2_event_queue::find(c_mxr000))
{
was_mxr000_queue_found = true;
break;
}
@@ -1701,13 +1700,14 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags)
return {};
}
cellAudio.error("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count);
(count < 3 ? cellAudio.warning : cellAudio.error)("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count);
lv2_sleep(50'000, &ppu);
}
if (has_sur_mixer_thread && lv2_event_queue::find(c_mxr000))
if (key == 0 && was_mxr000_queue_found)
{
// Correct key value argument
key = c_mxr000;
}
}
+2 -10
View File
@@ -524,19 +524,11 @@ error_code cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<f32> screenSize)
{
// Return Playstation 3D display value
// Some games call this function when 3D is enabled
*screenSize = 24.f;
*screenSize = static_cast<f32>(g_cfg.video.screen_size.get());
return CELL_OK;
}
// TODO: Use virtual screen size
#ifdef _WIN32
// HDC screen = GetDC(NULL);
// float diagonal = roundf(sqrtf((powf(float(GetDeviceCaps(screen, HORZSIZE)), 2) + powf(float(GetDeviceCaps(screen, VERTSIZE)), 2))) * 0.0393f);
#else
// TODO: Linux implementation, without using wx
// float diagonal = roundf(sqrtf((powf(wxGetDisplaySizeMM().GetWidth(), 2) + powf(wxGetDisplaySizeMM().GetHeight(), 2))) * 0.0393f);
#endif
// Let's just return not set for now
return CELL_VIDEO_OUT_ERROR_VALUE_IS_NOT_SET;
}
File diff suppressed because it is too large Load Diff
+91 -31
View File
@@ -1,7 +1,8 @@
#pragma once
#include "Emu/Memory/vm_ptr.h"
#include "cellPamf.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Utilities/BitField.h"
// Error Codes
enum CellDmuxError :u32
@@ -18,6 +19,10 @@ enum CellDmuxStreamType : s32
CELL_DMUX_STREAM_TYPE_UNDEF = 0,
CELL_DMUX_STREAM_TYPE_PAMF = 1,
CELL_DMUX_STREAM_TYPE_TERMINATOR = 2,
// Only used in cellSail
CELL_DMUX_STREAM_TYPE_MP4 = 0x81,
CELL_DMUX_STREAM_TYPE_AVI = 0x82
};
enum CellDmuxMsgType : s32
@@ -48,13 +53,14 @@ struct CellDmuxEsMsg
struct CellDmuxType
{
be_t<s32> streamType; // CellDmuxStreamType
be_t<u32> reserved[2];
be_t<s32> reserved1;
be_t<s32> reserved2;
};
struct CellDmuxType2
{
be_t<s32> streamType; // CellDmuxStreamType
be_t<u32> streamSpecificInfo;
be_t<s32> streamType;
vm::bcptr<void> streamSpecificInfo;
};
struct CellDmuxResource
@@ -73,8 +79,8 @@ struct CellDmuxResourceEx
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> spurs_addr;
u8 priority[8];
vm::bptr<void> spurs; // CellSpurs*
be_t<u64, 1> priority;
be_t<u32> maxContention;
};
@@ -85,33 +91,23 @@ struct CellDmuxResourceSpurs
be_t<u32> maxContention;
};
/*
struct CellDmuxResource2Ex
{
b8 isResourceEx; //true
CellDmuxResourceEx resourceEx;
};
struct CellDmuxResource2NoEx
{
b8 isResourceEx; //false
CellDmuxResource resource;
};
*/
struct CellDmuxResource2
{
b8 isResourceEx;
be_t<u32> memAddr;
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> shit[4];
union
{
CellDmuxResource resource;
CellDmuxResourceEx resourceEx;
};
};
using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
struct DmuxContext;
struct DmuxEsContext;
using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
using CellDmuxCbMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
using CellDmuxCbEsMsg = u32(vm::ptr<DmuxContext> demuxerHandle, vm::ptr<DmuxEsContext> esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
// Used for internal callbacks as well
template <typename F>
@@ -177,6 +173,70 @@ struct DmuxAuInfo
be_t<u32> specific_info_size;
};
struct DmuxAuQueueElement
{
be_t<u32> index;
u8 unk; // unused
DmuxAuInfo au_info;
};
CHECK_SIZE(DmuxAuQueueElement, 0x38);
enum DmuxState : u32
{
DMUX_STOPPED = 1 << 0,
DMUX_RUNNING = 1 << 1,
};
struct alignas(0x10) DmuxContext // CellDmuxHandle = DmuxContext*
{
vm::bptr<DmuxContext> _this;
be_t<u32> _this_size;
be_t<u32> version;
be_t<u32> dmux_state;
CellDmuxType dmux_type;
CellDmuxCb dmux_cb;
b8 stream_is_set;
vm::bptr<void> core_handle;
be_t<u32> version_; // Same value as 'version'
be_t<u64> user_data;
be_t<s32> max_enabled_es_num;
be_t<s32> enabled_es_num;
be_t<u32> _dx_mhd; // sys_mutex_t
u8 reserved[0x7c];
};
CHECK_SIZE_ALIGN(DmuxContext, 0xc0, 0x10);
struct alignas(0x10) DmuxEsContext // CellDmuxEsHandle = DmuxEsContext*
{
be_t<u32> _dx_mes; // sys_mutex_t
be_t<u32> is_enabled;
be_t<u32> error_mem_size;
be_t<u32> error_count;
vm::bptr<void> error_mem_addr;
vm::bptr<DmuxEsContext> _this;
be_t<u32> _this_size;
be_t<s32> _this_index;
vm::bptr<DmuxContext> dmux_handle;
CellDmuxEsCb es_cb;
vm::bptr<void> core_es_handle;
bf_t<be_t<u32>, 0, 1> flush_started;
struct
{
be_t<s32> max_size;
be_t<s32> allocated_size;
be_t<s32> size;
be_t<s32> front;
be_t<s32> back;
be_t<s32> allocated_back;
}
au_queue;
};
CHECK_SIZE_ALIGN(DmuxEsContext, 0x50, 0x10);
using DmuxNotifyDemuxDone = error_code(vm::ptr<void>, u32, vm::ptr<void>);
using DmuxNotifyFatalErr = error_code(vm::ptr<void>, u32, vm::ptr<void>);
using DmuxNotifyProgEndCode = error_code(vm::ptr<void>, vm::ptr<void>);
@@ -194,10 +254,10 @@ using CellDmuxCoreOpSetStream = error_code(vm::ptr<void>, vm::cptr<void>, u32, b
using CellDmuxCoreOpReleaseAu = error_code(vm::ptr<void>, vm::ptr<void>, u32);
using CellDmuxCoreOpQueryEsAttr = error_code(vm::cptr<void>, vm::cptr<void>, vm::ptr<CellDmuxPamfEsAttr>);
using CellDmuxCoreOpEnableEs = error_code(vm::ptr<void>, vm::cptr<void>, vm::cptr<CellDmuxEsResource>, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>>, vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>>, vm::cptr<void>, vm::pptr<void>);
using CellDmuxCoreOpDisableEs = u32(vm::ptr<void>);
using CellDmuxCoreOpFlushEs = u32(vm::ptr<void>);
using CellDmuxCoreOpResetEs = u32(vm::ptr<void>);
using CellDmuxCoreOpResetStreamAndWaitDone = u32(vm::ptr<void>);
using CellDmuxCoreOpDisableEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpFlushEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpResetEs = error_code(vm::ptr<void>);
using CellDmuxCoreOpResetStreamAndWaitDone = error_code(vm::ptr<void>);
struct CellDmuxCoreOps
{
+1 -1
View File
@@ -2591,7 +2591,7 @@ template <bool raw_es>
error_code _CellDmuxCoreOpEnableEs(ppu_thread& ppu, vm::ptr<CellDmuxPamfHandle> handle, vm::cptr<void> esFilterId, vm::cptr<CellDmuxEsResource> esResource, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>> notifyAuFound,
vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>> notifyFlushDone, vm::cptr<void> esSpecificInfo, vm::pptr<CellDmuxPamfEsHandle> esHandle)
{
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle)",
cellDmuxPamf.notice("_CellDmuxCoreOpEnableEs<raw_es=%d>(handle=*0x%x, esFilterId=*0x%x, esResource=*0x%x, notifyAuFound=*0x%x, notifyFlushDone=*0x%x, esSpecificInfo=*0x%x, esHandle=**0x%x)",
raw_es, handle, esFilterId, esResource, notifyAuFound, notifyFlushDone, esSpecificInfo, esHandle);
if (!handle || !esFilterId || !esResource || !esResource->memAddr || esResource->memSize == 0u || !notifyAuFound || !notifyAuFound->cbFunc || !notifyAuFound->cbArg || !notifyFlushDone || !notifyFlushDone->cbFunc || !notifyFlushDone->cbArg)
+5 -4
View File
@@ -520,10 +520,11 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
return CELL_GAMEDATA_ERROR_PARAM;
}
if (!fs::create_path(vfs::get(usrdir)))
{
return {CELL_GAME_ERROR_ACCESS_ERROR, usrdir};
}
// Nuked until correctly reversed engineered
//if (!fs::create_path(vfs::get(usrdir)))
//{
// return {CELL_GAME_ERROR_ACCESS_ERROR, usrdir};
//}
}
// Nuked until correctly reversed engineered
+354 -87
View File
@@ -27,39 +27,6 @@
LOG_CHANNEL(cellGem);
template <>
void fmt_class_string<gem_btn>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](gem_btn value)
{
switch (value)
{
case gem_btn::start: return "Start";
case gem_btn::select: return "Select";
case gem_btn::triangle: return "Triangle";
case gem_btn::circle: return "Circle";
case gem_btn::cross: return "Cross";
case gem_btn::square: return "Square";
case gem_btn::move: return "Move";
case gem_btn::t: return "T";
case gem_btn::x_axis: return "X-Axis";
case gem_btn::y_axis: return "Y-Axis";
case gem_btn::combo: return "Combo";
case gem_btn::combo_start: return "Combo Start";
case gem_btn::combo_select: return "Combo Select";
case gem_btn::combo_triangle: return "Combo Triangle";
case gem_btn::combo_circle: return "Combo Circle";
case gem_btn::combo_cross: return "Combo Cross";
case gem_btn::combo_square: return "Combo Square";
case gem_btn::combo_move: return "Combo Move";
case gem_btn::combo_t: return "Combo T";
case gem_btn::count: return "Count";
}
return unknown;
});
}
template <>
void fmt_class_string<CellGemError>::format(std::string& out, u64 arg)
{
@@ -234,7 +201,7 @@ public:
struct gem_controller
{
u32 status = CELL_GEM_STATUS_DISCONNECTED; // Connection status (CELL_GEM_STATUS_DISCONNECTED or CELL_GEM_STATUS_READY)
u32 ext_status = CELL_GEM_NO_EXTERNAL_PORT_DEVICE; // External port connection status
u32 ext_status = 0; // External port connection status
u32 ext_id = 0; // External device ID (type). For example SHARP_SHOOTER_DEVICE_ID
u32 port = 0; // Assigned port
bool enabled_magnetometer = true; // Whether the magnetometer is enabled (probably used for additional rotational precision)
@@ -254,6 +221,8 @@ public:
u64 calibration_start_us{0}; // The start timestamp of the calibration in microseconds
u64 calibration_status_flags = 0; // The calibration status flags
u8 firing_mode = button_flags::ss_firing_mode_1; // The firing mode of the sharpshooter. Used with fake PS-Move. This is a physical switch with 3 positions, not a button that can be pressed.
static constexpr u64 calibration_time_us = 500000; // The calibration supposedly takes 0.5 seconds (500000 microseconds)
};
@@ -2149,6 +2118,337 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
}
}
static const std::unordered_map<gem_btn, u16> ext_btn_map =
{
{ gem_btn::sharpshooter_firing_mode_1, button_flags::ss_firing_mode_1 },
{ gem_btn::sharpshooter_firing_mode_2, button_flags::ss_firing_mode_2 },
{ gem_btn::sharpshooter_firing_mode_3, button_flags::ss_firing_mode_3 },
{ gem_btn::sharpshooter_trigger, button_flags::ss_trigger },
{ gem_btn::sharpshooter_reload, button_flags::ss_reload },
{ gem_btn::racing_wheel_d_pad_up, CELL_PAD_CTRL_UP },
{ gem_btn::racing_wheel_d_pad_right, CELL_PAD_CTRL_RIGHT },
{ gem_btn::racing_wheel_d_pad_down, CELL_PAD_CTRL_DOWN },
{ gem_btn::racing_wheel_d_pad_left, CELL_PAD_CTRL_LEFT },
{ gem_btn::racing_wheel_throttle, 0 },
{ gem_btn::racing_wheel_l1, CELL_PAD_CTRL_L1 },
{ gem_btn::racing_wheel_r1, CELL_PAD_CTRL_R1 },
{ gem_btn::racing_wheel_l2, 0 },
{ gem_btn::racing_wheel_r2, 0 },
{ gem_btn::racing_wheel_paddle_l, button_flags::rw_paddle_l },
{ gem_btn::racing_wheel_paddle_r, button_flags::rw_paddle_r },
{ gem_btn::combo_sharpshooter_firing_mode_1, button_flags::ss_firing_mode_1 },
{ gem_btn::combo_sharpshooter_firing_mode_2, button_flags::ss_firing_mode_2 },
{ gem_btn::combo_sharpshooter_firing_mode_3, button_flags::ss_firing_mode_3 },
{ gem_btn::combo_sharpshooter_trigger, button_flags::ss_trigger },
{ gem_btn::combo_sharpshooter_reload, button_flags::ss_reload },
{ gem_btn::combo_racing_wheel_d_pad_up, CELL_PAD_CTRL_UP },
{ gem_btn::combo_racing_wheel_d_pad_right, CELL_PAD_CTRL_RIGHT },
{ gem_btn::combo_racing_wheel_d_pad_down, CELL_PAD_CTRL_DOWN },
{ gem_btn::combo_racing_wheel_d_pad_left, CELL_PAD_CTRL_LEFT },
{ gem_btn::combo_racing_wheel_throttle, 0 },
{ gem_btn::combo_racing_wheel_l1, CELL_PAD_CTRL_L1 },
{ gem_btn::combo_racing_wheel_r1, CELL_PAD_CTRL_R1 },
{ gem_btn::combo_racing_wheel_l2, 0 },
{ gem_btn::combo_racing_wheel_r2, 0 },
{ gem_btn::combo_racing_wheel_paddle_l, button_flags::rw_paddle_l },
{ gem_btn::combo_racing_wheel_paddle_r, button_flags::rw_paddle_r },
};
static const std::unordered_map<gem_btn, u16> ext_btn_indices =
{
{ gem_btn::racing_wheel_throttle, 0 },
{ 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::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 },
};
static u32 ext_device_id(gem_ext_id id)
{
switch (id)
{
case gem_ext_id::disconnected: return 0;
case gem_ext_id::sharpshooter: return SHARP_SHOOTER_DEVICE_ID;
case gem_ext_id::racing_wheel: return RACING_WHEEL_DEVICE_ID;
}
fmt::throw_exception("Mo ID found for id = %d", static_cast<s32>(id));
}
template <bool has_combo, bool is_combo>
static void input_to_ext(u32 external_device_id, CellGemExtPortData& ext, std::set<pad_button>& combos, gem_btn btn, pad_button pad_btn, u16 value, bool pressed)
{
if (!pressed)
return;
const auto set_firing_mode = [&ext](gem_btn btn)
{
// The firing mode is exclusive
ext.custom[0] &= ~button_flags::ss_firing_mode_mask;
ext.custom[0] |= ::at32(ext_btn_map, btn);
};
if constexpr (has_combo && is_combo)
{
if (external_device_id == SHARP_SHOOTER_DEVICE_ID)
{
switch (btn)
{
case gem_btn::combo_sharpshooter_firing_mode_1:
case gem_btn::combo_sharpshooter_firing_mode_2:
case gem_btn::combo_sharpshooter_firing_mode_3:
set_firing_mode(btn);
combos.insert(pad_btn);
break;
case gem_btn::combo_sharpshooter_trigger:
case gem_btn::combo_sharpshooter_reload:
ext.custom[0] |= ::at32(ext_btn_map, btn);
combos.insert(pad_btn);
break;
default:
break;
}
}
else if (external_device_id == RACING_WHEEL_DEVICE_ID)
{
switch (btn)
{
case gem_btn::combo_racing_wheel_throttle:
case gem_btn::combo_racing_wheel_l2:
case gem_btn::combo_racing_wheel_r2:
ext.custom[::at32(ext_btn_indices, btn)] = static_cast<u8>(value);
combos.insert(pad_btn);
break;
case gem_btn::combo_racing_wheel_paddle_l:
case gem_btn::combo_racing_wheel_paddle_r:
ext.custom[::at32(ext_btn_indices, btn)] |= ::at32(ext_btn_map, btn);
combos.insert(pad_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:
ext.digital1 |= ::at32(ext_btn_map, btn);
combos.insert(pad_btn);
break;
case gem_btn::combo_racing_wheel_l1:
case gem_btn::combo_racing_wheel_r1:
ext.digital2 |= ::at32(ext_btn_map, btn);
combos.insert(pad_btn);
break;
default:
break;
}
}
}
else
{
if constexpr (has_combo)
{
if (combos.contains(pad_btn))
{
return;
}
}
if (external_device_id == SHARP_SHOOTER_DEVICE_ID)
{
switch (btn)
{
case gem_btn::sharpshooter_firing_mode_1:
case gem_btn::sharpshooter_firing_mode_2:
case gem_btn::sharpshooter_firing_mode_3:
set_firing_mode(btn);
break;
case gem_btn::sharpshooter_trigger:
case gem_btn::sharpshooter_reload:
ext.custom[0] |= ::at32(ext_btn_map, btn);
break;
default:
break;
}
}
else if (external_device_id == RACING_WHEEL_DEVICE_ID)
{
switch (btn)
{
case gem_btn::combo_racing_wheel_throttle:
case gem_btn::combo_racing_wheel_l2:
case gem_btn::combo_racing_wheel_r2:
ext.custom[::at32(ext_btn_indices, btn)] = static_cast<u8>(value);
break;
case gem_btn::combo_racing_wheel_paddle_l:
case gem_btn::combo_racing_wheel_paddle_r:
ext.custom[::at32(ext_btn_indices, btn)] |= ::at32(ext_btn_map, 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:
ext.digital1 |= ::at32(ext_btn_map, btn);
break;
case gem_btn::combo_racing_wheel_l1:
case gem_btn::combo_racing_wheel_r1:
ext.digital2 |= ::at32(ext_btn_map, btn);
break;
default:
break;
}
}
}
}
static void real_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot())
{
controller.ext_status = 0;
controller.ext_id = 0;
return;
}
ps_move_data& move_data = pad->move_data;
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
if (!move_data.external_device_connected)
{
return;
}
// TODO:
// ext.analog_left_x
// ext.analog_left_y
// ext.analog_right_x
// ext.analog_right_y
// ext.digital1
// ext.digital2
ext.status = controller.ext_status;
std::memcpy(ext.custom, move_data.external_device_data.data(), 5);
}
static void fake_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot())
{
controller.ext_status = 0;
controller.ext_id = 0;
return;
}
ps_move_data& move_data = pad->move_data;
const auto& cfg = ::at32(g_cfg_gem_fake.players, gem_num);
move_data.external_device_id = ext_device_id(cfg->external_device);
move_data.external_device_connected = move_data.external_device_id != 0;
move_data.external_device_data = {};
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
if (!move_data.external_device_connected)
{
return;
}
// Restore firing mode
ext.custom[0] = controller.firing_mode;
cfg->handle_input(pad, true, [&move_data, &ext](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
{
static std::set<pad_button> s_combos = {};
input_to_ext<false, false>(move_data.external_device_id, ext, s_combos, btn, pad_btn, value, pressed);
});
ext.status = controller.ext_status;
// Save firing mode
controller.firing_mode = ext.custom[0] & button_flags::ss_firing_mode_mask;
}
static void mouse_input_to_ext(u32 mouse_no, gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
auto& handler = g_fxo->get<MouseHandlerBase>();
std::scoped_lock lock(handler.mutex);
// Make sure that the mouse handler is initialized
handler.Init(std::min<u32>(g_fxo->get<gem_config>().attribute.max_connect, CELL_GEM_MAX_NUM));
if (mouse_no >= handler.GetMice().size())
{
controller.ext_status = 0;
controller.ext_id = 0;
return;
}
const Mouse& mouse_data = ::at32(handler.GetMice(), mouse_no);
const auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
const u32 external_device_id = ext_device_id(cfg->external_device);
const bool external_device_connected = external_device_id != 0;
controller.ext_status = external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
controller.ext_id = external_device_connected ? external_device_id : 0;
if (!external_device_connected)
{
return;
}
// Restore firing mode
ext.custom[0] = controller.firing_mode;
bool combo_active = false;
std::set<pad_button> combos;
// Check combo button first
cfg->handle_input(mouse_data, [&combo_active](gem_btn btn, pad_button /*pad_btn*/, u16 /*value*/, bool pressed, bool& abort)
{
if (pressed && btn == gem_btn::combo)
{
combo_active = true;
abort = true;
}
});
// Check combos
if (combo_active)
{
cfg->handle_input(mouse_data, [external_device_id, &ext, &combos](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
{
input_to_ext<true, true>(external_device_id, ext, combos, btn, pad_btn, value, pressed);
});
}
// Check normal buttons
cfg->handle_input(mouse_data, [external_device_id, &ext, &combos](gem_btn btn, pad_button pad_btn, u16 value, bool pressed, bool& /*abort*/)
{
input_to_ext<true, false>(external_device_id, ext, combos, btn, pad_btn, value, pressed);
});
ext.status = controller.ext_status;
// Save firing mode
controller.firing_mode = ext.custom[0] & button_flags::ss_firing_mode_mask;
}
/**
* \brief Maps external Move controller data to DS3 input. (This can be input from any physical pad, not just the DS3)
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
@@ -2157,7 +2457,7 @@ static void ps_move_pos_to_gem_state(u32 gem_num, gem_config::gem_controller& co
* \param ext External data to modify
* \return true on success, false if controller is disconnected
*/
static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
static void get_external_device_input(u32 gem_num, gem_config::gem_controller& controller, CellGemExtPortData& ext)
{
ext = {};
@@ -2166,53 +2466,6 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
return;
}
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_pad_thread();
const auto& pad = ::at32(handler->GetPads(), pad_num(gem_num));
if (!pad->is_connected() || pad->is_copilot())
{
return;
}
const auto& move_data = pad->move_data;
controller.ext_status = move_data.external_device_connected ? CELL_GEM_EXT_CONNECTED : 0; // TODO: | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
controller.ext_id = move_data.external_device_connected ? move_data.external_device_id : 0;
ext.status = controller.ext_status;
for (const AnalogStick& stick : pad->m_sticks_external)
{
switch (stick.m_offset)
{
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: ext.analog_left_x = stick.m_value; break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: ext.analog_left_y = stick.m_value; break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: ext.analog_right_x = stick.m_value; break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: ext.analog_right_y = stick.m_value; break;
default: break;
}
}
for (const Button& button : pad->m_buttons_external)
{
if (!button.m_pressed)
continue;
switch (button.m_offset)
{
case CELL_PAD_BTN_OFFSET_DIGITAL1: ext.digital1 |= button.m_outKeyCode; break;
case CELL_PAD_BTN_OFFSET_DIGITAL2: ext.digital2 |= button.m_outKeyCode; break;
default: break;
}
}
if (!move_data.external_device_connected)
{
return;
}
// The sharpshooter only sets the custom bytes as follows:
// custom[0] (0x01): Firing mode selector is in position 1.
// custom[0] (0x02): Firing mode selector is in position 2.
@@ -2227,7 +2480,21 @@ static void ds3_input_to_ext(u32 gem_num, gem_config::gem_controller& controller
// custom[3] (0x01): Left paddle
// custom[3] (0x02): Right paddle
std::memcpy(ext.custom, move_data.external_device_data.data(), 5);
switch (g_cfg.io.move)
{
case move_handler::real:
real_input_to_ext(gem_num, controller, ext);
break;
case move_handler::fake:
fake_input_to_ext(gem_num, controller, ext);
break;
case move_handler::mouse:
case move_handler::raw_mouse:
mouse_input_to_ext(gem_num, controller, ext);
break;
default:
break;
}
}
/**
@@ -2260,7 +2527,7 @@ static bool mouse_input_to_pad(u32 mouse_no, be_t<u16>& digital_buttons, be_t<u1
}
const Mouse& mouse_data = ::at32(handler.GetMice(), mouse_no);
auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
const auto& cfg = ::at32(g_cfg_gem_mouse.players, mouse_no);
bool combo_active = false;
std::set<pad_button> combos;
@@ -2998,7 +3265,7 @@ error_code cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, v
if (g_cfg.io.move != move_handler::null)
{
ds3_input_to_ext(gem_num, gem.controllers[gem_num], inertial_state->ext);
get_external_device_input(gem_num, gem.controllers[gem_num], inertial_state->ext);
inertial_state->timestamp = (get_guest_system_time() - gem.start_timestamp_us);
inertial_state->counter = gem.inertial_counter++;
@@ -3192,7 +3459,7 @@ error_code cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<Ce
if (g_cfg.io.move != move_handler::null)
{
ds3_input_to_ext(gem_num, controller, gem_state->ext);
get_external_device_input(gem_num, controller, gem_state->ext);
if (controller.enabled_tracking)
{
@@ -3609,7 +3876,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr<u32> ext_id, v
{
// Get external device status
CellGemExtPortData ext_port_data{};
ds3_input_to_ext(gem_num, controller, ext_port_data);
get_external_device_input(gem_num, controller, ext_port_data);
}
if (!(controller.ext_status & CELL_GEM_EXT_CONNECTED))
+35
View File
@@ -278,3 +278,38 @@ struct CellGemVideoConvertAttribute
ENABLE_BITWISE_SERIALIZATION;
};
namespace
{
enum button_flags : u16
{
select = 0x01,
start = 0x08,
triangle = 0x10,
circle = 0x20,
cross = 0x40,
square = 0x80,
ps = 0x0001,
move = 0x4008,
t = 0x8010,
ext_dev = 0x1000,
// Sharpshooter
ss_firing_mode_1 = 0x01,
ss_firing_mode_2 = 0x02,
ss_firing_mode_3 = 0x04,
ss_firing_mode_mask = ss_firing_mode_1 | ss_firing_mode_2 | ss_firing_mode_3,
ss_trigger = 0x40,
ss_reload = 0x80,
// Racing Wheel
rw_d_pad_up = 0x10,
rw_d_pad_right = 0x20,
rw_d_pad_down = 0x40,
rw_d_pad_left = 0x80,
rw_l1 = 0x04,
rw_r1 = 0x08,
rw_paddle_l = 0x01,
rw_paddle_r = 0x02,
};
}
-1
View File
@@ -1,6 +1,5 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Memory/vm_ref.h"
#ifdef _WIN32
#include <Windows.h>
+73 -14
View File
@@ -66,6 +66,7 @@ void fmt_class_string<CellMusic2Error>::format(std::string& out, u64 arg)
struct music_state
{
public:
shared_mutex mutex;
vm::ptr<void(u32 event, vm::ptr<void> param, vm::ptr<void> userData)> func{};
@@ -79,33 +80,58 @@ struct music_state
music_state()
{
handler = Emu.GetCallbacks().get_music_handler();
handler->set_status_callback([this](music_handler_base::player_status status)
handler->set_event_status_callback([this](u32 status)
{
// TODO: disabled until I find a game that uses CELL_MUSIC_EVENT_STATUS_NOTIFICATION
return;
if (!func)
{
return;
}
s32 result = CELL_OK;
// Known to be used by NFS: Hot Pursuit
sysutil_register_cb([this, state = status](ppu_thread& ppu) -> s32
{
cellMusic.notice("Sending status notification %d", state);
func(ppu, CELL_MUSIC_EVENT_STATUS_NOTIFICATION, vm::addr_t(state), userData);
return CELL_OK;
});
});
handler->set_playback_status_callback([this](music_handler_base::player_status status)
{
switch (status)
{
case music_handler_base::player_status::end_of_media:
result = CELL_MUSIC_PLAYBACK_FINISHED;
// Let's just play the next song for now if we are in list mode.
// Due to potential main thread recursion this may cause a deadlock with the internal mutex of the handler.
// Let's just call it from another thread instead.
m_wake_up_thread = 1;
m_wake_up_thread.notify_one();
break;
default:
return;
}
});
sysutil_register_cb([this, &result](ppu_thread& ppu) -> s32
m_thread = std::make_unique<named_thread<std::function<void()>>>("cellMusic State", [this]()
{
while (thread_ctrl::state() != thread_state::aborting)
{
cellMusic.notice("Sending status notification %d", result);
func(ppu, CELL_MUSIC_EVENT_STATUS_NOTIFICATION, vm::addr_t(result), userData);
return CELL_OK;
});
while (thread_ctrl::state() != thread_state::aborting && !m_wake_up_thread)
{
thread_ctrl::wait_on(m_wake_up_thread, 0);
}
m_wake_up_thread = 0;
if (thread_ctrl::state() == thread_state::aborting)
{
return;
}
// Play the next song
if (const error_code error = set_playback_command(CELL_MUSIC_PB_CMD_NEXT_TRACK))
{
cellMusic.error("Failed to play next track. error=0x%x", +error);
}
}
});
}
@@ -115,6 +141,19 @@ struct music_state
save(ar);
}
~music_state()
{
if (m_thread)
{
auto& thread = *m_thread;
thread = thread_state::aborting;
m_wake_up_thread = 1;
m_wake_up_thread.notify_one();
thread();
m_thread.reset();
}
}
void save(utils::serial& ar)
{
ar(func);
@@ -130,7 +169,7 @@ struct music_state
}
// NOTE: This function only uses CELL_MUSIC enums. CELL_MUSIC2 enums are identical.
error_code set_playback_command(s32 command)
error_code set_playback_command(u32 command)
{
switch (command)
{
@@ -145,11 +184,27 @@ struct music_state
case CELL_MUSIC_PB_CMD_FASTREVERSE:
case CELL_MUSIC_PB_CMD_NEXT:
case CELL_MUSIC_PB_CMD_PREV:
case CELL_MUSIC_PB_CMD_NEXT_TRACK:
{
std::string path;
bool automatic = false;
bool no_more_tracks = false;
{
std::lock_guard lock(mtx);
// Handle auto-play of the next track in the current playlist.
if (command == CELL_MUSIC_PB_CMD_NEXT_TRACK)
{
// We only auto-play the next song if we are in list mode and the music is playing.
if (current_selection_context.content_type != CELL_SEARCH_CONTENTTYPE_MUSICLIST || handler->get_state() != CELL_MUSIC_PB_STATUS_PLAY)
{
return CELL_OK;
}
command = CELL_MUSIC_PB_CMD_NEXT;
automatic = true;
}
const std::vector<std::string>& playlist = current_selection_context.playlist;
const u32 current_track = current_selection_context.current_track;
u32 next_track = current_track;
@@ -191,7 +246,7 @@ struct music_state
handler->fast_reverse(path);
break;
default:
handler->play(path);
handler->play(path, automatic);
break;
}
@@ -203,6 +258,10 @@ struct music_state
return CELL_OK;
}
private:
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
atomic_t<u32> m_wake_up_thread{0};
};
error_code cell_music_select_contents()
+2
View File
@@ -90,6 +90,8 @@ enum
CELL_MUSIC_PB_CMD_PREV = 4,
CELL_MUSIC_PB_CMD_FASTFORWARD = 5,
CELL_MUSIC_PB_CMD_FASTREVERSE = 6,
CELL_MUSIC_PB_CMD_NEXT_TRACK = 7, // RPCS3 helper for auto-play of the next track in the current playlist
};
enum
+12 -3
View File
@@ -74,6 +74,7 @@ struct music_decode
cellMusicDecode.notice("set_decode_command(START): context: %s", current_selection_context.to_string());
music_selection_context context = current_selection_context;
context.current_track = context.first_track;
for (usz i = 0; i < context.playlist.size(); i++)
{
@@ -90,6 +91,7 @@ struct music_decode
case CELL_MUSIC_DECODE_CMD_PREV:
{
decoder.stop();
read_pos = 0;
if (decoder.set_next_index(command == CELL_MUSIC_DECODE_CMD_NEXT) == umax)
{
@@ -195,9 +197,10 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
if (dec.decoder.m_size == 0)
{
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA;
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "m_size == 0" };
}
ensure(dec.decoder.m_size >= dec.read_pos);
const u64 size_left = dec.decoder.m_size - dec.read_pos;
if (dec.read_pos == 0)
@@ -229,10 +232,10 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
if (size_to_read == 0)
{
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA; // TODO: speculative
return { CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA, "size_to_read == 0" }; // TODO: speculative
}
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.read_pos], size_to_read);
std::memcpy(buf.get_ptr(), &::at32(dec.decoder.data, dec.read_pos), size_to_read);
if (size_to_read < reqSize)
{
@@ -384,6 +387,12 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
return CELL_OK;
});
//sysutil_register_cb([&dec, command](ppu_thread& ppu) -> s32
//{
// dec.func(ppu, CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION, vm::addr_t(command), dec.userData);
// return CELL_OK;
//});
return CELL_OK;
}
@@ -267,9 +267,10 @@ void music_selection_context::set_track(std::string_view track)
for (usz i = 0; i < playlist.size(); i++)
{
cellMusicSelectionContext.error("set_track: Comparing track '%s' vs '%s'", track, playlist[i]);
cellMusicSelectionContext.notice("set_track: Comparing track '%s' vs '%s'", track, playlist[i]);
if (track.ends_with(playlist[i]))
{
cellMusicSelectionContext.notice("set_track: Found track '%s': '%s'", track, playlist[i]);
first_track = current_track = static_cast<u32>(i);
return;
}
@@ -287,6 +288,8 @@ u32 music_selection_context::step_track(bool next)
return umax;
}
const std::string last_track = (current_track < playlist.size()) ? playlist[current_track] : "";
switch (repeat_mode)
{
case CELL_SEARCH_REPEATMODE_NONE:
@@ -298,7 +301,7 @@ u32 music_selection_context::step_track(bool next)
{
// We are at the end of the playlist.
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
current_track = umax;
current_track = ::size32(playlist) - 1; // NOTE: We could use size instead of size - 1 to allow to use PREV to play the last track again.
return umax;
}
}
@@ -309,7 +312,6 @@ u32 music_selection_context::step_track(bool next)
{
// We are at the start of the playlist.
cellMusicSelectionContext.notice("step_track: No more tracks to play in playlist...");
current_track = umax;
return umax;
}
@@ -334,7 +336,7 @@ u32 music_selection_context::step_track(bool next)
// Play the previous track. Start with the last track if we reached the start of the playlist.
if (current_track == 0)
{
current_track = ::narrow<u32>(playlist.size() - 1);
current_track = ::size32(playlist) - 1;
}
else
{
@@ -362,8 +364,15 @@ u32 music_selection_context::step_track(bool next)
{
// We reached the first or last track again. Let's shuffle!
cellMusicSelectionContext.notice("step_track: Shuffling playlist...");
auto engine = std::default_random_engine{};
std::random_device rd;
auto engine = std::default_random_engine{rd()};
std::shuffle(std::begin(playlist), std::end(playlist), engine);
// Don't play the same track twice
if (last_track == ::at32(playlist, current_track))
{
current_track = (current_track + 1) % playlist.size();
}
}
}
+40 -1
View File
@@ -192,7 +192,46 @@ error_code cellNetCtlDelHandler(s32 hid)
error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
{
cellNetCtl.warning("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
bool log_it_once = false;
switch (code)
{
case CELL_NET_CTL_INFO_ETHER_ADDR:
case CELL_NET_CTL_INFO_DEVICE:
case CELL_NET_CTL_INFO_MTU:
case CELL_NET_CTL_INFO_LINK_TYPE:
case CELL_NET_CTL_INFO_IP_CONFIG:
case CELL_NET_CTL_INFO_IP_ADDRESS:
case CELL_NET_CTL_INFO_NETMASK:
case CELL_NET_CTL_INFO_DEFAULT_ROUTE:
case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG:
case CELL_NET_CTL_INFO_UPNP_CONFIG:
{
log_it_once = true;
break;
}
default:
{
break;
}
}
bool log_it = true;
if (log_it_once && vm::check_addr(info.addr()))
{
struct logged_t
{
std::array<atomic_t<bool>, 256> logged_code{};
};
if (g_fxo->get<logged_t>().logged_code[::narrow<u8>(code)].exchange(true))
{
log_it = false;
}
}
(log_it ? cellNetCtl.warning : cellNetCtl.trace)("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
+5 -5
View File
@@ -56,9 +56,9 @@ void fmt_class_string<CellPadFilterError>::format(std::string& out, u64 arg)
extern void sys_io_serialize(utils::serial& ar);
pad_info::pad_info(utils::serial& ar)
: max_connect(ar)
, port_setting(ar)
, reported_info(ar)
: max_connect(ar.pop<u32>())
, port_setting(ar.pop<decltype(port_setting)>())
, reported_info(ar.pop<decltype(reported_info)>())
{
//reported_info = {};
sys_io_serialize(ar);
@@ -418,7 +418,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
}
};
for (const Button& button : pad->m_buttons_external)
for (const ButtonExternal& button : pad->m_buttons_external)
{
// here we check btns, and set pad accordingly,
// if something changed, set btnChanged
@@ -497,7 +497,7 @@ void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
}
}
for (const AnalogStick& stick : pad->m_sticks_external)
for (const AnalogStickExternal& stick : pad->m_sticks_external)
{
switch (stick.m_offset)
{
+6 -12
View File
@@ -1,18 +1,10 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "util/bit_set.hpp"
#include <bitset>
#include "cellPamf.h"
const std::function<bool()> SQUEUE_ALWAYS_EXIT = []() { return true; };
const std::function<bool()> SQUEUE_NEVER_EXIT = []() { return false; };
bool squeue_test_exit()
{
return Emu.IsStopped();
}
LOG_CHANNEL(cellPamf);
template<>
@@ -508,7 +500,7 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
const auto streams = &pAddr->seq_info.grouping_periods.groups.streams;
std::bitset<16> channels_used[6]{};
std::array<bit_set<16>, 6> channels_used{};
u32 end_of_streams_addr = 0;
u32 next_ep_table_addr = 0;
@@ -524,14 +516,16 @@ error_code pamfVerify(vm::cptr<PamfHeader> pAddr, u64 fileSize, vm::ptr<CellPamf
return CELL_PAMF_ERROR_UNKNOWN_STREAM;
}
bit_set<16>& used_channels = ::at32(channels_used, *type);
// Every channel may only be used once per type
if (channels_used[*type].test(*ch))
if (used_channels.test(*ch))
{
return { CELL_PAMF_ERROR_INVALID_PAMF, "pamfVerify() failed: invalid channel" };
}
// Mark channel as used
channels_used[*type].set(*ch);
used_channels.set(*ch, true);
const u32 ep_offset = streams[stream_idx].ep_offset;
const u32 ep_num = streams[stream_idx].ep_num;
-342
View File
@@ -595,345 +595,3 @@ struct CellPamfReader
CHECK_SIZE(CellPamfReader, 128);
error_code cellPamfReaderInitialize(vm::ptr<CellPamfReader> pSelf, vm::cptr<PamfHeader> pAddr, u64 fileSize, u32 attribute);
#include <mutex>
#include <condition_variable>
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
extern const std::function<bool()> SQUEUE_NEVER_EXIT;
bool squeue_test_exit();
// TODO: eliminate this boolshit
template<typename T, u32 sq_size = 256>
class squeue_t
{
struct squeue_sync_var_t
{
struct
{
u32 position : 31;
u32 pop_lock : 1;
};
struct
{
u32 count : 31;
u32 push_lock : 1;
};
};
atomic_t<squeue_sync_var_t> m_sync;
mutable std::mutex m_rcv_mutex;
mutable std::mutex m_wcv_mutex;
mutable std::condition_variable m_rcv;
mutable std::condition_variable m_wcv;
T m_data[sq_size];
enum squeue_sync_var_result : u32
{
SQSVR_OK = 0,
SQSVR_LOCKED = 1,
SQSVR_FAILED = 2,
};
public:
squeue_t()
: m_sync(squeue_sync_var_t{})
{
}
static u32 get_max_size()
{
return sq_size;
}
bool is_full() const
{
return m_sync.load().count == sq_size;
}
bool push(const T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.push_lock)
{
return SQSVR_LOCKED;
}
if (sync.count == sq_size)
{
return SQSVR_FAILED;
}
sync.push_lock = 1;
pos = sync.position + sync.count;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> wcv_lock(m_wcv_mutex);
m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1));
}
m_data[pos >= sq_size ? pos - sq_size : pos] = data;
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.push_lock);
sync.push_lock = 0;
sync.count++;
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool push(const T& data, const volatile bool* do_exit)
{
return push(data, [do_exit]() { return do_exit && *do_exit; });
}
bool push(const T& data)
{
return push(data, SQUEUE_NEVER_EXIT);
}
bool try_push(const T& data)
{
return push(data, SQUEUE_ALWAYS_EXIT);
}
bool pop(T& data, const std::function<bool()>& test_exit)
{
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (!sync.count)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
sync.pop_lock = 0;
sync.position++;
sync.count--;
if (sync.position == sq_size)
{
sync.position = 0;
}
});
m_rcv.notify_one();
m_wcv.notify_one();
return true;
}
bool pop(T& data, const volatile bool* do_exit)
{
return pop(data, [do_exit]() { return do_exit && *do_exit; });
}
bool pop(T& data)
{
return pop(data, SQUEUE_NEVER_EXIT);
}
bool try_pop(T& data)
{
return pop(data, SQUEUE_ALWAYS_EXIT);
}
bool peek(T& data, u32 start_pos, const std::function<bool()>& test_exit)
{
ensure(start_pos < sq_size);
u32 pos = 0;
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.count <= start_pos)
{
return SQSVR_FAILED;
}
if (sync.pop_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
pos = sync.position + start_pos;
return SQSVR_OK;
}))
{
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
{
return false;
}
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
data = m_data[pos >= sq_size ? pos - sq_size : pos];
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
sync.pop_lock = 0;
});
m_rcv.notify_one();
return true;
}
bool peek(T& data, u32 start_pos, const volatile bool* do_exit)
{
return peek(data, start_pos, [do_exit]() { return do_exit && *do_exit; });
}
bool peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_NEVER_EXIT);
}
bool try_peek(T& data, u32 start_pos = 0)
{
return peek(data, start_pos, SQUEUE_ALWAYS_EXIT);
}
class squeue_data_t
{
T* const m_data;
const u32 m_pos;
const u32 m_count;
squeue_data_t(T* data, u32 pos, u32 count)
: m_data(data)
, m_pos(pos)
, m_count(count)
{
}
public:
T& operator [] (u32 index)
{
ensure(index < m_count);
index += m_pos;
index = index < sq_size ? index : index - sq_size;
return m_data[index];
}
};
void process(void(*proc)(squeue_data_t data))
{
u32 pos, count;
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
pos = sync.position;
count = sync.count;
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
proc(squeue_data_t(m_data, pos, count));
m_sync.atomic_op([](squeue_sync_var_t& sync)
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
ensure(!!sync.pop_lock);
ensure(!!sync.push_lock);
sync.pop_lock = 0;
sync.push_lock = 0;
});
m_wcv.notify_one();
m_rcv.notify_one();
}
void clear()
{
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
{
ensure(sync.count <= sq_size);
ensure(sync.position < sq_size);
if (sync.pop_lock || sync.push_lock)
{
return SQSVR_LOCKED;
}
sync.pop_lock = 1;
sync.push_lock = 1;
return SQSVR_OK;
}))
{
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
}
m_sync.exchange({});
m_wcv.notify_one();
m_rcv.notify_one();
}
};
+34 -12
View File
@@ -1711,12 +1711,6 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
auto content = std::find_if(searchObject->content_ids.begin(), searchObject->content_ids.end(), [&content_hash](const content_id_type& cid){ return cid.first == content_hash; });
if (content != searchObject->content_ids.cend() && content->second)
{
// Check if the type of the found content is correct
if (content->second->type != CELL_SEARCH_CONTENTTYPE_MUSIC)
{
return { CELL_SEARCH_ERROR_INVALID_CONTENTTYPE, "Type: %d, Expected: CELL_SEARCH_CONTENTTYPE_MUSIC"};
}
// Check if the type of the found content matches our search content type
if (content->second->type != first_content->type)
{
@@ -1724,8 +1718,36 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
}
// Use the found content
context.playlist.push_back(content->second->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path=%s", content_hash, +content->second->type, context.playlist.back());
if (content->second->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
{
const std::string path = content->second->infoPath.contentPath;
const std::string vfs_path = vfs::get(path);
if (!fs::is_dir(vfs_path))
{
return { CELL_SEARCH_ERROR_CONTENT_NOT_FOUND, "Not a directory: Path='%s'", vfs_path };
}
for (auto&& dir_entry : fs::dir{vfs_path})
{
if (dir_entry.name == "." || dir_entry.name == "..")
{
continue;
}
std::string track = path + "/" + dir_entry.name;
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path='%s'", content_hash, +content->second->type, track);
context.playlist.push_back(std::move(track));
}
}
else if (content->second->type == CELL_SEARCH_CONTENTTYPE_MUSIC)
{
context.playlist.push_back(content->second->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path='%s'", content_hash, +content->second->type, context.playlist.back());
}
else
{
return { CELL_SEARCH_ERROR_INVALID_CONTENTTYPE, "Type: %d, Expected: CELL_SEARCH_CONTENTTYPE_MUSIC or CELL_SEARCH_CONTENTTYPE_MUSICLIST", +content->second->type };
}
}
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
{
@@ -1738,14 +1760,14 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
// TODO: whole playlist
shared_ptr<search_content_t> content = get_random_content();
context.playlist.push_back(content->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path=%s", content_hash, +content->type, context.playlist.back());
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path='%s'", content_hash, +content->type, context.playlist.back());
}
else
{
// Select the first track by default
// TODO: whole playlist
context.playlist.push_back(first_content->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path=%s", content_hash, +first_content->type, context.playlist.back());
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path='%s'", content_hash, +first_content->type, context.playlist.back());
}
}
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
@@ -1759,14 +1781,14 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
// TODO: whole playlist
shared_ptr<search_content_t> content = get_random_content();
context.playlist.push_back(content->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path=%s", +content->type, context.playlist.back());
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path='%s'", +content->type, context.playlist.back());
}
else
{
// Select the first track by default
// TODO: whole playlist
context.playlist.push_back(first_content->infoPath.contentPath);
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path=%s", +first_content->type, context.playlist.back());
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path='%s'", +first_content->type, context.playlist.back());
}
context.content_type = first_content->type;
+11 -9
View File
@@ -1,6 +1,5 @@
#include "stdafx.h"
#include <bitset>
#include <string>
#include "cellSsl.h"
@@ -8,6 +7,7 @@
#include "Utilities/File.h"
#include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "util/bit_set.hpp"
#include "cellRtc.h"
@@ -92,7 +92,7 @@ std::string getCert(const std::string& certPath, const int certID, const bool is
newID = certID - 1;
}
std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID);
const std::string filePath = fmt::format("%sCA%02d.cer", certPath, newID);
if (!fs::exists(filePath))
{
@@ -106,7 +106,7 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
{
cellSsl.trace("cellSslCertificateLoader(flag=%llu, buffer=*0x%x, size=%zu, required=*0x%x)", flag, buffer, size, required);
const std::bitset<58> flagBits(flag);
const bit_set<58> flagBits(flag);
const std::string certPath = vfs::get("/dev_flash/data/cert/");
if (required)
@@ -114,10 +114,11 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
*required = 0;
for (uint i = 1; i <= flagBits.size(); i++)
{
if (!flagBits[i-1])
if (!flagBits.test(i - 1))
continue;
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
*required += ::size32(getCert(certPath, i, flagBits[BaltimoreCert-1]));
*required += ::size32(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
}
}
else
@@ -125,14 +126,15 @@ error_code cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm
std::string final;
for (uint i = 1; i <= flagBits.size(); i++)
{
if (!flagBits[i-1])
if (!flagBits.test(i - 1))
continue;
// If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs.
final.append(getCert(certPath, i, flagBits[BaltimoreCert-1]));
final.append(getCert(certPath, i, flagBits.test(BaltimoreCert - 1)));
}
memset(buffer.get_ptr(), '\0', size - 1);
memcpy(buffer.get_ptr(), final.c_str(), final.size());
std::memset(buffer.get_ptr(), '\0', size - 1);
std::memcpy(buffer.get_ptr(), final.c_str(), final.size());
}
return CELL_OK;
File diff suppressed because it is too large Load Diff
+121 -3
View File
@@ -9,10 +9,11 @@ enum CellVdecError : u32
CELL_VDEC_ERROR_EMPTY = 0x80610104,
CELL_VDEC_ERROR_AU = 0x80610105,
CELL_VDEC_ERROR_PIC = 0x80610106,
CELL_VDEC_ERROR_UNK = 0x80610110,
CELL_VDEC_ERROR_FATAL = 0x80610180,
};
enum CellVdecCodecType : s32
enum CellVdecCodecType : u32
{
CELL_VDEC_CODEC_TYPE_MPEG2 = 0,
CELL_VDEC_CODEC_TYPE_AVC = 1,
@@ -96,7 +97,7 @@ struct CellVdecTypeEx
{
be_t<s32> codecType; // CellVdecCodecType
be_t<u32> profileLevel;
be_t<u32> codecSpecificInfo_addr;
vm::bcptr<void> codecSpecificInfo;
};
// Library Attributes
@@ -220,7 +221,7 @@ enum
CELL_VDEC_AVC_CCD_MAX = 128,
};
enum AVC_level : u8
enum AVC_level : u32
{
CELL_VDEC_AVC_LEVEL_1P0 = 10,
CELL_VDEC_AVC_LEVEL_1P1 = 11,
@@ -235,6 +236,7 @@ enum AVC_level : u8
CELL_VDEC_AVC_LEVEL_4P0 = 40,
CELL_VDEC_AVC_LEVEL_4P1 = 41,
CELL_VDEC_AVC_LEVEL_4P2 = 42,
CELL_VDEC_AVC_LEVEL_UNK = 1042 // Same as 4.2, but disables the deblocking filter
};
struct CellVdecAvcSpecificInfo
@@ -399,6 +401,7 @@ enum DIVX_level : u8
CELL_VDEC_DIVX_HOME_THEATER = 12,
CELL_VDEC_DIVX_HD_720 = 13,
CELL_VDEC_DIVX_HD_1080 = 14,
CELL_VDEC_DIVX_UNK = 15 // Only used for DivX version 3.11
};
struct CellVdecDivxSpecificInfo
@@ -695,3 +698,118 @@ struct CellVdecMpeg4SpecificInfo
be_t<u16> maxDecodedFrameWidth;
be_t<u16> maxDecodedFrameHeight;
};
enum VC1_level
{
CELL_VDEC_VC1_SP_LL,
CELL_VDEC_VC1_SP_ML,
CELL_VDEC_VC1_MP_LL,
CELL_VDEC_VC1_MP_ML,
CELL_VDEC_VC1_MP_HL,
CELL_VDEC_VC1_AP_L0,
CELL_VDEC_VC1_AP_L1,
CELL_VDEC_VC1_AP_L2,
CELL_VDEC_VC1_AP_L3,
CELL_VDEC_VC1_AP_L4
};
struct CellVdecVc1SpecificInfo
{
be_t<u32> thisSize;
be_t<u16> maxDecodedFrameWidth;
be_t<u16> maxDecodedFrameHeight;
};
struct VdecDecoderAttr
{
be_t<u32> mem_size;
be_t<u32> unk1;
u8 cmd_depth;
be_t<u32> unk2;
be_t<u32> decoder_version;
};
struct VdecDecoderSpecificOps
{
error_code (&query_attr)(ppu_thread& ppu, VdecDecoderAttr& attr, u32 profile_level, const void* codec_specific_info);
// TODO remaining functions
};
// Abstraction layer for decoders with "S...D" in their names
enum class VdecSceDecoderType : u8
{
mpeg2,
mpeg4,
vc1,
jvt
};
template <typename param_t>
struct VdecSceDecoderOps
{
error_code (&get_memory_size)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
error_code (&get_memory_size_2)(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<param_t> params);
error_code (&get_version_number)(ppu_thread& ppu, vm::ptr<u32> version);
// TODO remaining functions
};
struct VdecSceDecoderUnk
{
u32 unk1;
u32 unk2;
u32 unk3;
u32 unk4;
};
constexpr VdecSceDecoderOps<Smvd2Params> VDEC_SCE_DECODER_OPS_MPEG2 =
{
.get_memory_size = ppu_execute<&smvd2GetMemorySize>,
.get_memory_size_2 = ppu_execute<&smvd2GetMemorySize2>,
.get_version_number = ppu_execute<&smvd2GetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<Smvd4Params> VDEC_SCE_DECODER_OPS_MPEG4 =
{
.get_memory_size = ppu_execute<&smvd4GetMemorySize>,
.get_memory_size_2 = ppu_execute<&smvd4GetMemorySize2>,
.get_version_number = ppu_execute<&smvd4GetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<Svc1dParams> VDEC_SCE_DECODER_OPS_VC1 =
{
.get_memory_size = ppu_execute<&svc1dGetMemorySize>,
.get_memory_size_2 = ppu_execute<&svc1dGetMemorySize2>,
.get_version_number = ppu_execute<&svc1dGetVersionNumber>,
// TODO remaining functions
};
constexpr VdecSceDecoderOps<SjvtdParams> VDEC_SCE_DECODER_OPS_JVT =
{
.get_memory_size = ppu_execute<&sjvtdGetMemorySize>,
.get_memory_size_2 = ppu_execute<&sjvtdGetMemorySize2>,
.get_version_number = ppu_execute<&sjvtdGetVersionNumber>,
// TODO remaining functions
};
constexpr std::array VDEC_SCE_DECODER_ERROR_BASE_MAP = { 0x80615000u, 0x80615100u, 0x80615300u, 0x80615800u };
constexpr std::array VDEC_SCE_DECODER_MAX_MEM_SIZE_MAP =
{
std::array<u32, 13>{ 0xc8400, 0x295680, 0x9b8200, 0xce4f00 },
std::array<u32, 13>{ 0x44480, 0x44480, 0xa8b00, 0x1e6800, 0x1b4d00, 0x23e400 },
std::array<u32, 13>{ 0x1b44f0, 0x2dfef0, 0x38f9f0, 0x999e30, 0x2827f70, 0x2dfef0, 0x754130, 0x11bd9f0, 0x1fdee70, 0x3900070 },
std::array<u32, 13>{ 0x380fd0, 0x380fd0, 0x380fd0, 0x380fd0, 0x780950, 0xcb6d50, 0xcb6d50, 0x17a4950, 0x3245ed0, 0x4499fd0, 0x4499fd0, 0x4499fd0, 0x6b979d0 }
};
constexpr std::array VDEC_SCE_DECODER_UNK_MAP =
{
VdecSceDecoderUnk{ .unk1 = 2, .unk2 = 0, .unk4 = 0x1c8 },
VdecSceDecoderUnk{ .unk1 = 1, .unk2 = 6, .unk4 = 0x38 },
VdecSceDecoderUnk{ .unk1 = 3, .unk2 = 6, .unk4 = 0x170 },
VdecSceDecoderUnk{ .unk1 = 4, .unk2 = 6, .unk4 = 0x160 }
};
error_code cellVdecQueryAttrEx(ppu_thread& ppu, vm::cptr<CellVdecTypeEx> type, vm::ptr<CellVdecAttr> attr);
+107
View File
@@ -0,0 +1,107 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libavcdec.h"
LOG_CHANNEL(libavcdec)
template<>
void fmt_class_string<AvcDecError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(AVC_DEC_ERROR_ARG);
}
return unknown;
});
}
error_code avcDecQueryMemory([[maybe_unused]] ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr)
{
libavcdec.todo("avcDecQueryMemory(params=*0x%x, attr=*0x%x)", params, attr);
return CELL_OK;
}
void avcDecGetVersion([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
{
libavcdec.notice("avcDecGetVersion(version=*0x%x)", version);
if (version)
{
*version = 0x11300;
}
}
error_code avcDecQueryCharacteristics([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> unk)
{
libavcdec.notice("avcDecGetVersion(unk=*0x%x)", unk);
if (!unk)
{
return AVC_DEC_ERROR_ARG;
}
unk[0] = 5; // Command queue depth
unk[1] = 20;
return CELL_OK;
}
error_code avcDecOpen()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecClose()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecReset()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecFlush()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecAccessUnit()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecRlsFrame()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
error_code avcDecGetStatus()
{
UNIMPLEMENTED_FUNC(libavcdec);
return CELL_OK;
}
DECLARE(ppu_module_manager::libavcdec)("libavcdec", []
{
REG_FUNC(libavcdec, avcDecQueryMemory);
REG_FUNC(libavcdec, avcDecGetVersion);
REG_FUNC(libavcdec, avcDecQueryCharacteristics);
REG_FUNC(libavcdec, avcDecOpen);
REG_FUNC(libavcdec, avcDecClose);
REG_FUNC(libavcdec, avcDecReset);
REG_FUNC(libavcdec, avcDecFlush);
REG_FUNC(libavcdec, avcDecAccessUnit);
REG_FUNC(libavcdec, avcDecRlsFrame);
REG_FUNC(libavcdec, avcDecGetStatus);
});
+26
View File
@@ -0,0 +1,26 @@
#pragma once
enum AvcDecError : u32
{
AVC_DEC_ERROR_ARG = 0x80615201,
};
struct AvcDecParams
{
u8 profile_level;
b8 disable_deblocking_filter;
u8 number_of_decoded_frame_buffer;
be_t<u16> max_decoded_frame_width;
be_t<u16> max_decoded_frame_height;
};
struct AvcDecAttr
{
u32 unk;
be_t<u32> mem_size;
be_t<u32> mem_align;
};
error_code avcDecQueryMemory(ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr);
void avcDecGetVersion(ppu_thread& ppu, vm::ptr<u32> version);
error_code avcDecQueryCharacteristics(ppu_thread& ppu, vm::ptr<u32> unk);
+87
View File
@@ -0,0 +1,87 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libdivx311dec.h"
LOG_CHANNEL(libdivx311dec)
template<>
void fmt_class_string<Divx311DecError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(DIVX311_DEC_ERROR_ARG);
}
return unknown;
});
}
error_code divx311DecQueryAttr([[maybe_unused]] ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version)
{
libdivx311dec.todo("divx311DecQueryAttr(params=*0x%x, memSize=*0x%x, version=*0x%x)", params, memSize, version);
return CELL_OK;
}
error_code divx311DecOpen()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecClose()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecReset()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecFlushPicture()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecGetNumOfPictures()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecGetPicture()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecDecodeAu()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
error_code divx311DecReleasePicture()
{
UNIMPLEMENTED_FUNC(libdivx311dec);
return CELL_OK;
}
DECLARE(ppu_module_manager::libdivx311dec)("libdivx311dec", []
{
REG_FUNC(libdivx311dec, divx311DecQueryAttr);
REG_FUNC(libdivx311dec, divx311DecOpen);
REG_FUNC(libdivx311dec, divx311DecClose);
REG_FUNC(libdivx311dec, divx311DecReset);
REG_FUNC(libdivx311dec, divx311DecFlushPicture);
REG_FUNC(libdivx311dec, divx311DecGetNumOfPictures);
REG_FUNC(libdivx311dec, divx311DecGetPicture);
REG_FUNC(libdivx311dec, divx311DecDecodeAu);
REG_FUNC(libdivx311dec, divx311DecReleasePicture);
});
+10
View File
@@ -0,0 +1,10 @@
#pragma once
enum Divx311DecError : u32
{
DIVX311_DEC_ERROR_ARG = 0x80615502,
};
struct DivxDecParams;
error_code divx311DecQueryAttr(ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version);
+87
View File
@@ -0,0 +1,87 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libdivxdec.h"
LOG_CHANNEL(libdivxdec)
template<>
void fmt_class_string<DivxDecError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(DIVX_DEC_ERROR_ARG);
}
return unknown;
});
}
error_code divxDecQueryAttr([[maybe_unused]] ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version)
{
libdivxdec.todo("divxDecQueryAttr(params=*0x%x, memSize=*0x%x, version=*0x%x)", params, memSize, version);
return CELL_OK;
}
error_code divxDecOpen()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecClose()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecReset()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecFlushPicture()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecGetNumOfPictures()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecGetPicture()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecDecodeAu()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
error_code divxDecReleasePicture()
{
UNIMPLEMENTED_FUNC(libdivxdec);
return CELL_OK;
}
DECLARE(ppu_module_manager::libdivxdec)("libdivxdec", []
{
REG_FUNC(libdivxdec, divxDecQueryAttr);
REG_FUNC(libdivxdec, divxDecOpen);
REG_FUNC(libdivxdec, divxDecClose);
REG_FUNC(libdivxdec, divxDecReset);
REG_FUNC(libdivxdec, divxDecFlushPicture);
REG_FUNC(libdivxdec, divxDecGetNumOfPictures);
REG_FUNC(libdivxdec, divxDecGetPicture);
REG_FUNC(libdivxdec, divxDecDecodeAu);
REG_FUNC(libdivxdec, divxDecReleasePicture);
});
+16
View File
@@ -0,0 +1,16 @@
#pragma once
enum DivxDecError : u32
{
DIVX_DEC_ERROR_ARG = 0x80615502,
};
struct DivxDecParams
{
be_t<u32> profile_level;
be_t<u32> max_decoded_frame_width;
be_t<u32> max_decoded_frame_height;
be_t<u32> number_of_decoded_frame_buffer;
};
error_code divxDecQueryAttr(ppu_thread& ppu, vm::cptr<DivxDecParams> params, vm::ptr<u32> memSize, vm::ptr<u32> version);
+100
View File
@@ -0,0 +1,100 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libmvcdec.h"
LOG_CHANNEL(libmvcdec)
template<>
void fmt_class_string<MvcDecError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(MVC_DEC_ERROR_ARG);
}
return unknown;
});
}
error_code mvcDecQueryMemory([[maybe_unused]] ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr)
{
libmvcdec.todo("mvcDecQueryMemory(params=*0x%x, attr=*0x%x)", params, attr);
return CELL_OK;
}
void mvcDecGetVersion([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
{
libmvcdec.notice("mvcDecGetVersion(version=*0x%x)", version);
if (version)
{
*version = 0x10804;
}
}
error_code mvcDecQueryCharacteristics([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> unk)
{
libmvcdec.notice("mvcDecGetVersion(unk=*0x%x)", unk);
if (!unk)
{
return MVC_DEC_ERROR_ARG;
}
unk[0] = 5; // Command queue depth
unk[1] = 36;
return CELL_OK;
}
error_code mvcDecOpen()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
error_code mvcDecClose()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
error_code mvcDecReset()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
error_code mvcDecFlush()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
error_code mvcDecAccessUnit()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
error_code mvcDecRlsFrame()
{
UNIMPLEMENTED_FUNC(libmvcdec);
return CELL_OK;
}
DECLARE(ppu_module_manager::libmvcdec)("libmvcdec", []
{
REG_FUNC(libmvcdec, mvcDecQueryMemory);
REG_FUNC(libmvcdec, mvcDecGetVersion);
REG_FUNC(libmvcdec, mvcDecQueryCharacteristics);
REG_FUNC(libmvcdec, mvcDecOpen);
REG_FUNC(libmvcdec, mvcDecClose);
REG_FUNC(libmvcdec, mvcDecReset);
REG_FUNC(libmvcdec, mvcDecFlush);
REG_FUNC(libmvcdec, mvcDecAccessUnit);
REG_FUNC(libmvcdec, mvcDecRlsFrame);
});
+13
View File
@@ -0,0 +1,13 @@
#pragma once
enum MvcDecError : u32
{
MVC_DEC_ERROR_ARG = 0x80615901,
};
struct AvcDecAttr;
struct AvcDecParams;
error_code mvcDecQueryMemory(ppu_thread& ppu, vm::cptr<AvcDecParams> params, vm::ptr<AvcDecAttr> attr);
void mvcDecGetVersion(ppu_thread& ppu, vm::ptr<u32> version);
error_code mvcDecQueryCharacteristics(ppu_thread& ppu, vm::ptr<u32> unk);
+165
View File
@@ -0,0 +1,165 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libsjvtd.h"
LOG_CHANNEL(libsjvtd)
template<>
void fmt_class_string<SjvtdError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(SJVTD_ERROR_ARG);
}
return unknown;
});
}
error_code sjvtdGetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
{
libsjvtd.todo("sjvtdGetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
return CELL_OK;
}
error_code sjvtdGetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<SjvtdParams> params)
{
libsjvtd.todo("sjvtdGetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
return CELL_OK;
}
error_code sjvtdGetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
{
libsjvtd.notice("sjvtdGetVersionNumber(version=*0x%x)", version);
if (!version)
{
return SJVTD_ERROR_ARG;
}
*version = 0x1050000;
return CELL_OK;
}
error_code sjvtdInitialize()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdCreateInstance2()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdReleaseInstance()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdCancelDecode()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdFlushPicture()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetNumPicture()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetPicture()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdReleasePicture()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetAuxData()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetSideInfoSeq()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetSideInfoPic()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdSetAuxData()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdSetDecodeMode()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdDecodePicture()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdGetExistenceFlag()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
error_code sjvtdDiscontinue()
{
UNIMPLEMENTED_FUNC(libsjvtd);
return CELL_OK;
}
DECLARE(ppu_module_manager::libsjvtd)("libsjvtd", []
{
REG_FUNC(libsjvtd, sjvtdGetMemorySize);
REG_FUNC(libsjvtd, sjvtdGetMemorySize2);
REG_FUNC(libsjvtd, sjvtdGetVersionNumber);
REG_FUNC(libsjvtd, sjvtdInitialize);
REG_FUNC(libsjvtd, sjvtdCreateInstance2);
REG_FUNC(libsjvtd, sjvtdReleaseInstance);
REG_FUNC(libsjvtd, sjvtdCancelDecode);
REG_FUNC(libsjvtd, sjvtdFlushPicture);
REG_FUNC(libsjvtd, sjvtdGetNumPicture);
REG_FUNC(libsjvtd, sjvtdGetPicture);
REG_FUNC(libsjvtd, sjvtdReleasePicture);
REG_FUNC(libsjvtd, sjvtdGetAuxData);
REG_FUNC(libsjvtd, sjvtdGetSideInfoSeq);
REG_FUNC(libsjvtd, sjvtdGetSideInfoPic);
REG_FUNC(libsjvtd, sjvtdSetAuxData);
REG_FUNC(libsjvtd, sjvtdSetDecodeMode);
REG_FUNC(libsjvtd, sjvtdDecodePicture);
REG_FUNC(libsjvtd, sjvtdGetExistenceFlag);
REG_FUNC(libsjvtd, sjvtdDiscontinue);
});
+39
View File
@@ -0,0 +1,39 @@
#pragma once
enum SjvtdError : u32
{
SJVTD_ERROR_ARG = 0x80615841,
};
enum SjvtdProfileLevel : u8
{
SJVTD_LEVEL_1P0 = 1,
SJVTD_LEVEL_1P1,
SJVTD_LEVEL_1P2,
SJVTD_LEVEL_1P3,
SJVTD_LEVEL_2P0,
SJVTD_LEVEL_2P1,
SJVTD_LEVEL_2P2,
SJVTD_LEVEL_3P0,
SJVTD_LEVEL_3P1,
SJVTD_LEVEL_3P2,
SJVTD_LEVEL_4P0,
SJVTD_LEVEL_4P1,
SJVTD_LEVEL_4P2
};
struct SjvtdParams
{
be_t<u32> unk1;
be_t<u32> unk2;
be_t<u32> unk3;
be_t<s32> max_decoded_frame_width;
be_t<s32> max_decoded_frame_height;
be_t<u32> enable_deblocking_filter;
be_t<u32> unk;
be_t<u32> number_of_decoded_frame_buffer;
};
error_code sjvtdGetMemorySize(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
error_code sjvtdGetMemorySize2(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<SjvtdParams> params);
error_code sjvtdGetVersionNumber(ppu_thread& ppu, vm::ptr<u32> version);
+179
View File
@@ -0,0 +1,179 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libsmvd2.h"
LOG_CHANNEL(libsmvd2)
template<>
void fmt_class_string<Smvd2Error>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(SMVD2_ERROR_ARG);
}
return unknown;
});
}
error_code smvd2GetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
{
libsmvd2.todo("smvd2GetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
return CELL_OK;
}
error_code smvd2GetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd2Params> params)
{
libsmvd2.todo("smvd2GetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
return CELL_OK;
}
error_code smvd2GetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
{
libsmvd2.notice("smvd2GetVersionNumber(version=*0x%x)", version);
if (!version)
{
return SMVD2_ERROR_ARG;
}
*version = 0x1030000;
return CELL_OK;
}
error_code smvd2Initialize()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2CreateInstance2()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2ReleaseInstance()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2CancelDecode()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2FlushPicture()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetNumPicture()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetPicture()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2ReleasePicture()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetAuxData()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetSideInfoSeq()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetBnrData()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetSideInfoPic()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2SetAuxData()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2SetDecodeMode()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2DecodePicture()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2DecodePicture2()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2GetExistenceFlag()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
error_code smvd2Discontinue()
{
UNIMPLEMENTED_FUNC(libsmvd2);
return CELL_OK;
}
DECLARE(ppu_module_manager::libsmvd2)("libsmvd2", []
{
REG_FUNC(libsmvd2, smvd2GetMemorySize);
REG_FUNC(libsmvd2, smvd2GetMemorySize2);
REG_FUNC(libsmvd2, smvd2GetVersionNumber);
REG_FUNC(libsmvd2, smvd2Initialize);
REG_FUNC(libsmvd2, smvd2CreateInstance2);
REG_FUNC(libsmvd2, smvd2ReleaseInstance);
REG_FUNC(libsmvd2, smvd2CancelDecode);
REG_FUNC(libsmvd2, smvd2FlushPicture);
REG_FUNC(libsmvd2, smvd2GetNumPicture);
REG_FUNC(libsmvd2, smvd2GetPicture);
REG_FUNC(libsmvd2, smvd2ReleasePicture);
REG_FUNC(libsmvd2, smvd2GetAuxData);
REG_FUNC(libsmvd2, smvd2GetSideInfoSeq);
REG_FUNC(libsmvd2, smvd2GetBnrData);
REG_FUNC(libsmvd2, smvd2GetSideInfoPic);
REG_FUNC(libsmvd2, smvd2SetAuxData);
REG_FUNC(libsmvd2, smvd2SetDecodeMode);
REG_FUNC(libsmvd2, smvd2DecodePicture);
REG_FUNC(libsmvd2, smvd2DecodePicture2);
REG_FUNC(libsmvd2, smvd2GetExistenceFlag);
REG_FUNC(libsmvd2, smvd2Discontinue);
});
+27
View File
@@ -0,0 +1,27 @@
#pragma once
enum Smvd2Error : u32
{
SMVD2_ERROR_ARG = 0x80615041,
};
enum Smvd2ProfileLevel : u32
{
SMVD2_MP_LL = 1,
SMVD2_MP_ML,
SMVD2_MP_H14,
SMVD2_MP_HL
};
struct Smvd2Params
{
be_t<u32> unk1;
be_t<u32> unk2;
be_t<u32> unk3;
be_t<u32> max_decoded_frame_width;
be_t<u32> max_decoded_frame_height;
};
error_code smvd2GetMemorySize(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel);
error_code smvd2GetMemorySize2(ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd2Params> params);
error_code smvd2GetVersionNumber(ppu_thread& ppu, vm::ptr<u32> version);
+158
View File
@@ -0,0 +1,158 @@
#include "stdafx.h"
#include "Emu/Cell/PPUModule.h"
#include "libsmvd4.h"
LOG_CHANNEL(libsmvd4)
template<>
void fmt_class_string<Smvd4Error>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(SMVD4_ERROR_ARG);
}
return unknown;
});
}
error_code smvd4GetMemorySize([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel)
{
libsmvd4.todo("smvd4GetMemorySize(memSize=*0x%x, profileLevel=%d)", memSize, +profileLevel);
return CELL_OK;
}
error_code smvd4GetMemorySize2([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> memSize, u32 profileLevel, vm::cptr<Smvd4Params> params)
{
libsmvd4.todo("smvd4GetMemorySize2(memSize=*0x%x, profileLevel=%d, params=*0x%x)", memSize, profileLevel, params);
return CELL_OK;
}
error_code smvd4GetVersionNumber([[maybe_unused]] ppu_thread& ppu, vm::ptr<u32> version)
{
libsmvd4.notice("smvd4GetVersionNumber(version=*0x%x)", version);
if (!version)
{
return SMVD4_ERROR_ARG;
}
*version = 0x1080000;
return CELL_OK;
}
error_code smvd4Initialize()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4CreateInstance2()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4ReleaseInstance()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4CancelDecode()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4FlushPicture()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetNumPicture()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetPicture()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4ReleasePicture()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetAuxData()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetSideInfoSeq()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetSideInfoPic()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4SetAuxData()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4DecodePicture()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4GetExistenceFlag()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
error_code smvd4Discontinue()
{
UNIMPLEMENTED_FUNC(libsmvd4);
return CELL_OK;
}
DECLARE(ppu_module_manager::libsmvd4)("libsmvd4", []
{
REG_FUNC(libsmvd4, smvd4GetMemorySize);
REG_FUNC(libsmvd4, smvd4GetMemorySize2);
REG_FUNC(libsmvd4, smvd4GetVersionNumber);
REG_FUNC(libsmvd4, smvd4Initialize);
REG_FUNC(libsmvd4, smvd4CreateInstance2);
REG_FUNC(libsmvd4, smvd4ReleaseInstance);
REG_FUNC(libsmvd4, smvd4CancelDecode);
REG_FUNC(libsmvd4, smvd4FlushPicture);
REG_FUNC(libsmvd4, smvd4GetNumPicture);
REG_FUNC(libsmvd4, smvd4GetPicture);
REG_FUNC(libsmvd4, smvd4ReleasePicture);
REG_FUNC(libsmvd4, smvd4GetAuxData);
REG_FUNC(libsmvd4, smvd4GetSideInfoSeq);
REG_FUNC(libsmvd4, smvd4GetSideInfoPic);
REG_FUNC(libsmvd4, smvd4SetAuxData);
REG_FUNC(libsmvd4, smvd4DecodePicture);
REG_FUNC(libsmvd4, smvd4GetExistenceFlag);
REG_FUNC(libsmvd4, smvd4Discontinue);
});

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