Files
AnyPS5/docs/dev/debt/libSceFont.md
T
Dean fe86ec4a3d Merge pull request #10539 from hypermoist/feat/font-get-glyphs-outline-profile
feat(libSceFont): report the glyph outline profile through the FreeType driver
2026-10-26 02:20:13 +13:00

17 KiB

libSceFont technical debt

Unknown function info

  • sceFontRenderCharGlyphImage, sceFontRenderCharGlyphImageHorizontal, sceFontRenderCharGlyphImageVertical (libSceFont) accept a surface whose width or height is 0, with any row stride, and return the glyph metrics and an empty update rectangle without writing to the surface. A negative width, height or stride, a zero stride on a surface with pixels, a null buffer and a pixel size other than 1 or 4 bytes still return SCE_FONT_ERROR_NO_SUPPORT_SURFACE; those cases and the exact console result for a zero-extent surface are not measured. The extent-0 behaviour is inferred from the Tekken Dark Resurrection PSP emulator which, to measure a string, initialises the surface with width 0 and height 31 and an unbound stride, renders each character and sums the returned advances.

  • sceFontCreateString: a string created from a default-form (0x10) text source gets the direction of its first strong character, 0x11 for right to left and 0x12 for left to right, and 0x12 when there is none; explicit 0x11 and 0x12 sources keep their form and sceFontStringGetWritingForm returns the stored form. LRM and RLM count as strong, embeddings and overrides do not, and text between an isolate initiator and its PDI is skipped. The rule follows a run of the firmware 13.42 library in a harness reported on the pull request: for non-empty text it was checked before a later glyph-stage failure and only empty text completed, so it is not full native right-to-left rendering. Which characters are strong is decided here by Unicode block ranges, not by the full bidi class tables.

  • Typographic design: native selector, feature, font-set eligibility and output-clearing rules are recovered. Driver typography selection and queries, feature-cache updates and glyph substitution remain unimplemented and throw instead of storing arbitrary selector/value pairs.

  • sceFontSetScriptLanguage and sceFontGetScriptLanguage: native script/language ranges, handle-error precedence and clearing of failed getter outputs are implemented. Valid driver queries and updates throw; the previous arbitrary-value map and fabricated zero default are no longer used. Driver selection and actual language-dependent shaping remain unimplemented.

  • 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

  • sceFontGetFontGlyphsCount, sceFontGetCharGlyphCode: signatures, error precedence, failed-output clearing and external-font glyph-code encoding are confirmed by the PS5 library. Multi-face system-font glyph counts, face-selection bits and typographic glyph substitutions are not modeled.

  • sceFontGetFontResolution: the 32-bit resolution output, float scale and nullable outputs are confirmed by the PS5 library and FontFt driver. Native system font sets return resolution 1000; the host still uses its selected face resolution because multi-face system font sets are not modeled.

  • Writing-line ABI and allocation: PS5 layouts, creation forms, memory validation, allocator callbacks and error handling are recovered. The 2208-byte line and 64-byte orders use caller memory. Host registry bookkeeping still allocates separately; memory lifetime and concurrent destruction require caller synchronization.

  • sceFontWritingRefersRenderStepCharacter (libSceFont) - the second argument is an out pointer to the writing's letter step, as in KytyPS5 and the SharpProspero bindings. Measured on PS4 firmware 11.00 libSceFont.sprx (not on a PS5): exactly 8 bytes are written, the address of a step stored inside the writing, and a null writing writes null; shadPS4 and prosper treat the argument as a struct, which the measurement contradicts. The letter step lives in the writing, so the pointer stays valid as long as the writing does, and its contents are rebuilt by each call of this function. On the PS4 library repeated calls on one step walk the characters it covers and then return null; here every call returns the step's one character, so the second call differs. Assumed: each step covers one character and one glyph. PPSA06699 (Hot Shots Golf 2) dereferences the returned pointer for its character counts.

  • sceFontWritingLineGetOrderingSpace: PS5 confirms output order, calculations and zeroed outputs on invalid handles. A non-null head output with a null tail output causes an unchecked native write; the host throws instead. With no head output, the tail output remains untouched.

  • sceFontCharacterGetSyllableStringState (libSceFont) - the signature (textCharacter, int* syllableStringState) follows SharpProspero. Measured on PS4 firmware 11.00 libSceFont.sprx (not on a PS5): the state is 0 for Latin, CJK, Hangul, Arabic, Hebrew, Devanagari and the other scripts tried, and the library sets 1, 3 or 4 (never 2 on real text) for Thai while it creates the string; a null character or a null result returns SCE_FONT_ERROR_INVALID_PARAMETER, and a null character writes 0 to a non-null result. This returns 0 for every character outside the Thai block (U+0E00 to U+0E7F), as KytyPS5 does, and throws for Thai characters, whose cluster states are not modelled. SharpProspero's SceFontSyllableString names 0 NonDetection, 1 Starting, 2 Following, 3 Depending and 4 Connecting. PPSA06699 (Hot Shots Golf 2) calls it.

  • sceFontCharactersRefersTextCodes, sceFontTextCodesStepNext and sceFontTextCodesStepBack (libSceFont) - the signatures follow the Orbital IDA type database and SharpProspero, and the 64-byte FontTextCodes with the order pointer at 0 and the code at 8 follows SharpProspero's SceFontTextCodes. The traversal follows the PS4 11.00 libSceFont.sprx disassembly posted in #4050, not a PS5 measurement: the cursor keeps the current, start and term characters at +0x28, +0x30 and +0x38; Refers never compares the character with the term and returns null for a clustered or synthetic character; StepNext returns null at the term after visiting it; StepBack returns null when the current character is the start; both steps skip synthetic characters. Assumed and not covered by the report, which says StepNext returns null only at the term: StepNext also returns null at the end of the chain (for example with a null term), and StepBack at the head of the chain. A null cursor or output returns null as in KytyPS5; a null text character throws, as its console result is unknown

  • sceFontDefineAttribute, sceFontGetAttribute, sceFontGlyphDefineAttribute, sceFontGlyphGetAttribute (libSceFont) - the signatures (handle, int attribute, int* oldAttribute) and (handle, int attribute, int* nowAttribute), and the same shape for a glyph, follow the SharpProspero bindings, matching the IDA type database of Orbital and KytyPS5, not confirmed on a title. Every handle and every glyph keeps one attribute, an opaque int whose meaning and valid values are unknown: define stores the new value and returns the previous one, and get returns the stored value with its attribute argument unused, as in KytyPS5. An attribute never set is 0. The null checks and the invalid-object errors follow the other libSceFont functions and diverge from KytyPS5, which returns OK for a null handle or glyph after writing 0 to the old-attribute pointer: every other handle and glyph entry point in this library reports the invalid object instead, and a title that passes one is already failing. shadPS4 leaves both exports unimplemented, so there is no second implementation to compare. PPSA01325 calls the glyph define with 0x11 on a glyph from sceFontGenerateCharGlyph that it deletes unused and draws with sceFontRenderCharGlyphImageHorizontal; the three bindings all read the third argument as an out pointer, not a value, so this call sets the glyph's attribute to 0x11. The attribute changes neither shaping nor rendering.

