CopyToBuffer checked the destination size against the string length, then copied with strncpy up to the destination size minus one. When the source had no terminator within maxLength, strncpy kept reading the source past maxLength until it found a NUL, so the copy could exceed the limit the caller passed. The copy now moves exactly length bytes and zero-fills the rest of the destination. Output is byte-identical to the previous code for every input where the source ends within maxLength.
sceNpStrParseHex and sceNpStrnParseHex in firmware 13.60 write the high
nibble of each byte before they read the low digit, so a refused low digit
leaves that byte as the high nibble shifted left by four.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Read from libSceNpCommon.sprx of firmware 13.60 (disassembly, not measured
on a console).
- sceNpUtilParseJid(jid, text, buffer, bufferSize, required) skips leading
spaces, splits user@domain/resource into terminated copies in the
buffer and points the jid at them, or only measures without a jid or a
buffer. It skips exactly one character after the trimmed domain, as the
module does, and clears the jid when the buffer is too small.
- sceNpUtilJidToNpId(npId, jid) accepts XX.YY.<label>.playstation.net
domains, users of 1 to 16 bytes and resources of up to 4, and builds the
npid from the user, the four domain characters and the resource.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both take (dst, dstSize, src, srcLength, required), as in libSceNpCommon.sprx
of firmware 13.60 (read from disassembly, not measured on a console).
- Escaping writes the five predefined entities for " & ' < >, copies tab,
LF, CR, other printable ASCII and UTF-8 sequences, and refuses other
control characters, DEL and malformed UTF-8 with 0x80550608.
- Unescaping turns the five entities back and copies every other byte.
- Both write a terminator, report the length with it, measure without a
destination, and return 0x80550601 for a missing source, a zero length
or neither a destination nor a length, and 0x80550602 when the
destination is too small.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The final room check ran even when dst was null, so a size query such as
(nullptr, 0, src, n, &length) returned NP_UTIL_ERROR_BUFFER_TOO_SMALL and never
wrote the required length, although the per-character checks in the same
function already skip the comparison for a null destination. The check now
applies only when there is a destination.
After a high surrogate, sceNpUtilJsonGetOneChar parsed four hex digits at offset
8 without checking that offsets 6 and 7 hold a backslash and a 'u'. The string
"\ud83dXXde00" decoded to U+1F600 and consumed 12 bytes, swallowing the two
unrelated characters. sceNpUtilJsonUnescape already makes that check. A high
surrogate not followed by \u now takes the same failure path as an invalid low
surrogate.
sceNpUtilJsonEscape appends to the destination with strcat, but the room check
started its running length at 0 and ignored the text the buffer already held,
so escaping into a partly filled buffer wrote past its end:
sceNpUtilJsonEscape(buf, 16, "\n\n\n", 3) on a buffer holding ten characters
placed 16 characters and the terminator in 16 bytes.
The running length now starts at the length of the existing text, and a
destination that is already full returns NP_UTIL_ERROR_BUFFER_TOO_SMALL.
Builds on #9037. Adds threads, event flags, semaphores, atomics, heaps
and kernel memory, callout contexts, time and SDK version queries,
base64 and hex codecs, integer and string helpers, JSON escaping, URL
encoding, online id strings, language codes, title ids and debug output.
The behaviour follows libSceNpCommon.sprx from firmware 12.00, read
from disassembly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RbLgECLQ2bpDM7oe2urGZK