At 0.7f the stale stop was already excluded by the position comparison
(0.5 >= 0.7 fails), leaving the epoch conjunct of the stop walk
uncovered. At 0.3f the position check passes and only the epoch match
keeps the walk from splicing past the stale stop; removing the conjunct
aborts guest_font_graphics.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Same DrawupPackColor fix as on the parent branch: NaN (admitted by
DrawupColorValid) is undefined through static_cast<int32_t>; firmware
converts with vcvttss2si into 64-bit registers, so an invalid channel
contributes a zero byte from the indefinite value's low dword. Assert
the packed stop color for a NaN channel (0x80800080) and correct the
FlatColor NaN expectation to 0x00000000. Verified under
-fsanitize=float-cast-overflow -fno-sanitize-recover on Linux.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
GraphicsAppendGradientSegment computed unitUsed % unitSize ahead of the
unitSize==0 check that selects 0x804600a0, leaving the guard dead behind
an undefined division. The firmware body carries the same defect (the
unguarded div at 0x20703 faults before its own dead 0x208fa branch), but
on the host the check is the only defined outcome for a corrupt frame,
so hoist it ahead of the first modulo; behaviour on every valid frame
(unitSize is always positive after object init) is unchanged. Adds the
zero-size regression case.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
sceFontGraphicsAppendGradientSegment decodes from the 4.03 libSceFont
body at 0x20500: sorted-inserts a 0x18-byte color-stop node into the +8
chain of linear (0x20) and radial (0x30) gradient records while
node->+4 == record->+4 and position >= node->+0x10. Stop records live in
a unit byte stream built from the per-object segment arena at
object->+0x10 (+0xc0 descriptor, mutex at +0x40, stats +0x48/+0x4c),
allocated by the span allocator at 0x1b5e0 (12-byte nodes: count(22b),
chain index(21b), span end(21b); index 0 is the sentinel). Units chain
through frame->+0x18/+0x20 with byte position in frame->+0x10/+0x14;
sub-0x18 tails get a 0xff pad record plus a zeroed tail. Validation
order, error codes (0x80460020/0x80460086/0x80460002/0x804600a0), NaN
acceptance, stats updates skipped on alloc failure, and the pad loop's
abandon-on-failure all follow the disassembly; guest_font_graphics
covers insert order, epoch stop, pads, unit boundaries and exhaustion.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Firmware 4.03 re-disassembly of the drawup bodies fixes three
transliteration errors:
- Option1Flags[1] is 0x3000, not 0x2000: the option1 jump table at
0x37fcc routes index 1 to case code that ors 0x2000 then falls into
or 0x1000.
- Sealed paths run the shared error epilogue (store at frame+4 and
invoke the registered callback), like every other error path; use
DrawupRecordError instead of a bare return.
- DrawupPoolAllocate returns memory + completingNodeIndex*slotSize
(0x1bcc0 marks slots start..start+count but returns the node whose
span finished the run), not the run's first slot.
The 13.42 emulator report matches on all three; its callback
suppression and +0x20 pool entries are firmware differences, not
deviations here.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
DrawupPackColor converted channel floats with static_cast<int32_t>,
which is undefined for NaN — the same input DrawupColorValid admits.
The firmware bodies pack with vcvttss2si into 64-bit registers, whose
invalid-conversion result is 0x8000000000000000; the low dword the
packing ORs in is 0, so an unrepresentable channel contributes a zero
byte. Implement the conversion explicitly: in-range values truncate
through int64_t, everything else contributes 0. The FlatColor NaN
regression now checks the firmware-faithful packed word (0x00000000
instead of the int32-indefinite 0x80000000 the old codegen happened to
emit). Verified under -fsanitize=float-cast-overflow
-fno-sanitize-recover on Linux: clean run, test passes.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Firmware 4.03 re-disassembly of the drawup bodies fixes three
transliteration errors:
- Option1Flags[1] is 0x3000, not 0x2000: the option1 jump table at
0x37fcc routes index 1 to case code that ors 0x2000 then falls into
or 0x1000.
- Sealed paths run the shared error epilogue (store at frame+4 and
invoke the registered callback), like every other error path; use
DrawupRecordError instead of a bare return.
- DrawupPoolAllocate returns memory + completingNodeIndex*slotSize
(0x1bcc0 marks slots start..start+count but returns the node whose
span finished the run), not the run's first slot.
The 13.42 emulator report matches on all three; its callback
suppression and +0x20 pool entries are firmware differences, not
deviations here.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Firmware 4.03 bodies at 0x20cb0 (image) and 0x20f50 (pattern) recovered:
frame magic/sticky/parameter checks with error store at +4 and hook,
record-pool u16 free-run allocation under the pool mutex, 0x80 record
header (type 0x40/0x50, sequence, payload at +0x80), descriptor memcpy
of mode bytes, and same-sequence queue append bumping frame+0x28.
libSceFont links libkernel for scePthreadMutexLock/Unlock; guest test
covers validation, record bytes, queue order and the allocator.
Firmware 4.03 body at 0x20f50 takes ten integer arguments with the\noutput pointer last - the PPSA20397 libSceFontGsm.prx caller rotates\nsix registers and pushes four stack arguments, no float registers.\nUpdate the declaration, stub and signature assertion, and record the\nverified offsets and record/queue semantics in the debt file.