AnyPS5/docs/dev/TechnicalDebt.md

30 KiB

Project technical debt

Build

  • Building on Windows requires a specific version of mingw - MinGW-w64 GCC 15.2.0 (winlibs-gcc15, x86_64-ucrt-posix-seh)
  • Even compiled prx libraries on Windows require nearby (static linking of these dependencies causes conflicts):
    • libgcc_s_seh-1.dll
    • libstdc++-6.dll
    • libwinpthread-1.dll

Silent stubs

Throughout the project, every function at every stage either does exactly what it's supposed to or throws an exception. Everywhere... except:

  • libSceSaveDataDialog.native
  • libSceMsgDialog.native - dialogs finish at sceMsgDialogOpen without UI and answer with button 1, so sceMsgDialogClose returns NOT_RUNNING; its result before sceMsgDialogInitialize follows libSceMsgDialog
  • libSceCommonDialog
  • libSceErrorDialog - the error dialog runs the state machine but shows nothing; the error code passed to sceErrorDialogOpen is only logged
  • libSceHmd implements only the disconnected-headset path: initialization succeeds, device queries report NotDetected, and opening a device returns DeviceDisconnected. Headset support, tracking, rendering and additional HMD exports are not implemented; SDK-level ABI compatibility and in-game behaviour remain unverified.
  • libSceAudioPropagation implements only the no-acoustic-propagation path: the system keeps a 16-byte header in the title's CPU memory (no GPU memory), rooms, portals, sources and materials are tracked handles, attributes are validated and have no effect, no rays are requested and sources report no audio paths. Ray results, path calculation and audio paths are accepted only when empty and throw otherwise; sceAudioPropagationSourceRender writes silence (zeroes) to each source's output buffer for the given size, without a dry signal. Objects still alive when their system is destroyed become unusable, and their later destroy or unregister is a no-op: PPSA21567 releases its reference-counted room, portal and source owners just before sceAudioPropagationSystemDestroy, so the order for every object is not proven. Occlusion, reflections and reverb from level geometry are absent; in-game behaviour remains unverified.
  • libSceNpCommerce - the PS Store icon show/hide calls and sceNpCommerceSetPsStoreIconLayout do nothing
  • sceVideoOutOpen validates the priority and CPU affinity that the open param requests for the VideoOut service thread but does not apply them: the port's present and vblank threads are host threads, and guest priorities and affinities do not reach host scheduling
  • The shader recompiler skips baryctric coordinates (is not even passed to SpirvTargetOptions at row 212).
  • libSceAvPlayer: sceAvPlayerSetLogCallback accepts a callback that is never called, as the player produces no log messages, and sceAvPlayerSetAvailableBandwidth has no effect, as it governs HLS sources, which sceAvPlayerAddSource does not implement.
  • libSceNpTrophy2: sceNpTrophy2RegisterUnlockCallback accepts a callback that is never called, as no trophy is ever unlocked, and sceNpTrophy2UnregisterUnlockCallback only returns success.
  • sceAvPlayerSetTrickSpeed (libSceAvPlayer) with a negative speed runs the clock backwards but delivers no frames; when a forward speed is set again, playback resumes from the rewound time.
  • ulobjmgr registers no object: _sceUlobjmgrRegisterObject always hands out id 0 and _sceUlobjmgrUnregisterObject releases nothing, as shadPS4 does