Functional

  • sceFontOpenFontSet (libSceFont) - the other script files of a set are chosen from its type, not measured on a console: Vietnamese for low-byte bit 0x10, Thai for script bit 0x10, Japanese for 0x04, simplified Chinese for 0x80 (before Japanese with 0x08), Hangul for 0x20 and Arabic for low-byte bit 0x80, as the SDK set names (..._JP_AR, ..._GBCJK_TH) listed by shadPS4 suggest; the file order and the Hangul file name (SCEPS4Yoongd-Medium.otf, SCEPS4Yoongd-Bold.otf) also come from shadPS4. A character the primary file lacks is taken from the first of them that has it; kerning between two files is 0, and sceFontGetCharGlyphCode and sceFontGetFontGlyphsCount answer for the primary file only

  • sceFontOpenFontSet (libSceFont) - the console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (0x1A......) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans Arabic, Noto Sans CJK with its Korean face for Hangul) is loaded instead; its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for.

  • sceFontGetCharGlyphMetrics, sceFontGetRenderCharGlyphMetrics (libSceFont) report the vertical metrics the way the FreeType driver reports them for a rendered glyph, not confirmed on a title: FreeType's vertical advance, and for a face without vertical metrics a bearing of half the horizontal advance left of the horizontal bearing and of half the advance left over by the glyph height.

  • sceFontGetFontMetrics (libSceFont) - the 64-byte output, the form fallbacks (2 and 3 fall back to the hhea values when OS/2 is missing or gives an empty range, 4 builds vertical values from vhea or OS/2) and the raw weight deltas and slant copied from the style are not measured on a console. For a font set the values are those of its primary file; which file the console reports is not recovered. Closes when: measured on a PS5

  • sceFontGetFontStyleInformation (libSceFont) - the 24-byte output (FreeType family and style names, italic and bold flags, OS/2 weight class when it is a multiple of 100 from 100 to 900, width class below 10, and 1 plus a right-to-left head direction plus 0x80 for vertical metrics) and the font-set record path are not measured on a console. A face without a head table, such as a BDF font, throws, as its console result is unknown. Closes when: measured on a PS5

  • sceFontGetFontGlyphsOutlineProfile (libSceFont) - the 16-byte output (the maxp maximum contours, points, composite contours and composite points) and SCE_FONT_ERROR_NO_SUPPORT_FUNCTION for a maxp older than 1.0 are not measured on a console. A handle with a font-set record throws, as the maximum over the record's system font entries is not modelled. A face without a maxp table throws, as its console result is unknown. Closes when: measured on a PS5

  • Font writing lines: visual ordering and spacing originated from PS4 measurements and match the supplied PS5 structure and control flow. Exhaustive floating-point equivalence, including NaN payloads and rounding, has not been verified against a running PS5 module.

  • Font graphics and metric signatures: graphics-device/service creation takes font memory, parameters and a handle output; destruction takes a handle pointer. Attribute selectors and outputs are 32-bit values. Expansion-buffer outputs remain opaque pending field recovery. Protected implementations are unchanged.

  • Font graphics fill and transform signatures: region layout and scalar float arguments are confirmed from assembly, including the extra 32-bit corner flags omitted by the roundish-region decompilation. Fill-method, plot and rates layouts, layout selector meanings and mapping parameter meanings remain unresolved. Protected bodies are unchanged.

