Files
AnyPS5/core/libs/tests/GuestFontMetrics.cpp
T
XairRrR a38cd73f56 test(libSceFont): cover several live handles sharing one memory font entry
guest_font_metrics now opens the same buffer five times while the library
supports four external fonts. The fifth open only succeeds if ClaimEntry
reuses the active entry by address, so the address match is now covered.

AI-assisted: yes
2026-10-29 06:24:02 +01:00

155 lines
7.4 KiB
C++

#include "prx/libSceFont/include/FontDriver.hpp"
#include "prx/libSceFont/libSceFont.hpp"
#include "prx/libSceFontFt/libSceFontFt.hpp"
#include "tests/FontFixture.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <string>
#include <vector>
static void Check(bool value, int line) {
if (!value) {
std::fprintf(stderr, "Font metrics check failed at line %d\n", line);
std::abort();
}
}
#define Require(value) Check((value), __LINE__)
struct Expected {
std::uint32_t form;
float ascender;
float descender;
float lineGap;
float maxAdvance;
float verticalDescender;
float verticalAscender;
};
static FontMetrics Metrics(FontHandle font, std::uint32_t form) {
FontMetrics metrics{};
std::memset(&metrics, 0x7F, sizeof(metrics));
Require(sceFontGetFontMetrics(font, form, &metrics) == SCE_FONT_OK);
return metrics;
}
static void RequireMetrics(FontHandle font, std::uint32_t form, const Expected& expected) {
const FontMetrics metrics = Metrics(font, form);
Require(metrics.form == expected.form);
Require(metrics.ascender == expected.ascender && metrics.descender == expected.descender && metrics.lineGap == expected.lineGap);
Require(metrics.maxAdvance == expected.maxAdvance);
Require(metrics.verticalDescender == expected.verticalDescender && metrics.verticalAscender == expected.verticalAscender);
Require(metrics.xMin == 0.0f && metrics.yMin == 0.0f && metrics.xMax == 0.0f && metrics.yMax == 0.0f);
Require(metrics.emWidth == 99.984375f && metrics.emHeight == 99.984375f);
}
static void* APS5_VABI Allocate(void*, std::uint32_t size) {
return std::malloc(size);
}
static void APS5_VABI Release(void*, void* pointer) {
std::free(pointer);
}
using FontFixture::BuildFont;
using FontFixture::Words;
static std::vector<unsigned char> Os2Table() {
return Words({0, 500, 400, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0x20, 0x7E, 700, -300, 100, 900, 250});
}
static FontHandle Open(FontLibrary library, const std::vector<unsigned char>& data) {
FontHandle font = nullptr;
Require(sceFontOpenFontMemory(library, data.data(), static_cast<std::uint32_t>(data.size()), nullptr, &font) == SCE_FONT_OK && font != nullptr);
Require(sceFontSetScalePixel(font, 100.0f, 100.0f) == SCE_FONT_OK);
return font;
}
static std::vector<unsigned char> VerticalHeader(int major) {
return Words({major, 0, 880, -120, 60, 1000, 40, 0, 950, 0, 0, 0, 0, 0, 0, 0, 0, 1});
}
int main() {
const FontMemoryInterface iface{Allocate, Release, nullptr, nullptr, nullptr, nullptr};
FontMemory memory{};
Require(sceFontMemoryInit(&memory, nullptr, 0, &iface, nullptr, nullptr, nullptr) == SCE_FONT_OK);
FontLibrary library = nullptr;
Require(sceFontCreateLibrary(&memory, sceFontSelectLibraryFt(0), &library) == SCE_FONT_OK);
Require(sceFontSupportExternalFonts(library, 4, 0x52) == SCE_FONT_OK);
const auto plainData = BuildFont(1, {});
FontHandle plain = Open(library, plainData);
RequireMetrics(plain, 1, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(plain, 2, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(plain, 3, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(plain, 4, {4, 0.0f, 100.0f, 0.0f, 100.0f, -50.0f, 80.0f});
FontMetrics untouched{};
untouched.ascender = 5.0f;
untouched.descender = 6.0f;
untouched.lineGap = 7.0f;
Require(sceFontGetFontMetrics(plain, 7, &untouched) == SCE_FONT_OK);
Require(untouched.form == 7 && untouched.ascender == 5.0f && untouched.descender == 6.0f && untouched.lineGap == 7.0f);
Require(untouched.maxAdvance == 50.0f && untouched.verticalDescender == 0.0f && untouched.verticalAscender == 0.0f);
const auto typoData = BuildFont(1, {{"OS/2", Os2Table()}, {"vhea", VerticalHeader(0x11)}, {"vmtx", Words({1000, 40})}});
FontHandle typo = Open(library, typoData);
RequireMetrics(typo, 1, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(typo, 2, {2, 70.0f, -30.0f, 10.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(typo, 3, {3, 90.0f, -25.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(typo, 4, {4, 4.0f, 95.0f, -93.984375f, 100.0f, -12.0f, 70.0f});
Require(sceFontSetEffectWeight(typo, 1.03125f, 0.984375f, 0) == SCE_FONT_OK);
Require(sceFontSetEffectSlant(typo, 0.25f) == SCE_FONT_OK);
const FontMetrics styled = Metrics(typo, 2);
Require(styled.effectWeightX == 0.03125f && styled.effectWeightY == -0.015625f && styled.slantRatio == 0.25f);
Require(sceFontSetEffectWeight(typo, 1.5f, 0.5f, 0) == SCE_FONT_OK);
const FontMetrics clamped = Metrics(typo, 2);
Require(clamped.effectWeightX == 0.04f && clamped.effectWeightY == -0.04f);
const auto versionOneData = BuildFont(1, {{"vhea", VerticalHeader(1)}, {"vmtx", Words({1000, 40})}});
FontHandle versionOne = Open(library, versionOneData);
RequireMetrics(versionOne, 4, {4, 4.0f, 95.0f, 0.0f, 100.0f, -12.0f, 4.0f});
const auto os2OnlyData = BuildFont(1, {{"OS/2", Os2Table()}});
FontHandle os2Only = Open(library, os2OnlyData);
RequireMetrics(os2Only, 4, {4, 70.0f, 70.0f, 0.0f, 100.0f, -50.0f, 70.0f});
FontMetrics cleared{};
std::memset(&cleared, 0x7F, sizeof(cleared));
Require(sceFontGetFontMetrics(nullptr, 1, &cleared) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(cleared.form == 0 && cleared.ascender == 0.0f && cleared.maxAdvance == 0.0f && cleared.emHeight == 0.0f);
Require(cleared.effectWeightX == 1.0f && cleared.effectWeightY == 1.0f && cleared.slantRatio == 0.0f);
std::uint64_t notAFont[32]{};
std::memset(&cleared, 0x7F, sizeof(cleared));
Require(sceFontGetFontMetrics(reinterpret_cast<FontHandle>(notAFont), 1, &cleared) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(cleared.form == 0 && cleared.effectWeightX == 1.0f);
Require(sceFontGetFontMetrics(plain, 1, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontGetFontMetrics(nullptr, 1, nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontSetEffectWeight(nullptr, 1.0f, 1.0f, 0) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontSetEffectWeight(plain, 1.0f, 1.0f, 1) == SCE_FONT_ERROR_INVALID_PARAMETER);
Require(sceFontSetEffectSlant(nullptr, 0.0f) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontSetScalePixel(nullptr, 16.0f, 16.0f) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
Require(sceFontCloseFont(nullptr) == SCE_FONT_ERROR_INVALID_FONT_HANDLE);
for (FontHandle font : {plain, typo, versionOne, os2Only}) Require(sceFontCloseFont(font) == SCE_FONT_OK);
FontHandle reopened = Open(library, plainData);
const auto typoCopy = typoData;
FontHandle copied = Open(library, typoCopy);
RequireMetrics(reopened, 2, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
RequireMetrics(copied, 2, {2, 70.0f, -30.0f, 10.0f, 50.0f, 0.0f, 0.0f});
for (FontHandle font : {reopened, copied}) Require(sceFontCloseFont(font) == SCE_FONT_OK);
std::vector<FontHandle> shared;
for (int i = 0; i < 5; ++i) shared.push_back(Open(library, plainData));
for (FontHandle font : shared) RequireMetrics(font, 2, {1, 80.0f, -20.0f, 0.0f, 50.0f, 0.0f, 0.0f});
for (FontHandle font : shared) Require(sceFontCloseFont(font) == SCE_FONT_OK);
Require(sceFontDestroyLibrary(&library) == SCE_FONT_OK);
Require(sceFontMemoryTerm(&memory) == SCE_FONT_OK);
std::puts("Font metrics tests passed");
return 0;
}