Unknown function info

  • sceAvPlayerAddSource (libSceAvPlayer) - requests up to 4 additional decode-ahead video framebuffers from the memory replacement beyond num_output_video_framebuffers when memory is available; the console's internal buffering behaviour is unknown
  • sceVideoOutRegisterBuffers2 (libSceVideoOut) - buffer option 8 (STRICT_COLORIMETRY) is presented as option 0, with the stored values unchanged; what the console changes for it is unknown. Only options 0 and 8 are accepted
  • sceVideoOutOpen (libSceVideoOut) - the open param's first word is unknown (PPSA21564 passes 16; it is not the byte size, since the affinity mask is at offset 16); only 16 is accepted. Whether the param continues past offset 24 is also unknown
  • sceRtcParseDateTime (libSceRtc) - accepted format assumed to be the ISO 8601 subset YYYY-MM-DDTHH:MM:SS[.ffffff] (T/t/space separator, optional fractional seconds) with the sibling RFC 3339 zone suffix (Z or ±HH:MM); a string without a zone is taken as UTC, and anything outside this shape throws instead of returning a parse error, unverified against hardware
  • AMPR WriteAddressFromCounterPairOnCompletion (libSceAmpr) - the pair of an even counter n is assumed to give counter n in the low 32 bits and n + 1 in the high 32 bits. Wait compare values, wait flush and counter index ranges follow the ampr_emu reimplementation; the flush is accepted and has no effect, as commands run in order
  • AMPR markers (libSceAmpr) - signatures taken from the ampr_emu reimplementation, not confirmed on a title: sceAmprCommandBufferSetMarkerWithColor takes the color by pointer, the push and measure variants by value. Colors are not recorded
  • sceFontGetRenderScaledKerning (libSceFont) - arguments, the cleared output and the bound renderer check taken from cellFontGetRenderScaledKerning in RPCS3 (PS3 predecessor of the library), not confirmed on a title. The kerning is the sceFontGetKerning one at the render scale
  • sceFontGetPixelResolution (libSceFont) - signature taken from the IDA type database of Orbital, not confirmed on a title. The value is the one the library's driver reports (64 for FreeType); the argument checks and the cleared output follow sceFontGetLibrary
  • sceSaveDataTransferringMountPs4, sceSaveDataDirNameSearchPs4 (libSceSaveData.native) - arguments assumed to be those of sceSaveDataTransferringMount and sceSaveDataDirNameSearch: KytyPS5 binds both NIDs to the PS5 functions, and prosper captured the same result layout on a title for the search. There is no PS4 save area, so the mount returns NOT_FOUND and the search reports zero hits
  • sceAgcSetSubmitMode (libSceAgc) - mode values unknown; only 0 is accepted
  • sceAgcGetIsTrinityMode (libSceAgc) - signature from the only PPSA26344 call (a pointer to a one-byte flag, result ignored); a null pointer throws because its error code is unknown
  • zARR5aCmkoY (libSceAgc) - unknown name, signature
  • qj7QZpgr9Uw (libSceAgc) - unknown name
  • fd5Bp5tGTgo (libSceAgc) - unknown name
  • dolOmWH+huQ (libSceAgc) - unknown name
  • dbOlWdppb4o (libSceAgc) - unknown name; same arguments as sceAgcCreateInterpolantMapping; the SPI_PS_INPUT_CNTL bits for is_f16 2 inputs follow KytyPS5, not confirmed on hardware
  • V++UgBtQhn0 (libSceAgc) - unknown name
  • gQkqkLttcpw (libSceAgc) - unknown name, signature
  • sceAgcDcbPrimeUtcl2 (libSceAgc) - assumed to write the packet sceAgcAcbPrimeUtcl2 writes: PRIME_UTCL2 has the same 5 dwords on the graphics and compute rings. Not confirmed on a title. Both write a NOP of that size, as there is no TLB to prime
  • sceKernelInternalMemoryGetModuleSegmentInfo (libkernel) - unknown signature
  • sceKernelSyncOnAddressWait (libkernel) - the only known caller passes a null timeout and a name string as the fourth argument; the timeout is assumed to point to microseconds like the other kernel waits, and the name is ignored
  • sceKernelSyncOnAddressWait32 / sceKernelSyncOnAddressWait64 (libkernel) - libc's __std_atomic_wait_direct_32/64 pass a microsecond timeout or null and ignore the result; the by-value comparand, return values and Wait64's 8-byte alignment are assumed
  • sceKernelMapNamedFlexibleMemoryInternal (libkernel) - flag 0x8000 unknown; only the sceKernelMapNamedFlexibleMemory flags are accepted
  • Guest arena (libc) - the application map area is assumed to end at 0xFC_0000_0000, as SCE_KERNEL_APP_MAP_AREA_END_ADDR does on the PS4. On Windows, host allocations made in the arena before libc loads stay, and a fixed guest mapping over one throws
  • sceLibcInternalBacktraceForGame (libSceLibcInternal, implemented in libc) - unknown signature
  • sceLibcInternalHeapErrorReportForGame (libSceLibcInternal, implemented in libc) - unknown signature
  • _sceLibcInternalThreadAtexit, _sceLibcInternalThreadDtors (libSceLibcInternal) - FreeBSD __cxa_thread_atexit semantics assumed; a null or non-image destructor and an allocation failure throw
  • _sceLibcInternalForceTlsDestructor (libSceLibcInternal) - unknown return type and behaviour; a title's libc.prx calls it from __cxa_finalize with the module handle of a non-null dso
  • __progname (libkernel) - unknown data export
  • pthread_barrierattr_setpshared (libkernel) - PTHREAD_PROCESS_SHARED throws: FreeBSD 9.0 libthr rejects it with EINVAL and 11.0 accepts it, and which one the console follows is unknown
  • sceSslClose (libSceSsl) - unknown signature
  • sceSslGetSerialNumber (libSceSsl) - unknown signature
  • X+4jdIS75P0 (libSceAudioIn) - unknown name, signature
  • sceAudioOut2Set3DLatency (libSceAudioOut) - assumed to take (user_id, latency): KytyPS5 reads (user_id, output, latency_us), but the only known call sets just edi and esi; only (0xFF, 2) is accepted
  • sceAudioOut2MasteringInit (libSceAudioOut) - flag values unknown; only 0 is accepted
  • sceAudioOutSetMixLevelPadSpk (libSceAudioOut) - effect of a negative mix level unknown; only 0 to 32768 (0 dB) is accepted
  • sceAudioOutSetMixLevelPadSpk (libSceAudioOut) - no source applies the level (shadPS4 only stores it); it is assumed to be a linear gain on the port's own output, and the -9 dB default (11626) is applied to every pad speaker port, including those of titles that never call the function
  • sceAudioOutGetLastOutputTime (libSceAudioOut) - signature and behaviour taken from the shadPS4 and fpPS4 reimplementations of the PS4 library, not confirmed on a PS5 title: the time is the process time at which sceAudioOutOutput or sceAudioOutOutputs accepted the port's last block, not the time the block reached the device
  • AOWqIYsgVHs (libSceContentExport) - unknown name, signature
  • scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100, scePsmlMfsrGetDispatchMfsrPacket1100 (libScePsml_debug) - argument layouts unknown; only 0x8A810001 (not initialized) is returned
  • BnMAMrsfVWo (libc) - unknown name, signature
  • scePngEncEncode (libScePngEnc) - whether rows may stay unfiltered when filter_type names a subset of the filters is unknown; as in shadPS4, they may only for 0 and for the all-filters mask
  • AudioIn, NpSessionSignaling and PlayerInvitationDialog exports added without an implementation have assumed signatures
  • libSceAudioPropagation (libSceAudioPropagation) - signatures and struct descriptors recovered from PPSA21567's calls (#481), not from documentation. The system options layout is unknown (only checked for non-null); the fourth argument of sceAudioPropagationSourceCalculateAudioPaths is unknown; sceAudioPropagationSourceGetAudioPath is assumed to output an 8-byte path handle. The meaning of the RenderInfo word at +0x28 is unknown (PPSA21567 and PPSA21564 pass 2); only 2 is accepted. Both titles pass one RenderInfo per call: a count of 0 throws, and larger counts are handled element by element without proof. No error code is known, so invalid input throws instead of returning one
  • sceAvPlayerStartEx (libSceAvPlayer) - start info layout unknown; it is ignored and playback starts as with sceAvPlayerStart
  • sceAvPlayerInit / sceAvPlayerInitEx (libSceAvPlayer) - behaviour without a memory replacement unknown; frame and sample buffers then come from the guest heap
  • SceAvPlayerVideoEx (libSceAvPlayer) - frame rate field and encoding unknown; it is left zero in frame and stream info
  • sceRazorCpuPushMarkerStatic, sceRazorCpuPopMarker (libSceRazorCpu) - signatures from PPSA26344's calls; (name, color, flags) assumed for the push; both do nothing without a capture
  • sceAjmDecMp3ParseFrame (libSceAjm.native) - signature, result layout and rate tables taken from the shadPS4 reimplementation of the PS4 library, not confirmed on a PS5 title: the layer field is not checked and MPEG-2.5 stops at 64 kbps
  • sceAjmBatchJobGetCodecInfo, sceAjmBatchJobGetGaplessDecode (libSceAjm.native) - the RUN_GET_CODEC_INFO flag (0x800), the ATRAC9 codec info layout and its next frame size are taken from the shadPS4 and RPCSX reimplementations, not confirmed on a PS5 title; a run job without buffers returns result 0, as in shadPS4. The gapless sideband reports the total and skip counts the title set and does not count them down: RPCSX does the same, shadPS4 counts them down and BryKytyPS5 returns its current counts
  • AJM Opus decoder (libSceAjm.native, codec 24) - the initialize parameter layout (u32 channels, u32 sample rate, u32, seen as 2, 48000, 0) and the little-endian u16 byte count before each packet come from one title (The Smurfs Dreams); only 48000 Hz and a zero third word are accepted
  • sceHttpParseResponseHeader (libSceHttp) - unknown signature
  • sceHttpParseStatusLine (libSceHttp) - behaviour and error codes follow the shadPS4 reimplementation of the PS4 library, not confirmed on a PS5 title; unlike it, no byte past lineLen is read
  • sceHttp2GetMemoryPoolStats (libSceHttp2) - unknown signature
  • sceNetGetMemoryPoolStats (libSceNet) - unknown signature
  • sceHttpSetRequestStatusCallback (libSceHttp) - no reference implements it (shadPS4 only logs and returns 0); when the callback runs and what status it gets is unknown
  • sce::Np::CppWebApi::Common::IntrusivePtr (libSceNpCppWebApi) - the 0x18-byte layout (object, deleter, LibContext*) and the offset of the 32-bit reference count in the pointee (0x00, 0x30 for Vector) come from the module bundled with one title (PPSA21564); other SDK versions are not checked. The destructor throws on the last reference of a pointer without a deleter: destroying the object needs its destructor and the LibContext allocator, which are not implemented
  • sceNpSessionSignalingGetMemoryInfo (libSceNpSessionSignaling) - unknown signature
  • sceNpSessionSignalingGetConnectionStatistics (libSceNpSessionSignaling) - unknown signature
  • sce::Np::CppWebApi::Common::String (libSceNpCppWebApi) - the 0x20-byte layout (32-bit reference count, buffer at 0x08, buffer size at 0x10, LibContext* at 0x18) comes from the module bundled with one title (PPSA21564); other SDK versions are not checked. The destructor throws for a non-null buffer: releasing it needs the LibContext allocator, which is not implemented
  • sceSystemServiceDisableMediaPlay (libSceSystemService) - unknown signature
  • sceSystemServiceReenableMediaPlay (libSceSystemService) - unknown signature
  • sceSystemServiceParamGetString (libSceSystemService) - the system name (parameter 6) is the fixed PS5, not the console's name; other parameters and buffers of 1 to 64 bytes throw
  • sceVoiceSetMuteFlag (libSceVoice) - unknown signature
  • snwprintf_s (libc) - follows C11 K.3.9.1.3 with RSIZE_MAX assumed to be SIZE_MAX >> 1. A runtime-constraint violation only returns a negative value: set_constraint_handler_s is not exported and the console's default handler is unknown. An invalid multibyte %s argument is copied byte by byte instead of being reported as an encoding error, as in vswprintf
  • libSceUlt queues - the added sceUltQueuePop, sceUltQueueTryPush, sceUltQueueDestroy and sceUltQueueDataResourcePoolDestroy signatures are inferred from the existing push/pop and object APIs. Queue destruction is assumed to reject blocked callers with ULT_ERROR_BUSY and otherwise discard queued data; pool exhaustion returns ULT_ERROR_AGAIN. These rules, alignment checks and shared slot limits have not been confirmed on PS5 hardware or a title. Non-null queue and data-pool option parameters throw because their layouts are unknown.
  • _sceUlobjmgrRegisterObject (ulobjmgr) - parameters taken from shadPS4 (PS4): a nonzero 64-bit object, a nonzero 32-bit kind and a 32-bit id output; the meaning of the first two and the PS5 signature are unverified. The id bound of _sceUlobjmgrUnregisterObject (below 0x4000) and the raw EINVAL (22) return also come from shadPS4

