Files
AnyPS5/docs/dev/debt/libSceNpUniversalDataSystem.md
T
Adrià 437037fd64 Merge pull request #14912 from edubox/feat/uds-int-record-builders
feat(libSceNpUniversalDataSystem): implement the Int record builders
2026-11-07 00:07:55 +01:00

9.7 KiB

libSceNpUniversalDataSystem technical debt

Silent stubs

  • sceNpUniversalDataSystemRegisterContext (libSceNpUniversalDataSystem) - returns SCE_NP_UNIVERSAL_DATA_SYSTEM_OK without checking the context or handle and without recording anything. sceNpUniversalDataSystemPostEvent uses only its context and ignores the handle, so nothing depends on the registration. All ten titles in my set call it.
  • sceNpUniversalDataSystemDestroyHandle and sceNpUniversalDataSystemAbortHandle (libSceNpUniversalDataSystem) - return SCE_NP_UNIVERSAL_DATA_SYSTEM_OK without checking the handle. sceNpUniversalDataSystemCreateHandle always hands out the same handle and keeps no state, so there is nothing to tear down. All ten titles in my set call sceNpUniversalDataSystemDestroyHandle; Metal Gear Solid 4 (PPSA18194) also calls sceNpUniversalDataSystemAbortHandle.

Unknown function info

  • libSceNpUniversalDataSystem additional exports - these name/NID pairs are present in the 13.60 export inventory. Parameter lists, return types and behavior are unverified. Parameterless int declarations are import-resolution placeholders, not verified ABI prototypes; every entry throws through NotImplemented_nid_no_patch. No runtime functionality is implemented.
