Files
AnyPS5/core/libs/tests/GuestFont.cpp
T
KernelPanicNTandClaude Opus 5.5 cf762829d1 feat(libSceFont): implement sceFontCharactersRefersTextCodes and text code stepping
sceFontCharactersRefersTextCodes fills a 64-byte FontTextCodes with the
order and code of a text character and keeps the current, start and
term characters at +0x28, +0x30 and +0x38. The traversal follows the
PS4 11.00 libSceFont.sprx disassembly posted in the review of #4050,
which implemented the same rules: Refers never compares the character
with the term and returns null for a clustered or synthetic character,
sceFontTextCodesStepNext returns null at the term after visiting it,
sceFontTextCodesStepBack returns null at the start character, and both
steps skip synthetic characters. Null from StepNext at the end of the
chain and from StepBack at the head of the chain is assumed and not
covered by the report.

The signatures follow Orbital's IDA type database and SharpProspero. A
null cursor or output returns null as in KytyPS5; a null text character
throws. guest_font_tests gets configure_windows_unwind so the expected
exception reaches the test on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:44:41 +00:00

445 lines
28 KiB
C++

#include "prx/libSceFont/include/FontDriver.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <initializer_list>
#include <map>
#include <random>
#include <stdexcept>
#include <string>
#include <vector>
extern "C" {
int APS5_VABI sceFontMemoryInit(FontMemory*, void*, std::uint32_t, const FontMemoryInterface*, void*, FontMemoryDestroyFunction, void*);
int APS5_VABI sceFontMemoryTerm(FontMemory*);
int APS5_VABI sceFontCreateLibrary(const FontMemory*, const void*, FontLibrary*);
int APS5_VABI sceFontDestroyLibrary(FontLibrary*);
int APS5_VABI sceFontCreateRenderer(const FontMemory*, const void*, FontRenderer*);
int APS5_VABI sceFontDestroyRenderer(FontRenderer*);
int APS5_VABI sceFontGetPixelResolution(FontLibrary, std::uint32_t*);
int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary, void*, std::uint32_t);
int APS5_VABI sceFontClearDeviceCache(FontLibrary);
int APS5_VABI sceFontDettachDeviceCacheBuffer(FontLibrary, void**, std::uint32_t*);
int APS5_VABI sceFontSupportSystemFonts(FontLibrary);
int APS5_VABI sceFontSupportExternalFonts(FontLibrary, std::uint32_t, std::uint32_t);
int APS5_VABI sceFontOpenFontSet(FontLibrary, std::uint32_t, std::uint32_t, const FontOpenDetail*, FontHandle*);
int APS5_VABI sceFontOpenFontMemory(FontLibrary, const void*, std::uint32_t, const FontOpenDetail*, FontHandle*);
int APS5_VABI sceFontOpenFontFile(FontLibrary, const char*, std::uint32_t, const FontOpenDetail*, FontHandle*);
int APS5_VABI sceFontCloseFont(FontHandle);
int APS5_VABI sceFontSetScriptLanguage(FontHandle, int, int);
int APS5_VABI sceFontGetScriptLanguage(FontHandle, int, int*);
int APS5_VABI sceFontSetTypographicDesign(FontHandle, int, int);
int APS5_VABI sceFontGetTypographicDesign(FontHandle, int, int*);
int APS5_VABI sceFontSetResolutionDpi(FontHandle, std::uint32_t, std::uint32_t);
int APS5_VABI sceFontGetResolutionDpi(FontHandle, std::uint32_t*, std::uint32_t*);
int APS5_VABI sceFontSetScalePixel(FontHandle, float, float);
int APS5_VABI sceFontBindRenderer(FontHandle, FontRenderer);
int APS5_VABI sceFontUnbindRenderer(FontHandle);
int APS5_VABI sceFontSetupRenderScalePixel(FontHandle, float, float);
int APS5_VABI sceFontSetupRenderScalePoint(FontHandle, float, float);
int APS5_VABI sceFontGetKerning(FontHandle, std::uint32_t, std::uint32_t, FontKerning*);
int APS5_VABI sceFontGetFontGlyphsCount(FontHandle, std::uint32_t*);
int APS5_VABI sceFontGetCharGlyphCode(FontHandle, std::uint32_t, std::uint32_t*);
int APS5_VABI sceFontGetFontResolution(FontHandle, std::uint32_t*, float*);
int APS5_VABI sceFontGetRenderScaledKerning(FontHandle, std::uint32_t, std::uint32_t, FontKerning*);
int APS5_VABI sceFontGenerateCharGlyph(FontHandle, std::uint32_t, const FontGenerateGlyphDetail*, FontGlyph*);
int APS5_VABI sceFontGlyphDefineAttribute(FontGlyph, std::uint32_t, std::uint64_t);
int APS5_VABI sceFontDeleteGlyph(const FontMemory*, FontGlyph*);
int APS5_VABI sceFontTextSourceInit(FontTextSource*, const void*, std::uint32_t, FontTextParseFunction, void*);
int APS5_VABI sceFontCreateString(const FontMemory*, FontTextSource*, const FontCreateStringDetail*, FontString*);
int APS5_VABI sceFontDestroyString(FontString*);
FontTextCharacter* APS5_VABI sceFontStringRefersTextCharacters(FontString, std::uint32_t*);
int APS5_VABI sceFontWritingInit(FontWriting*, FontString, const FontTextCharacter*);
const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting*);
FontTextCharacter* APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting*, const FontWritingLetterStep**);
const void* APS5_VABI sceFontSelectLibraryFt(int);
const void* APS5_VABI sceFontSelectRendererFt(int);
int APS5_VABI sceFontCharacterGetSyllableStringState(const FontTextCharacter*, int*);
FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(const FontTextCharacter*, const FontTextCharacter*, FontTextCodes*);
FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes*);
FontTextCodes* APS5_VABI sceFontTextCodesStepBack(FontTextCodes*);
}
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Font check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
static void SetFontDirectory(const std::filesystem::path& directory) {
#ifdef _WIN32
Require(_putenv_s("ANYPS5_SYSTEM_FONTS", directory.string().c_str()) == 0);
#else
Require(::setenv("ANYPS5_SYSTEM_FONTS", directory.string().c_str(), 1) == 0);
#endif
}
static int allocations = 0;
static void* APS5_VABI Allocate(void*, std::uint32_t size) {
++allocations;
return std::malloc(size);
}
static void APS5_VABI Release(void*, void* pointer) {
if (pointer) --allocations;
std::free(pointer);
}
static std::uint32_t APS5_VABI CoarsePixelResolution() {
return 16;
}
static void Put16(std::vector<unsigned char>& out, int value) {
out.push_back(static_cast<unsigned char>((value >> 8) & 0xFF));
out.push_back(static_cast<unsigned char>(value & 0xFF));
}
static std::vector<unsigned char> Words(std::initializer_list<int> values) {
std::vector<unsigned char> out;
for (const int value : values) Put16(out, value);
return out;
}
static std::vector<unsigned char> BuildFont(int glyphCount, std::map<std::string, std::vector<unsigned char>> tables) {
tables["glyf"] = Words({0, 0});
tables["head"] = Words({1, 0, 1, 0, 0, 0, 0x5F0F, 0x3CF5, 0, 1000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0});
tables["hhea"] = Words({1, 0, 800, -200, 0, 500, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1});
tables["hmtx"] = Words({500});
tables["hmtx"].resize(2 + 2 * static_cast<std::size_t>(glyphCount));
tables["loca"].assign(2 * (static_cast<std::size_t>(glyphCount) + 1), 0);
tables["maxp"] = Words({1, 0, glyphCount, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0});
const int tableCount = static_cast<int>(tables.size());
int entrySelector = 0;
while ((2 << entrySelector) <= tableCount) ++entrySelector;
std::vector<unsigned char> font = Words({1, 0, tableCount, 16 << entrySelector, entrySelector, 16 * tableCount - (16 << entrySelector)});
int offset = 12 + 16 * tableCount;
for (const auto& table : tables) {
font.insert(font.end(), table.first.begin(), table.first.end());
for (const int value : {0, 0, offset >> 16, offset & 0xFFFF, 0, static_cast<int>(table.second.size())}) Put16(font, value);
offset += static_cast<int>((table.second.size() + 3) & ~std::size_t{3});
}
for (const auto& table : tables) {
font.insert(font.end(), table.second.begin(), table.second.end());
font.resize((font.size() + 3) & ~std::size_t{3});
}
return font;
}
static std::vector<unsigned char> EmptyGlyphFont() {
return BuildFont(1, {});
}
static std::vector<unsigned char> KerningFont() {
return BuildFont(3, {
{"cmap", Words({0, 1, 3, 1, 0, 12, 4, 40, 0, 6, 4, 1, 2, 'A', 'V', 0xFFFF, 0, 'A', 'V', 0xFFFF, 1 - 'A', 2 - 'V', 1, 0, 0, 0})},
{"kern", Words({0, 1, 0, 20, 1, 1, 6, 0, 0, 1, 2, -200})},
});
}
struct ParsedText {
const char* text;
std::size_t at;
FontHandle font;
};
static std::int32_t APS5_VABI ParseText(FontTextSource* source, void** order, FontTextParseResult* result) {
auto* parsed = static_cast<ParsedText*>(source->textObject);
*order = const_cast<char*>(parsed->text + parsed->at);
if (parsed->text[parsed->at] == 0) {
result->Terminate.terminateCode = 0;
return 0;
}
result->FontCode.font = parsed->font;
result->FontCode.code = static_cast<unsigned char>(parsed->text[parsed->at++]);
return 1;
}
static void CheckTextCodes(const FontMemory* memory, FontHandle font) {
ParsedText parsed{"AVA", 0, font};
FontTextSource source{};
Require(sceFontTextSourceInit(&source, parsed.text, 3, ParseText, &parsed) == SCE_FONT_OK);
FontString string = nullptr;
Require(sceFontCreateString(memory, &source, nullptr, &string) == SCE_FONT_OK && string != nullptr);
std::uint32_t count = 0;
FontTextCharacter* characters = sceFontStringRefersTextCharacters(string, &count);
Require(characters != nullptr && count == 3);
FontTextCodes codes;
std::memset(&codes, 0xa5, sizeof(codes));
Require(sceFontCharactersRefersTextCodes(characters, nullptr, &codes) == &codes);
Require(codes.text.order == parsed.text && codes.text.code == 'A' && codes.text.reserved == 0);
Require(codes.systemUse[5] == characters && codes.systemUse[6] == characters && codes.systemUse[7] == nullptr);
Require(sceFontTextCodesStepNext(&codes) == &codes && codes.text.code == 'V' && codes.text.order == parsed.text + 1);
Require(sceFontTextCodesStepNext(&codes) == &codes && codes.text.code == 'A' && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepNext(&codes) == nullptr && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepBack(&codes) == &codes && codes.text.code == 'V' && codes.text.order == parsed.text + 1);
Require(sceFontTextCodesStepBack(&codes) == &codes && codes.text.code == 'A' && codes.text.order == parsed.text);
Require(sceFontTextCodesStepBack(&codes) == nullptr && codes.text.order == parsed.text);
Require(sceFontCharactersRefersTextCodes(characters + 1, characters + 2, &codes) == &codes && codes.text.code == 'V');
Require(sceFontTextCodesStepNext(&codes) == &codes && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepNext(&codes) == nullptr && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepBack(&codes) == &codes && codes.text.order == parsed.text + 1);
Require(sceFontTextCodesStepBack(&codes) == nullptr && codes.text.order == parsed.text + 1);
Require(sceFontCharactersRefersTextCodes(characters + 2, characters + 2, &codes) == &codes && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepNext(&codes) == nullptr && sceFontTextCodesStepBack(&codes) == nullptr);
characters[1].synthetic = 1;
Require(sceFontCharactersRefersTextCodes(characters, nullptr, &codes) == &codes);
Require(sceFontTextCodesStepNext(&codes) == &codes && codes.text.order == parsed.text + 2);
Require(sceFontTextCodesStepBack(&codes) == &codes && codes.text.order == parsed.text);
Require(sceFontCharactersRefersTextCodes(characters + 1, nullptr, &codes) == nullptr && codes.text.order == parsed.text);
characters[1].synthetic = 0;
characters[1].clusterIndex = 1;
Require(sceFontCharactersRefersTextCodes(characters + 1, nullptr, &codes) == nullptr && codes.text.order == parsed.text);
Require(sceFontTextCodesStepNext(&codes) == &codes && codes.text.order == parsed.text + 1);
characters[1].clusterIndex = 0;
Require(sceFontCharactersRefersTextCodes(characters, nullptr, nullptr) == nullptr);
Require(sceFontTextCodesStepNext(nullptr) == nullptr && sceFontTextCodesStepBack(nullptr) == nullptr);
bool threw = false;
try {
sceFontCharactersRefersTextCodes(nullptr, nullptr, &codes);
} catch (const std::runtime_error&) {
threw = true;
}
Require(threw && codes.text.order == parsed.text + 1);
Require(sceFontDestroyString(&string) == SCE_FONT_OK);
}
static bool KerningIs(const FontKerning& kerning, float offsetX) {
return kerning.offsetX == offsetX && kerning.offsetY == 0.0f && kerning.positionX == 0.0f && kerning.positionY == 0.0f;
}
int main() {
constexpr std::uint32_t SystemFontSet = 0x18070043u;
const std::filesystem::path fontRoot = std::filesystem::temp_directory_path() / ("anyps5_guest_font-" + std::to_string(std::random_device{}()));
std::filesystem::remove_all(fontRoot);
std::filesystem::create_directories(fontRoot);
SetFontDirectory(fontRoot);
const FontMemoryInterface iface{Allocate, Release, nullptr, nullptr, nullptr, nullptr};
FontMemory memory{};
Require(sceFontMemoryInit(&memory, nullptr, 0, &iface, nullptr, nullptr, nullptr) == SCE_FONT_OK);
Require(sceFontSelectLibraryFt(0) != nullptr && sceFontSelectLibraryFt(1) == nullptr);
Require(sceFontSelectRendererFt(0) != nullptr && sceFontSelectRendererFt(1) == nullptr);
FontLibrary library = nullptr;
Require(sceFontCreateLibrary(&memory, nullptr, &library) == SCE_FONT_ERROR_INVALID_PARAMETER && library == nullptr);
Require(sceFontCreateLibrary(&memory, sceFontSelectLibraryFt(0), &library) == SCE_FONT_OK && library != nullptr);
std::uint32_t subPixelCount = 1;
Require(sceFontGetPixelResolution(library, &subPixelCount) == SCE_FONT_OK && subPixelCount == 64);
Require(sceFontGetPixelResolution(library, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontGetPixelResolution(nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
subPixelCount = 1;
Require(sceFontGetPixelResolution(nullptr, &subPixelCount) == SCE_FONT_ERROR_INVALID_LIBRARY && subPixelCount == 0);
FontHandleOpaque notALibrary{};
subPixelCount = 1;
Require(sceFontGetPixelResolution(&notALibrary, &subPixelCount) == SCE_FONT_ERROR_INVALID_LIBRARY && subPixelCount == 0);
Font::SysDriver coarseDriver = *static_cast<const Font::SysDriver*>(sceFontSelectLibraryFt(0));
coarseDriver.pixel_resolution = CoarsePixelResolution;
FontLibrary coarseLibrary = nullptr;
Require(sceFontCreateLibrary(&memory, &coarseDriver, &coarseLibrary) == SCE_FONT_OK && coarseLibrary != nullptr);
Require(sceFontGetPixelResolution(coarseLibrary, &subPixelCount) == SCE_FONT_OK && subPixelCount == 16);
Require(sceFontGetPixelResolution(library, &subPixelCount) == SCE_FONT_OK && subPixelCount == 64);
coarseDriver.pixel_resolution = nullptr;
Require(sceFontGetPixelResolution(coarseLibrary, &subPixelCount) == SCE_FONT_ERROR_INVALID_LIBRARY && subPixelCount == 0);
Require(sceFontDestroyLibrary(&coarseLibrary) == SCE_FONT_OK && coarseLibrary == nullptr);
void* cacheBuffer = nullptr;
std::uint32_t cacheSize = 0;
Require(sceFontDettachDeviceCacheBuffer(nullptr, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_INVALID_LIBRARY);
Require(sceFontDettachDeviceCacheBuffer(&notALibrary, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_INVALID_LIBRARY);
Require(sceFontClearDeviceCache(library) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER);
Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER);
std::vector<unsigned char> callerCache(0x2000);
Require(sceFontAttachDeviceCacheBuffer(library, callerCache.data(), 0x1000) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontAttachDeviceCacheBuffer(library, callerCache.data(), static_cast<std::uint32_t>(callerCache.size())) == SCE_FONT_OK);
Require(sceFontAttachDeviceCacheBuffer(library, callerCache.data(), static_cast<std::uint32_t>(callerCache.size())) == SCE_FONT_ERROR_ALREADY_ATTACHED);
Require(sceFontClearDeviceCache(library) == SCE_FONT_OK);
Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_OK && cacheBuffer == callerCache.data() && cacheSize == callerCache.size());
Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER);
Require(sceFontAttachDeviceCacheBuffer(library, nullptr, 0x2000) == SCE_FONT_OK);
cacheBuffer = nullptr;
cacheSize = 0;
Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_OK && cacheBuffer != nullptr && cacheSize == 0x2000);
Release(nullptr, cacheBuffer);
Require(sceFontDettachDeviceCacheBuffer(library, &cacheBuffer, &cacheSize) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER);
Require(sceFontAttachDeviceCacheBuffer(library, nullptr, 0x2000) == SCE_FONT_OK);
Require(sceFontDettachDeviceCacheBuffer(library, nullptr, nullptr) == SCE_FONT_OK);
Require(sceFontDettachDeviceCacheBuffer(library, nullptr, nullptr) == SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER);
FontHandle font = reinterpret_cast<FontHandle>(&memory);
Require(sceFontOpenFontSet(library, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FUNCTION && font == nullptr);
Require(sceFontSupportSystemFonts(library) == SCE_FONT_OK);
Require(sceFontOpenFontSet(library, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_FONT_OPEN_FAILED && font == nullptr);
Require(sceFontOpenFontSet(library, 0x12345678u, 1, nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FONTSET);
Require(sceFontOpenFontSet(library, SystemFontSet, 7, nullptr, &font) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontOpenFontSet(nullptr, SystemFontSet, 1, nullptr, &font) == SCE_FONT_ERROR_INVALID_LIBRARY);
const unsigned char notAFont[64] = {1, 2, 3, 4};
Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FUNCTION && font == nullptr);
Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_OK);
Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_ERROR_ALREADY_SPECIFIED);
font = reinterpret_cast<FontHandle>(&memory);
Require(sceFontOpenFontFile(library, "anyps5-missing-font-parent/../font.ttf", 1, nullptr, &font) == SCE_FONT_ERROR_FS_OPEN_FAILED && font == nullptr);
Require(sceFontOpenFontMemory(library, nullptr, 0, nullptr, &font) == SCE_FONT_ERROR_INVALID_PARAMETER && font == nullptr);
Require(sceFontOpenFontMemory(library, notAFont, sizeof(notAFont), nullptr, &font) == SCE_FONT_ERROR_NO_SUPPORT_FORMAT && font == nullptr);
const std::vector<unsigned char> fontData = EmptyGlyphFont();
Require(sceFontOpenFontMemory(library, fontData.data(), static_cast<std::uint32_t>(fontData.size()), nullptr, &font) == SCE_FONT_OK && font != nullptr);
int setting = -1;
Require(sceFontGetScriptLanguage(font, 3, &setting) == SCE_FONT_OK && setting == 0);
Require(sceFontSetScriptLanguage(font, 3, 7) == SCE_FONT_OK && sceFontSetScriptLanguage(font, 4, 9) == SCE_FONT_OK);
Require(sceFontGetScriptLanguage(font, 3, &setting) == SCE_FONT_OK && setting == 7);
Require(sceFontGetScriptLanguage(font, 4, &setting) == SCE_FONT_OK && setting == 9);
Require(sceFontGetScriptLanguage(font, 3, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontSetScriptLanguage(nullptr, 3, 7) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontSetTypographicDesign(font, 1, 2) == SCE_FONT_OK);
Require(sceFontGetTypographicDesign(font, 1, &setting) == SCE_FONT_OK && setting == 2);
Require(sceFontGetTypographicDesign(font, 5, &setting) == SCE_FONT_OK && setting == 0);
Require(sceFontGetTypographicDesign(nullptr, 1, &setting) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
std::uint32_t hDpi = 1;
std::uint32_t vDpi = 1;
Require(sceFontGetResolutionDpi(font, &hDpi, &vDpi) == SCE_FONT_OK && hDpi == 72 && vDpi == 72);
Require(sceFontSetResolutionDpi(font, 96, 144) == SCE_FONT_OK);
Require(sceFontGetResolutionDpi(font, &hDpi, &vDpi) == SCE_FONT_OK && hDpi == 96 && vDpi == 144);
hDpi = 1;
vDpi = 1;
Require(sceFontGetResolutionDpi(font, &hDpi, nullptr) == SCE_FONT_OK && hDpi == 96);
Require(sceFontGetResolutionDpi(font, nullptr, &vDpi) == SCE_FONT_OK && vDpi == 144);
Require(sceFontGetResolutionDpi(font, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontSetResolutionDpi(font, 0, 300) == SCE_FONT_OK);
Require(sceFontGetResolutionDpi(font, &hDpi, &vDpi) == SCE_FONT_OK && hDpi == 72 && vDpi == 300);
Require(sceFontGetResolutionDpi(nullptr, &hDpi, &vDpi) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && hDpi == 0 && vDpi == 0);
FontHandleOpaque unopened{};
hDpi = 1;
vDpi = 1;
Require(sceFontGetResolutionDpi(&unopened, &hDpi, &vDpi) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && hDpi == 0 && vDpi == 0);
std::uint32_t glyphsCount = 0;
Require(sceFontGetFontGlyphsCount(font, &glyphsCount) == SCE_FONT_OK && glyphsCount == 1);
Require(sceFontGetFontGlyphsCount(font, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
glyphsCount = 1;
Require(sceFontGetFontGlyphsCount(nullptr, &glyphsCount) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && glyphsCount == 0);
glyphsCount = 1;
Require(sceFontGetFontGlyphsCount(&unopened, &glyphsCount) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && glyphsCount == 0);
std::uint32_t glyphCode = 1;
Require(sceFontGetCharGlyphCode(font, 'A', &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_GLYPH && glyphCode == 0);
Require(sceFontGetCharGlyphCode(font, 'A', nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
glyphCode = 1;
Require(sceFontGetCharGlyphCode(nullptr, 'A', &glyphCode) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && glyphCode == 0);
glyphCode = 1;
Require(sceFontGetCharGlyphCode(&unopened, 'A', &glyphCode) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && glyphCode == 0);
std::uint32_t resolution = 0;
float scalePixel = 0.0f;
Require(sceFontGetFontResolution(font, &resolution, &scalePixel) == SCE_FONT_OK && resolution == 1000 && scalePixel == 15.625f);
resolution = 0;
Require(sceFontGetFontResolution(font, &resolution, nullptr) == SCE_FONT_OK && resolution == 1000);
scalePixel = 0.0f;
Require(sceFontGetFontResolution(font, nullptr, &scalePixel) == SCE_FONT_OK && scalePixel == 15.625f);
Require(sceFontGetFontResolution(font, nullptr, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontGetFontResolution(nullptr, &resolution, &scalePixel) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && resolution == 0 && scalePixel == 0.0f);
resolution = 1;
scalePixel = 1.0f;
Require(sceFontGetFontResolution(&unopened, &resolution, &scalePixel) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && resolution == 0 && scalePixel == 0.0f);
Require(sceFontCloseFont(font) == SCE_FONT_OK);
FontRenderer renderer = nullptr;
Require(sceFontCreateRenderer(&memory, sceFontSelectRendererFt(0), &renderer) == SCE_FONT_OK && renderer != nullptr);
const std::vector<unsigned char> kerningData = KerningFont();
font = nullptr;
Require(sceFontOpenFontMemory(library, kerningData.data(), static_cast<std::uint32_t>(kerningData.size()), nullptr, &font) == SCE_FONT_OK && font != nullptr);
Require(sceFontGetFontGlyphsCount(font, &glyphsCount) == SCE_FONT_OK && glyphsCount == 3);
Require(sceFontGetCharGlyphCode(font, 'A', &glyphCode) == SCE_FONT_OK && glyphCode == 1);
Require(sceFontGetCharGlyphCode(font, 'V', &glyphCode) == SCE_FONT_OK && glyphCode == 2);
glyphCode = 1;
Require(sceFontGetCharGlyphCode(font, 'B', &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_GLYPH && glyphCode == 0);
glyphCode = 1;
Require(sceFontGetCharGlyphCode(font, 0, &glyphCode) == SCE_FONT_ERROR_NO_SUPPORT_CODE && glyphCode == 0);
Require(sceFontSetResolutionDpi(font, 144, 144) == SCE_FONT_OK);
Require(sceFontSetScalePixel(font, 100.0f, 100.0f) == SCE_FONT_OK);
FontGlyph glyph = nullptr;
Require(sceFontGenerateCharGlyph(font, 'A', nullptr, &glyph) == SCE_FONT_OK && glyph != nullptr);
Require(sceFontGlyphDefineAttribute(glyph, 0x11, 0) == SCE_FONT_OK);
Require(sceFontGlyphDefineAttribute(nullptr, 0x11, 0) == SCE_FONT_ERROR_INVALID_GLYPH);
FontGlyphOpaque notAGlyph{};
Require(sceFontGlyphDefineAttribute(&notAGlyph, 0x11, 0) == SCE_FONT_ERROR_INVALID_GLYPH);
Require(sceFontDeleteGlyph(&memory, &glyph) == SCE_FONT_OK && glyph == nullptr);
FontKerning kerning{1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -20.0f));
kerning = {1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', &kerning) == SCE_FONT_ERROR_NOT_BOUND_RENDERER && KerningIs(kerning, 0.0f));
Require(sceFontBindRenderer(font, renderer) == SCE_FONT_OK);
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -20.0f));
Require(sceFontSetupRenderScalePixel(font, 50.0f, 50.0f) == SCE_FONT_OK);
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -10.0f));
Require(sceFontGetKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -20.0f));
Require(sceFontSetupRenderScalePoint(font, 20.0f, 20.0f) == SCE_FONT_OK);
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', &kerning) == SCE_FONT_OK && KerningIs(kerning, -8.0f));
kerning = {1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetRenderScaledKerning(font, 'V', 'A', &kerning) == SCE_FONT_OK && KerningIs(kerning, 0.0f));
kerning = {1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetRenderScaledKerning(font, 'A', 'B', &kerning) == SCE_FONT_OK && KerningIs(kerning, 0.0f));
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
kerning = {1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetRenderScaledKerning(nullptr, 'A', 'V', &kerning) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && KerningIs(kerning, 0.0f));
kerning = {1.0f, 1.0f, 1.0f, 1.0f};
Require(sceFontGetRenderScaledKerning(&unopened, 'A', 'V', &kerning) == SCE_FONT_ERROR_INVALID_FONT_HANDLE && KerningIs(kerning, 0.0f));
Require(sceFontUnbindRenderer(font) == SCE_FONT_OK);
Require(sceFontGetRenderScaledKerning(font, 'A', 'V', &kerning) == SCE_FONT_ERROR_NOT_BOUND_RENDERER);
ParsedText parsed{"AV", 0, font};
FontTextSource source{};
Require(sceFontTextSourceInit(&source, parsed.text, 2, ParseText, &parsed) == SCE_FONT_OK);
FontString string = nullptr;
Require(sceFontCreateString(&memory, &source, nullptr, &string) == SCE_FONT_OK && string != nullptr);
std::uint32_t characterCount = 0;
FontTextCharacter* characters = sceFontStringRefersTextCharacters(string, &characterCount);
Require(characters != nullptr && characterCount == 2);
FontWriting writing{};
Require(sceFontWritingInit(&writing, string, characters) == SCE_FONT_OK);
constexpr std::uint64_t guard = 0x5a5a5a5a5a5a5a5aull;
struct {
const FontWritingLetterStep* letter;
std::uint64_t guard;
} out{reinterpret_cast<const FontWritingLetterStep*>(&memory), guard};
Require(sceFontWritingRefersRenderStepCharacter(&writing, &out.letter) == nullptr && out.letter == nullptr && out.guard == guard);
const FontWritingStep* step = sceFontWritingRefersRenderStep(&writing);
Require(step != nullptr);
Require(sceFontWritingRefersRenderStepCharacter(&writing, &out.letter) == characters && out.letter != nullptr && out.guard == guard);
Require(out.letter->x == step->x && out.letter->advanceX == step->advanceX && out.letter->Components.textsIndex == 0 && out.letter->Components.characterTextCount == 1);
step = sceFontWritingRefersRenderStep(&writing);
Require(step != nullptr);
Require(sceFontWritingRefersRenderStepCharacter(&writing, &out.letter) == characters + 1 && out.guard == guard);
Require(out.letter->x == step->x && out.letter->Components.textsIndex == 1 && out.letter->Components.glyphsIndex == 1);
Require(sceFontWritingRefersRenderStepCharacter(&writing, nullptr) == characters + 1);
Require(sceFontDestroyString(&string) == SCE_FONT_OK);
CheckTextCodes(&memory, font);
Require(sceFontCloseFont(font) == SCE_FONT_OK);
Require(sceFontDestroyRenderer(&renderer) == SCE_FONT_OK && renderer == nullptr);
Require(sceFontDestroyRenderer(&renderer) == SCE_FONT_ERROR_INVALID_RENDERER);
Require(sceFontDestroyLibrary(&library) == SCE_FONT_OK && library == nullptr);
Require(sceFontDestroyLibrary(&library) == SCE_FONT_ERROR_INVALID_LIBRARY);
Require(allocations == 0);
Require(sceFontMemoryTerm(&memory) == SCE_FONT_OK);
Require(sceFontMemoryTerm(&memory) == SCE_FONT_ERROR_INVALID_MEMORY);
FontTextCharacter character{};
character.characterCode = 'A';
int syllableState = -1;
Require(sceFontCharacterGetSyllableStringState(&character, &syllableState) == SCE_FONT_OK && syllableState == 0);
syllableState = -1;
Require(sceFontCharacterGetSyllableStringState(nullptr, &syllableState) == SCE_FONT_ERROR_INVALID_PARAMETER && syllableState == 0);
Require(sceFontCharacterGetSyllableStringState(&character, nullptr) == SCE_FONT_ERROR_INVALID_PARAMETER);
std::filesystem::remove_all(fontRoot);
}