Functional

  • libSceUlt queues use preallocated host storage instead of the supplied guest work area; its existing size formula and the 512-byte object layouts remain unverified. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with ULT_ERROR_STATE.
  • MIMG tfe and lwe never write the status VGPR after the data. On hardware it is written only when a texel fetch hits an unmapped page of a partially resident texture, or, for lwe on sample/gather, when the LOD is below the T# MIN_LOD_WARN; texture memory is always resident here, and lwe on sample, gather and image_get_lod throws.
  • Image atomics on 32_SINT and 32_FLOAT surfaces act on the raw dword through an R32_UINT storage view; their float results are assumed to match those measured on 32_UINT surfaces. Other formats throw.
  • The shader recompiler ignores s_setreg_b32: writes to MODE (float rounding and denormal controls) and the other hardware registers have no effect, and s_getreg_b32 reads of the MODE round fields report round to nearest even even after s_setreg_b32 wrote another mode. The initial round mode from the shader's program registers is not read either.
  • Shader recompilation currently occurs right before it was transferred to Vulkan with caching, but should be moved to the relinker stage. For this purpose, shader/recompiler was written completely independently from libs/prx.
  • v_fma_f64 is fused exactly only for normal inputs: with a subnormal input it is a separate multiply and add, which rounds twice.
  • COND_EXEC reads its condition after the earlier packets of its queue complete. Without PFP_SYNC_ME the console's graphics PFP reads it ahead of the ME, so a title that races the read sees a later value here.
  • A flip inside a COND_EXEC range throws: the flip is reserved with the video output when the buffer is submitted, and how the console withdraws a skipped one is unknown.
  • A COND_EXEC range must hold whole packets and end inside its command buffer. A chained INDIRECT_BUFFER or a REWIND inside a range throws, as what the CP does with the skipped dwords then is unknown.
  • v_mullit_f32 follows the hardware measured with the MODE register at IEEE=1 and denormals kept (a signaling NaN src0 is quieted, a positive denormal src2 counts as positive): the recompiler does not read MODE, so other IEEE/denormal settings are not modelled.
  • v_fma_legacy_f32 (VOP3 0x140) is assumed to be fused, which has not been measured on hardware. LLVM decodes this encoding as v_mad_legacy_f32 for gfx1013, and v_mac_legacy_f32 (VOP2 0x06) was measured to round its product before the add, so 0x140 may do the same.
  • The executable file that relinker generates opens the console when launched, which is inconvenient for playability.
  • libSceJpegEnc encodes 4:2:2 sampling requests with 4:2:0 chroma subsampling, and grayscale requests as a 3-component JPEG with neutral chroma instead of a single-component one: the JPEG encoder (stb) only produces 3-component 4:2:0 and 4:4:4 images.
  • PA_CL_CLIP_CNTL (libSceAgcDriver) - DX_LINEAR_ATTR_CLIP_ENA (bit 24) clear is drawn like bit 24 set, with Vulkan's clipping of NoPerspective outputs; what a clear bit changes on the console is undocumented.
  • v_rcp, v_rsq, v_sqrt, v_log, v_exp, v_sin and v_cos (f32 and f16) use the driver's approximation for finite results, not the console's: the low bits can differ. Only NaN, invalid, zero and infinite results and the exact sin/cos zeros are chosen on the bits. v_sin_f32/v_cos_f32 with inputs beyond ±256 cycles were not compared with the console.
  • --to-intel does not lower RDPRU/MCOMMIT; the matcher fails the relink instead. SHA-1 and SHA-256 instructions with a RIP-relative operand fail the relink.
  • The length-changing path of the instruction rewriter is not used by the converter: it does not adjust VEX/0F38/0F3A RIP-relative operands, data-to-code references (relocations, FDEs, jump tables) or segment sizes, so every substitution keeps the instruction length.
  • DB_SHADER_CONTROL.CONSERVATIVE_Z_EXPORT (libSceAgcDriver) becomes DepthLess/DepthGreater. For a pixel shader that breaks the promise, the host driver decides whether its interpolated Z is depth-tested before shading; what the console does then was not measured.
  • The Linux placement of the --to-intel stubs in the ELF patcher is covered only by a synthetic test.
  • The libc SSE4a trap emulation on Windows is superseded by --to-intel and remains only until the relinked title has been verified without it.
  • --to-intel guest module trampolines are covered only by a synthetic relinker test; no game title has been verified with them on Linux or Windows.
  • --to-intel replaces the approximate VEX.128 register forms of VRSQRTPS and VRCPPS with correctly rounded 1/sqrt(x) and 1/x, not with the AMD approximation: its tables are not public. Intel's approximation of 1/sqrt(1.0) is 0x3F7FF000, and two Newton-Raphson steps on it settle on 0.99999994 instead of 1.0; a renormalised identity quaternion then turns into a NaN axis in PPSA21564, which the console does not show. Refined results now reach the exact value; unrefined ones can still differ from the console in the low bits. Each site costs an out-of-line stub (two jumps, a spill below the red zone, SQRTPS/DIVPS). The 256-bit, scalar, legacy and memory forms keep the native instruction, as does a 4-byte site with no movable instruction after it for the jump (5 sites in PPSA21564).
  • sceKeyboardGetKey2Char (libSceKeyboard) translates only the 101-key (US) arrangement and throws for the 106-key (Japanese) one; Ctrl and Alt do not change the character.
  • sceAudioOut2Set3DLatency and sceAudioOut2MasteringInit (libSceAudioOut) have no effect: there is no 3D rendering or mastering stage.
  • libSceAudiodec throws for the 24-bit PCM word size (iBwPcm 0): its sample layout is unknown. ATRAC9 decoding is covered only by configuration and error tests, as no ATRAC9 encoder is available for a fixture.
  • sceAjmDecMp3ParseFrame (libSceAjm.native) throws when the original file length is requested (parse_ofl non-zero): reading it from the frame's Xing/Info, VBRI or Fraunhofer data is not implemented.
  • The output modifier (mul:2, mul:4, div:2) follows the hardware with the IEEE mode off and f32 denormals flushed, the LLVM graphics default; the shader's MODE register is not read, and the hardware ignores the modifier in the other modes. On f16 results it is ignored, as on the hardware while f16 denormals are enabled; with them flushed the hardware applies it.
  • libScePlayerInvitationDialog simulates dialog completion without displaying UI or sending invitations; its parameter ABI remains unverified.
  • 64-bit LDS atomics (ds_*_u64, ds_*_b64, ds_*_f64) read and write the two dwords under a workgroup lock stored after the guest LDS, as LDS is a 32-bit array without 64-bit atomics. They are atomic with respect to each other only: 32-bit accesses to the same dwords issued concurrently are not ordered by the lock. 64-bit GDS atomics throw. ds_min/max(_rtn)_f64 and ds_cmpst(_rtn)_f64 follow the NaN and signed-zero rules measured for the f32 forms; the f64 forms were not measured on hardware.
  • Thread exit destructors (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only _sceLibcInternalThreadDtors runs them, as exit does, else they are dropped