Frame lifecycle, drawing completion, device usage, frame policy, clipping and fill setup/update argument signatures in Unimplemented.cpp are recovered. Frame and resource layouts remain opaque; their runtime implementation is still unavailable.

Glyph image rendering uses six pointer arguments and two scalar coordinates, with existing style, surface, metrics and output layouts. Positioning takes a mask and two floats. Control, word-character search and resource release/exchange/render signatures in Unimplemented.cpp are recovered; control payload types depend on the command and remain unmodeled.

Canvas, design, surface texture and fill-pattern construction signatures in Unimplemented.cpp are recovered. Pattern mode/count semantics and the graphics surface descriptor fields still require reconstruction; distinct opaque types preserve the confirmed object categories.

Unimplemented.cpp now defines the 32-byte fill method, 64-byte fill plot/rates and 96-byte device usage layouts, with size and field-offset assertions. Uninterpreted descriptor tails remain reserved; pool statistics expose capacity, current usage, available bytes, peak and signed change.

The glyph support registration and texture-image fill signatures are recovered from native argument use. The image constructor takes a byte span and four scalar coordinates; the decompiler omits its successful path. CreateWords and CreateGraphicsServiceWithEdition consume the output pointer in the fourth general-purpose argument slot but do not read the third slot, whose source type is not established.