Name NID
sceNpUniversalDataSystemIntCloseStream pSyd06cKZVk
sceNpUniversalDataSystemIntDeleteUserData IiSeRSN++Mc
sceNpUniversalDataSystemIntGetNpCommIdByNpTitleId ONOoGEhy+mg
sceNpUniversalDataSystemIntGetNpTitleIdListByNpCommId FgmhJGcGztU
sceNpUniversalDataSystemIntGetObjectState KSHif9MWQjQ
sceNpUniversalDataSystemIntOpenObjectsDefinitionConfStream O5enyfsJoK0
sceNpUniversalDataSystemIntOpenObjectsDefinitionConfStreamByObjectId vOdDBWSu-Gk
sceNpUniversalDataSystemIntOpenStreamByUri tkUOy4Ag4wo
sceNpUniversalDataSystemIntReadStream ABFxQ2B50RA
  • sceNpUniversalDataSystemIntPostRecordData (libSceNpUniversalDataSystem) - declared without parameters only to resolve the known import; its signature and behaviour are unverified and it throws through NotImplemented_nid_no_patch.

  • Events: sceNpUniversalDataSystemCreateEvent copies prop and returns the event's own property object through prop_ptr, as KytyPS5 does; sceNpUniversalDataSystemEventPropertyObjectSetObject, SetArray and the array setters for objects and arrays store a copy of value and return the stored copy through value_ptr. Destroying a property object or array that its Create function did not return (an event's property object or a stored copy) does nothing, as its owner frees it. These ownership rules are not verified on a console, nor is the text of sceNpUniversalDataSystemEventToString (a debug rendering of the event, not the console's format) and the size that sceNpUniversalDataSystemEventEstimateSize returns (a MessagePack size model, not measured).

  • sceNpUniversalDataSystemCreateContext and sceNpUniversalDataSystemCreateHandle (libSceNpUniversalDataSystem) - native return codes for null output pointers (context == nullptr and handle == nullptr) are unresolved and unverified on a console. The reimplementation in KytyPS5 returns 0x80553102 (SCE_NP_UNIVERSAL_DATA_SYSTEM_ERROR_INVALID_ARGUMENT), but this error return is unverified against native hardware; host paths continue to throw std::invalid_argument until the native contract is established. No call that could be decoded in the titles checked passes a constant null pointer to either function.

  • sceNpUniversalDataSystemInitialize (libSceNpUniversalDataSystem) - the host throws std::invalid_argument for param == nullptr or any size != sizeof(NpUniversalDataSystemInitParam) (16 bytes), accepts pool_size == 0, and keeps no initialized state, whereas the native module returns error codes and tracks state. When libSceNpUniversalDataSystem.sprx (PS5 firmware 13.42, at 0x1ae0) was run in a host harness (on Windows, not on a console), the module verified strictly size == 16 (returning 0x80553112 for any other size), returned 0x80553102 for null param or pool_size == 0, and returned 0x80553118 when already initialized. Direct calls in inspected titles (including PPSA18194) pass constant 16 in size and 0x10000 or 0x40000 in pool_size.

  • sceNpUniversalDataSystemIntAbortHandle, sceNpUniversalDataSystemIntCheckNetSyncTitles, sceNpUniversalDataSystemIntCreateContext, sceNpUniversalDataSystemIntCreateHandle, sceNpUniversalDataSystemIntDeleteAllData and sceNpUniversalDataSystemIntDeleteUserDataWithServerData (libSceNpUniversalDataSystem) - names and NIDs are present in the PS5 3.20 export inventory, but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim, and each throws until its signature is verified

  • sceNpUniversalDataSystemIntDestroyContext, sceNpUniversalDataSystemIntDestroyHandle and sceNpUniversalDataSystemIntNetSyncTitles (libSceNpUniversalDataSystem) - names and NIDs are present in the PS5 3.20 export inventory, but no credible prototypes were found. They are declared without parameters solely to resolve known imports; this is not an ABI claim, and each throws until its signature is verified

Functional

  • Record builders (libSceNpUniversalDataSystem) - the console record store, its serialization and the size estimate are not measured: the memory pool passed to IntInitialize (a 16-byte {size, memory} from the 11.00 wrapper, other sizes 0x80553112) is ignored, and IntGetMemoryStat and IntRecordDataEstimateSize throw after their argument checks. The record model (arrays append values, objects set keys, nested values are cloned and returned through value_ptr, destruction invalidates children), the error codes 0x80553116, 0x80553117 and 0x80553118, and the check order are unverified; 0x80553101, 0x80553102, 0x8055310D, 0x80553114, 0x80553115, 0x80553119 and 0x8055311A come from the 11.00 wrappers. IntDestroyRecordData forwards to IntDestroyRecordObject, so either frees records created by the other.
  • Trophy events: sceNpUniversalDataSystemPostEvent stores an _UnlockTrophy event with an int32 _trophy_id of 0 or more as an unlock for the user and service label of its context, as KytyPS5 does, except an id that libSceNpTrophy2 refuses when the title's trophy data is loaded (one not in the data, a platinum or a progressive trophy). _UpdateTrophyProgress goes through the title's statistic rules like any other event (below). A context need not be registered, and every post of an event that sceNpUniversalDataSystemCreateEvent returned and that is not yet destroyed returns success (a null or any other event pointer stops the game): KytyPS5's errors for an unknown or unregistered context and for a malformed unlock are not verified. The storage statistics stay zero.
  • Statistics: every other event goes through the title's rules in sce_sys/uds/udsNN.ucp, which libSceNpTrophy2 reads. Only "aggregation":"latest" and "max" statistics of dataType uint32, uint64 or int32 are kept; no public source documents the other aggregations (such as count, collection and eachLatest) or types, so their definitions and rules are skipped. Only rules whose input is a $.name path (dotted for nested objects) are applied. A value is used when it is an integer of any width that is not negative and fits the statistic's type and minValue/maxValue; KytyPS5 requires the statistic's exact width, and whether the console converts, clamps or rejects other values is not known. Statistics are stored per user and service label, and every trophy's unlockCondition is compared after each event that sets one.
  • Event checks: before sceNpUniversalDataSystemInitialize the 13.42 module returns 0x80553117 from the event and property functions (creating, setting, destroying, sizing and describing), even for null arguments; these functions keep no initialized state and run normally. The module's sceNpUniversalDataSystemCreateEvent also compares packed sizes with 0x5000 bytes when prop is given (read from its disassembly, not measured); this one does not. sceNpUniversalDataSystemEventToString writes NaN and infinite properties as std::to_chars does; the console's text for them is not measured.