feat(font): port shadPS4's sceFont and sceFontFt on FreeType

Ported from shadPS4 (GPL-2.0-or-later) with system fonts removed, stubs made to throw,
and sceFontCloseFont implemented.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
This commit is contained in:
Dean Galvin 2026-09-28 16:44:52 +13:00
parent 7656458121
commit 00e4f7678d
24 changed files with 5718 additions and 1252 deletions

View File

@ -310,6 +310,14 @@ if(BUILD_TESTING)
set_tests_properties(guest_rtc PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRtc>")
endif()
add_executable(guest_font_tests tests/GuestFont.cpp)
target_include_directories(guest_font_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_font_tests PRIVATE libSceFont libSceFontFt libc)
add_test(NAME guest_font COMMAND guest_font_tests)
if(WIN32)
set_tests_properties(guest_font PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceFont>")
endif()
add_executable(mspace_tests tests/Mspace.cpp)
target_include_directories(mspace_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(mspace_tests PRIVATE libc)

View File

@ -1150,207 +1150,6 @@ struct PositionAndForm {
std::uint32_t height;
};
using FontHandle = void*;
using FontLibrary = void*;
using FontLibrarySelection = void*;
using FontRenderer = void*;
using FontRendererSelection = void*;
using FontMemoryDestroyCallback = void (*)(void*);
struct FontMemoryInterface {
void* (*malloc_func)(std::size_t);
void (*free_func)(void*);
};
struct FontMemory {
std::uint16_t type;
std::uint16_t attr;
std::uint32_t size;
void* address;
void* mspace_object;
const FontMemoryInterface* mem_interface;
FontMemoryDestroyCallback destroy_callback;
void* destroy_object;
void* user_object;
void* parent_object;
};
using FontOpenDetail = void;
struct FontHorizontalLayout {
float base_line_y;
float line_height;
float effect_height;
};
struct FontVerticalLayout {
float base_line_x;
float line_width;
float effect_width;
};
struct FontGlyphMetrics {
float width;
float height;
struct {
float bearing_x;
float bearing_y;
float advance;
} horizontal;
struct {
float bearing_x;
float bearing_y;
float advance;
} vertical;
};
struct FontRenderSurface {
void* buffer;
std::int32_t width_byte;
std::int8_t pixel_size_byte;
std::uint8_t system_ext0;
std::uint8_t system_ext1;
std::uint8_t system_ext2;
std::int32_t width;
std::int32_t height;
struct {
std::uint32_t x0;
std::uint32_t y0;
std::uint32_t x1;
std::uint32_t y1;
} scissor;
std::uint32_t system_use[22];
};
struct FontRenderCharacter { std::uint8_t reserved[128]; };
struct FontTransImage {
std::uint8_t* address;
std::uint32_t width_byte;
std::uint32_t image_width;
std::uint32_t image_height;
};
struct FontSurfaceImage {
std::uint8_t* address;
std::uint32_t width_byte;
std::uint8_t pixel_size_byte;
std::uint8_t pixel_format;
};
struct FontGlyphImageMetrics {
float bearing_x;
float bearing_y;
float advance;
float stride;
std::uint32_t width;
std::uint32_t height;
};
struct FontRenderResult {
const FontTransImage* trans_image;
FontSurfaceImage surface_image;
struct {
std::uint32_t x;
std::uint32_t y;
std::uint32_t w;
std::uint32_t h;
} update_rect;
FontGlyphImageMetrics image_metrics;
};
using FontCreateStringOrdersFunction = void (*)(void*, void*);
struct FontCreateStringDetail {
std::uint16_t detail_id;
std::uint8_t detail_type;
std::uint8_t detections;
std::uint32_t orders_option;
FontHandle default_font;
struct {
FontCreateStringOrdersFunction function;
void* object;
} orders;
};
struct FontGenerateGlyphDetail { std::uint8_t reserved[64]; };
struct FontString { void* system_use[32]; };
struct FontWriting { void* system_use[32]; };
struct FontWritingExtent {
float top;
float bottom;
float left;
float right;
};
struct FontWritingMetrics {
float advance_x;
float advance_y;
FontWritingExtent extent;
};
struct FontWritingStep {
float x;
float y;
float advance_x;
float advance_y;
FontHandle font;
struct {
std::uint32_t character_count : 8;
std::uint32_t invisible_glyph : 1;
} profile;
std::uint32_t glyph_code;
struct {
float x;
float y;
} positioning;
FontGlyphMetrics glyph_metrics;
};
struct FontWritingLine { std::uint8_t reserved[128]; };
struct FontWritingLineStep {
float x;
float y;
float advance_x;
float advance_y;
float spacing_progress;
void* writing_orderer;
struct {
float x;
float y;
} adjusting;
FontWritingMetrics metrics;
};
using FontTextParseFunction = int (*)(void*, std::uint32_t, void*);
struct FontTextSource {
std::uint64_t system_use0;
const void* start;
const void* end;
const void* current;
FontTextParseFunction text_parser;
void* text_object;
FontHandle default_font;
void* system_use[5];
};
struct FontTextCharacter { std::uint8_t reserved[64]; };
union FontTextCodes {
struct {
void* order;
std::uint32_t code;
std::uint32_t reserved;
} text;
void* system_reserved[8];
};
struct NetEtherAddr { std::uint8_t data[6]; };
union NetEpollData {

View File

@ -0,0 +1,2 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/../libSceFont/FontLibrary.cmake)
add_sce_font_library(libSceFont.native)

View File

@ -1,520 +0,0 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, uint32_t size) {
(void)library;
(void)buffer;
(void)size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontBindRenderer(FontHandle font_handle, FontRenderer renderer) {
(void)font_handle;
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetBidiLevel(FontTextCharacter text_character, int* bidi_level) {
(void)text_character;
(void)bidi_level;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetSyllableStringState(FontTextCharacter text_character, int* syllable_string_state) {
(void)text_character;
(void)syllable_string_state;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetTextFontCode(FontTextCharacter text_character, FontHandle* font_handle, uint32_t* text_code) {
(void)text_character;
(void)font_handle;
(void)text_code;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetTextOrder(FontTextCharacter text_character, void** text_order) {
(void)text_character;
(void)text_order;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontTextCharacter APS5_VABI sceFontCharacterRefersTextBack(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCharacter APS5_VABI sceFontCharacterRefersTextNext(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(FontTextCharacter text_character, FontTextCharacter term_character, FontTextCodes* text_codes) {
(void)text_character;
(void)term_character;
(void)text_codes;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontCloseFont(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, FontLibrarySelection selection, FontLibrary* library) {
(void)memory;
(void)selection;
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, FontLibrarySelection selection, uint64_t edition, FontLibrary* library) {
(void)memory;
(void)selection;
(void)edition;
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, FontRendererSelection selection, uint64_t edition, FontRenderer* renderer) {
(void)memory;
(void)selection;
(void)edition;
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateString(const FontMemory* memory, FontTextSource* font_text_source, const FontCreateStringDetail* string_detail, FontString* font_string) {
(void)memory;
(void)font_text_source;
(void)string_detail;
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateWritingLine(const FontMemory* memory, int writing_form, const void* writing_line_detail, FontWritingLine* writing_line) {
(void)memory;
(void)writing_form;
(void)writing_line_detail;
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDefineAttribute(FontHandle font_handle, int attribute, int* old_attribute) {
(void)font_handle;
(void)attribute;
(void)old_attribute;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, void** font_glyph) {
(void)memory;
(void)font_glyph;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyLibrary(FontLibrary* library) {
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyRenderer(FontRenderer* renderer) {
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyString(FontString* font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyWritingLine(FontWritingLine* writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGenerateCharGlyph(FontHandle font_handle, uint32_t code, const FontGenerateGlyphDetail* detail, void** font_glyph) {
(void)font_handle;
(void)code;
(void)detail;
(void)font_glyph;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) {
(void)font_handle;
(void)code;
(void)metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetHorizontalLayout(FontHandle font_handle, FontHorizontalLayout* layout) {
(void)font_handle;
(void)layout;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) {
(void)font_handle;
(void)code;
(void)metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetVerticalLayout(FontHandle font_handle, FontVerticalLayout* layout) {
(void)font_handle;
(void)layout;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphDefineAttribute(void* font_glyph, int attribute, int* old_attribute) {
(void)font_glyph;
(void)attribute;
(void)old_attribute;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontMemoryInit(FontMemory* font_memory, void* address, uint32_t size_byte, const FontMemoryInterface* mem_interface, void* mspace_object, FontMemoryDestroyCallback destroy_callback, void* destroy_object) {
(void)font_memory;
(void)address;
(void)size_byte;
(void)mem_interface;
(void)mspace_object;
(void)destroy_callback;
(void)destroy_object;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontMemoryTerm(FontMemory* font_memory) {
(void)font_memory;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontInstance(FontHandle font_handle, void* setup_font, FontHandle* out_font_handle) {
(void)font_handle;
(void)setup_font;
(void)out_font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* font_address, uint32_t font_size, const FontOpenDetail* detail, FontHandle* handle) {
(void)library;
(void)font_address;
(void)font_size;
(void)detail;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontSet(FontLibrary library, uint32_t font_set_type, uint32_t open_mode, const FontOpenDetail* detail, FontHandle* handle) {
(void)library;
(void)font_set_type;
(void)open_mode;
(void)detail;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontRebindRenderer(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle font_handle, uint32_t code, FontRenderSurface* surf, float x, float y, FontGlyphMetrics* metrics, FontRenderResult* result) {
(void)font_handle;
(void)code;
(void)surf;
(void)x;
(void)y;
(void)metrics;
(void)result;
NotImplemented_nid_no_patch(__func__);
return 0;
}
void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* surf, void* buffer, int buf_width_byte, int pixel_size_byte, int width, int height) {
(void)surf;
(void)buffer;
(void)buf_width_byte;
(void)pixel_size_byte;
(void)width;
(void)height;
NotImplemented_nid_no_patch(__func__);
}
void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* surf, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1) {
(void)surf;
(void)x0;
(void)y0;
(void)x1;
(void)y1;
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceFontSetEffectSlant(FontHandle font_handle, float slant_ratio) {
(void)font_handle;
(void)slant_ratio;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) {
(void)font_handle;
(void)weight_x_scale;
(void)weight_y_scale;
(void)mode;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetScalePixel(FontHandle font_handle, float w, float h) {
(void)font_handle;
(void)w;
(void)h;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle font_handle, float slant_ratio) {
(void)font_handle;
(void)slant_ratio;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) {
(void)font_handle;
(void)weight_x_scale;
(void)weight_y_scale;
(void)mode;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderScalePixel(FontHandle font_handle, float w, float h) {
(void)font_handle;
(void)w;
(void)h;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
void* APS5_VABI sceFontStringGetTerminateOrder(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontStringGetWritingForm(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontRenderCharacter APS5_VABI sceFontStringRefersRenderCharacters(FontString font_string, FontTextCharacter start_character, FontTextCharacter last_character, uint32_t* character_count) {
(void)font_string;
(void)start_character;
(void)last_character;
(void)character_count;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCharacter APS5_VABI sceFontStringRefersTextCharacters(FontString font_string, uint32_t* character_count) {
(void)font_string;
(void)character_count;
NotImplemented_nid_no_patch(__func__);
return {};
}
int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, uint32_t font_max, uint32_t formats) {
(void)library;
(void)font_max;
(void)formats;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) {
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontTextCodes* APS5_VABI sceFontTextCodesStepBack(FontTextCodes* text_codes_step) {
(void)text_codes_step;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes* text_codes_step) {
(void)text_codes_step;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontTextSourceInit(FontTextSource* font_text_source, const void* text_address, uint32_t text_size_byte, FontTextParseFunction text_parser, void* text_object) {
(void)font_text_source;
(void)text_address;
(void)text_size_byte;
(void)text_parser;
(void)text_object;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceRewind(FontTextSource* font_text_source) {
(void)font_text_source;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* font_text_source, FontHandle default_font) {
(void)font_text_source;
(void)default_font;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* font_text_source, int writing_form) {
(void)font_text_source;
(void)writing_form;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontUnbindRenderer(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* font_writing, FontWritingMetrics* writing_metrics) {
(void)font_writing;
(void)writing_metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingInit(FontWriting* font_writing, FontString font_string, FontRenderCharacter font_character) {
(void)font_writing;
(void)font_string;
(void)font_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineClear(FontWritingLine writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetOrderingSpace(FontWritingLine writing_line, float* head_space, float* inline_space, float* tail_space, float* advance_space) {
(void)writing_line;
(void)head_space;
(void)inline_space;
(void)tail_space;
(void)advance_space;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetRenderMetrics(FontWritingLine writing_line, FontWritingMetrics* writing_metrics) {
(void)writing_line;
(void)writing_metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(FontWritingLine writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontWritingLineWritesOrder(FontWritingLine writing_line, uint64_t writing_attribute, const FontWritingMetrics* writing_metrics, void* writing_orderer) {
(void)writing_line;
(void)writing_attribute;
(void)writing_metrics;
(void)writing_orderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* font_writing) {
(void)font_writing;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
FontTextCharacter APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* font_writing, const void** letter_step) {
(void)font_writing;
(void)letter_step;
NotImplemented_nid_no_patch(__func__);
return {};
}
int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* font_writing, int mask) {
(void)font_writing;
(void)mask;
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -0,0 +1,2 @@
include(${CMAKE_CURRENT_SOURCE_DIR}/FontLibrary.cmake)
add_sce_font_library(libSceFont)

View File

@ -1,520 +0,0 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, uint32_t size) {
(void)library;
(void)buffer;
(void)size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontBindRenderer(FontHandle font_handle, FontRenderer renderer) {
(void)font_handle;
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetBidiLevel(FontTextCharacter text_character, int* bidi_level) {
(void)text_character;
(void)bidi_level;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetSyllableStringState(FontTextCharacter text_character, int* syllable_string_state) {
(void)text_character;
(void)syllable_string_state;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetTextFontCode(FontTextCharacter text_character, FontHandle* font_handle, uint32_t* text_code) {
(void)text_character;
(void)font_handle;
(void)text_code;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharacterGetTextOrder(FontTextCharacter text_character, void** text_order) {
(void)text_character;
(void)text_order;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontTextCharacter APS5_VABI sceFontCharacterRefersTextBack(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCharacter APS5_VABI sceFontCharacterRefersTextNext(FontTextCharacter text_character) {
(void)text_character;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCodes* APS5_VABI sceFontCharactersRefersTextCodes(FontTextCharacter text_character, FontTextCharacter term_character, FontTextCodes* text_codes) {
(void)text_character;
(void)term_character;
(void)text_codes;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontCloseFont(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, FontLibrarySelection selection, FontLibrary* library) {
(void)memory;
(void)selection;
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, FontLibrarySelection selection, uint64_t edition, FontLibrary* library) {
(void)memory;
(void)selection;
(void)edition;
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, FontRendererSelection selection, uint64_t edition, FontRenderer* renderer) {
(void)memory;
(void)selection;
(void)edition;
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateString(const FontMemory* memory, FontTextSource* font_text_source, const FontCreateStringDetail* string_detail, FontString* font_string) {
(void)memory;
(void)font_text_source;
(void)string_detail;
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateWritingLine(const FontMemory* memory, int writing_form, const void* writing_line_detail, FontWritingLine* writing_line) {
(void)memory;
(void)writing_form;
(void)writing_line_detail;
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDefineAttribute(FontHandle font_handle, int attribute, int* old_attribute) {
(void)font_handle;
(void)attribute;
(void)old_attribute;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, void** font_glyph) {
(void)memory;
(void)font_glyph;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyLibrary(FontLibrary* library) {
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyRenderer(FontRenderer* renderer) {
(void)renderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyString(FontString* font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyWritingLine(FontWritingLine* writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGenerateCharGlyph(FontHandle font_handle, uint32_t code, const FontGenerateGlyphDetail* detail, void** font_glyph) {
(void)font_handle;
(void)code;
(void)detail;
(void)font_glyph;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) {
(void)font_handle;
(void)code;
(void)metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetHorizontalLayout(FontHandle font_handle, FontHorizontalLayout* layout) {
(void)font_handle;
(void)layout;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle font_handle, uint32_t code, FontGlyphMetrics* metrics) {
(void)font_handle;
(void)code;
(void)metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetVerticalLayout(FontHandle font_handle, FontVerticalLayout* layout) {
(void)font_handle;
(void)layout;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphDefineAttribute(void* font_glyph, int attribute, int* old_attribute) {
(void)font_glyph;
(void)attribute;
(void)old_attribute;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontMemoryInit(FontMemory* font_memory, void* address, uint32_t size_byte, const FontMemoryInterface* mem_interface, void* mspace_object, FontMemoryDestroyCallback destroy_callback, void* destroy_object) {
(void)font_memory;
(void)address;
(void)size_byte;
(void)mem_interface;
(void)mspace_object;
(void)destroy_callback;
(void)destroy_object;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontMemoryTerm(FontMemory* font_memory) {
(void)font_memory;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontInstance(FontHandle font_handle, void* setup_font, FontHandle* out_font_handle) {
(void)font_handle;
(void)setup_font;
(void)out_font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* font_address, uint32_t font_size, const FontOpenDetail* detail, FontHandle* handle) {
(void)library;
(void)font_address;
(void)font_size;
(void)detail;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontOpenFontSet(FontLibrary library, uint32_t font_set_type, uint32_t open_mode, const FontOpenDetail* detail, FontHandle* handle) {
(void)library;
(void)font_set_type;
(void)open_mode;
(void)detail;
(void)handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontRebindRenderer(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle font_handle, uint32_t code, FontRenderSurface* surf, float x, float y, FontGlyphMetrics* metrics, FontRenderResult* result) {
(void)font_handle;
(void)code;
(void)surf;
(void)x;
(void)y;
(void)metrics;
(void)result;
NotImplemented_nid_no_patch(__func__);
return 0;
}
void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* surf, void* buffer, int buf_width_byte, int pixel_size_byte, int width, int height) {
(void)surf;
(void)buffer;
(void)buf_width_byte;
(void)pixel_size_byte;
(void)width;
(void)height;
NotImplemented_nid_no_patch(__func__);
}
void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* surf, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1) {
(void)surf;
(void)x0;
(void)y0;
(void)x1;
(void)y1;
NotImplemented_nid_no_patch(__func__);
}
int APS5_VABI sceFontSetEffectSlant(FontHandle font_handle, float slant_ratio) {
(void)font_handle;
(void)slant_ratio;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) {
(void)font_handle;
(void)weight_x_scale;
(void)weight_y_scale;
(void)mode;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetScalePixel(FontHandle font_handle, float w, float h) {
(void)font_handle;
(void)w;
(void)h;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle font_handle, float slant_ratio) {
(void)font_handle;
(void)slant_ratio;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle font_handle, float weight_x_scale, float weight_y_scale, uint32_t mode) {
(void)font_handle;
(void)weight_x_scale;
(void)weight_y_scale;
(void)mode;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetupRenderScalePixel(FontHandle font_handle, float w, float h) {
(void)font_handle;
(void)w;
(void)h;
NotImplemented_nid_no_patch(__func__);
return 0;
}
uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
void* APS5_VABI sceFontStringGetTerminateOrder(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontStringGetWritingForm(FontString font_string) {
(void)font_string;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontRenderCharacter APS5_VABI sceFontStringRefersRenderCharacters(FontString font_string, FontTextCharacter start_character, FontTextCharacter last_character, uint32_t* character_count) {
(void)font_string;
(void)start_character;
(void)last_character;
(void)character_count;
NotImplemented_nid_no_patch(__func__);
return {};
}
FontTextCharacter APS5_VABI sceFontStringRefersTextCharacters(FontString font_string, uint32_t* character_count) {
(void)font_string;
(void)character_count;
NotImplemented_nid_no_patch(__func__);
return {};
}
int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, uint32_t font_max, uint32_t formats) {
(void)library;
(void)font_max;
(void)formats;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) {
(void)library;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontTextCodes* APS5_VABI sceFontTextCodesStepBack(FontTextCodes* text_codes_step) {
(void)text_codes_step;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
FontTextCodes* APS5_VABI sceFontTextCodesStepNext(FontTextCodes* text_codes_step) {
(void)text_codes_step;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontTextSourceInit(FontTextSource* font_text_source, const void* text_address, uint32_t text_size_byte, FontTextParseFunction text_parser, void* text_object) {
(void)font_text_source;
(void)text_address;
(void)text_size_byte;
(void)text_parser;
(void)text_object;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceRewind(FontTextSource* font_text_source) {
(void)font_text_source;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* font_text_source, FontHandle default_font) {
(void)font_text_source;
(void)default_font;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* font_text_source, int writing_form) {
(void)font_text_source;
(void)writing_form;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontUnbindRenderer(FontHandle font_handle) {
(void)font_handle;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* font_writing, FontWritingMetrics* writing_metrics) {
(void)font_writing;
(void)writing_metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingInit(FontWriting* font_writing, FontString font_string, FontRenderCharacter font_character) {
(void)font_writing;
(void)font_string;
(void)font_character;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineClear(FontWritingLine writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetOrderingSpace(FontWritingLine writing_line, float* head_space, float* inline_space, float* tail_space, float* advance_space) {
(void)writing_line;
(void)head_space;
(void)inline_space;
(void)tail_space;
(void)advance_space;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetRenderMetrics(FontWritingLine writing_line, FontWritingMetrics* writing_metrics) {
(void)writing_line;
(void)writing_metrics;
NotImplemented_nid_no_patch(__func__);
return 0;
}
FontWritingLineStep* APS5_VABI sceFontWritingLineRefersRenderStep(FontWritingLine writing_line) {
(void)writing_line;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
int APS5_VABI sceFontWritingLineWritesOrder(FontWritingLine writing_line, uint64_t writing_attribute, const FontWritingMetrics* writing_metrics, void* writing_orderer) {
(void)writing_line;
(void)writing_attribute;
(void)writing_metrics;
(void)writing_orderer;
NotImplemented_nid_no_patch(__func__);
return 0;
}
const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* font_writing) {
(void)font_writing;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
FontTextCharacter APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* font_writing, const void** letter_step) {
(void)font_writing;
(void)letter_step;
NotImplemented_nid_no_patch(__func__);
return {};
}
int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* font_writing, int mask) {
(void)font_writing;
(void)mask;
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -0,0 +1,23 @@
find_package(Freetype REQUIRED)
function(add_sce_font_library target)
set(fontDir ${CMAKE_CURRENT_FUNCTION_LIST_DIR})
add_library(${target} SHARED EXCLUDE_FROM_ALL
${fontDir}/src/Layout.cpp
${fontDir}/src/Library.cpp
${fontDir}/src/Open.cpp
${fontDir}/src/Render.cpp
${fontDir}/src/State.cpp
${fontDir}/src/Style.cpp
${fontDir}/src/Text.cpp
${fontDir}/src/Unimplemented.cpp
)
target_include_directories(${target} PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(${target} PRIVATE Freetype::Freetype libc)
set_target_properties(${target} PROPERTIES
CXX_EXTENSIONS OFF
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
configure_windows_unwind(${target})
endfunction()

View File

@ -0,0 +1,360 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTDRIVER_HPP
#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTDRIVER_HPP
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "prx/libSceFont/include/FontTypes.hpp"
namespace Font {
constexpr std::uint16_t MEMORY_MAGIC = 0x0F00;
constexpr std::uint16_t LIBRARY_MAGIC = 0x0F01;
constexpr std::uint16_t HANDLE_MAGIC = 0x0F02;
constexpr std::uint16_t GLYPH_MAGIC = 0x0F03;
constexpr std::uint16_t RENDERER_MAGIC = 0x0F07;
constexpr std::uint16_t STYLE_FRAME_MAGIC = 0x0F09;
constexpr std::uint32_t LOCK_BIT = 0x80000000u;
constexpr std::uint32_t OPEN_BIT = 0x40000000u;
constexpr std::uint32_t COUNT_MASK = 0x0FFFFFFFu;
constexpr std::size_t HORIZONTAL_LAYOUT_SIZE = 0x30;
constexpr std::size_t HORIZONTAL_LINE_ADVANCE = 0x00;
constexpr std::size_t HORIZONTAL_BASELINE = 0x04;
constexpr std::size_t HORIZONTAL_X_BOUND_LO = 0x08;
constexpr std::size_t HORIZONTAL_X_BOUND_HI = 0x0C;
constexpr std::size_t HORIZONTAL_MAX_ADVANCE_WIDTH = 0x10;
constexpr std::size_t HORIZONTAL_CARET_RISE_ADJUST = 0x14;
constexpr std::size_t HORIZONTAL_EFFECT_HEIGHT = 0x18;
constexpr std::size_t HORIZONTAL_HALF_EFFECT_WIDTH = 0x1C;
constexpr std::size_t HORIZONTAL_LEFT_ADJUST = 0x20;
constexpr std::size_t VERTICAL_LAYOUT_SIZE = 0x20;
constexpr std::size_t VERTICAL_COLUMN_ADVANCE = 0x00;
constexpr std::size_t VERTICAL_BASELINE_OFFSET_X = 0x04;
constexpr std::size_t VERTICAL_METRICS_0X08 = 0x08;
constexpr std::size_t VERTICAL_METRICS_0X0C = 0x0C;
constexpr std::size_t VERTICAL_ADVANCE_HEIGHT = 0x10;
constexpr std::size_t VERTICAL_DECORATION_SPAN = 0x14;
constexpr std::size_t VERTICAL_DECORATION_0X08 = 0x18;
constexpr std::size_t VERTICAL_DECORATION_0X0C = 0x1C;
inline float LoadFloat(const std::uint8_t* base, std::size_t offset) {
float value;
std::memcpy(&value, base + offset, sizeof(value));
return value;
}
inline void StoreFloat(std::uint8_t* base, std::size_t offset, float value) {
std::memcpy(base + offset, &value, sizeof(value));
}
struct StyleStateBlock {
std::uint32_t dpi_x;
std::uint32_t dpi_y;
std::uint32_t scale_unit;
std::uint32_t reserved_0x0c;
float scale_w;
float scale_h;
float effect_weight_x;
float effect_weight_y;
float slant_ratio;
float reserved_0x24;
};
struct CachedStyle {
StyleStateBlock state;
std::uint32_t reserved_0x28;
std::uint32_t layout_cache_state;
std::uint32_t cache_flags_and_direction;
std::uint32_t cache_lock_word;
std::uint8_t layout_cache_bytes[0x20];
std::uint32_t reserved_0x58;
std::uint32_t cached_scalar_bits;
};
struct FontHandleOpenInfo {
std::uint32_t unique_id_packed;
std::uint32_t ctx_entry_index;
std::uint32_t sub_font_index;
std::uint32_t fontset_flags;
const void* fontset_record;
std::uint64_t reserved_0x18;
};
struct FontHandleNative {
std::uint16_t magic;
std::uint16_t flags;
std::uint32_t lock_word;
FontHandleOpenInfo open_info;
FontLibrary library;
void* renderer;
std::uint8_t reserved_0x38[0x08];
StyleStateBlock style;
CachedStyle cached_style;
std::uint16_t metricA;
std::uint16_t metricB;
std::uint8_t reserved_0xcc[0x1C];
std::uint8_t reserved_0xe8[0x08];
FontHandle prevFont;
FontHandle nextFont;
};
struct FontObj {
std::uint32_t refcount;
std::uint32_t reserved_0x04;
std::uint32_t reserved_0x08;
std::uint32_t sub_font_index;
FontObj* prev;
FontObj* next;
std::uint8_t reserved_0x20[0x08];
void* open_ctx_0x28;
void* ft_face;
FontHandle font_handle;
std::int32_t shift_units_x;
std::int32_t shift_units_y;
std::uint64_t layout_seed_pair;
float scale_x_0x50;
float scale_y_0x54;
void* ft_ctx_0x58;
std::uint8_t reserved_0x60[0x04];
std::int32_t cached_glyph_index_0x64;
std::uint64_t cached_units_x_0x68;
std::uint64_t cached_units_y_0x70;
std::int64_t shift_cache_x;
std::int64_t shift_cache_y;
std::array<std::uint64_t, 2> layout_seed_vec;
std::array<std::uint64_t, 2> layout_scale_vec;
std::uint8_t reserved_0xA8[0x130 - 0xA8];
std::uint32_t glyph_cfg_word_0x130;
std::uint8_t glyph_cfg_mode_0x134;
std::uint8_t glyph_cfg_byte_0x135;
std::uint8_t glyph_cfg_byte_0x136;
std::uint8_t reserved_0x137[0x200 - 0x137];
};
struct FontCtxHeader {
std::uint32_t lock_word;
std::uint32_t max_entries;
void* base;
std::uint8_t reserved_0x10[0x10];
};
struct FontCtxEntry {
std::uint32_t reserved_0x00;
std::uint32_t active;
std::uint64_t font_address;
std::uint32_t unique_id;
std::uint32_t lock_mode1;
std::uint32_t lock_mode3;
std::uint32_t lock_mode2;
void* obj_mode1;
void* obj_mode3;
void* obj_mode2;
std::uint8_t reserved_0x38[0x08];
};
struct SysDriver;
struct FontLibNative {
std::uint16_t magic;
std::uint16_t reserved0;
std::uint32_t lock_word;
std::uint32_t flags;
std::uint32_t feature_word;
std::uint32_t mem_kind;
std::uint32_t region_size;
void* region_base;
void* alloc_ctx;
const FontMemoryInterface* iface;
std::uint8_t reserved_0x30[0x20];
FontAllocFunction alloc_fn;
FontFreeFunction dealloc_fn;
FontReallocFunction realloc_fn;
FontCallocFunction calloc_fn;
std::uint8_t reserved_0x70[0x10];
const SysDriver* sys_driver;
void* fontset_registry;
std::uint8_t reserved_0x90[0x10];
void* sysfonts_ctx;
void* external_fonts_ctx;
std::uint32_t* device_cache_buf;
std::uint8_t reserved_0xb8[0x04];
std::uint32_t workspace_size;
void* workspace;
void* sysfont_desc_ptr;
std::uint64_t sysfont_flags;
FontHandle* list_head_ptr;
FontHandle list_head;
std::uint8_t reserved_0xe8[0x18];
};
using DriverPixelResolutionFunction = std::uint32_t (APS5_VABI*)();
using DriverInitFunction = int (APS5_VABI*)(const FontMemory* memory, FontLibNative* library);
using DriverTermFunction = int (APS5_VABI*)(FontLibNative* library);
using DriverSupportFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t formats);
using DriverOpenFunction = int (APS5_VABI*)(FontLibNative* library, std::uint32_t mode, const void* fontAddress, std::uint32_t fontSize, std::uint32_t subFontIndex, std::uint32_t uniqueWord, FontObj** inoutFontObj);
using DriverCloseFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t flags);
using DriverScaleFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint16_t* outUnitsPerEm, float* outScale);
using DriverMetricFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t metricId, std::uint16_t* outMetric);
using DriverGlyphsCountFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* outCount);
using DriverGlyphIndexFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t codepoint, std::uint32_t* outGlyphIndex);
using DriverSetCharWithDpiFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t dpiX, std::uint32_t dpiY, float scaleX, float scaleY, float* outScaleX, float* outScaleY);
using DriverSetCharDefaultDpiFunction = int (APS5_VABI*)(FontObj* fontObj, float scaleX, float scaleY, float* outScaleX, float* outScaleY);
using DriverComputeLayoutFunction = int (APS5_VABI*)(FontObj* fontObj, const StyleStateBlock* style, std::uint8_t* outWords);
using DriverLoadGlyphCachedFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t glyphIndex, std::int32_t mode, std::uint64_t* outWords);
using DriverGetGlyphMetricsFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* optParam2, std::uint8_t mode, std::uint8_t* outParams, FontGlyphMetrics* outMetrics);
using DriverApplyGlyphAdjustFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t p2, std::uint32_t glyphIndex, std::int32_t p4, std::int32_t p5, std::uint32_t* inoutGlyphIndex);
using DriverConfigureGlyphFunction = int (APS5_VABI*)(FontObj* fontObj, std::uint32_t* inParams, std::int32_t mode, std::uint32_t* inoutState);
struct SysDriver {
std::uint32_t magic;
std::uint32_t reserved;
void* reserved_ptr1;
DriverPixelResolutionFunction pixel_resolution;
DriverInitFunction init;
DriverTermFunction term;
DriverSupportFunction support_formats;
std::uint8_t reserved_0x30[0x08];
DriverOpenFunction open;
DriverCloseFunction close;
std::uint8_t reserved_0x48[0x08];
DriverScaleFunction scale;
std::uint8_t reserved_0x58[0x08];
DriverMetricFunction metric;
DriverGlyphsCountFunction glyphs_count;
std::uint8_t reserved_0x70[0x08];
DriverGlyphIndexFunction glyph_index;
DriverSetCharWithDpiFunction set_char_with_dpi;
DriverSetCharDefaultDpiFunction set_char_default_dpi;
std::uint8_t reserved_0x90[0x10];
DriverComputeLayoutFunction compute_layout;
DriverLoadGlyphCachedFunction load_glyph_cached;
std::uint8_t reserved_0xb0[0x08];
DriverGetGlyphMetricsFunction get_glyph_metrics;
std::uint8_t reserved_0xc0[0x20];
DriverApplyGlyphAdjustFunction apply_glyph_adjust;
std::uint8_t reserved_0xe8[0x20];
DriverConfigureGlyphFunction configure_glyph;
std::uint8_t reserved_0x110[0x08];
DriverComputeLayoutFunction compute_layout_alt;
};
struct RendererNative {
std::uint16_t magic;
std::uint16_t reserved02;
std::uint8_t reserved_04[0x08];
std::uint8_t feature_byte_0x0c;
std::uint8_t reserved_0d[0x03];
std::uint32_t mem_kind;
std::uint32_t region_size;
void* region_base;
void* alloc_ctx;
const FontMemoryInterface* mem_iface;
std::uint8_t reserved_30[0x20];
FontAllocFunction alloc_fn;
FontFreeFunction free_fn;
FontReallocFunction realloc_fn;
FontCallocFunction calloc_fn;
std::uint8_t reserved_70[0x10];
void* selection;
std::uint8_t reserved_88[0x08];
std::uint64_t outline_magic_0x90;
void* workspace;
std::uint64_t workspace_size;
std::uint32_t reserved_a8;
std::uint32_t outline_policy_flag;
};
struct RendererFtBackend {
void* renderer_header_0x10;
std::uintptr_t unknown_0x08;
std::uintptr_t unknown_0x10;
std::uintptr_t unknown_0x18;
void* initialized_marker;
std::uint8_t reserved_0x28[0x20];
};
struct RendererFt {
RendererNative base;
std::uint8_t reserved_0x0B0[0x70];
RendererFtBackend ft_backend;
};
using RendererCreateFunction = int (APS5_VABI*)(RendererNative* renderer);
using RendererDestroyFunction = int (APS5_VABI*)(RendererNative* renderer);
using RendererQueryFunction = std::uint64_t (APS5_VABI*)(RendererNative* renderer, std::uint8_t* params, std::int64_t* outPtr, std::uint8_t* outVector);
struct RendererSelection {
std::uint32_t magic;
std::uint32_t size;
RendererCreateFunction create_fn;
RendererDestroyFunction destroy_fn;
RendererQueryFunction query_fn;
};
static_assert(sizeof(StyleStateBlock) == 0x28);
static_assert(offsetof(StyleStateBlock, scale_w) == 0x10);
static_assert(offsetof(StyleStateBlock, slant_ratio) == 0x20);
static_assert(sizeof(CachedStyle) == 0x60);
static_assert(offsetof(CachedStyle, layout_cache_state) == 0x2C);
static_assert(offsetof(CachedStyle, cache_lock_word) == 0x34);
static_assert(offsetof(CachedStyle, layout_cache_bytes) == 0x38);
static_assert(offsetof(CachedStyle, cached_scalar_bits) == 0x5C);
static_assert(sizeof(FontHandleOpenInfo) == 0x20);
static_assert(sizeof(FontHandleNative) == 0x100);
static_assert(offsetof(FontHandleNative, open_info) == 0x08);
static_assert(offsetof(FontHandleNative, library) == 0x28);
static_assert(offsetof(FontHandleNative, renderer) == 0x30);
static_assert(offsetof(FontHandleNative, style) == 0x40);
static_assert(offsetof(FontHandleNative, cached_style) == 0x68);
static_assert(offsetof(FontHandleNative, metricA) == 0xC8);
static_assert(offsetof(FontHandleNative, prevFont) == 0xF0);
static_assert(sizeof(FontObj) == 0x200);
static_assert(offsetof(FontObj, ft_face) == 0x30);
static_assert(offsetof(FontObj, shift_cache_x) == 0x78);
static_assert(offsetof(FontObj, layout_scale_vec) == 0x98);
static_assert(offsetof(FontObj, glyph_cfg_word_0x130) == 0x130);
static_assert(sizeof(FontCtxHeader) == 0x20);
static_assert(sizeof(FontCtxEntry) == 0x40);
static_assert(offsetof(FontCtxEntry, obj_mode2) == 0x30);
static_assert(sizeof(FontLibNative) == 0x100);
static_assert(offsetof(FontLibNative, alloc_ctx) == 0x20);
static_assert(offsetof(FontLibNative, iface) == 0x28);
static_assert(offsetof(FontLibNative, alloc_fn) == 0x50);
static_assert(offsetof(FontLibNative, sys_driver) == 0x80);
static_assert(offsetof(FontLibNative, fontset_registry) == 0x88);
static_assert(offsetof(FontLibNative, sysfonts_ctx) == 0xA0);
static_assert(offsetof(FontLibNative, external_fonts_ctx) == 0xA8);
static_assert(offsetof(FontLibNative, device_cache_buf) == 0xB0);
static_assert(offsetof(FontLibNative, workspace) == 0xC0);
static_assert(offsetof(FontLibNative, sysfont_flags) == 0xD0);
static_assert(offsetof(FontLibNative, list_head_ptr) == 0xD8);
static_assert(offsetof(FontLibNative, list_head) == 0xE0);
static_assert(sizeof(SysDriver) == 0x120);
static_assert(offsetof(SysDriver, pixel_resolution) == 0x10);
static_assert(offsetof(SysDriver, open) == 0x38);
static_assert(offsetof(SysDriver, scale) == 0x50);
static_assert(offsetof(SysDriver, glyph_index) == 0x78);
static_assert(offsetof(SysDriver, compute_layout) == 0xA0);
static_assert(offsetof(SysDriver, get_glyph_metrics) == 0xB8);
static_assert(offsetof(SysDriver, apply_glyph_adjust) == 0xE0);
static_assert(offsetof(SysDriver, configure_glyph) == 0x108);
static_assert(offsetof(SysDriver, compute_layout_alt) == 0x118);
static_assert(sizeof(RendererNative) == 0xB0);
static_assert(offsetof(RendererNative, alloc_fn) == 0x50);
static_assert(offsetof(RendererNative, selection) == 0x80);
static_assert(offsetof(RendererNative, outline_policy_flag) == 0xAC);
static_assert(sizeof(RendererFtBackend) == 0x48);
static_assert(sizeof(RendererFt) == 0x168);
static_assert(offsetof(RendererFt, ft_backend) == 0x120);
}
#endif

View File

@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTFREETYPE_HPP
#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTFREETYPE_HPP
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <ft2build.h>
#include FT_FREETYPE_H
namespace Font {
constexpr float ONE_OVER_64 = 1.0f / 64.0f;
inline std::int32_t TruncateFloatToInt(float value) {
if (!std::isfinite(value) || value > 2147483647.0f || value < -2147483648.0f) return std::numeric_limits<std::int32_t>::min();
return static_cast<std::int32_t>(value);
}
inline std::int32_t RoundMulFixed(std::int64_t value, std::int64_t fixed16) {
const std::int64_t product = value * fixed16;
const std::int64_t signAdjust = ((~value) >> 63) * -0x10000LL;
const std::int64_t base = signAdjust + product;
std::int64_t rounded = base - 0x8000LL;
if (rounded < 0) rounded = base + 0x7FFFLL;
return static_cast<std::int32_t>(static_cast<std::uint64_t>(rounded) >> 16);
}
inline std::int32_t TruncFixed(std::int64_t fixed16) {
std::int64_t value = fixed16;
if (fixed16 < 0) value = fixed16 + 0xFFFFLL;
return static_cast<std::int32_t>(value >> 16);
}
inline std::int64_t TruncMulUnits(std::int64_t fixed16, std::uint16_t units) {
const std::int64_t product = fixed16 * static_cast<std::int64_t>(units);
std::int64_t rounded = product;
if (product < 0) rounded = product + 0xFFFFLL;
return static_cast<std::int64_t>(static_cast<std::int32_t>(rounded >> 16));
}
inline float FixedMulUnitsToF26Dot6(std::int64_t fixed16, std::uint16_t unitsPerEm) {
const std::int64_t product = fixed16 * static_cast<std::int64_t>(unitsPerEm);
std::int64_t rounded = product + 0xFFFF;
if (product >= 0) rounded = product;
return static_cast<float>(rounded >> 16) * ONE_OVER_64;
}
inline int FloorToInt(float value) {
int result = static_cast<int>(std::trunc(value));
if (static_cast<float>(result) > value) --result;
return result;
}
inline int CeilToInt(float value) {
int result = static_cast<int>(std::trunc(value));
if (static_cast<float>(result) < value) ++result;
return result;
}
inline FT_UInt ResolveGlyphIndexWithFallback(FT_Face face, std::uint32_t codepoint) {
if (!face) return 0;
const auto code = static_cast<FT_ULong>(codepoint);
const FT_CharMap original = face->charmap;
FT_UInt glyphIndex = FT_Get_Char_Index(face, code);
if (glyphIndex != 0 || !face->charmaps || face->num_charmaps <= 1) return glyphIndex;
for (int i = 0; i < face->num_charmaps; ++i) {
const FT_CharMap charmap = face->charmaps[i];
if (!charmap || charmap == original) continue;
if (FT_Set_Charmap(face, charmap) != 0) continue;
glyphIndex = FT_Get_Char_Index(face, code);
if (glyphIndex != 0) break;
}
if (original && face->charmap != original) FT_Set_Charmap(face, original);
return glyphIndex;
}
inline FT_F26Dot6 ClampPpem(FT_F26Dot6 value) {
constexpr FT_F26Dot6 maxPpem = static_cast<FT_F26Dot6>(65535 * 64);
return std::clamp(value, static_cast<FT_F26Dot6>(-maxPpem), maxPpem);
}
inline FT_Error SetCharSizeCompat(FT_Face face, FT_F26Dot6 charW, FT_F26Dot6 charH, std::uint32_t dpiX, std::uint32_t dpiY, FT_F26Dot6* usedW = nullptr, FT_F26Dot6* usedH = nullptr) {
const auto writeUsed = [&](FT_F26Dot6 w, FT_F26Dot6 h) {
if (usedW) *usedW = w;
if (usedH) *usedH = h;
};
if (!face) {
writeUsed(charW, charH);
return FT_Err_Invalid_Face_Handle;
}
FT_Error error = FT_Set_Char_Size(face, charW, charH, dpiX, dpiY);
if (error == 0) {
writeUsed(charW, charH);
return 0;
}
const FT_F26Dot6 clampedW = ClampPpem(charW);
const FT_F26Dot6 clampedH = ClampPpem(charH);
if (clampedW != charW || clampedH != charH) {
error = FT_Set_Char_Size(face, clampedW, clampedH, dpiX, dpiY);
if (error == 0) {
writeUsed(clampedW, clampedH);
return 0;
}
}
if (face->num_fixed_sizes <= 0 || !face->available_sizes) {
writeUsed(charW, charH);
return error;
}
const double requestW = static_cast<double>(std::llabs(static_cast<long long>(charW))) / 64.0;
const double requestH = static_cast<double>(std::llabs(static_cast<long long>(charH))) / 64.0;
const double targetW = requestW > 0.0 ? requestW : requestH;
const double targetH = requestH > 0.0 ? requestH : requestW;
if (targetW <= 0.0 || targetH <= 0.0) {
writeUsed(charW, charH);
return error;
}
int bestIndex = -1;
double bestScore = std::numeric_limits<double>::infinity();
for (int i = 0; i < face->num_fixed_sizes; ++i) {
const FT_Bitmap_Size& strike = face->available_sizes[i];
const double strikeW = std::max(1.0, static_cast<double>(strike.x_ppem) / 64.0);
const double strikeH = std::max(1.0, static_cast<double>(strike.y_ppem) / 64.0);
const double score = std::abs(strikeW - targetW) / targetW + std::abs(strikeH - targetH) / targetH;
if (score < bestScore) {
bestScore = score;
bestIndex = i;
}
}
if (bestIndex < 0) {
writeUsed(charW, charH);
return error;
}
error = FT_Select_Size(face, bestIndex);
if (error == 0) {
const FT_Bitmap_Size& strike = face->available_sizes[bestIndex];
writeUsed(strike.x_ppem, strike.y_ppem);
return 0;
}
writeUsed(charW, charH);
return error;
}
}
#endif

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTINTERNAL_HPP
#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTINTERNAL_HPP
#include <cstddef>
#include <cstdint>
#include <vector>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "prx/libSceFont/include/FontDriver.hpp"
namespace Font {
constexpr std::uint8_t STYLE_FRAME_FLAG_SCALE = 0x01;
constexpr std::uint8_t STYLE_FRAME_FLAG_SLANT = 0x02;
constexpr std::uint8_t STYLE_FRAME_FLAG_WEIGHT = 0x04;
constexpr float POINTS_PER_INCH = 72.0f;
struct FontState {
std::vector<unsigned char> faceData;
FT_Face face = nullptr;
float scaleW = 16.0f;
float scaleH = 16.0f;
FontState() = default;
FontState(const FontState&) = delete;
FontState& operator=(const FontState&) = delete;
~FontState();
};
struct GeneratedGlyph {
FontGlyphOpaque glyph{};
FontGlyphMetrics metrics{};
FontGlyphMetricsHorizontal metricsHorizontal{};
FontGlyphMetricsHorizontalX metricsHorizontalX{};
FontGlyphMetricsHorizontalAdvance metricsHorizontalAdvance{};
std::uint32_t codepoint = 0;
FontHandle owner = nullptr;
FontGlyphOutline outline{};
std::vector<FontGlyphOutlinePoint> outlinePoints;
std::vector<std::uint8_t> outlineTags;
std::vector<std::uint16_t> outlineContours;
bool metricsInitialized = false;
bool outlineInitialized = false;
};
struct RenderSurfaceSystemUse {
FontStyleFrame* styleframe;
float catchedScale;
};
void Backoff();
std::uint32_t AcquireWordLock(std::uint32_t& word);
FontHandleNative* GetNativeFont(FontHandle handle);
bool AcquireLibraryLock(FontLibNative* library, std::uint32_t& previous);
void ReleaseLibraryLock(FontLibNative* library, std::uint32_t previous);
bool AcquireFontLock(FontHandleNative* font, std::uint32_t& previous);
void ReleaseFontLock(FontHandleNative* font, std::uint32_t previous);
bool AcquireCachedStyleLock(FontHandleNative* font, std::uint32_t& previous);
void ReleaseCachedStyleLock(FontHandleNative* font, std::uint32_t previous);
void LinkFontToLibrary(FontLibNative* library, FontHandle handle);
void UnlinkFontFromLibrary(FontLibNative* library, FontHandle handle);
std::uint32_t* EntryLockWord(FontCtxEntry* entry, std::uint32_t modeLow);
void** EntryObjectSlot(FontCtxEntry* entry, std::uint32_t modeLow);
FontCtxEntry* AcquireFontCtxEntry(void* ctx, std::uint32_t index, std::uint32_t modeLow, FontObj** outObject, std::uint32_t* outLockWord);
void ReleaseFontCtxEntryLock(FontCtxEntry* entry, std::uint32_t modeLow, std::uint32_t lockWord);
FontObj* FindSubFont(FontObj* head, std::uint32_t subFontIndex);
void ReleaseFontObjectsForHandle(FontHandleNative* font);
FontState* TryGetState(FontHandle handle);
FontState& ResetState(FontHandle handle);
void RemoveState(FontHandle handle);
void LoadStateFace(FontState& state, std::vector<unsigned char> bytes, std::uint32_t subFontIndex);
void TrackGeneratedGlyph(FontGlyph glyph);
bool ForgetGeneratedGlyph(FontGlyph glyph);
GeneratedGlyph* TryGetGeneratedGlyph(FontGlyph glyph);
void PopulateGlyphMetricVariants(GeneratedGlyph& glyph);
void BuildBoundingOutline(GeneratedGlyph& glyph);
bool BuildTrueOutline(GeneratedGlyph& glyph);
int StyleStateSetScalePixel(StyleStateBlock* style, float w, float h);
int StyleStateSetScalePoint(StyleStateBlock* style, float w, float h);
int StyleStateGetScalePixel(const StyleStateBlock* style, float* w, float* h);
int StyleStateGetScalePoint(const StyleStateBlock* style, float* w, float* h);
int StyleStateSetDpi(StyleStateBlock* style, std::uint32_t hDpi, std::uint32_t vDpi);
int StyleStateSetSlantRatio(StyleStateBlock* style, float slantRatio);
int StyleStateGetSlantRatio(const StyleStateBlock* style, float* slantRatio);
int StyleStateSetWeightScale(StyleStateBlock* style, float weightXScale, float weightYScale);
int StyleStateGetWeightScale(const StyleStateBlock* style, float* weightXScale, float* weightYScale, std::uint32_t* mode);
bool ValidStyleFrame(const FontStyleFrame* frame);
std::uint8_t CachedStyleCacheFlags(const CachedStyle& style);
void CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags);
void CachedStyleSetDirectionWord(CachedStyle& style, std::uint16_t word);
float CachedStyleGetScalar(const CachedStyle& style);
void CachedStyleSetScalar(CachedStyle& style, float value);
void ClearRenderOutputs(FontGlyphMetrics* metrics, FontRenderOutput* result);
int ComputeHorizontalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords);
int ComputeVerticalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords);
int GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMetrics* metrics, bool useCachedStyle);
int RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result);
}
#endif

View File

@ -0,0 +1,432 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTTYPES_HPP
#define CORE_LIBS_PRX_LIBSCEFONT_INCLUDE_FONTTYPES_HPP
#include <cstddef>
#include <cstdint>
#include "prx/libc/include/General.hpp"
constexpr int SCE_FONT_OK = 0;
constexpr int SCE_FONT_ERROR_FATAL = static_cast<int>(0x80460001);
constexpr int SCE_FONT_ERROR_INVALID_PARAMETER = static_cast<int>(0x80460002);
constexpr int SCE_FONT_ERROR_INVALID_MEMORY = static_cast<int>(0x80460003);
constexpr int SCE_FONT_ERROR_INVALID_LIBRARY = static_cast<int>(0x80460004);
constexpr int SCE_FONT_ERROR_INVALID_FONT_HANDLE = static_cast<int>(0x80460005);
constexpr int SCE_FONT_ERROR_INVALID_GLYPH = static_cast<int>(0x80460006);
constexpr int SCE_FONT_ERROR_INVALID_RENDERER = static_cast<int>(0x80460007);
constexpr int SCE_FONT_ERROR_INVALID_TEXT_SOURCE = static_cast<int>(0x80460008);
constexpr int SCE_FONT_ERROR_INVALID_STRING = static_cast<int>(0x80460009);
constexpr int SCE_FONT_ERROR_INVALID_WRITING = static_cast<int>(0x8046000A);
constexpr int SCE_FONT_ERROR_INVALID_WORDS = static_cast<int>(0x8046000B);
constexpr int SCE_FONT_ERROR_ALLOCATION_FAILED = static_cast<int>(0x80460010);
constexpr int SCE_FONT_ERROR_FS_OPEN_FAILED = static_cast<int>(0x80460011);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_LIBRARY = static_cast<int>(0x80460018);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_FORMAT = static_cast<int>(0x80460019);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_FUNCTION = static_cast<int>(0x80460020);
constexpr int SCE_FONT_ERROR_ALREADY_SPECIFIED = static_cast<int>(0x80460021);
constexpr int SCE_FONT_ERROR_ALREADY_ATTACHED = static_cast<int>(0x80460022);
constexpr int SCE_FONT_ERROR_ALREADY_OPENED = static_cast<int>(0x80460023);
constexpr int SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER = static_cast<int>(0x80460025);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_FONTSET = static_cast<int>(0x80460031);
constexpr int SCE_FONT_ERROR_FONT_OPEN_MAX = static_cast<int>(0x80460033);
constexpr int SCE_FONT_ERROR_FONT_OPEN_FAILED = static_cast<int>(0x80460036);
constexpr int SCE_FONT_ERROR_FONT_CLOSE_FAILED = static_cast<int>(0x80460037);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY = static_cast<int>(0x80460040);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_CODE = static_cast<int>(0x80460041);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_GLYPH = static_cast<int>(0x80460042);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_SCRIPT = static_cast<int>(0x80460043);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_LANGUAGE = static_cast<int>(0x80460044);
constexpr int SCE_FONT_ERROR_NO_SUPPORT_SURFACE = static_cast<int>(0x80460050);
constexpr int SCE_FONT_ERROR_UNSET_PARAMETER = static_cast<int>(0x80460058);
constexpr int SCE_FONT_ERROR_FUNCTIONAL_LIMIT = static_cast<int>(0x8046005C);
constexpr int SCE_FONT_ERROR_ALREADY_BOUND_RENDERER = static_cast<int>(0x80460060);
constexpr int SCE_FONT_ERROR_NOT_BOUND_RENDERER = static_cast<int>(0x80460061);
constexpr int SCE_FONT_ERROR_RENDERER_ALLOCATION_FAILED = static_cast<int>(0x80460063);
constexpr int SCE_FONT_ERROR_RENDERER_ALLOCATION_LIMITED = static_cast<int>(0x80460064);
constexpr int SCE_FONT_ERROR_RENDERER_RENDER_FAILED = static_cast<int>(0x80460065);
struct FontHandleOpaque {
std::uint32_t reserved[64];
};
using FontHandle = FontHandleOpaque*;
using FontLibrary = void*;
using FontRenderer = void*;
struct FontMemory;
using FontAllocFunction = void* (APS5_VABI*)(void* object, std::uint32_t size);
using FontFreeFunction = void (APS5_VABI*)(void* object, void* p);
using FontReallocFunction = void* (APS5_VABI*)(void* object, void* p, std::uint32_t newSize);
using FontCallocFunction = void* (APS5_VABI*)(void* object, std::uint32_t nBlock, std::uint32_t size);
using FontMspaceCreateFunction = void* (APS5_VABI*)(void* parent, const char* name, void* address, std::uint32_t size, std::uint32_t attr);
using FontMspaceDestroyFunction = void (APS5_VABI*)(void* parent, void* mspace);
using FontMemoryDestroyFunction = void (APS5_VABI*)(FontMemory* fontMemory, void* object, void* destroyArg);
struct FontMemoryInterface {
FontAllocFunction alloc;
FontFreeFunction dealloc;
FontReallocFunction realloc_fn;
FontCallocFunction calloc_fn;
FontMspaceCreateFunction mspace_create;
FontMspaceDestroyFunction mspace_destroy;
};
struct FontMemory {
std::uint16_t mem_kind;
std::uint16_t attr_bits;
std::uint32_t region_size;
void* region_base;
void* mspace_handle;
const FontMemoryInterface* iface;
FontMemoryDestroyFunction on_destroy;
void* destroy_ctx;
void* some_ctx1;
void* some_ctx2;
};
struct FontOpenDetail {
std::uint16_t tag;
std::uint16_t pad16;
std::uint32_t flags;
std::uint32_t subfont_index;
std::int32_t unique_id;
const void* reserved_ptr2;
const void* reserved_ptr1;
};
struct FontGlyphMetrics {
float width;
float height;
struct {
float bearingX;
float bearingY;
float advance;
} Horizontal;
struct {
float bearingX;
float bearingY;
float advance;
} Vertical;
};
struct FontGlyphMetricsHorizontal {
float width;
float height;
struct {
float bearing_x;
float bearing_y;
float advance;
} horizontal;
};
struct FontGlyphMetricsHorizontalX {
float width;
struct {
float bearing_x;
float advance;
} horizontal;
};
struct FontGlyphMetricsHorizontalAdvance {
struct {
float advance;
} horizontal;
};
struct FontKerning {
float offsetX;
float offsetY;
float positionX;
float positionY;
};
struct FontGlyphImageMetrics {
float bearingX;
float bearingY;
float advance;
float stride;
std::uint32_t width;
std::uint32_t height;
};
struct FontGenerateGlyphDetail {
std::uint16_t id;
std::uint16_t res0;
std::uint16_t form_options;
std::uint8_t glyph_form;
std::uint8_t metrics_form;
const FontMemory* mem;
void* res1;
void* res2;
};
struct FontGlyphOutlinePoint {
float x;
float y;
};
struct FontGlyphOutline {
std::int16_t contours_cnt;
std::int16_t points_cnt;
std::uint32_t outline_flags;
FontGlyphOutlinePoint* points_ptr;
std::uint8_t* tags_ptr;
std::uint16_t* contour_end_idx;
};
struct FontGlyphOpaque {
std::uint16_t magic;
std::uint16_t flags;
std::uint8_t glyph_form;
std::uint8_t metrics_form;
std::uint16_t em_size;
std::uint16_t baseline;
std::uint16_t height_px;
std::uint16_t origin_x;
std::uint16_t origin_y;
float scale_x;
float base_scale;
const FontMemory* memory;
};
using FontGlyph = FontGlyphOpaque*;
struct FontResultStage {
std::uint8_t* p_00;
std::uint32_t u32_08;
std::uint32_t u32_0C;
std::uint32_t u32_10;
};
struct FontSurfaceImage {
std::uint8_t* address;
std::uint32_t widthByte;
std::uint8_t pixelSizeByte;
std::uint8_t pixelFormat;
std::uint16_t pad16;
};
struct FontRenderOutput {
const FontResultStage* stage;
FontSurfaceImage SurfaceImage;
struct {
std::uint32_t x;
std::uint32_t y;
std::uint32_t w;
std::uint32_t h;
} UpdateRect;
FontGlyphImageMetrics ImageMetrics;
};
struct FontTextCharacter {
FontTextCharacter* prev;
FontTextCharacter* next;
void* textOrder;
FontHandle font;
void* shapeEntry;
std::uint32_t characterCode;
std::uint8_t reserved_0x2c[4];
std::uint8_t clusterSpan;
std::uint8_t clusterIndex;
std::uint8_t clusterKind;
std::int8_t synthetic;
std::uint8_t reserved_0x34[4];
std::uint64_t flags;
std::uint8_t reserved_0x40[136];
};
struct FontRenderSurface {
void* buffer;
std::int32_t widthByte;
std::int8_t pixelSizeByte;
std::uint8_t pad0;
std::uint8_t styleFlag;
std::uint8_t pad2;
std::int32_t width;
std::int32_t height;
std::uint32_t sc_x0;
std::uint32_t sc_y0;
std::uint32_t sc_x1;
std::uint32_t sc_y1;
std::uint64_t reserved_q[11];
};
struct FontStyleFrame {
std::uint16_t magic;
std::uint8_t flags1;
std::uint8_t flags2;
std::uint32_t hDpi;
std::uint32_t vDpi;
std::uint32_t scaleUnit;
float baseScale;
float scalePixelW;
float scalePixelH;
float effectWeightX;
float effectWeightY;
float slantRatio;
std::uint32_t reserved_0x28;
std::uint32_t layout_cache_state;
std::uint32_t cache_flags_and_direction;
std::uint32_t cache_lock_word;
std::uint8_t layout_cache_bytes[0x20];
std::uint32_t reserved_0x58;
std::uint32_t cached_scalar_bits;
};
struct FontTextSource;
union FontTextParseResult {
void* reserved[4];
struct {
FontHandle font;
std::uint32_t code;
} FontCode;
struct {
void* reserved;
std::uint32_t terminateCode;
} Terminate;
struct {
void* reserved;
std::int32_t errorCode;
} Error;
};
using FontTextParseFunction = std::int32_t (APS5_VABI*)(FontTextSource* fontTextSource, void** order, FontTextParseResult* result);
struct FontTextSource {
std::uint64_t systemUse0;
const void* start;
const void* end;
const void* current;
FontTextParseFunction textParser;
void* textObject;
FontHandle defaultFont;
void* systemUse[5];
};
struct FontCreateStringDetail {
std::uint16_t detailId;
std::uint8_t detailType;
std::uint8_t detections;
std::uint32_t ordersOption;
FontHandle defaultFont;
union {
struct {
void* function;
void* object;
} Callback;
} Orders;
};
struct FontStringOpaque;
using FontString = FontStringOpaque*;
struct FontWriting {
void* systemUse[32];
};
struct FontWritingProfile {
std::uint32_t characterCount : 8;
std::uint32_t invisibleGlyph : 1;
std::uint32_t reserved : 23;
};
struct FontWritingStep {
float x;
float y;
float advanceX;
float advanceY;
FontHandle font;
FontWritingProfile Profile;
std::uint32_t glyphCode;
struct {
float x;
float y;
} Positioning;
FontGlyphMetrics GlyphMetrics;
};
struct FontWritingLetterStep {
float x;
float y;
float advanceX;
float advanceY;
struct {
std::uint32_t textsCount;
std::uint32_t textsIndex;
std::uint32_t glyphsCount;
std::uint32_t glyphsIndex;
std::uint8_t baseComponentsCount;
std::uint8_t baseComponentsIndex;
std::uint8_t baseFocusCount;
std::uint8_t baseFocusIndex;
std::uint8_t oppositeDirection;
std::uint8_t characterTextCount;
std::uint8_t baseTextCount;
std::uint8_t markTextCount;
std::uint32_t marksTextCount;
std::uint32_t marksTextNumber;
std::uint32_t reserved[3];
std::int32_t textLetterOffset;
} Components;
};
struct FontWritingExtent {
float top;
float bottom;
float left;
float right;
};
struct FontWritingMetrics {
float advanceX;
float advanceY;
FontWritingExtent Extent;
};
struct FontHorizontalLayout {
float baselineOffset;
float lineAdvance;
float decorationExtent;
};
struct FontVerticalLayout {
float baselineOffsetX;
float columnAdvance;
float decorationSpan;
};
static_assert(sizeof(FontHandleOpaque) == 0x100);
static_assert(sizeof(FontMemory) == 0x40);
static_assert(sizeof(FontMemoryInterface) == 0x30);
static_assert(sizeof(FontOpenDetail) == 0x20);
static_assert(sizeof(FontGlyphMetrics) == 0x20);
static_assert(sizeof(FontKerning) == 0x10);
static_assert(sizeof(FontGlyphImageMetrics) == 0x18);
static_assert(sizeof(FontGenerateGlyphDetail) == 0x20);
static_assert(sizeof(FontResultStage) == 0x18);
static_assert(sizeof(FontSurfaceImage) == 0x10);
static_assert(sizeof(FontRenderOutput) == 0x40);
static_assert(sizeof(FontTextCharacter) == 0xC8);
static_assert(offsetof(FontTextCharacter, flags) == 0x38);
static_assert(sizeof(FontRenderSurface) == 0x80);
static_assert(sizeof(FontStyleFrame) == 0x60);
static_assert(offsetof(FontStyleFrame, scalePixelW) == 0x14);
static_assert(offsetof(FontStyleFrame, cache_lock_word) == 0x34);
static_assert(offsetof(FontStyleFrame, reserved_0x58) == 0x58);
static_assert(sizeof(FontTextSource) == 0x60);
static_assert(offsetof(FontTextSource, textParser) == 0x20);
static_assert(offsetof(FontTextSource, defaultFont) == 0x30);
static_assert(offsetof(FontTextSource, systemUse) == 0x38);
static_assert(sizeof(FontCreateStringDetail) == 0x20);
static_assert(sizeof(FontWriting) == 0x100);
static_assert(sizeof(FontWritingProfile) == sizeof(std::uint32_t));
static_assert(sizeof(FontWritingStep) == 0x48);
static_assert(sizeof(FontWritingLetterStep) == 0x40);
static_assert(sizeof(FontWritingMetrics) == 0x18);
#endif

View File

@ -0,0 +1,522 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bit>
#include <cmath>
#include <cstring>
#include <vector>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include "prx/libSceFont/include/FontFreeType.hpp"
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
struct FloatPair {
float lo = 0.0f;
float hi = 0.0f;
};
bool OrderedLess(float a, float b) {
return !std::isnan(a) && !std::isnan(b) && a < b;
}
float MaxSs(float a, float b) {
if (std::isnan(a) || std::isnan(b)) return b;
if (a > b) return a;
if (a < b) return b;
if (a == 0.0f && b == 0.0f && std::signbit(a) != std::signbit(b)) return std::signbit(a) ? b : a;
return a;
}
float FlipSign(float value) {
return std::bit_cast<float>(std::bit_cast<std::uint32_t>(value) ^ 0x80000000u);
}
void UpdateShiftCache(FontObj* obj) {
obj->shift_cache_x = obj->shift_units_x;
obj->shift_cache_y = obj->shift_units_y;
const auto seedLow = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair & 0xFFFFFFFFu));
const auto seedHigh = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair >> 32));
obj->layout_seed_vec[0] = static_cast<std::uint64_t>(static_cast<std::int64_t>(seedLow));
obj->layout_seed_vec[1] = static_cast<std::uint64_t>(static_cast<std::int64_t>(seedHigh));
obj->layout_scale_vec[0] = 0x10000;
obj->layout_scale_vec[1] = 0x10000;
}
int SetDriverCharSize(const SysDriver* driver, FontObj* obj, const StyleStateBlock* style, float& scaleX, float& scaleY) {
if (style->scale_unit == 0) return driver->set_char_default_dpi(obj, scaleX, scaleY, &scaleX, &scaleY);
return driver->set_char_with_dpi(obj, style->dpi_x, style->dpi_y, scaleX, scaleY, &scaleX, &scaleY);
}
void ClearHorizontalLayout(std::uint8_t* outWords) {
if (!outWords) return;
std::memset(outWords, 0, 0x10);
std::memset(outWords + 0x18, 0, 0x0C);
}
void ClearVerticalLayout(std::uint8_t* outWords) {
if (!outWords) return;
std::memset(outWords, 0, 0x10);
std::memset(outWords + 0x14, 0, 0x0C);
}
const StyleStateBlock* ResolveSurfaceStyleState(const StyleStateBlock* cachedState, const FontRenderSurface* surface, StyleStateBlock& temporary) {
const auto* systemUse = reinterpret_cast<const RenderSurfaceSystemUse*>(surface->reserved_q);
if ((surface->styleFlag & 0x1) == 0 || !ValidStyleFrame(systemUse->styleframe)) return cachedState;
const FontStyleFrame* frame = systemUse->styleframe;
temporary = *cachedState;
if ((frame->flags1 & STYLE_FRAME_FLAG_SCALE) != 0) {
temporary.scale_unit = frame->scaleUnit;
temporary.scale_w = frame->scalePixelW;
temporary.scale_h = frame->scalePixelH;
temporary.dpi_x = frame->hDpi ? frame->hDpi : 0x48;
temporary.dpi_y = frame->vDpi ? frame->vDpi : 0x48;
} else {
const std::uint32_t wantX = frame->hDpi == 0 ? temporary.dpi_x : frame->hDpi;
const std::uint32_t wantY = frame->vDpi == 0 ? temporary.dpi_y : frame->vDpi;
temporary.dpi_x = wantX != 0 ? 0x48 : 0;
temporary.dpi_y = wantY != 0 ? 0x48 : 0;
}
if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) temporary.slant_ratio = frame->slantRatio;
if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) {
temporary.effect_weight_x = frame->effectWeightX;
temporary.effect_weight_y = frame->effectWeightY;
}
return &temporary;
}
void FillImageMetrics(FontRenderOutput* result, const FontGlyphMetrics* metrics, float x, float y) {
const float leftF = x + metrics->Horizontal.bearingX;
const float topF = y + metrics->Horizontal.bearingY;
const float rightF = leftF + metrics->width;
const float bottomF = topF - metrics->height;
const int left = FloorToInt(leftF);
const int top = CeilToInt(topF);
const int right = CeilToInt(rightF);
const int bottom = FloorToInt(bottomF);
const float advanceF = x + metrics->Horizontal.advance;
const int advance = CeilToInt(advanceF);
result->ImageMetrics.bearingX = static_cast<float>(left) - x;
result->ImageMetrics.bearingY = static_cast<float>(top) - y;
result->ImageMetrics.advance = static_cast<float>(advance) - x;
int stride = advance;
if (advanceF != static_cast<float>(advance)) {
const int candidate = static_cast<int>((static_cast<float>(right) - rightF) + advanceF);
stride = right + 1;
if (advanceF <= rightF) stride = candidate;
if (stride < candidate) stride = candidate;
}
result->ImageMetrics.stride = static_cast<float>(stride) - x;
result->ImageMetrics.width = static_cast<std::uint32_t>(std::max(0, right - left));
result->ImageMetrics.height = static_cast<std::uint32_t>(std::max(0, top - bottom));
}
int RenderGlyphIndexToSurface(FontObj& obj, std::uint32_t glyphIndex, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
ClearRenderOutputs(metrics, result);
if (!surface->buffer || surface->width <= 0 || surface->height <= 0 || surface->widthByte <= 0 || surface->pixelSizeByte <= 0) return SCE_FONT_ERROR_NO_SUPPORT_SURFACE;
const int bytesPerPixel = surface->pixelSizeByte;
if (bytesPerPixel != 1 && bytesPerPixel != 4) return SCE_FONT_ERROR_NO_SUPPORT_SURFACE;
const auto face = static_cast<FT_Face>(obj.ft_face);
if (!face || !face->size) return SCE_FONT_ERROR_NO_SUPPORT_GLYPH;
FT_Vector delta{};
delta.x = static_cast<FT_Pos>(static_cast<std::int32_t>((x - std::floor(x)) * 64.0f));
delta.y = static_cast<FT_Pos>(-static_cast<std::int32_t>((y - std::floor(y)) * 64.0f));
if (obj.shift_units_x != 0 || obj.shift_units_y != 0) {
delta.x += static_cast<FT_Pos>(RoundMulFixed(obj.shift_units_x, static_cast<std::int64_t>(face->size->metrics.x_scale)));
delta.y += static_cast<FT_Pos>(RoundMulFixed(obj.shift_units_y, static_cast<std::int64_t>(face->size->metrics.y_scale)));
}
FT_Set_Transform(face, nullptr, &delta);
const FT_Error error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT | FT_LOAD_RENDER);
FT_Set_Transform(face, nullptr, nullptr);
if (error != 0 || !face->glyph) return SCE_FONT_ERROR_NO_SUPPORT_GLYPH;
const FT_GlyphSlot slot = face->glyph;
const int glyphW = static_cast<int>(slot->bitmap.width);
const int glyphH = static_cast<int>(slot->bitmap.rows);
metrics->width = static_cast<float>(slot->metrics.width) / 64.0f;
metrics->height = static_cast<float>(slot->metrics.height) / 64.0f;
metrics->Horizontal.bearingX = static_cast<float>(slot->metrics.horiBearingX) / 64.0f;
metrics->Horizontal.bearingY = static_cast<float>(slot->metrics.horiBearingY) / 64.0f;
metrics->Horizontal.advance = static_cast<float>(slot->metrics.horiAdvance) / 64.0f;
std::vector<std::uint8_t> bitmap(static_cast<std::size_t>(glyphW) * static_cast<std::size_t>(glyphH));
if (glyphW > 0 && glyphH > 0) {
const int pitch = slot->bitmap.pitch;
const auto* source = slot->bitmap.buffer;
for (int row = 0; row < glyphH; ++row) {
const std::uint8_t* sourceRow = source + static_cast<std::ptrdiff_t>(row) * pitch;
std::uint8_t* targetRow = bitmap.data() + static_cast<std::size_t>(row) * static_cast<std::size_t>(glyphW);
if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
for (int column = 0; column < glyphW; ++column) targetRow[column] = (sourceRow[column >> 3] & (0x80u >> (column & 7))) ? 0xFF : 0x00;
} else {
std::memcpy(targetRow, sourceRow, static_cast<std::size_t>(glyphW));
}
}
}
const int destX = static_cast<int>(std::floor(x)) + slot->bitmap_left;
const int destY = static_cast<int>(std::floor(y)) - slot->bitmap_top;
const int clipX0 = std::clamp(static_cast<int>(surface->sc_x0), 0, surface->width);
const int clipY0 = std::clamp(static_cast<int>(surface->sc_y0), 0, surface->height);
const int clipX1 = std::clamp(static_cast<int>(surface->sc_x1), 0, surface->width);
const int clipY1 = std::clamp(static_cast<int>(surface->sc_y1), 0, surface->height);
int updateX = destX;
int updateY = destY;
int updateW = 0;
int updateH = 0;
if (glyphW > 0 && glyphH > 0 && clipX1 > clipX0 && clipY1 > clipY0) {
auto* targetBase = static_cast<std::uint8_t*>(surface->buffer);
const int startRow = std::max(destY, clipY0);
const int endRow = std::min(destY + glyphH, clipY1);
const int startColumn = std::max(destX, clipX0);
const int endColumn = std::min(destX + glyphW, clipX1);
updateX = startColumn;
updateY = startRow;
updateW = std::max(0, endColumn - startColumn);
updateH = std::max(0, endRow - startRow);
for (int row = startRow; row < endRow; ++row) {
const std::uint8_t* sourceRow = bitmap.data() + static_cast<std::size_t>(row - destY) * static_cast<std::size_t>(glyphW);
std::uint8_t* targetRow = targetBase + static_cast<std::size_t>(row) * static_cast<std::size_t>(surface->widthByte);
for (int column = startColumn; column < endColumn; ++column) {
const std::uint8_t coverage = sourceRow[column - destX];
std::memset(targetRow + static_cast<std::size_t>(column) * static_cast<std::size_t>(bytesPerPixel), coverage, static_cast<std::size_t>(bytesPerPixel));
}
}
}
result->stage = nullptr;
result->SurfaceImage.widthByte = static_cast<std::uint32_t>(surface->widthByte);
result->SurfaceImage.pixelSizeByte = static_cast<std::uint8_t>(surface->pixelSizeByte);
result->SurfaceImage.pixelFormat = 0;
result->SurfaceImage.pad16 = 0;
result->UpdateRect.x = static_cast<std::uint32_t>(std::max(updateX, 0));
result->UpdateRect.y = static_cast<std::uint32_t>(std::max(updateY, 0));
result->UpdateRect.w = static_cast<std::uint32_t>(std::max(updateW, 0));
result->UpdateRect.h = static_cast<std::uint32_t>(std::max(updateH, 0));
result->SurfaceImage.address = static_cast<std::uint8_t*>(surface->buffer) + static_cast<std::size_t>(result->UpdateRect.y) * static_cast<std::size_t>(surface->widthByte) + static_cast<std::size_t>(result->UpdateRect.x) * static_cast<std::size_t>(bytesPerPixel);
FillImageMetrics(result, metrics, x, y);
return SCE_FONT_OK;
}
}
int Font::ComputeHorizontalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords) {
ClearHorizontalLayout(outWords);
const auto fail = [&] {
ClearHorizontalLayout(outWords);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
};
auto* font = GetNativeFont(handle);
if (!font) return fail();
auto* library = static_cast<FontLibNative*>(font->library);
if (!library || !library->external_fonts_ctx) return fail();
const SysDriver* driver = library->sys_driver;
if (!driver || !driver->set_char_with_dpi || !driver->set_char_default_dpi || !driver->compute_layout || !style) return fail();
const std::uint32_t modeLow = font->flags & 0x0Fu;
const std::uint32_t fontId = font->open_info.ctx_entry_index;
float baselineMax = 0.0f;
float deltaMax = 0.0f;
float effectForBaseline = 0.0f;
float effectForDelta = 0.0f;
FloatPair accumulatedAdjust{};
FloatPair accumulatedBounds{};
if (static_cast<std::int32_t>(fontId) >= 0) {
FontObj* head = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord);
if (!entry) return fail();
FontObj* match = FindSubFont(head, font->open_info.sub_font_index);
int rc = SCE_FONT_ERROR_FATAL;
std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {};
if ((lockWord & OPEN_BIT) != 0 && (lockWord & COUNT_MASK) != 0 && match) {
float scaleX = style->scale_w;
float scaleY = style->scale_h;
match->font_handle = handle;
match->shift_units_x = 0;
match->shift_units_y = 0;
rc = SetDriverCharSize(driver, match, style, scaleX, scaleY);
if (rc == SCE_FONT_OK) {
UpdateShiftCache(match);
rc = driver->compute_layout(match, style, layout);
}
}
ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
if (rc != SCE_FONT_OK) return fail();
const float lineAdvance = LoadFloat(layout, HORIZONTAL_LINE_ADVANCE);
const float baseline = LoadFloat(layout, HORIZONTAL_BASELINE);
const float effectHeight = LoadFloat(layout, HORIZONTAL_EFFECT_HEIGHT);
const FloatPair bounds{LoadFloat(layout, HORIZONTAL_X_BOUND_LO), LoadFloat(layout, HORIZONTAL_X_BOUND_HI)};
const FloatPair adjust{LoadFloat(layout, HORIZONTAL_HALF_EFFECT_WIDTH), LoadFloat(layout, HORIZONTAL_LEFT_ADJUST)};
const FloatPair previousAdjust = accumulatedAdjust;
const FloatPair previousBounds = accumulatedBounds;
const bool baselineUpdate = OrderedLess(baselineMax, baseline);
baselineMax = MaxSs(baseline, baselineMax);
if (baselineUpdate) effectForBaseline = effectHeight;
const float delta = lineAdvance - baseline;
const bool deltaUpdate = OrderedLess(deltaMax, delta);
deltaMax = MaxSs(delta, deltaMax);
if (deltaUpdate) effectForDelta = effectHeight;
const FloatPair difference{bounds.lo - adjust.hi, bounds.hi - adjust.lo};
const FloatPair sum{previousAdjust.lo + previousBounds.lo, previousAdjust.hi + previousBounds.hi};
if (OrderedLess(difference.lo, sum.lo)) {
accumulatedBounds.lo = bounds.lo;
accumulatedAdjust.lo = FlipSign(adjust.hi);
}
if (OrderedLess(difference.hi, sum.hi)) {
accumulatedBounds.hi = bounds.hi;
accumulatedAdjust.hi = adjust.lo;
}
}
if (!outWords) return fail();
StoreFloat(outWords, HORIZONTAL_BASELINE, baselineMax);
StoreFloat(outWords, HORIZONTAL_LINE_ADVANCE, baselineMax + deltaMax);
StoreFloat(outWords, HORIZONTAL_X_BOUND_LO, accumulatedBounds.lo);
StoreFloat(outWords, HORIZONTAL_X_BOUND_HI, accumulatedBounds.hi);
StoreFloat(outWords, HORIZONTAL_EFFECT_HEIGHT, MaxSs(effectForBaseline, effectForDelta));
StoreFloat(outWords, HORIZONTAL_LEFT_ADJUST, accumulatedAdjust.lo);
StoreFloat(outWords, HORIZONTAL_HALF_EFFECT_WIDTH, accumulatedAdjust.hi);
return SCE_FONT_OK;
}
int Font::ComputeVerticalLayout(FontHandle handle, const StyleStateBlock* style, std::uint8_t* outWords) {
const auto fail = [&](int rc) {
ClearVerticalLayout(outWords);
return rc;
};
auto* font = GetNativeFont(handle);
auto* library = font ? static_cast<FontLibNative*>(font->library) : nullptr;
if (!library || !library->external_fonts_ctx || !library->sys_driver) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
const SysDriver* driver = library->sys_driver;
const std::uint32_t modeLow = font->flags & 0x0Fu;
const std::uint32_t fontId = font->open_info.ctx_entry_index;
float accNegOffsetMax = 0.0f;
float accSumMax = 0.0f;
float spanForNegOffset = 0.0f;
float spanForSum = 0.0f;
float accMetrics08Max = 0.0f;
float accMetrics0CMax = 0.0f;
float accDiffMin = 0.0f;
float accTempMin = 0.0f;
if (static_cast<std::int32_t>(fontId) >= 0) {
FontObj* head = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord);
FontObj* obj = FindSubFont(head, font->open_info.sub_font_index);
if (!entry || !obj) {
if (entry) ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
return fail(SCE_FONT_ERROR_FATAL);
}
int rc = SCE_FONT_ERROR_FATAL;
if ((lockWord & OPEN_BIT) != 0 && (lockWord & COUNT_MASK) != 0) {
float scaleX = style ? style->scale_w : 0.0f;
float scaleY = style ? style->scale_h : 0.0f;
obj->font_handle = handle;
obj->shift_units_x = 0;
obj->shift_units_y = 0;
int callRc = SCE_FONT_ERROR_FATAL;
if (style && style->scale_unit == 0) {
if (driver->set_char_default_dpi) callRc = driver->set_char_default_dpi(obj, scaleX, scaleY, &scaleX, &scaleY);
} else if (driver->set_char_with_dpi) {
callRc = driver->set_char_with_dpi(obj, style ? style->dpi_x : 0u, style ? style->dpi_y : 0u, scaleX, scaleY, &scaleX, &scaleY);
}
std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {};
if (callRc == SCE_FONT_OK) {
UpdateShiftCache(obj);
callRc = driver->compute_layout_alt ? driver->compute_layout_alt(obj, style, layout) : SCE_FONT_ERROR_FATAL;
}
if (callRc == SCE_FONT_OK) {
const float metrics00 = LoadFloat(layout, VERTICAL_COLUMN_ADVANCE);
const float offsetCandidate = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X);
const float metrics08 = LoadFloat(layout, VERTICAL_METRICS_0X08);
const float metrics0C = LoadFloat(layout, VERTICAL_METRICS_0X0C);
const float spanCandidate = LoadFloat(layout, VERTICAL_DECORATION_SPAN);
const float extra08 = LoadFloat(layout, VERTICAL_DECORATION_0X08);
const float extra0C = LoadFloat(layout, VERTICAL_DECORATION_0X0C);
const float negOffset = -offsetCandidate;
const float sum = offsetCandidate + metrics00;
if (accNegOffsetMax < negOffset) spanForNegOffset = spanCandidate;
accNegOffsetMax = std::max(accNegOffsetMax, negOffset);
if (accSumMax < sum) spanForSum = spanCandidate;
accSumMax = std::max(accSumMax, sum);
accDiffMin = std::min(extra08 - offsetCandidate, accDiffMin);
accMetrics08Max = std::max(metrics08, accMetrics08Max);
accTempMin = std::min(sum + extra0C, accDiffMin);
accMetrics0CMax = std::max(metrics0C, accMetrics0CMax);
rc = SCE_FONT_OK;
} else {
rc = callRc;
}
}
ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
if (rc != SCE_FONT_OK) return fail(rc);
}
if (outWords) {
StoreFloat(outWords, VERTICAL_COLUMN_ADVANCE, accNegOffsetMax + accSumMax);
StoreFloat(outWords, VERTICAL_BASELINE_OFFSET_X, -accNegOffsetMax);
StoreFloat(outWords, VERTICAL_METRICS_0X08, accMetrics08Max);
StoreFloat(outWords, VERTICAL_METRICS_0X0C, accMetrics0CMax);
StoreFloat(outWords, VERTICAL_DECORATION_SPAN, std::max(spanForSum, spanForNegOffset));
StoreFloat(outWords, VERTICAL_DECORATION_0X08, accTempMin - accNegOffsetMax);
StoreFloat(outWords, VERTICAL_DECORATION_0X0C, -accSumMax);
}
return SCE_FONT_OK;
}
int Font::GetCharGlyphMetrics(FontHandle handle, std::uint32_t code, FontGlyphMetrics* metrics, bool useCachedStyle) {
const auto clearMetrics = [&] {
if (metrics) *metrics = {};
};
auto* font = GetNativeFont(handle);
if (!font || font->magic != HANDLE_MAGIC) {
clearMetrics();
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
if (code == 0) {
clearMetrics();
return SCE_FONT_ERROR_NO_SUPPORT_CODE;
}
if (!metrics) return SCE_FONT_ERROR_INVALID_PARAMETER;
std::uint32_t previousFontLock = 0;
if (!AcquireFontLock(font, previousFontLock)) {
clearMetrics();
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
std::uint32_t previousCachedLock = 0;
if (useCachedStyle && !AcquireCachedStyleLock(font, previousCachedLock)) {
ReleaseFontLock(font, previousFontLock);
clearMetrics();
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
const auto finish = [&](int rc) {
if (useCachedStyle) ReleaseCachedStyleLock(font, previousCachedLock);
ReleaseFontLock(font, previousFontLock);
if (rc != SCE_FONT_OK) clearMetrics();
return rc;
};
FontState* state = TryGetState(handle);
if (!state) return finish(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
if (useCachedStyle && !font->renderer) return finish(SCE_FONT_ERROR_NOT_BOUND_RENDERER);
const StyleStateBlock* style = useCachedStyle ? &font->cached_style.state : &font->style;
float scaleW = 0.0f;
float scaleH = 0.0f;
const int scaleRc = StyleStateGetScalePixel(style, &scaleW, &scaleH);
if (scaleRc != SCE_FONT_OK) return finish(scaleRc);
const FT_Face face = state->face;
if (!face) return finish(SCE_FONT_ERROR_NO_SUPPORT_FUNCTION);
const FT_UInt glyphIndex = ResolveGlyphIndexWithFallback(face, code);
if (glyphIndex == 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH);
const auto charW = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(scaleW * 64.0f));
const auto charH = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(scaleH * 64.0f));
FT_F26Dot6 usedW = charW;
FT_F26Dot6 usedH = charH;
if (SetCharSizeCompat(face, charW, charH, 72, 72, &usedW, &usedH) != 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH);
FT_Set_Transform(face, nullptr, nullptr);
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT) != 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH);
const auto ratio = [](FT_F26Dot6 requested, FT_F26Dot6 applied) {
if (requested == 0 || applied == 0) return 1.0f;
const double value = static_cast<double>(std::llabs(static_cast<long long>(requested))) / static_cast<double>(std::llabs(static_cast<long long>(applied)));
return std::isfinite(value) && value > 0.0 ? static_cast<float>(value) : 1.0f;
};
const float ratioX = ratio(charW, usedW);
const float ratioY = ratio(charH, usedH);
const FT_GlyphSlot slot = face->glyph;
metrics->width = static_cast<float>(slot->metrics.width) / 64.0f * ratioX;
metrics->height = static_cast<float>(slot->metrics.height) / 64.0f * ratioY;
metrics->Horizontal.bearingX = static_cast<float>(slot->metrics.horiBearingX) / 64.0f * ratioX;
metrics->Horizontal.bearingY = static_cast<float>(slot->metrics.horiBearingY) / 64.0f * ratioY;
metrics->Horizontal.advance = static_cast<float>(slot->metrics.horiAdvance) / 64.0f * ratioX;
metrics->Vertical.bearingX = 0.0f;
metrics->Vertical.bearingY = 0.0f;
metrics->Vertical.advance = 0.0f;
return finish(SCE_FONT_OK);
}
int Font::RenderCharGlyphImageCore(FontHandle handle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
const auto fail = [&](int rc) {
ClearRenderOutputs(metrics, result);
return rc;
};
if (!handle) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
if (code == 0) return fail(SCE_FONT_ERROR_NO_SUPPORT_CODE);
if (!surface || !metrics || !result) return fail(SCE_FONT_ERROR_INVALID_PARAMETER);
auto* font = GetNativeFont(handle);
if (font->magic != HANDLE_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
if (!font->renderer) return fail(SCE_FONT_ERROR_NOT_BOUND_RENDERER);
auto* library = static_cast<FontLibNative*>(font->library);
if (!library || library->magic != LIBRARY_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
const SysDriver* driver = library->sys_driver;
if (!driver || !driver->glyph_index || !driver->set_char_with_dpi || !driver->set_char_default_dpi) return fail(SCE_FONT_ERROR_FATAL);
const std::uint32_t modeLow = font->flags & 0x0Fu;
StyleStateBlock surfaceStyle{};
const StyleStateBlock* style = ResolveSurfaceStyleState(&font->cached_style.state, surface, surfaceStyle);
FontObj* obj = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, font->open_info.ctx_entry_index, modeLow, &obj, &lockWord);
obj = FindSubFont(obj, font->open_info.sub_font_index);
if (!entry || !obj) {
if (entry) ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
return fail(SCE_FONT_ERROR_FATAL);
}
const auto finish = [&](int rc) {
ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result);
return rc;
};
if ((lockWord & OPEN_BIT) == 0 || (lockWord & COUNT_MASK) == 0) return finish(SCE_FONT_ERROR_FATAL);
obj->reserved_0x04 = static_cast<std::uint32_t>(font->flags >> 15);
obj->font_handle = handle;
obj->shift_units_x = 0;
obj->shift_units_y = 0;
obj->layout_seed_pair = 0;
StyleStateGetScalePixel(style, &obj->scale_x_0x50, &obj->scale_y_0x54);
float outScaleX = 0.0f;
float outScaleY = 0.0f;
int rc;
if (style->scale_unit == 0) {
rc = driver->set_char_default_dpi(obj, style->scale_w, style->scale_h, &outScaleX, &outScaleY);
} else {
rc = driver->set_char_with_dpi(obj, style->dpi_x ? style->dpi_x : 0x48, style->dpi_y ? style->dpi_y : 0x48, style->scale_w, style->scale_h, &outScaleX, &outScaleY);
}
if (rc != SCE_FONT_OK) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH);
std::uint32_t glyphIndex = 0;
rc = driver->glyph_index(obj, code, &glyphIndex);
if (rc != SCE_FONT_OK || glyphIndex == 0) return finish(SCE_FONT_ERROR_NO_SUPPORT_GLYPH);
return finish(RenderGlyphIndexToSurface(*obj, glyphIndex, surface, x, y, metrics, result));
}
bool Font::BuildTrueOutline(GeneratedGlyph& glyph) {
FontState* state = TryGetState(glyph.owner);
if (!state || !state->face) return false;
const auto* font = GetNativeFont(glyph.owner);
if (font->magic != HANDLE_MAGIC) return false;
const FT_Face face = state->face;
const FT_UInt glyphIndex = FT_Get_Char_Index(face, glyph.codepoint);
if (glyphIndex == 0) return false;
const auto charW = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(glyph.glyph.scale_x * 64.0f));
const auto charH = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(glyph.glyph.base_scale * 64.0f));
if (FT_Set_Char_Size(face, charW, charH, 72, 72) != 0) return false;
FT_Set_Transform(face, nullptr, nullptr);
if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP) != 0) return false;
const FT_GlyphSlot slot = face->glyph;
if (!slot || slot->format != FT_GLYPH_FORMAT_OUTLINE) return false;
const FT_Outline& outline = slot->outline;
if (outline.n_points <= 0 || outline.n_contours <= 0 || !outline.points || !outline.tags || !outline.contours) return false;
glyph.outlinePoints.clear();
glyph.outlineTags.clear();
glyph.outlineContours.clear();
for (int i = 0; i < outline.n_points; ++i) {
glyph.outlinePoints.push_back({static_cast<float>(outline.points[i].x) / 64.0f, static_cast<float>(outline.points[i].y) / 64.0f});
glyph.outlineTags.push_back(FT_CURVE_TAG(outline.tags[i]) == FT_CURVE_TAG_ON ? 1 : 0);
}
for (int i = 0; i < outline.n_contours; ++i) glyph.outlineContours.push_back(static_cast<std::uint16_t>(outline.contours[i]));
glyph.outline.points_ptr = glyph.outlinePoints.data();
glyph.outline.tags_ptr = glyph.outlineTags.data();
glyph.outline.contour_end_idx = glyph.outlineContours.data();
glyph.outline.points_cnt = static_cast<std::int16_t>(glyph.outlinePoints.size());
glyph.outline.contours_cnt = static_cast<std::int16_t>(glyph.outlineContours.size());
glyph.outlineInitialized = true;
return true;
}

View File

@ -0,0 +1,378 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <atomic>
#include <cstring>
#include <limits>
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
std::uint32_t EffectiveEdition(std::uint64_t edition) {
return static_cast<std::uint32_t>(edition >> 32);
}
std::uint32_t* AcquireDeviceCache(FontLibNative* library) {
auto* const busy = reinterpret_cast<std::uint32_t*>(std::numeric_limits<std::uintptr_t>::max());
std::atomic_ref<std::uint32_t*> slot(library->device_cache_buf);
for (;;) {
std::uint32_t* current = slot.load(std::memory_order_acquire);
if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current;
Backoff();
}
}
void ReleaseDeviceCache(FontLibNative* library, std::uint32_t* cache) {
std::atomic_ref<std::uint32_t*>(library->device_cache_buf).store(cache, std::memory_order_release);
}
void* AcquireRendererSelection(RendererNative* renderer) {
auto* const busy = reinterpret_cast<void*>(std::numeric_limits<std::uintptr_t>::max());
std::atomic_ref<void*> slot(renderer->selection);
for (;;) {
void* current = slot.load(std::memory_order_acquire);
if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current;
Backoff();
}
}
void* CreateFontContext(FontLibNative* library, std::uint32_t entries, std::uint32_t size) {
void* ctx = library->iface->alloc(library->alloc_ctx, size);
if (!ctx) return nullptr;
std::memset(ctx, 0, size);
auto* header = static_cast<FontCtxHeader*>(ctx);
header->lock_word = 0;
header->max_entries = entries;
header->base = entries != 0 ? static_cast<std::uint8_t*>(ctx) + sizeof(FontCtxHeader) : nullptr;
return ctx;
}
int SupportFonts(FontLibrary library, void* FontLibNative::*slot, std::uint32_t entries, std::uint32_t size, std::uint32_t formats) {
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY;
std::uint32_t previous = 0;
if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY;
const auto finish = [&](int rc) {
ReleaseLibraryLock(lib, previous);
return rc;
};
if (lib->*slot) return finish(SCE_FONT_ERROR_ALREADY_SPECIFIED);
void* ctx = CreateFontContext(lib, entries, size);
if (!ctx) return finish(SCE_FONT_ERROR_ALLOCATION_FAILED);
if (!lib->sys_driver || !lib->sys_driver->support_formats) {
lib->iface->dealloc(lib->alloc_ctx, ctx);
return finish(SCE_FONT_ERROR_INVALID_LIBRARY);
}
const int rc = lib->sys_driver->support_formats(lib, formats);
if (rc != SCE_FONT_OK) {
lib->iface->dealloc(lib->alloc_ctx, ctx);
return finish(rc);
}
lib->*slot = ctx;
return finish(SCE_FONT_OK);
}
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontMemoryInit(FontMemory* memory, void* regionAddress, std::uint32_t regionSize, const FontMemoryInterface* iface, void* mspaceObject, FontMemoryDestroyFunction destroyCallback, void* destroyObject) {
if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!iface) {
memory->mem_kind = 0;
if (!regionAddress || regionSize == 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
}
memory->mem_kind = MEMORY_MAGIC;
memory->attr_bits = 0;
memory->region_size = regionSize;
memory->region_base = regionAddress;
memory->mspace_handle = mspaceObject;
memory->iface = iface;
memory->on_destroy = destroyCallback;
memory->destroy_ctx = destroyObject;
memory->some_ctx1 = nullptr;
memory->some_ctx2 = mspaceObject;
return SCE_FONT_OK;
}
int APS5_VABI sceFontMemoryTerm(FontMemory* memory) {
if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (memory->mem_kind != MEMORY_MAGIC) return SCE_FONT_ERROR_INVALID_MEMORY;
if (static_cast<std::int8_t>(memory->attr_bits & 0xFF) < 0) {
if (memory->iface && memory->iface->mspace_destroy) memory->iface->mspace_destroy(memory->some_ctx2, memory->mspace_handle);
std::memset(memory, 0, sizeof(*memory));
return SCE_FONT_OK;
}
if (memory->on_destroy) {
memory->mem_kind = 0;
memory->on_destroy(memory, memory->mspace_handle, memory->destroy_ctx);
return SCE_FONT_OK;
}
std::memset(memory, 0, sizeof(*memory));
return SCE_FONT_OK;
}
int APS5_VABI sceFontCreateLibraryWithEdition(const FontMemory* memory, const void* createParams, std::uint64_t edition, FontLibrary* pLibrary) {
if (pLibrary) *pLibrary = nullptr;
if (!memory) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (memory->mem_kind != MEMORY_MAGIC || !memory->iface || !memory->iface->alloc || !memory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY;
if (!createParams || !pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER;
const auto allocFn = memory->iface->alloc;
const auto freeFn = memory->iface->dealloc;
auto* lib = static_cast<FontLibNative*>(allocFn(memory->mspace_handle, sizeof(FontLibNative)));
if (!lib) return SCE_FONT_ERROR_ALLOCATION_FAILED;
void* workspace = allocFn(memory->mspace_handle, 0x4000);
if (!workspace) {
freeFn(memory->mspace_handle, lib);
return SCE_FONT_ERROR_ALLOCATION_FAILED;
}
std::memset(lib, 0, sizeof(FontLibNative));
lib->mem_kind = MEMORY_MAGIC;
lib->region_size = memory->region_size;
lib->region_base = memory->region_base;
lib->alloc_ctx = memory->mspace_handle;
lib->iface = memory->iface;
lib->alloc_fn = memory->iface->alloc;
lib->dealloc_fn = memory->iface->dealloc;
lib->realloc_fn = memory->iface->realloc_fn;
lib->calloc_fn = memory->iface->calloc_fn;
lib->sys_driver = static_cast<const SysDriver*>(createParams);
lib->workspace_size = 0x4000;
lib->workspace = workspace;
lib->list_head_ptr = &lib->list_head;
lib->list_head = nullptr;
const auto initFn = lib->sys_driver->init;
const int rc = initFn ? initFn(memory, lib) : SCE_FONT_ERROR_INVALID_PARAMETER;
if (rc != SCE_FONT_OK) {
freeFn(memory->mspace_handle, workspace);
freeFn(memory->mspace_handle, lib);
return rc;
}
const std::uint32_t version = EffectiveEdition(edition);
if (version > 0x92FFFFu) {
if (version < 0x1500000u) {
lib->feature_word |= 1u | 2u | 4u;
} else if (version < 0x2000000u) {
lib->feature_word |= 2u | 4u;
} else if (version <= 0x2000070u) {
lib->feature_word |= 4u;
}
}
lib->magic = LIBRARY_MAGIC;
*pLibrary = lib;
return SCE_FONT_OK;
}
int APS5_VABI sceFontCreateLibrary(const FontMemory* memory, const void* createParams, FontLibrary* pLibrary) {
return sceFontCreateLibraryWithEdition(memory, createParams, 0, pLibrary);
}
int APS5_VABI sceFontDestroyLibrary(FontLibrary* pLibrary) {
if (!pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* lib = static_cast<FontLibNative*>(*pLibrary);
if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY;
if (lib->sys_driver && lib->sys_driver->term) lib->sys_driver->term(lib);
const auto freeFn = lib->iface->dealloc;
void* allocCtx = lib->alloc_ctx;
if ((lib->flags & 1u) != 0 && lib->device_cache_buf) freeFn(allocCtx, lib->device_cache_buf);
if (lib->external_fonts_ctx) freeFn(allocCtx, lib->external_fonts_ctx);
if (lib->sysfonts_ctx) freeFn(allocCtx, lib->sysfonts_ctx);
if (lib->workspace) freeFn(allocCtx, lib->workspace);
lib->magic = 0;
freeFn(allocCtx, lib);
*pLibrary = nullptr;
return SCE_FONT_OK;
}
int APS5_VABI sceFontCreateRendererWithEdition(const FontMemory* memory, const void* createParams, std::uint64_t edition, FontRenderer* pRenderer) {
if (!memory) {
if (pRenderer) *pRenderer = nullptr;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
int rc = SCE_FONT_ERROR_INVALID_MEMORY;
if (memory->mem_kind == MEMORY_MAGIC && memory->iface && memory->iface->alloc && memory->iface->dealloc) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
if (createParams && pRenderer) {
const auto allocFn = memory->iface->alloc;
const auto freeFn = memory->iface->dealloc;
const auto* selection = static_cast<const RendererSelection*>(createParams);
void* rendererMemory = allocFn(memory->mspace_handle, selection->size);
void* workspace = allocFn(memory->mspace_handle, 0x4000);
rc = SCE_FONT_ERROR_ALLOCATION_FAILED;
if (rendererMemory && workspace) {
auto* renderer = static_cast<RendererNative*>(rendererMemory);
std::memset(renderer, 0, offsetof(RendererNative, mem_kind));
renderer->magic = RENDERER_MAGIC;
renderer->mem_kind = MEMORY_MAGIC;
renderer->region_size = memory->region_size;
renderer->region_base = memory->region_base;
renderer->alloc_ctx = memory->mspace_handle;
renderer->mem_iface = memory->iface;
renderer->alloc_fn = memory->iface->alloc;
renderer->free_fn = memory->iface->dealloc;
renderer->realloc_fn = memory->iface->realloc_fn;
renderer->calloc_fn = memory->iface->calloc_fn;
renderer->selection = const_cast<void*>(createParams);
renderer->outline_magic_0x90 = 0x400000000000ull;
renderer->workspace = workspace;
renderer->workspace_size = 0x4000;
renderer->reserved_a8 = 0;
renderer->outline_policy_flag = 0;
rc = selection->create_fn ? selection->create_fn(renderer) : SCE_FONT_ERROR_FATAL;
if (rc == SCE_FONT_OK) {
if (EffectiveEdition(edition) - 0x930000u < 0x1BD0000u) {
renderer->feature_byte_0x0c |= 1u;
renderer->outline_policy_flag = 1;
}
*pRenderer = renderer;
return SCE_FONT_OK;
}
}
if (workspace) freeFn(memory->mspace_handle, workspace);
if (rendererMemory) freeFn(memory->mspace_handle, rendererMemory);
}
}
if (pRenderer) *pRenderer = nullptr;
return rc;
}
int APS5_VABI sceFontCreateRenderer(const FontMemory* memory, const void* createParams, FontRenderer* pRenderer) {
return sceFontCreateRendererWithEdition(memory, createParams, 0, pRenderer);
}
int APS5_VABI sceFontDestroyRenderer(FontRenderer* pRenderer) {
if (!pRenderer) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* renderer = static_cast<RendererNative*>(*pRenderer);
if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER;
const auto* selection = static_cast<const RendererSelection*>(AcquireRendererSelection(renderer));
int rc = SCE_FONT_ERROR_FATAL;
if (selection && selection->destroy_fn) rc = selection->destroy_fn(renderer);
renderer->selection = nullptr;
const auto freeFn = renderer->free_fn;
void* allocCtx = renderer->alloc_ctx;
if (renderer->workspace) freeFn(allocCtx, renderer->workspace);
freeFn(allocCtx, renderer);
*pRenderer = nullptr;
return rc;
}
int APS5_VABI sceFontRendererGetOutlineBufferSize(FontRenderer fontRenderer, std::uint32_t* size) {
if (!size) return SCE_FONT_ERROR_INVALID_PARAMETER;
*size = 0;
auto* renderer = static_cast<RendererNative*>(fontRenderer);
if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER;
*size = static_cast<std::uint32_t>(renderer->workspace_size);
return SCE_FONT_OK;
}
int APS5_VABI sceFontRendererResetOutlineBuffer(FontRenderer fontRenderer) {
auto* renderer = static_cast<RendererNative*>(fontRenderer);
if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER;
if (renderer->workspace && renderer->workspace_size) std::memset(renderer->workspace, 0, static_cast<std::size_t>(renderer->workspace_size));
return SCE_FONT_OK;
}
int APS5_VABI sceFontRendererSetOutlineBufferPolicy(FontRenderer fontRenderer, std::uint64_t bufferPolicy, std::uint32_t basalSize, std::uint32_t limitSize) {
(void)bufferPolicy;
auto* renderer = static_cast<RendererNative*>(fontRenderer);
if (!renderer || renderer->magic != RENDERER_MAGIC) return SCE_FONT_ERROR_INVALID_RENDERER;
if (limitSize != 0 && basalSize > limitSize) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!renderer->alloc_fn || !renderer->free_fn || !renderer->alloc_ctx) return SCE_FONT_ERROR_INVALID_MEMORY;
auto desiredSize = std::max(static_cast<std::uint64_t>(renderer->workspace_size), static_cast<std::uint64_t>(basalSize));
if (limitSize != 0) desiredSize = std::min(desiredSize, static_cast<std::uint64_t>(limitSize));
if (desiredSize == 0) desiredSize = 0x4000;
if (!renderer->workspace || renderer->workspace_size != desiredSize) {
void* workspace = renderer->alloc_fn(renderer->alloc_ctx, static_cast<std::uint32_t>(desiredSize));
if (!workspace) return SCE_FONT_ERROR_ALLOCATION_FAILED;
if (renderer->workspace) renderer->free_fn(renderer->alloc_ctx, renderer->workspace);
renderer->workspace = workspace;
renderer->workspace_size = desiredSize;
}
return SCE_FONT_OK;
}
int APS5_VABI sceFontAttachDeviceCacheBuffer(FontLibrary library, void* buffer, std::uint32_t size) {
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY;
std::uint32_t previous = 0;
if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY;
std::uint32_t* current = AcquireDeviceCache(lib);
std::uint32_t* stored = current;
int rc;
if (current) {
rc = SCE_FONT_ERROR_ALREADY_ATTACHED;
} else if (size < 0x1020) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
} else {
auto* header = static_cast<std::uint32_t*>(buffer);
std::uint32_t owned = 0;
if (!header) {
header = static_cast<std::uint32_t*>(lib->iface->alloc(lib->alloc_ctx, size));
owned = 1;
}
if (!header) {
rc = SCE_FONT_ERROR_ALLOCATION_FAILED;
} else {
const std::uint32_t pageCount = (size - 0x1000) >> 12;
header[0] = size;
header[1] = pageCount;
header[2] = 0;
header[3] = pageCount;
const std::uint64_t pageInfo = 0x0FF800001000ull;
std::memcpy(header + 4, &pageInfo, sizeof(pageInfo));
if (size - 0x1000 > 0x0FFF) {
lib->flags |= owned;
stored = header;
rc = SCE_FONT_OK;
} else {
if (owned) lib->iface->dealloc(lib->alloc_ctx, header);
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
}
}
}
ReleaseDeviceCache(lib, stored);
ReleaseLibraryLock(lib, previous);
return rc;
}
int APS5_VABI sceFontClearDeviceCache(FontLibrary library) {
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY;
std::uint32_t previous = 0;
if (!AcquireLibraryLock(lib, previous)) return SCE_FONT_ERROR_INVALID_LIBRARY;
std::uint32_t* current = AcquireDeviceCache(lib);
int rc = SCE_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER;
if (current) {
current[3] = current[1];
current[2] = 0;
rc = SCE_FONT_OK;
}
ReleaseDeviceCache(lib, current);
ReleaseLibraryLock(lib, previous);
return rc;
}
int APS5_VABI sceFontSupportExternalFonts(FontLibrary library, std::uint32_t fontMax, std::uint32_t formats) {
return SupportFonts(library, &FontLibNative::external_fonts_ctx, fontMax, (fontMax << 6) | 0x20u, formats);
}
int APS5_VABI sceFontSupportSystemFonts(FontLibrary library) {
return SupportFonts(library, &FontLibNative::sysfonts_ctx, 0x40, 0x1020, 0x52);
}
int APS5_VABI sceFontSetFontsOpenMode(FontLibrary library, std::uint32_t openMode) {
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC) return SCE_FONT_ERROR_INVALID_LIBRARY;
if (openMode > 2) return SCE_FONT_ERROR_INVALID_PARAMETER;
return SCE_FONT_OK;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,509 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
constexpr std::array<std::uint32_t, 88> SYSTEM_FONT_SET_TYPES = {
0x18070043u, 0x18070044u, 0x18070045u, 0x18070047u, 0x18070053u, 0x18070054u,
0x18070055u, 0x18070057u, 0x180700C3u, 0x180700C4u, 0x180700C5u, 0x180700C7u,
0x18070444u, 0x18070447u, 0x18070454u, 0x18070457u, 0x180704C4u, 0x180704C7u,
0x18071053u, 0x18071054u, 0x18071055u, 0x18071057u, 0x18071454u, 0x18071457u,
0x18072444u, 0x18072447u, 0x180724C4u, 0x180724C7u, 0x18073454u, 0x18073457u,
0x180734D4u, 0x180734D7u, 0x18078044u, 0x180780C4u, 0x18079054u, 0x1807A044u,
0x1807A0C4u, 0x1807A444u, 0x1807A4C4u, 0x1807AC44u, 0x1807ACC4u, 0x1807B054u,
0x1807B0D4u, 0x1807B454u, 0x1807B4D4u, 0x1807BC54u, 0x1807BCD4u, 0x18080444u,
0x18080447u, 0x18080454u, 0x18080457u, 0x180804C4u, 0x180804C7u, 0x18081454u,
0x18081457u, 0x18082444u, 0x18082447u, 0x180824C4u, 0x180824C7u, 0x18083454u,
0x18083457u, 0x180834D4u, 0x180834D7u, 0x1808A444u, 0x1808A4C4u, 0x1808B454u,
0x1808B4D4u, 0x180C8044u, 0x180C80C4u, 0x180C9054u, 0x180CA044u, 0x180CA0C4u,
0x180CAC44u, 0x180CACC4u, 0x180CB054u, 0x180CB0D4u, 0x180CBC54u, 0x180CBCD4u,
0x18170043u, 0x18170044u, 0x18170045u, 0x18170047u, 0x18170444u, 0x18170447u,
0x18370044u, 0x18370047u, 0x18370444u, 0x18370447u,
};
constexpr std::uint16_t HANDLE_OWNED = 0x10;
struct OpenRequest {
FontLibNative* library;
std::uint32_t modeLow;
std::uint32_t driverMode;
const void* source;
std::uint32_t sourceSize;
bool matchAddress;
std::uint32_t subFontIndex;
std::int32_t uniqueId;
};
FontHandle PrepareHandle(FontLibNative* library, FontHandle existing) {
if (existing) {
GetNativeFont(existing)->flags = 0;
RemoveState(existing);
return existing;
}
auto handle = static_cast<FontHandle>(library->iface->alloc(library->alloc_ctx, sizeof(FontHandleOpaque)));
if (!handle) return nullptr;
std::memset(handle, 0, sizeof(FontHandleOpaque));
GetNativeFont(handle)->flags = HANDLE_OWNED;
return handle;
}
void DiscardHandle(FontLibNative* library, FontHandle handle) {
auto* font = GetNativeFont(handle);
font->magic = 0;
font->open_info = {};
font->library = library;
const std::uint16_t previousFlags = font->flags;
font->flags = 0;
RemoveState(handle);
if ((previousFlags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, handle);
}
void InitializeOpenedHandle(FontHandleNative* font, FontLibNative* library, FontObj* obj, std::uint32_t modeLow, std::uint32_t packedId, std::uint32_t entryIndex, std::uint32_t subFontIndex) {
const SysDriver* driver = library->sys_driver;
font->magic = HANDLE_MAGIC;
font->flags = static_cast<std::uint16_t>(font->flags | modeLow);
font->open_info = {};
font->open_info.unique_id_packed = packedId;
font->open_info.ctx_entry_index = entryIndex;
font->open_info.sub_font_index = subFontIndex;
font->library = library;
font->renderer = nullptr;
std::memset(font->reserved_0x38, 0, sizeof(font->reserved_0x38));
std::memset(&font->cached_style, 0, sizeof(font->cached_style));
std::memset(font->reserved_0xcc, 0, sizeof(font->reserved_0xcc));
std::memset(font->reserved_0xe8, 0, sizeof(font->reserved_0xe8));
font->prevFont = nullptr;
font->nextFont = nullptr;
std::uint16_t metric[2] = {};
driver->metric(obj, 0x0E00, metric);
font->metricA = metric[0];
driver->metric(obj, 0xEA00, metric);
font->metricB = metric[0];
float scale = 0.0f;
driver->scale(obj, metric, &scale);
font->style = {};
font->style.dpi_x = 0x48;
font->style.dpi_y = 0x48;
font->style.scale_w = scale;
font->style.scale_h = scale;
}
void SetOpenFlags(FontLibNative* library, std::uint32_t modeLow) {
const std::uint32_t flags = library->flags;
const std::uint32_t firstBit = modeLow == 2 ? 0x100000u : 0x200000u;
const std::uint32_t secondBit = modeLow == 2 ? 0x10000u : 0x20000u;
if ((flags & firstBit) != 0) library->sysfont_flags |= 1;
if ((flags & secondBit) != 0) library->sysfont_flags |= 2;
}
std::uint32_t ClaimExternalEntry(FontCtxHeader* header, const OpenRequest& request) {
AcquireWordLock(header->lock_word);
auto* entries = static_cast<FontCtxEntry*>(header->base);
std::uint32_t entryIndex = 0xFFFFFFFFu;
if (header->max_entries != 0 && entries) {
const auto uniqueId = static_cast<std::uint32_t>(request.uniqueId);
const auto address = request.matchAddress ? reinterpret_cast<std::uint64_t>(request.source) : 0;
std::uint32_t firstFree = 0xFFFFFFFFu;
for (std::uint32_t i = 0; i < header->max_entries; ++i) {
const auto& entry = entries[i];
if ((entry.active != 0 && entry.unique_id == uniqueId) || (request.matchAddress && entry.font_address == address)) {
entryIndex = i;
break;
}
if (firstFree == 0xFFFFFFFFu && entry.active == 0) firstFree = i;
}
if (entryIndex == 0xFFFFFFFFu && firstFree != 0xFFFFFFFFu) {
entryIndex = firstFree;
auto& entry = entries[entryIndex];
entry = {};
entry.active = 1;
entry.font_address = address;
entry.unique_id = uniqueId != 0xFFFFFFFFu ? uniqueId : (entryIndex ^ 0x80000000u);
}
}
std::atomic_ref<std::uint32_t>(header->lock_word).store(0, std::memory_order_release);
return entryIndex;
}
int OpenExternalFont(const OpenRequest& request, std::uint32_t libraryLock, FontHandle* pFontHandle) {
FontLibNative* library = request.library;
FontHandle handle = PrepareHandle(library, *pFontHandle);
if (!handle) {
ReleaseLibraryLock(library, libraryLock);
*pFontHandle = nullptr;
return SCE_FONT_ERROR_ALLOCATION_FAILED;
}
const auto fail = [&](int rc) {
DiscardHandle(library, handle);
ReleaseLibraryLock(library, libraryLock);
*pFontHandle = nullptr;
return rc;
};
auto* header = static_cast<FontCtxHeader*>(library->external_fonts_ctx);
const std::uint32_t entryIndex = ClaimExternalEntry(header, request);
if (entryIndex == 0xFFFFFFFFu) return fail(SCE_FONT_ERROR_FONT_OPEN_MAX);
const std::uint32_t packedId = request.uniqueId == -1 ? entryIndex + 0x40000000u : (static_cast<std::uint32_t>(request.uniqueId) | 0x80000000u);
FontObj* fontObj = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(header, entryIndex, request.modeLow, &fontObj, &lockWord);
if (!entry) return fail(SCE_FONT_ERROR_FONT_OPEN_MAX);
const SysDriver* driver = library->sys_driver;
if (!driver->open || !driver->metric || !driver->scale) {
ReleaseFontCtxEntryLock(entry, request.modeLow, lockWord);
return fail(SCE_FONT_ERROR_INVALID_LIBRARY);
}
int rc = SCE_FONT_OK;
bool needOpen = (lockWord & OPEN_BIT) == 0;
if (!needOpen) {
rc = SCE_FONT_ERROR_FONT_OPEN_MAX;
if ((lockWord & COUNT_MASK) != COUNT_MASK) {
if (FontObj* node = FindSubFont(fontObj, request.subFontIndex)) {
++node->refcount;
fontObj = node;
rc = SCE_FONT_OK;
} else {
needOpen = true;
}
}
}
if (needOpen) {
SetOpenFlags(library, request.modeLow);
rc = driver->open(library, request.driverMode, request.source, request.sourceSize, request.subFontIndex, packedId, &fontObj);
library->sysfont_flags = 0;
if (rc == SCE_FONT_OK) {
*EntryObjectSlot(entry, request.modeLow) = fontObj;
lockWord |= OPEN_BIT;
}
}
if (rc == SCE_FONT_OK) {
InitializeOpenedHandle(GetNativeFont(handle), library, fontObj, request.modeLow, packedId, entryIndex, request.subFontIndex);
++lockWord;
*pFontHandle = handle;
}
ReleaseFontCtxEntryLock(entry, request.modeLow, lockWord);
if (rc != SCE_FONT_OK) return fail(rc);
if ((GetNativeFont(handle)->flags & HANDLE_OWNED) != 0) LinkFontToLibrary(library, handle);
ReleaseLibraryLock(library, libraryLock);
return SCE_FONT_OK;
}
int BeginOpen(FontLibrary library, FontHandle* pFontHandle, std::uint32_t& libraryLock) {
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC || !AcquireLibraryLock(lib, libraryLock)) {
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_LIBRARY;
}
int rc = SCE_FONT_OK;
if (!lib->fontset_registry || !lib->sys_driver || !lib->iface || !lib->iface->alloc || !lib->iface->dealloc) {
rc = SCE_FONT_ERROR_INVALID_LIBRARY;
} else if (!lib->external_fonts_ctx) {
rc = SCE_FONT_ERROR_NO_SUPPORT_FUNCTION;
}
if (rc != SCE_FONT_OK) {
ReleaseLibraryLock(lib, libraryLock);
if (pFontHandle) *pFontHandle = nullptr;
}
return rc;
}
bool ReadFileBytes(const std::filesystem::path& path, std::vector<unsigned char>& bytes) {
std::ifstream file(path, std::ios::binary);
if (!file) return false;
bytes.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
return !file.bad();
}
void CopyStyleToCache(FontHandleNative* font) {
std::memcpy(&font->cached_style.state, &font->style, sizeof(StyleStateBlock));
}
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontOpenFontMemory(FontLibrary library, const void* fontAddress, std::uint32_t fontSize, const FontOpenDetail* openDetail, FontHandle* pFontHandle) {
std::uint32_t libraryLock = 0;
const int beginRc = BeginOpen(library, pFontHandle, libraryLock);
if (beginRc != SCE_FONT_OK) return beginRc;
auto* lib = static_cast<FontLibNative*>(library);
const std::uint32_t subFontIndex = openDetail ? openDetail->subfont_index : 0u;
const std::int32_t uniqueId = openDetail ? openDetail->unique_id : -1;
if (!fontAddress || fontSize == 0 || uniqueId < -1 || !pFontHandle) {
ReleaseLibraryLock(lib, libraryLock);
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
const OpenRequest request{lib, 1, 1, fontAddress, fontSize, true, subFontIndex, uniqueId};
const int rc = OpenExternalFont(request, libraryLock, pFontHandle);
if (rc != SCE_FONT_OK) return rc;
const auto* bytes = static_cast<const unsigned char*>(fontAddress);
LoadStateFace(ResetState(*pFontHandle), std::vector<unsigned char>(bytes, bytes + fontSize), subFontIndex);
return SCE_FONT_OK;
}
int APS5_VABI sceFontOpenFontFile(FontLibrary library, const char* path, std::uint32_t openMode, const FontOpenDetail* openDetail, FontHandle* pFontHandle) {
if (!library) {
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_LIBRARY;
}
if (!pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER;
const std::filesystem::path hostPath = path ? ResolvePath_nid_no_patch(path) : std::filesystem::path{};
const std::string hostPathString = hostPath.string();
std::uint32_t libraryLock = 0;
const int beginRc = BeginOpen(library, pFontHandle, libraryLock);
if (beginRc != SCE_FONT_OK) return beginRc;
auto* lib = static_cast<FontLibNative*>(library);
const std::uint32_t subFontIndex = openDetail ? openDetail->subfont_index : 0u;
const std::int32_t uniqueId = openDetail ? openDetail->unique_id : -1;
const std::uint32_t modeLow = openMode & 0x0Fu;
if (!path || uniqueId < -1 || modeLow < 1 || modeLow > 3 || hostPathString.empty()) {
ReleaseLibraryLock(lib, libraryLock);
*pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
const OpenRequest request{lib, modeLow, modeLow + 4, hostPathString.c_str(), 0, false, subFontIndex, uniqueId};
const int rc = OpenExternalFont(request, libraryLock, pFontHandle);
if (rc != SCE_FONT_OK) return rc;
std::vector<unsigned char> bytes;
FontState& state = ResetState(*pFontHandle);
if (ReadFileBytes(hostPath, bytes)) LoadStateFace(state, std::move(bytes), subFontIndex);
return SCE_FONT_OK;
}
int APS5_VABI sceFontOpenFontInstance(FontHandle fontHandle, FontHandle setupFont, FontHandle* pFontHandle) {
if (!fontHandle) {
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
if (!setupFont && !pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* source = GetNativeFont(fontHandle);
std::uint32_t sourceLock = 0;
if (source->magic != HANDLE_MAGIC || !AcquireFontLock(source, sourceLock)) {
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
const auto fail = [&](int rc) {
ReleaseFontLock(source, sourceLock);
if (pFontHandle) *pFontHandle = nullptr;
return rc;
};
auto* library = static_cast<FontLibNative*>(source->library);
if (!library || library->magic != LIBRARY_MAGIC) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
const std::uint32_t modeLow = source->flags & 0x0Fu;
const std::uint32_t subFontIndex = source->open_info.sub_font_index;
const std::uint32_t entryIndex = source->open_info.ctx_entry_index;
auto* header = static_cast<FontCtxHeader*>(library->external_fonts_ctx);
if (!header) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
FontHandle target = setupFont;
bool owned = false;
if (!target) {
if (!library->iface || !library->iface->alloc || !library->iface->dealloc) return fail(SCE_FONT_ERROR_INVALID_FONT_HANDLE);
target = static_cast<FontHandle>(library->iface->alloc(library->alloc_ctx, sizeof(FontHandleOpaque)));
if (!target) return fail(SCE_FONT_ERROR_ALLOCATION_FAILED);
owned = true;
} else {
RemoveState(target);
}
std::memcpy(target, fontHandle, sizeof(FontHandleOpaque));
auto* font = GetNativeFont(target);
font->lock_word = 0;
font->cached_style.cache_lock_word = 0;
font->flags = static_cast<std::uint16_t>((font->flags & 0xFF0Fu) | (owned ? HANDLE_OWNED : 0u));
auto* entries = static_cast<FontCtxEntry*>(header->base);
if (!entries || header->max_entries < 1) {
font->magic = 0;
if (owned) library->iface->dealloc(library->alloc_ctx, target);
return fail(SCE_FONT_ERROR_INVALID_PARAMETER);
}
int rc = SCE_FONT_OK;
if (static_cast<std::int32_t>(entryIndex) >= 0) {
if (entryIndex >= header->max_entries || modeLow < 1 || modeLow > 3) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
} else {
FontCtxEntry* entry = entries + entryIndex;
std::uint32_t* lockWord = EntryLockWord(entry, modeLow);
const std::uint32_t previous = AcquireWordLock(*lockWord);
FontObj* node = FindSubFont(static_cast<FontObj*>(*EntryObjectSlot(entry, modeLow)), subFontIndex);
std::uint32_t updated = previous;
if ((previous & OPEN_BIT) == 0) {
rc = SCE_FONT_ERROR_INVALID_FONT_HANDLE;
} else if ((previous & COUNT_MASK) == COUNT_MASK) {
rc = SCE_FONT_ERROR_FONT_OPEN_MAX;
} else if (!node) {
rc = SCE_FONT_ERROR_FATAL;
} else {
++node->refcount;
++updated;
}
std::atomic_ref<std::uint32_t>(*lockWord).store(updated & ~LOCK_BIT, std::memory_order_release);
}
}
if (rc != SCE_FONT_OK) {
font->magic = 0;
font->open_info = {};
font->library = library;
const std::uint16_t previousFlags = font->flags;
font->flags = 0;
if ((previousFlags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, target);
RemoveState(target);
return fail(rc);
}
if (owned) LinkFontToLibrary(library, target);
ReleaseFontLock(source, sourceLock);
FontState& targetState = ResetState(target);
if (FontState* sourceState = TryGetState(fontHandle)) {
targetState.scaleW = sourceState->scaleW;
targetState.scaleH = sourceState->scaleH;
if (!sourceState->faceData.empty()) LoadStateFace(targetState, sourceState->faceData, subFontIndex);
}
if (pFontHandle) *pFontHandle = target;
return SCE_FONT_OK;
}
int APS5_VABI sceFontOpenFontSet(FontLibrary library, std::uint32_t fontSetType, std::uint32_t openMode, const FontOpenDetail* openDetail, FontHandle* pFontHandle) {
(void)openDetail;
auto* lib = static_cast<FontLibNative*>(library);
if (!lib || lib->magic != LIBRARY_MAGIC) {
if (pFontHandle) *pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_LIBRARY;
}
if (!pFontHandle) return SCE_FONT_ERROR_INVALID_PARAMETER;
std::uint32_t libraryLock = 0;
if (!AcquireLibraryLock(lib, libraryLock)) {
*pFontHandle = nullptr;
return SCE_FONT_ERROR_INVALID_LIBRARY;
}
int rc;
if (openMode != 1 && openMode != 2 && openMode != 3) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
} else if (!lib->fontset_registry || !lib->sys_driver) {
rc = SCE_FONT_ERROR_INVALID_LIBRARY;
} else if (!lib->iface || !lib->iface->alloc || !lib->iface->dealloc) {
rc = SCE_FONT_ERROR_INVALID_MEMORY;
} else if (!lib->sysfonts_ctx) {
rc = SCE_FONT_ERROR_NO_SUPPORT_FUNCTION;
} else if (std::find(SYSTEM_FONT_SET_TYPES.begin(), SYSTEM_FONT_SET_TYPES.end(), fontSetType) == SYSTEM_FONT_SET_TYPES.end()) {
rc = SCE_FONT_ERROR_NO_SUPPORT_FONTSET;
} else {
rc = SCE_FONT_ERROR_FONT_OPEN_FAILED;
}
ReleaseLibraryLock(lib, libraryLock);
*pFontHandle = nullptr;
return rc;
}
int APS5_VABI sceFontCloseFont(FontHandle fontHandle) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
auto* library = static_cast<FontLibNative*>(font->library);
std::uint32_t libraryLock = 0;
if (!library || library->magic != LIBRARY_MAGIC || !AcquireLibraryLock(library, libraryLock)) {
ReleaseFontLock(font, fontLock);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
ReleaseFontObjectsForHandle(font);
const std::uint16_t flags = font->flags;
if ((flags & HANDLE_OWNED) != 0) UnlinkFontFromLibrary(library, fontHandle);
RemoveState(fontHandle);
font->magic = 0;
font->open_info = {};
font->renderer = nullptr;
font->flags = 0;
font->lock_word = 0;
if ((flags & HANDLE_OWNED) != 0) library->iface->dealloc(library->alloc_ctx, fontHandle);
ReleaseLibraryLock(library, libraryLock);
return SCE_FONT_OK;
}
int APS5_VABI sceFontGetLibrary(FontHandle fontHandle, FontLibrary* pLibrary) {
if (!pLibrary) return SCE_FONT_ERROR_INVALID_PARAMETER;
const auto* font = GetNativeFont(fontHandle);
if (!font || font->magic != HANDLE_MAGIC) {
*pLibrary = nullptr;
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
*pLibrary = font->library;
return SCE_FONT_OK;
}
int APS5_VABI sceFontBindRenderer(FontHandle fontHandle, FontRenderer renderer) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
std::uint32_t cachedLock = 0;
if (!AcquireCachedStyleLock(font, cachedLock)) {
ReleaseFontLock(font, fontLock);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
int rc = SCE_FONT_ERROR_ALREADY_BOUND_RENDERER;
if (!font->renderer) {
rc = SCE_FONT_ERROR_INVALID_RENDERER;
if (renderer && static_cast<const RendererNative*>(renderer)->magic == RENDERER_MAGIC) {
CopyStyleToCache(font);
font->renderer = renderer;
rc = SCE_FONT_OK;
}
}
ReleaseCachedStyleLock(font, cachedLock);
ReleaseFontLock(font, fontLock);
return rc;
}
int APS5_VABI sceFontRebindRenderer(FontHandle fontHandle) {
auto* font = GetNativeFont(fontHandle);
if (!font || font->magic != HANDLE_MAGIC) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
std::uint32_t fontLock = 0;
if (!AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
std::uint32_t cachedLock = 0;
if (!AcquireCachedStyleLock(font, cachedLock)) {
ReleaseFontLock(font, fontLock);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
int rc;
if (!font->renderer) {
rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER;
} else if (static_cast<const RendererNative*>(font->renderer)->magic != RENDERER_MAGIC) {
rc = SCE_FONT_ERROR_FATAL;
} else {
CopyStyleToCache(font);
rc = SCE_FONT_OK;
}
ReleaseCachedStyleLock(font, cachedLock);
ReleaseFontLock(font, fontLock);
return rc;
}
int APS5_VABI sceFontUnbindRenderer(FontHandle fontHandle) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t cachedLock = 0;
if (!font || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
int rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER;
if (font->renderer) {
font->renderer = nullptr;
rc = SCE_FONT_OK;
}
ReleaseCachedStyleLock(font, cachedLock);
return rc;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,449 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <limits>
#include <new>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
constexpr std::uint16_t GENERATE_GLYPH_DETAIL_ID = 0x0FD3;
std::uint16_t ClampToU16(float value) {
if (value <= 0.0f) return 0;
return static_cast<std::uint16_t>(std::lround(std::min(value, static_cast<float>(std::numeric_limits<std::uint16_t>::max()))));
}
std::uint8_t* LayoutCacheBytes(FontHandleNative* font) {
return reinterpret_cast<std::uint8_t*>(&font->cached_style) + offsetof(CachedStyle, layout_cache_bytes);
}
RenderSurfaceSystemUse* SurfaceSystemUse(FontRenderSurface* surface) {
return reinterpret_cast<RenderSurfaceSystemUse*>(surface->reserved_q);
}
bool SurfaceScaleFrame(FontRenderSurface* surface, StyleStateBlock& state, int& rc) {
if (!surface || (surface->styleFlag & 0x1) == 0) return false;
const FontStyleFrame* frame = SurfaceSystemUse(surface)->styleframe;
if (!frame || (frame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return false;
state = {};
state.dpi_x = frame->hDpi;
state.dpi_y = frame->vDpi;
if ((frame->flags1 & STYLE_FRAME_FLAG_SLANT) != 0) state.slant_ratio = frame->slantRatio;
if ((frame->flags1 & STYLE_FRAME_FLAG_WEIGHT) != 0) {
state.effect_weight_x = frame->effectWeightX;
state.effect_weight_y = frame->effectWeightY;
}
StyleStateBlock frameScale{};
frameScale.dpi_x = frame->hDpi;
frameScale.dpi_y = frame->vDpi;
frameScale.scale_unit = frame->scaleUnit;
frameScale.scale_w = frame->scalePixelW;
frameScale.scale_h = frame->scalePixelH;
rc = StyleStateGetScalePixel(&frameScale, &state.scale_w, &state.scale_h);
return true;
}
int CachedBaseline(FontHandle handle, FontHandleNative* font, float& baseline) {
int rc = SCE_FONT_OK;
const std::uint8_t flags = CachedStyleCacheFlags(font->cached_style);
if ((flags & 0x1) == 0) {
rc = ComputeHorizontalLayout(handle, &font->cached_style.state, LayoutCacheBytes(font));
if (rc == SCE_FONT_OK) CachedStyleSetCacheFlags(font->cached_style, static_cast<std::uint8_t>(flags | 0x1));
}
if (rc == SCE_FONT_OK) baseline = LoadFloat(LayoutCacheBytes(font), HORIZONTAL_BASELINE);
return rc;
}
int CachedColumnOffset(FontHandle handle, FontHandleNative* font, float& offset) {
int rc = SCE_FONT_OK;
const std::uint8_t flags = CachedStyleCacheFlags(font->cached_style);
if ((flags & 0x2) == 0) {
std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {};
rc = ComputeVerticalLayout(handle, &font->cached_style.state, layout);
if (rc == SCE_FONT_OK) {
offset = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X);
CachedStyleSetScalar(font->cached_style, offset);
CachedStyleSetCacheFlags(font->cached_style, static_cast<std::uint8_t>(flags | 0x2));
}
}
if (rc == SCE_FONT_OK && (CachedStyleCacheFlags(font->cached_style) & 0x2) != 0) offset = CachedStyleGetScalar(font->cached_style);
return rc;
}
int RenderDirectional(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result, std::uint16_t direction) {
auto* font = GetNativeFont(fontHandle);
if (!font || font->magic != HANDLE_MAGIC) {
ClearRenderOutputs(metrics, result);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
if (code == 0) {
ClearRenderOutputs(metrics, result);
return SCE_FONT_ERROR_NO_SUPPORT_CODE;
}
if (!surface || !metrics || !result) {
ClearRenderOutputs(metrics, result);
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
std::uint32_t fontLock = 0;
if (!AcquireFontLock(font, fontLock)) {
ClearRenderOutputs(metrics, result);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
CachedStyleSetDirectionWord(font->cached_style, direction);
const int rc = RenderCharGlyphImageCore(fontHandle, code, surface, x, y, metrics, result);
ReleaseFontLock(font, fontLock);
if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result);
return rc;
}
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontGetCharGlyphMetrics(FontHandle fontHandle, std::uint32_t code, FontGlyphMetrics* metrics) {
return GetCharGlyphMetrics(fontHandle, code, metrics, false);
}
int APS5_VABI sceFontGetRenderCharGlyphMetrics(FontHandle fontHandle, std::uint32_t code, FontGlyphMetrics* metrics) {
return GetCharGlyphMetrics(fontHandle, code, metrics, true);
}
int APS5_VABI sceFontGetHorizontalLayout(FontHandle fontHandle, FontHorizontalLayout* layout) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) {
if (layout) *layout = {};
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
if (!layout) {
ReleaseFontLock(font, fontLock);
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
std::uint8_t blocks[HORIZONTAL_LAYOUT_SIZE] = {};
const int rc = ComputeHorizontalLayout(fontHandle, &font->style, blocks);
ReleaseFontLock(font, fontLock);
if (rc != SCE_FONT_OK) {
*layout = {};
return rc;
}
layout->baselineOffset = LoadFloat(blocks, HORIZONTAL_BASELINE);
layout->lineAdvance = LoadFloat(blocks, HORIZONTAL_LINE_ADVANCE);
layout->decorationExtent = LoadFloat(blocks, HORIZONTAL_EFFECT_HEIGHT);
return SCE_FONT_OK;
}
int APS5_VABI sceFontGetVerticalLayout(FontHandle fontHandle, FontVerticalLayout* layout) {
int rc = SCE_FONT_ERROR_INVALID_FONT_HANDLE;
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (font && font->magic == HANDLE_MAGIC && AcquireFontLock(font, fontLock)) {
if (!layout) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
} else {
std::uint8_t blocks[VERTICAL_LAYOUT_SIZE] = {};
rc = ComputeVerticalLayout(fontHandle, &font->style, blocks);
if (rc == SCE_FONT_OK) {
layout->baselineOffsetX = LoadFloat(blocks, VERTICAL_BASELINE_OFFSET_X);
layout->columnAdvance = LoadFloat(blocks, VERTICAL_COLUMN_ADVANCE);
layout->decorationSpan = LoadFloat(blocks, VERTICAL_DECORATION_SPAN);
}
}
ReleaseFontLock(font, fontLock);
if (rc == SCE_FONT_OK) return rc;
}
if (layout) *layout = {};
return rc;
}
int APS5_VABI sceFontGetKerning(FontHandle fontHandle, std::uint32_t preCode, std::uint32_t code, FontKerning* kerning) {
if (!kerning) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
*kerning = {};
const FontState* state = TryGetState(fontHandle);
const FT_Face face = state ? state->face : nullptr;
const FT_UInt previousGlyph = face ? FT_Get_Char_Index(face, preCode) : 0;
const FT_UInt glyph = face ? FT_Get_Char_Index(face, code) : 0;
if (face && previousGlyph != 0 && glyph != 0) {
const auto charW = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(state->scaleW * 64.0f));
const auto charH = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(state->scaleH * 64.0f));
FT_Set_Char_Size(face, charW, charH, 72, 72);
FT_Vector delta{};
FT_Get_Kerning(face, previousGlyph, glyph, FT_KERNING_DEFAULT, &delta);
kerning->offsetX = static_cast<float>(delta.x) / 64.0f;
}
ReleaseFontLock(font, fontLock);
return SCE_FONT_OK;
}
int APS5_VABI sceFontRenderCharGlyphImage(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) {
ClearRenderOutputs(metrics, result);
return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
}
int preRc = SCE_FONT_OK;
StyleStateBlock frameState{};
const bool horizontal = static_cast<std::int16_t>(font->flags) >= 0;
float xUsed = x;
float yUsed = y;
if (horizontal) {
float baseline = 0.0f;
if (!SurfaceScaleFrame(surface, frameState, preRc)) {
preRc = CachedBaseline(fontHandle, font, baseline);
} else {
std::uint8_t layout[HORIZONTAL_LAYOUT_SIZE] = {};
if (preRc == SCE_FONT_OK) preRc = ComputeHorizontalLayout(fontHandle, &frameState, layout);
if (preRc == SCE_FONT_OK) {
baseline = LoadFloat(layout, HORIZONTAL_BASELINE);
SurfaceSystemUse(surface)->catchedScale = baseline;
}
}
yUsed = y + baseline;
CachedStyleSetDirectionWord(font->cached_style, 1);
} else {
float offset = 0.0f;
if (!SurfaceScaleFrame(surface, frameState, preRc)) {
preRc = CachedColumnOffset(fontHandle, font, offset);
} else {
std::uint8_t layout[VERTICAL_LAYOUT_SIZE] = {};
if (preRc == SCE_FONT_OK) preRc = ComputeVerticalLayout(fontHandle, &frameState, layout);
if (preRc == SCE_FONT_OK) {
offset = LoadFloat(layout, VERTICAL_BASELINE_OFFSET_X);
SurfaceSystemUse(surface)->catchedScale = offset;
} else {
preRc = CachedColumnOffset(fontHandle, font, offset);
}
}
xUsed = x + offset;
CachedStyleSetDirectionWord(font->cached_style, 2);
}
int rc;
if (code == 0) {
rc = SCE_FONT_ERROR_NO_SUPPORT_CODE;
} else if (!surface || !metrics || !result) {
rc = SCE_FONT_ERROR_INVALID_PARAMETER;
} else if (preRc != SCE_FONT_OK) {
rc = preRc;
} else {
rc = RenderCharGlyphImageCore(fontHandle, code, surface, xUsed, yUsed, metrics, result);
}
ReleaseFontLock(font, fontLock);
if (rc != SCE_FONT_OK) ClearRenderOutputs(metrics, result);
return rc;
}
int APS5_VABI sceFontRenderCharGlyphImageHorizontal(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
return RenderDirectional(fontHandle, code, surface, x, y, metrics, result, 1);
}
int APS5_VABI sceFontRenderCharGlyphImageVertical(FontHandle fontHandle, std::uint32_t code, FontRenderSurface* surface, float x, float y, FontGlyphMetrics* metrics, FontRenderOutput* result) {
return RenderDirectional(fontHandle, code, surface, x, y, metrics, result, 2);
}
void APS5_VABI sceFontRenderSurfaceInit(FontRenderSurface* renderSurface, void* buffer, int bufWidthByte, int pixelSizeByte, int widthPixel, int heightPixel) {
if (!renderSurface) return;
const auto width = static_cast<std::uint32_t>(std::max(widthPixel, 0));
const auto height = static_cast<std::uint32_t>(std::max(heightPixel, 0));
renderSurface->buffer = buffer;
renderSurface->widthByte = bufWidthByte;
renderSurface->pixelSizeByte = static_cast<std::int8_t>(pixelSizeByte);
renderSurface->pad0 = 0;
renderSurface->styleFlag = 0;
renderSurface->pad2 = 0;
renderSurface->width = static_cast<std::int32_t>(width);
renderSurface->height = static_cast<std::int32_t>(height);
renderSurface->sc_x0 = 0;
renderSurface->sc_y0 = 0;
renderSurface->sc_x1 = width;
renderSurface->sc_y1 = height;
}
void APS5_VABI sceFontRenderSurfaceSetScissor(FontRenderSurface* renderSurface, int x0, int y0, int w, int h) {
if (!renderSurface) return;
const auto surfaceW = static_cast<std::uint32_t>(renderSurface->width);
if (surfaceW != 0) {
std::uint32_t x1;
std::uint32_t left;
auto width = static_cast<std::uint32_t>(w);
if (x0 < 0) {
x1 = width + static_cast<std::uint32_t>(x0);
if (surfaceW < x1) x1 = surfaceW;
if (width <= static_cast<std::uint32_t>(-x0)) x1 = 0;
left = 0;
} else {
x1 = surfaceW;
left = surfaceW;
if (static_cast<std::uint32_t>(x0) <= surfaceW) {
if (surfaceW < width) width = surfaceW;
x1 = width + static_cast<std::uint32_t>(x0);
left = static_cast<std::uint32_t>(x0);
if (surfaceW < x1) x1 = surfaceW;
}
}
renderSurface->sc_x0 = left;
renderSurface->sc_x1 = x1;
}
const auto surfaceH = static_cast<std::uint32_t>(renderSurface->height);
if (surfaceH == 0) return;
std::uint32_t top;
std::uint32_t y1 = surfaceH;
auto height = static_cast<std::uint32_t>(h);
if (y0 < 0) {
top = 0;
if (height <= static_cast<std::uint32_t>(-y0)) {
renderSurface->sc_y0 = 0;
renderSurface->sc_y1 = 0;
return;
}
} else {
if (surfaceH < static_cast<std::uint32_t>(y0)) {
renderSurface->sc_y0 = surfaceH;
renderSurface->sc_y1 = y1;
return;
}
top = static_cast<std::uint32_t>(y0);
if (surfaceH < height) height = surfaceH;
}
const std::uint32_t candidate = height + static_cast<std::uint32_t>(y0);
if (candidate <= surfaceH) y1 = candidate;
renderSurface->sc_y0 = top;
renderSurface->sc_y1 = y1;
}
int APS5_VABI sceFontRenderSurfaceSetStyleFrame(FontRenderSurface* renderSurface, FontStyleFrame* styleFrame) {
if (!renderSurface) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!styleFrame) {
renderSurface->styleFlag &= static_cast<std::uint8_t>(~0x1u);
renderSurface->reserved_q[0] = 0;
renderSurface->reserved_q[1] = 0;
return SCE_FONT_OK;
}
if (styleFrame->magic != STYLE_FRAME_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER;
renderSurface->styleFlag |= 0x1;
renderSurface->reserved_q[0] = reinterpret_cast<std::uint64_t>(styleFrame);
renderSurface->reserved_q[1] = 0;
return SCE_FONT_OK;
}
int APS5_VABI sceFontGenerateCharGlyph(FontHandle fontHandle, std::uint32_t code, const FontGenerateGlyphDetail* detail, FontGlyph* pGlyph) {
if (!pGlyph) return SCE_FONT_ERROR_INVALID_PARAMETER;
*pGlyph = nullptr;
if (!fontHandle) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
if (code == 0) return SCE_FONT_ERROR_NO_SUPPORT_CODE;
const FontState* state = TryGetState(fontHandle);
if (!state) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
const std::uint8_t glyphForm = detail ? detail->glyph_form : 0;
const std::uint8_t metricsForm = detail ? detail->metrics_form : 0;
const std::uint16_t formOptions = detail ? detail->form_options : 0;
const FontMemory* glyphMemory = detail ? detail->mem : nullptr;
if (detail && detail->id != GENERATE_GLYPH_DETAIL_ID) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((formOptions & static_cast<std::uint16_t>(~0x11u)) != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (glyphForm == 0 && metricsForm != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((glyphForm != 0 && metricsForm == 0) || glyphForm > 1 || metricsForm > 4) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (glyphMemory && glyphMemory->mem_kind != MEMORY_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER;
FontGlyphMetrics metrics{};
const int rc = GetCharGlyphMetrics(fontHandle, code, &metrics, false);
if (rc != SCE_FONT_OK) return rc;
auto* generated = new (std::nothrow) GeneratedGlyph();
if (!generated) return SCE_FONT_ERROR_ALLOCATION_FAILED;
generated->codepoint = code;
generated->metrics = metrics;
generated->glyph.magic = GLYPH_MAGIC;
generated->glyph.flags = formOptions;
generated->glyph.glyph_form = glyphForm;
generated->glyph.metrics_form = metricsForm;
generated->glyph.em_size = ClampToU16(state->scaleH);
generated->glyph.baseline = ClampToU16(metrics.Horizontal.bearingY);
generated->glyph.height_px = ClampToU16(metrics.height);
generated->glyph.origin_x = 0;
generated->glyph.origin_y = generated->glyph.baseline;
generated->glyph.scale_x = state->scaleW;
generated->glyph.base_scale = state->scaleH;
generated->glyph.memory = glyphMemory;
generated->owner = fontHandle;
generated->outline.outline_flags = generated->glyph.flags;
TrackGeneratedGlyph(&generated->glyph);
*pGlyph = &generated->glyph;
return SCE_FONT_OK;
}
int APS5_VABI sceFontDeleteGlyph(const FontMemory* memory, FontGlyph* pGlyph) {
(void)memory;
if (!pGlyph) return SCE_FONT_ERROR_INVALID_PARAMETER;
const FontGlyph glyph = *pGlyph;
if (!glyph || glyph->magic != GLYPH_MAGIC || !ForgetGeneratedGlyph(glyph)) return SCE_FONT_ERROR_INVALID_GLYPH;
delete reinterpret_cast<GeneratedGlyph*>(glyph);
*pGlyph = nullptr;
return SCE_FONT_OK;
}
int APS5_VABI sceFontGlyphGetGlyphForm(FontGlyph glyph) {
if (!glyph || glyph->magic != GLYPH_MAGIC) return SCE_FONT_ERROR_INVALID_GLYPH;
return glyph->glyph_form;
}
int APS5_VABI sceFontGlyphGetMetricsForm(FontGlyph glyph) {
if (!glyph || glyph->magic != GLYPH_MAGIC) return SCE_FONT_ERROR_INVALID_GLYPH;
return glyph->metrics_form;
}
int APS5_VABI sceFontGlyphGetScalePixel(FontGlyph glyph, float* w, float* h) {
if (!glyph || glyph->magic != GLYPH_MAGIC || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (w) *w = glyph->scale_x;
if (h) *h = glyph->base_scale;
return SCE_FONT_OK;
}
const FontGlyphMetrics* APS5_VABI sceFontGlyphRefersMetrics(FontGlyph glyph) {
auto* generated = TryGetGeneratedGlyph(glyph);
return generated ? &generated->metrics : nullptr;
}
const FontGlyphMetricsHorizontal* APS5_VABI sceFontGlyphRefersMetricsHorizontal(FontGlyph glyph) {
auto* generated = TryGetGeneratedGlyph(glyph);
if (!generated) return nullptr;
PopulateGlyphMetricVariants(*generated);
return &generated->metricsHorizontal;
}
const FontGlyphMetricsHorizontalAdvance* APS5_VABI sceFontGlyphRefersMetricsHorizontalAdvance(FontGlyph glyph) {
auto* generated = TryGetGeneratedGlyph(glyph);
if (!generated) return nullptr;
PopulateGlyphMetricVariants(*generated);
return &generated->metricsHorizontalAdvance;
}
const FontGlyphMetricsHorizontalX* APS5_VABI sceFontGlyphRefersMetricsHorizontalX(FontGlyph glyph) {
auto* generated = TryGetGeneratedGlyph(glyph);
if (!generated) return nullptr;
PopulateGlyphMetricVariants(*generated);
return &generated->metricsHorizontalX;
}
FontGlyphOutline* APS5_VABI sceFontGlyphRefersOutline(FontGlyph glyph) {
if (!glyph || glyph->magic != GLYPH_MAGIC || glyph->glyph_form != 1) return nullptr;
auto* generated = TryGetGeneratedGlyph(glyph);
if (!generated) return nullptr;
if (!generated->outlineInitialized && !BuildTrueOutline(*generated)) BuildBoundingOutline(*generated);
return &generated->outline;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,433 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstring>
#include <limits>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
std::mutex stateMutex;
std::unordered_map<FontHandle, std::unique_ptr<Font::FontState>> fontStates;
std::mutex glyphMutex;
std::unordered_set<FontGlyph> generatedGlyphs;
FT_Library HostFreeTypeLibrary() {
static const FT_Library library = [] {
FT_Library created = nullptr;
if (FT_Init_FreeType(&created) != 0) return static_cast<FT_Library>(nullptr);
return created;
}();
return library;
}
bool AcquireMagicLock(std::uint32_t& word, const std::uint16_t& magic, std::uint16_t expectedMagic, std::uint32_t& previous) {
for (;;) {
const std::uint32_t current = std::atomic_ref<std::uint32_t>(word).load(std::memory_order_acquire);
if (magic != expectedMagic) return false;
if (static_cast<std::int32_t>(current) >= 0) {
std::uint32_t expected = current;
if (std::atomic_ref<std::uint32_t>(word).compare_exchange_weak(expected, current | Font::LOCK_BIT, std::memory_order_acq_rel)) {
previous = current;
return true;
}
}
Font::Backoff();
}
}
void ReleaseWord(std::uint32_t& word, std::uint32_t previous) {
std::atomic_ref<std::uint32_t>(word).store(previous & ~Font::LOCK_BIT, std::memory_order_release);
}
}
Font::FontState::~FontState() {
if (face) FT_Done_Face(face);
}
void Font::Backoff() {
std::this_thread::sleep_for(std::chrono::microseconds(30));
}
std::uint32_t Font::AcquireWordLock(std::uint32_t& word) {
for (;;) {
const std::uint32_t current = std::atomic_ref<std::uint32_t>(word).load(std::memory_order_acquire);
if (static_cast<std::int32_t>(current) >= 0) {
std::uint32_t expected = current;
if (std::atomic_ref<std::uint32_t>(word).compare_exchange_weak(expected, current | LOCK_BIT, std::memory_order_acq_rel)) return current;
}
Backoff();
}
}
Font::FontHandleNative* Font::GetNativeFont(FontHandle handle) {
return reinterpret_cast<FontHandleNative*>(handle);
}
bool Font::AcquireLibraryLock(FontLibNative* library, std::uint32_t& previous) {
return library && AcquireMagicLock(library->lock_word, library->magic, LIBRARY_MAGIC, previous);
}
void Font::ReleaseLibraryLock(FontLibNative* library, std::uint32_t previous) {
if (library) ReleaseWord(library->lock_word, previous);
}
bool Font::AcquireFontLock(FontHandleNative* font, std::uint32_t& previous) {
return font && AcquireMagicLock(font->lock_word, font->magic, HANDLE_MAGIC, previous);
}
void Font::ReleaseFontLock(FontHandleNative* font, std::uint32_t previous) {
if (font) ReleaseWord(font->lock_word, previous);
}
bool Font::AcquireCachedStyleLock(FontHandleNative* font, std::uint32_t& previous) {
return font && AcquireMagicLock(font->cached_style.cache_lock_word, font->magic, HANDLE_MAGIC, previous);
}
void Font::ReleaseCachedStyleLock(FontHandleNative* font, std::uint32_t previous) {
if (font) ReleaseWord(font->cached_style.cache_lock_word, previous);
}
namespace {
FontHandle* AcquireFontList(Font::FontLibNative* library) {
auto* const busy = reinterpret_cast<FontHandle*>(std::numeric_limits<std::uintptr_t>::max());
std::atomic_ref<FontHandle*> slot(library->list_head_ptr);
for (;;) {
FontHandle* current = slot.load(std::memory_order_acquire);
if (current != busy && slot.compare_exchange_weak(current, busy, std::memory_order_acq_rel)) return current;
Font::Backoff();
}
}
void ReleaseFontList(Font::FontLibNative* library, FontHandle* head) {
std::atomic_ref<FontHandle*>(library->list_head_ptr).store(head, std::memory_order_release);
}
}
void Font::LinkFontToLibrary(FontLibNative* library, FontHandle handle) {
FontHandle* head = AcquireFontList(library);
if (head) {
const FontHandle oldHead = *head;
auto* font = GetNativeFont(handle);
font->prevFont = nullptr;
font->nextFont = oldHead;
if (oldHead) GetNativeFont(oldHead)->prevFont = handle;
*head = handle;
}
ReleaseFontList(library, head);
}
void Font::UnlinkFontFromLibrary(FontLibNative* library, FontHandle handle) {
FontHandle* head = AcquireFontList(library);
auto* font = GetNativeFont(handle);
if (head) {
if (font->prevFont) {
GetNativeFont(font->prevFont)->nextFont = font->nextFont;
} else if (*head == handle) {
*head = font->nextFont;
}
if (font->nextFont) GetNativeFont(font->nextFont)->prevFont = font->prevFont;
}
font->prevFont = nullptr;
font->nextFont = nullptr;
ReleaseFontList(library, head);
}
std::uint32_t* Font::EntryLockWord(FontCtxEntry* entry, std::uint32_t modeLow) {
if (modeLow == 3) return &entry->lock_mode3;
if (modeLow == 1) return &entry->lock_mode1;
return &entry->lock_mode2;
}
void** Font::EntryObjectSlot(FontCtxEntry* entry, std::uint32_t modeLow) {
if (modeLow == 3) return &entry->obj_mode3;
if (modeLow == 1) return &entry->obj_mode1;
return &entry->obj_mode2;
}
Font::FontCtxEntry* Font::AcquireFontCtxEntry(void* ctx, std::uint32_t index, std::uint32_t modeLow, FontObj** outObject, std::uint32_t* outLockWord) {
if (!ctx || !outObject || !outLockWord) return nullptr;
auto* header = static_cast<FontCtxHeader*>(ctx);
if (index >= header->max_entries || !header->base) {
*outLockWord = 0;
*outObject = nullptr;
return nullptr;
}
auto* entry = static_cast<FontCtxEntry*>(header->base) + index;
if (modeLow != 1 && modeLow != 2 && modeLow != 3) {
*outLockWord = 0;
*outObject = nullptr;
return entry;
}
*outLockWord = AcquireWordLock(*EntryLockWord(entry, modeLow)) | LOCK_BIT;
*outObject = static_cast<FontObj*>(*EntryObjectSlot(entry, modeLow));
return entry;
}
void Font::ReleaseFontCtxEntryLock(FontCtxEntry* entry, std::uint32_t modeLow, std::uint32_t lockWord) {
if (!entry) return;
if ((lockWord & COUNT_MASK) == 0) *EntryObjectSlot(entry, modeLow) = nullptr;
if ((lockWord & COUNT_MASK) == 0 && !entry->obj_mode1 && !entry->obj_mode3 && !entry->obj_mode2) {
entry->unique_id = 0;
entry->active = 0;
}
ReleaseWord(*EntryLockWord(entry, modeLow), lockWord);
}
Font::FontObj* Font::FindSubFont(FontObj* head, std::uint32_t subFontIndex) {
for (auto* node = head; node; node = node->next) {
if (node->sub_font_index == subFontIndex) return node;
}
return nullptr;
}
void Font::ReleaseFontObjectsForHandle(FontHandleNative* font) {
auto* library = static_cast<FontLibNative*>(font->library);
if (!library || library->magic != LIBRARY_MAGIC || !library->sys_driver || !library->sys_driver->close) return;
const std::uint32_t fontId = font->open_info.ctx_entry_index;
if (static_cast<std::int32_t>(fontId) < 0) return;
const std::uint32_t modeLow = font->flags & 0x0Fu;
FontObj* head = nullptr;
std::uint32_t lockWord = 0;
auto* entry = AcquireFontCtxEntry(library->external_fonts_ctx, fontId, modeLow, &head, &lockWord);
if (!entry) return;
FontObj* match = FindSubFont(head, font->open_info.sub_font_index);
const std::uint32_t openFlag = lockWord & OPEN_BIT;
const std::uint32_t countBits = lockWord & COUNT_MASK;
if (openFlag != 0 && countBits != 0 && match) {
const std::uint32_t refcount = match->refcount;
const std::uint32_t decremented = lockWord - 1;
lockWord = decremented;
if (refcount == 1) {
FontObj* next = match->next;
FontObj* prev = match->prev;
library->sys_driver->close(match, 0);
if (!prev) *EntryObjectSlot(entry, modeLow) = next;
} else if (refcount != 0) {
match->refcount = refcount - 1;
}
if (countBits == 1) lockWord = ~openFlag & decremented;
}
ReleaseFontCtxEntryLock(entry, modeLow, lockWord);
}
Font::FontState* Font::TryGetState(FontHandle handle) {
if (!handle) return nullptr;
std::lock_guard lock(stateMutex);
const auto it = fontStates.find(handle);
return it == fontStates.end() ? nullptr : it->second.get();
}
Font::FontState& Font::ResetState(FontHandle handle) {
std::lock_guard lock(stateMutex);
auto& slot = fontStates[handle];
slot = std::make_unique<FontState>();
return *slot;
}
void Font::RemoveState(FontHandle handle) {
if (!handle) return;
std::lock_guard lock(stateMutex);
fontStates.erase(handle);
}
void Font::LoadStateFace(FontState& state, std::vector<unsigned char> bytes, std::uint32_t subFontIndex) {
if (state.face) {
FT_Done_Face(state.face);
state.face = nullptr;
}
state.faceData = std::move(bytes);
const FT_Library library = HostFreeTypeLibrary();
if (!library || state.faceData.empty()) return;
FT_Face face = nullptr;
if (FT_New_Memory_Face(library, state.faceData.data(), static_cast<FT_Long>(state.faceData.size()), static_cast<FT_Long>(subFontIndex), &face) != 0) return;
FT_Select_Charmap(face, FT_ENCODING_UNICODE);
state.face = face;
}
void Font::TrackGeneratedGlyph(FontGlyph glyph) {
std::lock_guard lock(glyphMutex);
generatedGlyphs.insert(glyph);
}
bool Font::ForgetGeneratedGlyph(FontGlyph glyph) {
std::lock_guard lock(glyphMutex);
return generatedGlyphs.erase(glyph) > 0;
}
Font::GeneratedGlyph* Font::TryGetGeneratedGlyph(FontGlyph glyph) {
if (!glyph || glyph->magic != GLYPH_MAGIC) return nullptr;
std::lock_guard lock(glyphMutex);
if (!generatedGlyphs.contains(glyph)) return nullptr;
return reinterpret_cast<GeneratedGlyph*>(glyph);
}
void Font::PopulateGlyphMetricVariants(GeneratedGlyph& glyph) {
if (glyph.metricsInitialized) return;
glyph.metricsHorizontal.width = glyph.metrics.width;
glyph.metricsHorizontal.height = glyph.metrics.height;
glyph.metricsHorizontal.horizontal.bearing_x = glyph.metrics.Horizontal.bearingX;
glyph.metricsHorizontal.horizontal.bearing_y = glyph.metrics.Horizontal.bearingY;
glyph.metricsHorizontal.horizontal.advance = glyph.metrics.Horizontal.advance;
glyph.metricsHorizontalX.width = glyph.metrics.width;
glyph.metricsHorizontalX.horizontal.bearing_x = glyph.metrics.Horizontal.bearingX;
glyph.metricsHorizontalX.horizontal.advance = glyph.metrics.Horizontal.advance;
glyph.metricsHorizontalAdvance.horizontal.advance = glyph.metrics.Horizontal.advance;
glyph.metricsInitialized = true;
}
void Font::BuildBoundingOutline(GeneratedGlyph& glyph) {
const float left = glyph.metrics.Horizontal.bearingX;
const float top = glyph.metrics.Horizontal.bearingY;
const float right = left + glyph.metrics.width;
const float bottom = top - glyph.metrics.height;
glyph.outlinePoints = {{left, top}, {right, top}, {right, bottom}, {left, bottom}};
glyph.outlineTags.assign(glyph.outlinePoints.size(), 1);
glyph.outlineContours = {static_cast<std::uint16_t>(glyph.outlinePoints.size() - 1)};
glyph.outline.points_ptr = glyph.outlinePoints.data();
glyph.outline.tags_ptr = glyph.outlineTags.data();
glyph.outline.contour_end_idx = glyph.outlineContours.data();
glyph.outline.points_cnt = static_cast<std::int16_t>(glyph.outlinePoints.size());
glyph.outline.contours_cnt = static_cast<std::int16_t>(glyph.outlineContours.size());
glyph.outlineInitialized = true;
}
bool Font::ValidStyleFrame(const FontStyleFrame* frame) {
return frame && frame->magic == STYLE_FRAME_MAGIC;
}
int Font::StyleStateSetScalePixel(StyleStateBlock* style, float w, float h) {
if (!style) return 0;
if (style->scale_w == w && style->scale_h == h && style->scale_unit == 0) return 0;
style->scale_unit = 0;
style->scale_w = w;
style->scale_h = h;
return 1;
}
int Font::StyleStateSetScalePoint(StyleStateBlock* style, float w, float h) {
if (!style) return 0;
if (style->scale_w == w && style->scale_h == h && style->scale_unit == 1) return 0;
style->scale_unit = 1;
style->scale_w = w;
style->scale_h = h;
return 1;
}
int Font::StyleStateGetScalePixel(const StyleStateBlock* style, float* w, float* h) {
if (!style || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER;
const bool points = style->scale_unit != 0;
if (w) {
float value = style->scale_w;
if (points && style->dpi_x != 0) value *= static_cast<float>(style->dpi_x) / POINTS_PER_INCH;
*w = value;
}
if (h) {
float value = style->scale_h;
if (points && style->dpi_y != 0) value *= static_cast<float>(style->dpi_y) / POINTS_PER_INCH;
*h = value;
}
return SCE_FONT_OK;
}
int Font::StyleStateGetScalePoint(const StyleStateBlock* style, float* w, float* h) {
if (!style || (!w && !h)) return SCE_FONT_ERROR_INVALID_PARAMETER;
const bool pixels = style->scale_unit == 0;
if (w) {
float value = style->scale_w;
if (pixels && style->dpi_x != 0) value *= POINTS_PER_INCH / static_cast<float>(style->dpi_x);
*w = value;
}
if (h) {
float value = style->scale_h;
if (pixels && style->dpi_y != 0) value *= POINTS_PER_INCH / static_cast<float>(style->dpi_y);
*h = value;
}
return SCE_FONT_OK;
}
int Font::StyleStateSetDpi(StyleStateBlock* style, std::uint32_t hDpi, std::uint32_t vDpi) {
if (!style) return 0;
if (hDpi == 0) hDpi = 0x48;
if (vDpi == 0) vDpi = 0x48;
if (style->dpi_x == hDpi && style->dpi_y == vDpi) return 0;
style->dpi_x = hDpi;
style->dpi_y = vDpi;
return 1;
}
int Font::StyleStateSetSlantRatio(StyleStateBlock* style, float slantRatio) {
if (!style || style->slant_ratio == slantRatio) return 0;
style->slant_ratio = std::clamp(slantRatio, -1.0f, 1.0f);
return 1;
}
int Font::StyleStateGetSlantRatio(const StyleStateBlock* style, float* slantRatio) {
if (!style || !slantRatio) return SCE_FONT_ERROR_INVALID_PARAMETER;
*slantRatio = style->slant_ratio;
return SCE_FONT_OK;
}
int Font::StyleStateSetWeightScale(StyleStateBlock* style, float weightXScale, float weightYScale) {
if (!style) return 0;
const float deltaX = std::clamp(weightXScale - 1.0f, -0.04f, 0.04f);
const float deltaY = std::clamp(weightYScale - 1.0f, -0.04f, 0.04f);
bool changed = false;
if (style->effect_weight_x != deltaX) {
style->effect_weight_x = deltaX;
changed = true;
}
if (style->effect_weight_y != deltaY) {
style->effect_weight_y = deltaY;
changed = true;
}
return changed ? 1 : 0;
}
int Font::StyleStateGetWeightScale(const StyleStateBlock* style, float* weightXScale, float* weightYScale, std::uint32_t* mode) {
if (!weightXScale && !weightYScale && !mode) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!style) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (weightXScale) *weightXScale = style->effect_weight_x + 1.0f;
if (weightYScale) *weightYScale = style->effect_weight_y + 1.0f;
if (mode) *mode = 0;
return SCE_FONT_OK;
}
std::uint8_t Font::CachedStyleCacheFlags(const CachedStyle& style) {
return static_cast<std::uint8_t>(style.cache_flags_and_direction & 0xFFu);
}
void Font::CachedStyleSetCacheFlags(CachedStyle& style, std::uint8_t flags) {
style.cache_flags_and_direction = (style.cache_flags_and_direction & 0xFFFFFF00u) | flags;
}
void Font::CachedStyleSetDirectionWord(CachedStyle& style, std::uint16_t word) {
style.cache_flags_and_direction = (style.cache_flags_and_direction & 0x0000FFFFu) | (static_cast<std::uint32_t>(word) << 16);
}
float Font::CachedStyleGetScalar(const CachedStyle& style) {
float value;
std::memcpy(&value, &style.cached_scalar_bits, sizeof(value));
return value;
}
void Font::CachedStyleSetScalar(CachedStyle& style, float value) {
std::memcpy(&style.cached_scalar_bits, &value, sizeof(value));
}
void Font::ClearRenderOutputs(FontGlyphMetrics* metrics, FontRenderOutput* result) {
if (metrics) *metrics = {};
if (result) *result = {};
}

View File

@ -0,0 +1,303 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
template<typename Setter>
int UpdateFontStyle(FontHandle fontHandle, Setter&& setter) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
if (setter(font) != 0) font->cached_style.layout_cache_state = 0;
ReleaseFontLock(font, fontLock);
return SCE_FONT_OK;
}
template<typename Setter>
int UpdateRenderStyle(FontHandle fontHandle, Setter&& setter) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t cachedLock = 0;
if (!font || font->magic != HANDLE_MAGIC || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
int rc = SCE_FONT_ERROR_NOT_BOUND_RENDERER;
if (font->renderer) rc = setter(font);
ReleaseCachedStyleLock(font, cachedLock);
return rc;
}
template<typename Getter>
int ReadRenderStyle(FontHandle fontHandle, bool checkMagic, Getter&& getter) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t cachedLock = 0;
if (!font || (checkMagic && font->magic != HANDLE_MAGIC) || !AcquireCachedStyleLock(font, cachedLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
const int rc = font->renderer ? getter(font) : SCE_FONT_ERROR_NOT_BOUND_RENDERER;
ReleaseCachedStyleLock(font, cachedLock);
return rc;
}
template<typename Getter>
int ReadFontStyle(FontHandle fontHandle, bool checkMagic, Getter&& getter) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || (checkMagic && font->magic != HANDLE_MAGIC) || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
const int rc = getter(font);
ReleaseFontLock(font, fontLock);
return rc;
}
void ResetScaleOutputs(float* w, float* h) {
if (w) *w = 0.0f;
if (h) *h = 0.0f;
}
void ResetWeightOutputs(float* weightXScale, float* weightYScale, std::uint32_t* mode) {
if (weightXScale) *weightXScale = 1.0f;
if (weightYScale) *weightYScale = 1.0f;
if (mode) *mode = 0;
}
float ClampWeightDelta(float scale) {
return std::clamp(scale - 1.0f, -0.04f, 0.04f);
}
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontSetScalePixel(FontHandle fontHandle, float w, float h) {
return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) {
const int changed = StyleStateSetScalePixel(&font->style, w, h);
if (FontState* state = TryGetState(fontHandle)) {
state->scaleW = w;
state->scaleH = h;
}
return changed;
});
}
int APS5_VABI sceFontSetScalePoint(FontHandle fontHandle, float w, float h) {
return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetScalePoint(&font->style, w, h); });
}
int APS5_VABI sceFontSetResolutionDpi(FontHandle fontHandle, std::uint32_t hDpi, std::uint32_t vDpi) {
auto* font = GetNativeFont(fontHandle);
std::uint32_t fontLock = 0;
if (!font || !AcquireFontLock(font, fontLock)) return SCE_FONT_ERROR_INVALID_FONT_HANDLE;
if (StyleStateSetDpi(&font->style, hDpi, vDpi) != 0) font->cached_style.layout_cache_state = 0;
ReleaseFontLock(font, fontLock);
return SCE_FONT_OK;
}
int APS5_VABI sceFontSetEffectSlant(FontHandle fontHandle, float slantRatio) {
return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetSlantRatio(&font->style, slantRatio); });
}
int APS5_VABI sceFontSetEffectWeight(FontHandle fontHandle, float weightXScale, float weightYScale, std::uint32_t mode) {
if (mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
return UpdateFontStyle(fontHandle, [&](FontHandleNative* font) { return StyleStateSetWeightScale(&font->style, weightXScale, weightYScale); });
}
int APS5_VABI sceFontGetScalePixel(FontHandle fontHandle, float* w, float* h) {
const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetScalePixel(&font->style, w, h); });
if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h);
return rc;
}
int APS5_VABI sceFontGetScalePoint(FontHandle fontHandle, float* w, float* h) {
const int rc = ReadFontStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePoint(&font->style, w, h); });
if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h);
return rc;
}
int APS5_VABI sceFontGetEffectSlant(FontHandle fontHandle, float* slantRatio) {
const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetSlantRatio(&font->style, slantRatio); });
if (rc != SCE_FONT_OK && slantRatio) *slantRatio = 0.0f;
return rc;
}
int APS5_VABI sceFontGetEffectWeight(FontHandle fontHandle, float* weightXScale, float* weightYScale, std::uint32_t* mode) {
const int rc = ReadFontStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetWeightScale(&font->style, weightXScale, weightYScale, mode); });
if (rc != SCE_FONT_OK) ResetWeightOutputs(weightXScale, weightYScale, mode);
return rc;
}
int APS5_VABI sceFontSetupRenderScalePixel(FontHandle fontHandle, float w, float h) {
return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) {
if (StyleStateSetScalePixel(&font->cached_style.state, w, h) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u;
return SCE_FONT_OK;
});
}
int APS5_VABI sceFontSetupRenderScalePoint(FontHandle fontHandle, float w, float h) {
return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) {
if (StyleStateSetScalePoint(&font->cached_style.state, w, h) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u;
return SCE_FONT_OK;
});
}
int APS5_VABI sceFontSetupRenderEffectSlant(FontHandle fontHandle, float slantRatio) {
return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) {
if (StyleStateSetSlantRatio(&font->cached_style.state, slantRatio) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u;
return SCE_FONT_OK;
});
}
int APS5_VABI sceFontSetupRenderEffectWeight(FontHandle fontHandle, float weightXScale, float weightYScale, std::uint32_t mode) {
return UpdateRenderStyle(fontHandle, [&](FontHandleNative* font) {
if (mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (StyleStateSetWeightScale(&font->cached_style.state, weightXScale, weightYScale) != 0) font->cached_style.cache_flags_and_direction &= 0xFFFF0000u;
return SCE_FONT_OK;
});
}
int APS5_VABI sceFontGetRenderScalePixel(FontHandle fontHandle, float* w, float* h) {
const int rc = ReadRenderStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePixel(&font->cached_style.state, w, h); });
if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h);
return rc;
}
int APS5_VABI sceFontGetRenderScalePoint(FontHandle fontHandle, float* w, float* h) {
const int rc = ReadRenderStyle(fontHandle, false, [&](FontHandleNative* font) { return StyleStateGetScalePoint(&font->cached_style.state, w, h); });
if (rc != SCE_FONT_OK) ResetScaleOutputs(w, h);
return rc;
}
int APS5_VABI sceFontGetRenderEffectSlant(FontHandle fontHandle, float* slantRatio) {
const int rc = ReadRenderStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetSlantRatio(&font->cached_style.state, slantRatio); });
if (rc != SCE_FONT_OK && slantRatio) *slantRatio = 0.0f;
return rc;
}
int APS5_VABI sceFontGetRenderEffectWeight(FontHandle fontHandle, float* weightXScale, float* weightYScale, std::uint32_t* mode) {
const int rc = ReadRenderStyle(fontHandle, true, [&](FontHandleNative* font) { return StyleStateGetWeightScale(&font->cached_style.state, weightXScale, weightYScale, mode); });
if (rc != SCE_FONT_OK) ResetWeightOutputs(weightXScale, weightYScale, mode);
return rc;
}
int APS5_VABI sceFontStyleFrameInit(FontStyleFrame* styleFrame) {
if (!styleFrame) return SCE_FONT_ERROR_INVALID_PARAMETER;
std::memset(&styleFrame->scaleUnit, 0, 0x20);
std::memset(&styleFrame->layout_cache_state, 0, 0x20);
std::memset(&styleFrame->layout_cache_bytes[0x08], 0, 0x20);
styleFrame->magic = STYLE_FRAME_MAGIC;
styleFrame->flags1 = 0;
styleFrame->flags2 = 0;
styleFrame->hDpi = 0x48;
styleFrame->vDpi = 0x48;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameSetScalePixel(FontStyleFrame* styleFrame, float w, float h) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->scaleUnit = 0;
styleFrame->scalePixelW = w;
styleFrame->scalePixelH = h;
styleFrame->flags1 |= STYLE_FRAME_FLAG_SCALE;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameSetScalePoint(FontStyleFrame* styleFrame, float w, float h) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->scaleUnit = 1;
styleFrame->scalePixelW = w;
styleFrame->scalePixelH = h;
styleFrame->flags1 |= STYLE_FRAME_FLAG_SCALE;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameSetResolutionDpi(FontStyleFrame* styleFrame, std::uint32_t hDpi, std::uint32_t vDpi) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->hDpi = hDpi == 0 ? 0x48 : hDpi;
styleFrame->vDpi = vDpi == 0 ? 0x48 : vDpi;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameSetEffectSlant(FontStyleFrame* styleFrame, float slantRatio) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->slantRatio = std::clamp(slantRatio, -1.0f, 1.0f);
styleFrame->flags1 |= STYLE_FRAME_FLAG_SLANT;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameSetEffectWeight(FontStyleFrame* styleFrame, float weightXScale, float weightYScale, std::uint32_t mode) {
if (!ValidStyleFrame(styleFrame) || mode != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->effectWeightX = ClampWeightDelta(weightXScale);
styleFrame->effectWeightY = ClampWeightDelta(weightYScale);
styleFrame->flags1 |= STYLE_FRAME_FLAG_WEIGHT;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameUnsetScale(FontStyleFrame* styleFrame) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->flags1 &= static_cast<std::uint8_t>(~STYLE_FRAME_FLAG_SCALE);
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameUnsetEffectSlant(FontStyleFrame* styleFrame) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->flags1 &= static_cast<std::uint8_t>(~STYLE_FRAME_FLAG_SLANT);
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameUnsetEffectWeight(FontStyleFrame* styleFrame) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
styleFrame->flags1 &= static_cast<std::uint8_t>(~STYLE_FRAME_FLAG_WEIGHT);
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameGetScalePixel(const FontStyleFrame* styleFrame, float* w, float* h) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER;
if (!w && !h) return SCE_FONT_ERROR_INVALID_PARAMETER;
const bool points = styleFrame->scaleUnit != 0;
if (w) *w = points && styleFrame->hDpi != 0 ? styleFrame->scalePixelW * (static_cast<float>(styleFrame->hDpi) / POINTS_PER_INCH) : styleFrame->scalePixelW;
if (h) *h = points && styleFrame->vDpi != 0 ? styleFrame->scalePixelH * (static_cast<float>(styleFrame->vDpi) / POINTS_PER_INCH) : styleFrame->scalePixelH;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameGetScalePoint(const FontStyleFrame* styleFrame, float* w, float* h) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SCALE) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER;
if (!w && !h) return SCE_FONT_ERROR_INVALID_PARAMETER;
const bool pixels = styleFrame->scaleUnit == 0;
if (w) *w = pixels && styleFrame->hDpi != 0 ? styleFrame->scalePixelW * (POINTS_PER_INCH / static_cast<float>(styleFrame->hDpi)) : styleFrame->scalePixelW;
if (h) *h = pixels && styleFrame->vDpi != 0 ? styleFrame->scalePixelH * (POINTS_PER_INCH / static_cast<float>(styleFrame->vDpi)) : styleFrame->scalePixelH;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameGetResolutionDpi(const FontStyleFrame* styleFrame, std::uint32_t* hDpi, std::uint32_t* vDpi) {
if (!ValidStyleFrame(styleFrame) || (!hDpi && !vDpi)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (hDpi) *hDpi = styleFrame->hDpi;
if (vDpi) *vDpi = styleFrame->vDpi;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameGetEffectSlant(const FontStyleFrame* styleFrame, float* slantRatio) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((styleFrame->flags1 & STYLE_FRAME_FLAG_SLANT) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER;
if (!slantRatio) return SCE_FONT_ERROR_INVALID_PARAMETER;
*slantRatio = styleFrame->slantRatio;
return SCE_FONT_OK;
}
int APS5_VABI sceFontStyleFrameGetEffectWeight(const FontStyleFrame* styleFrame, float* weightXScale, float* weightYScale, std::uint32_t* mode) {
if (!ValidStyleFrame(styleFrame)) return SCE_FONT_ERROR_INVALID_PARAMETER;
if ((styleFrame->flags1 & STYLE_FRAME_FLAG_WEIGHT) == 0) return SCE_FONT_ERROR_UNSET_PARAMETER;
if (!weightXScale && !weightYScale && !mode) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (weightXScale) *weightXScale = styleFrame->effectWeightX + 1.0f;
if (weightYScale) *weightYScale = styleFrame->effectWeightY + 1.0f;
if (mode) *mode = 0;
return SCE_FONT_OK;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,633 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstring>
#include <new>
#include <vector>
#include "prx/libSceFont/include/FontInternal.hpp"
namespace {
using namespace Font;
constexpr std::uint16_t TEXT_SOURCE_MAGIC = 0x0F04;
constexpr std::uint16_t FONT_STRING_MAGIC = 0x0F05;
constexpr std::uint16_t FONT_WRITING_MAGIC = 0x0F06;
constexpr std::uint16_t CREATE_STRING_DETAIL_ID = 0x0FD4;
constexpr std::uint32_t WRITING_FORM_HORIZONTAL = 0x10;
constexpr std::uint32_t WRITING_FORM_HORIZONTAL_LTR = 0x12;
constexpr std::int32_t PARSER_RESULT_ERROR = -1;
constexpr std::int32_t PARSER_RESULT_TERMINATE = 0;
constexpr std::int32_t PARSER_RESULT_FONT_CODE = 1;
constexpr std::size_t MAX_PARSED_CHARACTERS = 4096;
constexpr std::int32_t WRITING_MASK_NONE = 0;
constexpr std::int32_t WRITING_MASK_FORMAT_CHARACTERS = 1;
constexpr std::uint64_t CHARACTER_FLAG_FORMAT = 1ull << 42;
struct CharacterStorage {
std::atomic<std::uint32_t> refCount{1};
std::vector<FontTextCharacter> characters;
};
struct FontStringData {
std::uint16_t magic = FONT_STRING_MAGIC;
std::uint8_t flags = 0;
std::uint8_t reserved0 = 0;
std::uint32_t writingForm = WRITING_FORM_HORIZONTAL;
const FontMemory* memory = nullptr;
FontTextSource* source = nullptr;
FontHandle defaultFont = nullptr;
std::uint32_t terminateCode = 0;
void* terminateOrder = nullptr;
std::uint32_t textCount = 0;
std::uint32_t renderCount = 0;
CharacterStorage* storage = nullptr;
};
struct FontWritingData {
std::uint16_t magic = FONT_WRITING_MAGIC;
std::uint16_t reserved0 = 0;
std::uint32_t writingForm = WRITING_FORM_HORIZONTAL;
FontString string = nullptr;
CharacterStorage* storage = nullptr;
FontHandle defaultFont = nullptr;
std::size_t nextIndex = 0;
std::size_t currentIndex = 0;
float penX = 0.0f;
float penY = 0.0f;
bool hasAggregateMetrics = false;
bool hasCurrentStep = false;
bool maskFormatCharacters = true;
bool ownsStorage = false;
FontWritingStep step{};
FontWritingMetrics metrics{};
FontGlyphMetrics rawGlyphMetrics{};
bool hasRawGlyphMetrics = false;
};
static_assert(sizeof(FontWritingData) <= sizeof(FontWriting));
FontStringData* GetStringData(FontString fontString) {
auto* data = reinterpret_cast<FontStringData*>(fontString);
return data && data->magic == FONT_STRING_MAGIC ? data : nullptr;
}
FontWritingData* GetWritingData(FontWriting* fontWriting) {
if (!fontWriting) return nullptr;
auto* data = reinterpret_cast<FontWritingData*>(fontWriting->systemUse);
return data->magic == FONT_WRITING_MAGIC ? data : nullptr;
}
void ReleaseStorage(CharacterStorage*& storage) {
if (!storage) return;
if (storage->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1) delete storage;
storage = nullptr;
}
void ReleaseWritingStorage(FontWritingData& writing) {
if (!writing.ownsStorage) return;
ReleaseStorage(writing.storage);
writing.ownsStorage = false;
}
std::uint16_t TextSourceMagic(const FontTextSource* source) {
return static_cast<std::uint16_t>(source->systemUse0 & 0xFFFFu);
}
bool IsWhitespaceCode(std::uint32_t code) {
switch (code) {
case 0x0009: case 0x000A: case 0x000B: case 0x000C: case 0x000D: case 0x0020: case 0x0085:
case 0x00A0: case 0x1680: case 0x2028: case 0x2029: case 0x202F: case 0x205F: case 0x3000:
return true;
default:
return code >= 0x2000 && code <= 0x200A;
}
}
bool IsFormatCode(std::uint32_t code) {
switch (code) {
case 0x00AD: case 0x061C: case 0x200B: case 0x200C: case 0x200D: case 0x200E: case 0x200F:
case 0x2060: case 0x2061: case 0x2062: case 0x2063: case 0x2064: case 0x2066: case 0x2067:
case 0x2068: case 0x2069: case 0xFE0E: case 0xFE0F: case 0xFEFF:
return true;
default:
return false;
}
}
FontTextCharacter MakeCharacter(FontHandle font, std::uint32_t code, void* textOrder) {
FontTextCharacter character{};
character.textOrder = textOrder;
character.font = font;
character.characterCode = code;
character.clusterSpan = 1;
character.flags = static_cast<std::uint64_t>(IsWhitespaceCode(code) ? 0x0E : 0) << 8;
if (IsFormatCode(code)) character.flags |= CHARACTER_FLAG_FORMAT;
return character;
}
void LinkCharacters(std::vector<FontTextCharacter>& characters) {
for (std::size_t i = 0; i < characters.size(); ++i) {
characters[i].prev = i == 0 ? nullptr : &characters[i - 1];
characters[i].next = i + 1 < characters.size() ? &characters[i + 1] : nullptr;
}
}
FontTextCharacter* TextCharacters(FontStringData* data) {
return data && data->storage && !data->storage->characters.empty() ? data->storage->characters.data() : nullptr;
}
std::uint32_t LevelOf(const FontTextCharacter* character) {
return static_cast<std::uint32_t>((character->flags >> 24) & 0xFFu);
}
void FillWritingMetricsFromGlyph(FontWritingData& writing, const FontGlyphMetrics& glyphMetrics) {
writing.rawGlyphMetrics = glyphMetrics;
writing.hasRawGlyphMetrics = true;
writing.step.advanceX = glyphMetrics.Horizontal.advance;
writing.step.advanceY = glyphMetrics.Vertical.advance;
writing.step.GlyphMetrics = glyphMetrics;
writing.step.Positioning.x = writing.step.x;
writing.step.Positioning.y = writing.step.y;
writing.step.GlyphMetrics.width = writing.step.x;
writing.step.GlyphMetrics.height = writing.step.y;
}
bool PopulateWritingStep(FontWritingData& writing, std::size_t index) {
if (!writing.storage || index >= writing.storage->characters.size()) return false;
const FontTextCharacter& character = writing.storage->characters[index];
const FontHandle font = character.font ? character.font : writing.defaultFont;
writing.step.x = writing.penX;
writing.step.y = writing.penY;
writing.step.advanceX = 0.0f;
writing.step.advanceY = 0.0f;
writing.step.font = font;
writing.step.Profile = {};
writing.step.Profile.characterCount = 1;
writing.step.glyphCode = character.characterCode;
writing.step.Positioning.x = 0.0f;
writing.step.Positioning.y = 0.0f;
writing.step.GlyphMetrics = {};
writing.rawGlyphMetrics = {};
writing.hasRawGlyphMetrics = false;
if ((character.flags & CHARACTER_FLAG_FORMAT) != 0 && writing.maskFormatCharacters) writing.step.Profile.invisibleGlyph = 1;
if (font && character.characterCode != 0) {
FontGlyphMetrics glyphMetrics{};
int rc = GetCharGlyphMetrics(font, character.characterCode, &glyphMetrics, true);
if (rc != SCE_FONT_OK) rc = GetCharGlyphMetrics(font, character.characterCode, &glyphMetrics, false);
if (rc == SCE_FONT_OK) FillWritingMetricsFromGlyph(writing, glyphMetrics);
}
writing.penX += writing.step.advanceX;
writing.penY += writing.step.advanceY;
return true;
}
void AccumulateWritingMetrics(FontWritingData& writing) {
const FontWritingMetrics previous = writing.metrics;
const FontGlyphMetrics& glyphMetrics = writing.hasRawGlyphMetrics ? writing.rawGlyphMetrics : writing.step.GlyphMetrics;
const float left = writing.step.x + glyphMetrics.Horizontal.bearingX;
const float right = left + glyphMetrics.width;
const float top = writing.step.y - glyphMetrics.Horizontal.bearingY;
const float bottom = top + glyphMetrics.height;
writing.metrics.advanceX = writing.step.x + writing.step.advanceX;
writing.metrics.advanceY = writing.step.y + writing.step.advanceY;
if (!writing.hasAggregateMetrics) {
writing.metrics.Extent = {top, bottom, left, right};
writing.hasAggregateMetrics = true;
return;
}
writing.metrics.Extent.left = std::min(previous.Extent.left, left);
writing.metrics.Extent.right = std::max(previous.Extent.right, right);
writing.metrics.Extent.top = std::min(previous.Extent.top, top);
writing.metrics.Extent.bottom = std::max(previous.Extent.bottom, bottom);
}
}
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontTextSourceInit(FontTextSource* textSource, const void* textAddress, std::uint32_t textSizeByte, FontTextParseFunction textParser, void* textObject) {
if (!textSource) return SCE_FONT_ERROR_INVALID_PARAMETER;
std::memset(textSource, 0, sizeof(*textSource));
if (!textParser) return SCE_FONT_ERROR_INVALID_PARAMETER;
const void* end = textSizeByte != 0 ? static_cast<const std::uint8_t*>(textAddress) + textSizeByte : nullptr;
textSource->systemUse0 = (static_cast<std::uint64_t>(WRITING_FORM_HORIZONTAL) << 32) | TEXT_SOURCE_MAGIC;
textSource->start = textAddress;
textSource->end = end;
textSource->current = textAddress;
textSource->textParser = textParser;
textSource->textObject = textObject;
textSource->defaultFont = nullptr;
textSource->systemUse[0] = const_cast<void*>(textAddress);
textSource->systemUse[1] = const_cast<void*>(end);
textSource->systemUse[2] = reinterpret_cast<void*>(textParser);
textSource->systemUse[3] = textObject;
textSource->systemUse[4] = nullptr;
return SCE_FONT_OK;
}
int APS5_VABI sceFontTextSourceRewind(FontTextSource* textSource) {
if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER;
textSource->start = textSource->systemUse[0];
textSource->end = textSource->systemUse[1];
textSource->current = textSource->systemUse[0];
textSource->textParser = reinterpret_cast<FontTextParseFunction>(textSource->systemUse[2]);
textSource->textObject = textSource->systemUse[3];
textSource->defaultFont = static_cast<FontHandle>(textSource->systemUse[4]);
return SCE_FONT_OK;
}
int APS5_VABI sceFontTextSourceSetDefaultFont(FontTextSource* textSource, FontHandle defaultFont) {
if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER;
textSource->defaultFont = defaultFont;
textSource->systemUse[4] = defaultFont;
return SCE_FONT_OK;
}
int APS5_VABI sceFontTextSourceSetWritingForm(FontTextSource* textSource, std::int32_t writingForm) {
if (!textSource || TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (writingForm < static_cast<std::int32_t>(WRITING_FORM_HORIZONTAL) || writingForm > static_cast<std::int32_t>(WRITING_FORM_HORIZONTAL_LTR)) return SCE_FONT_ERROR_INVALID_PARAMETER;
textSource->systemUse0 = (textSource->systemUse0 & 0xFFFFFFFFull) | (static_cast<std::uint64_t>(writingForm) << 32);
return SCE_FONT_OK;
}
int APS5_VABI sceFontCreateString(const FontMemory* fontMemory, FontTextSource* textSource, const FontCreateStringDetail* detail, FontString* pFontString) {
if (!fontMemory || !textSource || !pFontString || !textSource->textParser) {
if (pFontString) *pFontString = nullptr;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
*pFontString = nullptr;
if (TextSourceMagic(textSource) != TEXT_SOURCE_MAGIC) return SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
if (fontMemory->mem_kind != MEMORY_MAGIC || !fontMemory->iface || !fontMemory->iface->alloc || !fontMemory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY;
if (detail) {
constexpr std::uint32_t validOrders = (1u << 0) | (1u << 1) | (1u << 2) | (1u << 31);
if (detail->detailId != CREATE_STRING_DETAIL_ID || detail->detailType > 1 || (detail->detections & ~0x07u) != 0 || (detail->ordersOption & ~validOrders) != 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
}
void* raw = fontMemory->iface->alloc(fontMemory->mspace_handle, sizeof(FontStringData));
if (!raw) return SCE_FONT_ERROR_ALLOCATION_FAILED;
auto* data = new (raw) FontStringData{};
data->memory = fontMemory;
data->source = textSource;
data->defaultFont = textSource->defaultFont;
data->storage = new CharacterStorage{};
if (!data->defaultFont && detail) data->defaultFont = detail->defaultFont;
data->writingForm = static_cast<std::uint32_t>(textSource->systemUse0 >> 32);
std::int32_t state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
void* textOrder = nullptr;
for (std::size_t parsed = 0; parsed < MAX_PARSED_CHARACTERS; ++parsed) {
FontTextParseResult result{};
state = textSource->textParser(textSource, &textOrder, &result);
if (state == PARSER_RESULT_FONT_CODE) {
const FontHandle font = result.FontCode.font ? result.FontCode.font : data->defaultFont;
if (!font) {
state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
break;
}
data->storage->characters.push_back(MakeCharacter(font, result.FontCode.code, textOrder));
continue;
}
if (state == PARSER_RESULT_TERMINATE) {
data->terminateOrder = textOrder;
data->terminateCode = result.Terminate.terminateCode;
state = SCE_FONT_OK;
} else if (state == PARSER_RESULT_ERROR) {
state = result.Error.errorCode < 0 ? result.Error.errorCode : SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
} else {
state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
}
break;
}
if (state == PARSER_RESULT_FONT_CODE) state = SCE_FONT_ERROR_INVALID_TEXT_SOURCE;
if (state != SCE_FONT_OK) {
ReleaseStorage(data->storage);
data->~FontStringData();
fontMemory->iface->dealloc(fontMemory->mspace_handle, raw);
return state;
}
LinkCharacters(data->storage->characters);
data->textCount = static_cast<std::uint32_t>(data->storage->characters.size());
data->renderCount = data->textCount;
*pFontString = reinterpret_cast<FontString>(data);
return SCE_FONT_OK;
}
int APS5_VABI sceFontDestroyString(FontString* pFontString) {
if (!pFontString) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* data = GetStringData(*pFontString);
if (!data) {
*pFontString = nullptr;
return SCE_FONT_ERROR_INVALID_STRING;
}
const FontMemory* memory = data->memory;
ReleaseStorage(data->storage);
data->~FontStringData();
memory->iface->dealloc(memory->mspace_handle, data);
*pFontString = nullptr;
return SCE_FONT_OK;
}
std::uint32_t APS5_VABI sceFontStringGetTerminateCode(FontString fontString) {
const auto* data = GetStringData(fontString);
return data ? data->terminateCode : 0;
}
void* APS5_VABI sceFontStringGetTerminateOrder(FontString fontString) {
const auto* data = GetStringData(fontString);
return data ? data->terminateOrder : nullptr;
}
int APS5_VABI sceFontStringGetWritingForm(FontString fontString) {
const auto* data = GetStringData(fontString);
return data ? static_cast<int>(data->writingForm) : 0;
}
FontTextCharacter* APS5_VABI sceFontStringRefersTextCharacters(FontString fontString, std::uint32_t* characterCount) {
auto* data = GetStringData(fontString);
if (!data || data->textCount == 0) {
if (characterCount) *characterCount = 0;
return nullptr;
}
if (characterCount) *characterCount = data->textCount;
return TextCharacters(data);
}
const FontTextCharacter* APS5_VABI sceFontStringRefersRenderCharacters(FontString fontString, FontTextCharacter* startCharacter, FontTextCharacter* lastCharacter, std::uint32_t* characterCount) {
auto* data = GetStringData(fontString);
if (!data || !characterCount || data->renderCount == 0) {
if (characterCount) *characterCount = 0;
return nullptr;
}
FontTextCharacter* characters = TextCharacters(data);
if (!characters) {
*characterCount = 0;
return nullptr;
}
const FontTextCharacter* begin = characters;
const FontTextCharacter* end = characters + data->renderCount;
if (!startCharacter) startCharacter = characters;
if (startCharacter < begin || startCharacter >= end || (lastCharacter && (lastCharacter < begin || lastCharacter >= end))) {
*characterCount = 0;
return nullptr;
}
FontTextCharacter* probe = startCharacter;
if (startCharacter->clusterKind > 1) {
do {
startCharacter = probe;
probe = startCharacter->prev;
if (!probe) break;
} while (probe->synthetic < 0);
}
auto startFlags = static_cast<std::uint32_t>(startCharacter->flags);
const std::uint32_t paragraphLevel = (data->writingForm >> 8) & 1u;
if ((startFlags & 0xFF00u) == 0x100u && startCharacter->prev) {
if (startCharacter->prev->clusterKind == 1) startCharacter = startCharacter->prev;
startFlags = static_cast<std::uint32_t>(startCharacter->flags);
}
std::uint32_t startLevel = startFlags >> 24;
std::uint32_t lastLevel = startLevel;
std::uint32_t minLevel = startLevel;
std::uint32_t count = startCharacter->clusterIndex == 0 ? 1u : 0u;
probe = startCharacter;
while (probe != lastCharacter) {
probe = probe->next;
if (!probe) {
if (lastCharacter) {
*characterCount = 0;
return nullptr;
}
break;
}
if (probe->clusterIndex == 0) {
lastLevel = LevelOf(probe);
if (lastLevel < minLevel && ((lastLevel ^ paragraphLevel) & 1u) == 0) minLevel = lastLevel;
++count;
}
}
if (startLevel != lastLevel || startLevel != minLevel) {
probe = startCharacter->prev;
while (probe) {
if (probe->clusterIndex == 0) {
if (LevelOf(probe) <= minLevel) break;
++count;
}
startCharacter = probe;
probe = probe->prev;
}
if (lastCharacter) {
probe = lastCharacter->next;
while (probe) {
if (probe->clusterIndex == 0) {
if (LevelOf(probe) <= minLevel) break;
++count;
lastCharacter = probe;
}
probe = probe->next;
}
}
}
if (lastCharacter) {
probe = lastCharacter->next;
std::uint32_t clusterTrim = 0;
while (probe) {
std::uint32_t nextTrim = clusterTrim;
if (probe->clusterIndex == 0) {
if (probe->synthetic == 0) break;
nextTrim = 0;
FontTextCharacter* nextLast = probe;
if (probe->synthetic < 1) {
if (probe->clusterKind == 1) break;
nextTrim = clusterTrim + 1;
nextLast = lastCharacter;
}
lastCharacter = nextLast;
++count;
}
probe = probe->next;
count -= nextTrim;
clusterTrim = nextTrim;
}
}
startLevel = LevelOf(startCharacter);
if (startLevel != paragraphLevel) {
probe = startCharacter;
if (!lastCharacter || LevelOf(lastCharacter) != startLevel) {
do {
probe = probe->next;
if (!probe) {
if (lastCharacter) startCharacter = nullptr;
break;
}
FontTextCharacter* best = startCharacter;
std::uint32_t bestLevel = startLevel;
if (probe->clusterIndex == 0) {
const std::uint32_t probeLevel = LevelOf(probe);
if (probeLevel <= paragraphLevel) break;
if ((startLevel & 1u) == paragraphLevel && probeLevel < startLevel && (probeLevel & 1u) != paragraphLevel) {
best = probe;
bestLevel = probeLevel;
}
}
startLevel = bestLevel;
startCharacter = best;
} while (probe != lastCharacter);
} else if ((LevelOf(startCharacter) & 1u) != paragraphLevel) {
startCharacter = lastCharacter;
}
}
if (!startCharacter) count = 0;
*characterCount = count;
return startCharacter;
}
int APS5_VABI sceFontCharacterGetBidiLevel(const FontTextCharacter* textCharacter, int* bidiLevel) {
if (!textCharacter || !bidiLevel) return SCE_FONT_ERROR_INVALID_PARAMETER;
*bidiLevel = static_cast<int>(LevelOf(textCharacter));
return SCE_FONT_OK;
}
int APS5_VABI sceFontCharacterGetTextFontCode(const FontTextCharacter* textCharacter, FontHandle* pFontHandle, std::uint32_t* textCode) {
if (!textCharacter) {
if (pFontHandle) *pFontHandle = nullptr;
if (textCode) *textCode = 0;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
if (!pFontHandle || !textCode) return SCE_FONT_ERROR_INVALID_PARAMETER;
*pFontHandle = textCharacter->font;
*textCode = textCharacter->characterCode;
return SCE_FONT_OK;
}
int APS5_VABI sceFontCharacterGetTextOrder(const FontTextCharacter* textCharacter, void** pTextOrder) {
if (!pTextOrder) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!textCharacter) {
*pTextOrder = nullptr;
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
*pTextOrder = textCharacter->textOrder;
return SCE_FONT_OK;
}
std::uint32_t APS5_VABI sceFontCharacterLooksFormatCharacters(const FontTextCharacter* textCharacter) {
if (!textCharacter) return 0;
return (textCharacter->flags & CHARACTER_FLAG_FORMAT) != 0 ? textCharacter->characterCode : 0;
}
std::uint32_t APS5_VABI sceFontCharacterLooksWhiteSpace(const FontTextCharacter* textCharacter) {
if (!textCharacter) return 0;
return ((textCharacter->flags >> 8) & 0xFFu) == 0x0E ? textCharacter->characterCode : 0;
}
FontTextCharacter* APS5_VABI sceFontCharacterRefersTextBack(const FontTextCharacter* textCharacter) {
if (!textCharacter) return nullptr;
for (FontTextCharacter* current = textCharacter->prev; current; current = current->prev) {
if (current->synthetic == 0 && current->clusterIndex == 0) return current;
}
return nullptr;
}
FontTextCharacter* APS5_VABI sceFontCharacterRefersTextNext(const FontTextCharacter* textCharacter) {
if (!textCharacter) return nullptr;
for (FontTextCharacter* current = textCharacter->next; current; current = current->next) {
if (current->synthetic == 0 && current->clusterIndex == 0) return current;
}
return nullptr;
}
int APS5_VABI sceFontWritingInit(FontWriting* fontWriting, FontString fontString, const FontTextCharacter* fontCharacter) {
if (!fontWriting || !fontString || !fontCharacter) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* data = GetStringData(fontString);
if (!data) return SCE_FONT_ERROR_INVALID_STRING;
if (auto* previous = GetWritingData(fontWriting)) ReleaseWritingStorage(*previous);
std::memset(fontWriting, 0, sizeof(*fontWriting));
auto* writing = new (fontWriting->systemUse) FontWritingData{};
writing->string = fontString;
writing->storage = data->storage;
writing->defaultFont = data->defaultFont;
if (writing->storage) writing->storage->refCount.fetch_add(1, std::memory_order_relaxed);
writing->ownsStorage = true;
writing->writingForm = data->writingForm;
const FontTextCharacter* characters = TextCharacters(data);
if (!characters || fontCharacter < characters || fontCharacter >= characters + data->storage->characters.size()) {
ReleaseWritingStorage(*writing);
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
writing->nextIndex = static_cast<std::size_t>(fontCharacter - characters);
writing->currentIndex = writing->nextIndex;
writing->hasCurrentStep = false;
if (!PopulateWritingStep(*writing, writing->nextIndex)) {
ReleaseWritingStorage(*writing);
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
writing->penX = 0.0f;
writing->penY = 0.0f;
writing->hasAggregateMetrics = false;
writing->metrics = {};
return SCE_FONT_OK;
}
int APS5_VABI sceFontWritingGetRenderMetrics(FontWriting* fontWriting, FontWritingMetrics* writingMetrics) {
if (!fontWriting || !writingMetrics) {
if (writingMetrics) *writingMetrics = {};
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
const auto* writing = GetWritingData(fontWriting);
if (!writing) {
*writingMetrics = {};
return SCE_FONT_ERROR_INVALID_WRITING;
}
*writingMetrics = writing->metrics;
return SCE_FONT_OK;
}
const FontWritingStep* APS5_VABI sceFontWritingRefersRenderStep(FontWriting* fontWriting) {
auto* writing = GetWritingData(fontWriting);
if (!writing || !writing->storage || writing->nextIndex >= writing->storage->characters.size()) return nullptr;
if (!PopulateWritingStep(*writing, writing->nextIndex)) return nullptr;
writing->currentIndex = writing->nextIndex;
writing->hasCurrentStep = true;
AccumulateWritingMetrics(*writing);
++writing->nextIndex;
return &writing->step;
}
FontTextCharacter* APS5_VABI sceFontWritingRefersRenderStepCharacter(FontWriting* fontWriting, FontWritingLetterStep* letterStep) {
if (letterStep) std::memset(letterStep, 0, sizeof(*letterStep));
auto* writing = GetWritingData(fontWriting);
if (!writing || !writing->hasCurrentStep || !writing->storage || writing->currentIndex >= writing->storage->characters.size()) return nullptr;
if (letterStep) {
letterStep->x = writing->step.x;
letterStep->y = writing->step.y;
letterStep->advanceX = writing->step.advanceX;
letterStep->advanceY = writing->step.advanceY;
letterStep->Components.textsCount = 1;
letterStep->Components.textsIndex = static_cast<std::uint32_t>(writing->currentIndex);
letterStep->Components.glyphsCount = 1;
letterStep->Components.glyphsIndex = static_cast<std::uint32_t>(writing->currentIndex);
letterStep->Components.characterTextCount = static_cast<std::uint8_t>(std::max<std::uint32_t>(writing->step.Profile.characterCount, 1));
}
return &writing->storage->characters[writing->currentIndex];
}
int APS5_VABI sceFontWritingSetMaskInvisible(FontWriting* fontWriting, std::int32_t mask) {
auto* writing = GetWritingData(fontWriting);
if (!writing) return SCE_FONT_ERROR_INVALID_WRITING;
if (mask == WRITING_MASK_FORMAT_CHARACTERS) {
writing->maskFormatCharacters = false;
return SCE_FONT_OK;
}
if (mask == WRITING_MASK_NONE) {
writing->maskFormatCharacters = true;
return SCE_FONT_OK;
}
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,477 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "prx/libc/include/General.hpp"
#pragma GCC visibility push(default)
extern "C" {
int APS5_VABI sceFontCharacterGetSyllableStringState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCharactersRefersTextCodes() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontControl() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateGraphicsDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateGraphicsService() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateGraphicsServiceWithEdition() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateWords() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontCreateWritingLine() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDefineAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyGraphicsDevice() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyGraphicsService() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyWords() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDestroyWritingLine() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontDettachDeviceCacheBuffer() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetCharGlyphCode() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetFontGlyphsCount() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetFontGlyphsOutlineProfile() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetFontMetrics() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetFontResolution() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetFontStyleInformation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetGlyphExpandBufferState() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetPixelResolution() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetRenderScaledKerning() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetResolutionDpi() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetScriptLanguage() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGetTypographicDesign() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphDefineAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphGetAttribute() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphRenderImage() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphRenderImageHorizontal() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGlyphRenderImageVertical() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsBeginFrame() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsDrawingCancel() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsDrawingFinish() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsEndFrame() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsExchangeResource() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillMethodInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillPlotInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillPlotSetLayout() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillPlotSetMapping() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillRatesInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillRatesSetFillEffect() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillRatesSetLayout() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsFillRatesSetMapping() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsGetDeviceUsage() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsRegionInit() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsRegionInitCircular() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsRegionInitRoundish() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsRelease() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsRenderResource() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetFramePolicy() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupClipping() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupColorRates() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupFillMethod() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupFillRates() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupGlyphFill() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupGlyphFillPlot() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupHandleDefault() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupLocation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupPositioning() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupRotation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupScaling() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupShapeFill() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsSetupShapeFillPlot() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsStructureCanvas() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsStructureCanvasSequence() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsStructureDesign() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsStructureDesignResource() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsStructureSurfaceTexture() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateClipping() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateColorRates() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateFillMethod() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateFillRates() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateGlyphFill() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateGlyphFillPlot() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateLocation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdatePositioning() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateRotation() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateScaling() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateShapeFill() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontGraphicsUpdateShapeFillPlot() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetScriptLanguage() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSetTypographicDesign() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSupportGlyphs() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextCodesStepBack() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontTextCodesStepNext() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWordsFindWordCharacters() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineClear() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetOrderingSpace() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineGetRenderMetrics() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineRefersRenderStep() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontWritingLineWritesOrder() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,15 @@
find_package(Freetype REQUIRED)
add_library(libSceFontFt SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Driver.cpp
)
target_include_directories(libSceFontFt PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceFontFt PRIVATE Freetype::Freetype libc)
set_target_properties(libSceFontFt PROPERTIES
CXX_EXTENSIONS OFF
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
configure_windows_unwind(libSceFontFt)

View File

@ -1,20 +1,116 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "prx/libSceFontFt/include/FontFtDriver.hpp"
#include "prx/libc/include/General.hpp"
#pragma GCC visibility push(default)
extern "C" {
void* APS5_VABI sceFontSelectLibraryFt(int value) {
(void)value;
NotImplemented_nid_no_patch(__func__);
return nullptr;
const Font::SysDriver* APS5_VABI sceFontSelectLibraryFt(int value) {
return value == 0 ? FontFt::DriverTable() : nullptr;
}
void* APS5_VABI sceFontSelectRendererFt(int value) {
(void)value;
NotImplemented_nid_no_patch(__func__);
return nullptr;
const Font::RendererSelection* APS5_VABI sceFontSelectRendererFt(int value) {
return value == 0 ? FontFt::RendererTable() : nullptr;
}
int APS5_VABI sceFontFtInitAliases() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSetAliasFont() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSetAliasPath() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportBdf() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportCid() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportFontFormats() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportOpenType() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportOpenTypeOtf() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportOpenTypeTtf() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportPcf() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportPfr() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportSystemFonts() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportTrueType() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportTrueTypeGx() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportType1() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportType42() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtSupportWinFonts() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontFtTermAliases() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceFontSelectGlyphsFt() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}
#pragma GCC visibility pop

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP
#define CORE_LIBS_PRX_LIBSCEFONTFT_INCLUDE_FONTFTDRIVER_HPP
#include "prx/libSceFont/include/FontDriver.hpp"
namespace FontFt {
const Font::SysDriver* DriverTable();
const Font::RendererSelection* RendererTable();
}
#endif

View File

@ -0,0 +1,711 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_MODULE_H
#include FT_SYSTEM_H
#include FT_TRUETYPE_TABLES_H
#include "prx/libSceFont/include/FontFreeType.hpp"
#include "prx/libSceFontFt/include/FontFtDriver.hpp"
namespace {
using namespace Font;
struct FtLibraryContext {
void* allocCtx;
const FontMemoryInterface* iface;
FT_Memory memory;
FT_Library library;
};
void* FtAlloc(FT_Memory memory, long size) {
if (!memory || size <= 0) return nullptr;
auto* ctx = static_cast<FtLibraryContext*>(memory->user);
if (!ctx || !ctx->iface || !ctx->iface->alloc) return nullptr;
return ctx->iface->alloc(ctx->allocCtx, static_cast<std::uint32_t>(size));
}
void FtFree(FT_Memory memory, void* block) {
if (!memory || !block) return;
auto* ctx = static_cast<FtLibraryContext*>(memory->user);
if (!ctx || !ctx->iface || !ctx->iface->dealloc) return;
ctx->iface->dealloc(ctx->allocCtx, block);
}
void* FtRealloc(FT_Memory memory, long currentSize, long newSize, void* block) {
if (!memory) return nullptr;
auto* ctx = static_cast<FtLibraryContext*>(memory->user);
if (!ctx || !ctx->iface) return nullptr;
if (ctx->iface->realloc_fn) return ctx->iface->realloc_fn(ctx->allocCtx, block, static_cast<std::uint32_t>(newSize));
if (newSize <= 0) {
FtFree(memory, block);
return nullptr;
}
void* result = FtAlloc(memory, newSize);
if (!result) return nullptr;
if (block && currentSize > 0) std::memcpy(result, block, static_cast<std::size_t>(std::min(currentSize, newSize)));
FtFree(memory, block);
return result;
}
void WriteU16(std::uint8_t* out, std::size_t offset, std::uint16_t value) {
std::memcpy(out + offset, &value, sizeof(value));
}
void WriteU32(std::uint8_t* out, std::size_t offset, std::uint32_t value) {
std::memcpy(out + offset, &value, sizeof(value));
}
void WriteS32(std::uint8_t* out, std::size_t offset, std::int32_t value) {
std::memcpy(out + offset, &value, sizeof(value));
}
void WritePointer(std::uint8_t* out, std::size_t offset, const void* value) {
std::memcpy(out + offset, &value, sizeof(value));
}
float FromF26Dot6(std::int64_t value) {
return static_cast<float>(value) * ONE_OVER_64;
}
FT_Face FaceOf(const FontObj* obj) {
return static_cast<FT_Face>(obj->ft_face);
}
std::uint32_t APS5_VABI LibraryGetPixelResolution() {
return 0x40;
}
int APS5_VABI LibraryInit(const FontMemory* memory, FontLibNative* library) {
if (!memory || !library) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (!memory->iface || !memory->iface->alloc || !memory->iface->dealloc) return SCE_FONT_ERROR_INVALID_MEMORY;
const auto allocFn = memory->iface->alloc;
const auto freeFn = memory->iface->dealloc;
void* allocCtx = memory->mspace_handle;
auto* ctx = static_cast<FtLibraryContext*>(allocFn(allocCtx, sizeof(FtLibraryContext)));
if (!ctx) return SCE_FONT_ERROR_ALLOCATION_FAILED;
std::memset(ctx, 0, sizeof(FtLibraryContext));
ctx->allocCtx = allocCtx;
ctx->iface = memory->iface;
auto* ftMemory = static_cast<FT_Memory>(allocFn(allocCtx, sizeof(FT_MemoryRec_)));
if (!ftMemory) {
freeFn(allocCtx, ctx);
return SCE_FONT_ERROR_ALLOCATION_FAILED;
}
std::memset(ftMemory, 0, sizeof(*ftMemory));
ftMemory->user = ctx;
ftMemory->alloc = &FtAlloc;
ftMemory->free = &FtFree;
ftMemory->realloc = &FtRealloc;
ctx->memory = ftMemory;
FT_Library ftLibrary = nullptr;
if (FT_New_Library(ftMemory, &ftLibrary) != 0 || !ftLibrary) {
freeFn(allocCtx, ftMemory);
freeFn(allocCtx, ctx);
return SCE_FONT_ERROR_ALLOCATION_FAILED;
}
FT_Add_Default_Modules(ftLibrary);
ctx->library = ftLibrary;
library->fontset_registry = ctx;
library->flags = 0x60000000;
return SCE_FONT_OK;
}
int APS5_VABI LibraryTerm(FontLibNative* library) {
if (!library) return SCE_FONT_ERROR_INVALID_PARAMETER;
const auto freeFn = library->iface ? library->iface->dealloc : nullptr;
if (!freeFn) return SCE_FONT_ERROR_INVALID_PARAMETER;
auto* ctx = static_cast<FtLibraryContext*>(library->fontset_registry);
if (!ctx) return SCE_FONT_ERROR_INVALID_PARAMETER;
if (ctx->library) {
FT_Done_Library(ctx->library);
ctx->library = nullptr;
}
if (ctx->memory) {
freeFn(library->alloc_ctx, ctx->memory);
ctx->memory = nullptr;
}
freeFn(library->alloc_ctx, ctx);
library->fontset_registry = nullptr;
return SCE_FONT_OK;
}
int APS5_VABI LibrarySupport(FontLibNative* library, std::uint32_t formats) {
(void)formats;
if (!library || !library->fontset_registry) return SCE_FONT_ERROR_INVALID_PARAMETER;
return SCE_FONT_OK;
}
int APS5_VABI LibraryOpenFont(FontLibNative* library, std::uint32_t mode, const void* fontAddress, std::uint32_t fontSize, std::uint32_t subFontIndex, std::uint32_t uniqueWord, FontObj** inoutFontObj) {
(void)uniqueWord;
if (!library || !fontAddress || !inoutFontObj) return SCE_FONT_ERROR_INVALID_PARAMETER;
const auto allocFn = library->iface ? library->iface->alloc : nullptr;
const auto freeFn = library->iface ? library->iface->dealloc : nullptr;
if (!allocFn || !freeFn) return SCE_FONT_ERROR_INVALID_PARAMETER;
const bool fromMemory = mode == 1;
if (fromMemory) {
if (fontSize == 0) return SCE_FONT_ERROR_INVALID_PARAMETER;
} else if (mode == 5 || mode == 6 || mode == 7) {
if (static_cast<const char*>(fontAddress)[0] == '\0') return SCE_FONT_ERROR_INVALID_PARAMETER;
} else {
return SCE_FONT_ERROR_INVALID_PARAMETER;
}
auto* ctx = static_cast<FtLibraryContext*>(library->fontset_registry);
if (!ctx || !ctx->library) return SCE_FONT_ERROR_INVALID_LIBRARY;
FT_Face face = nullptr;
FT_Error error;
if (fromMemory) {
error = FT_New_Memory_Face(ctx->library, static_cast<const FT_Byte*>(fontAddress), static_cast<FT_Long>(fontSize), static_cast<FT_Long>(subFontIndex), &face);
} else {
error = FT_New_Face(ctx->library, static_cast<const char*>(fontAddress), static_cast<FT_Long>(subFontIndex), &face);
}
if (error != 0 || !face) {
if (fromMemory || error == FT_Err_Unknown_File_Format) return SCE_FONT_ERROR_NO_SUPPORT_FORMAT;
return SCE_FONT_ERROR_FS_OPEN_FAILED;
}
FT_Select_Charmap(face, FT_ENCODING_UNICODE);
auto* obj = static_cast<FontObj*>(allocFn(library->alloc_ctx, sizeof(FontObj)));
if (!obj) {
FT_Done_Face(face);
return SCE_FONT_ERROR_ALLOCATION_FAILED;
}
std::memset(obj, 0, sizeof(FontObj));
obj->refcount = 1;
obj->sub_font_index = subFontIndex;
obj->prev = nullptr;
obj->next = *inoutFontObj;
if (obj->next) obj->next->prev = obj;
obj->ft_face = face;
obj->ft_ctx_0x58 = &ctx->library;
*inoutFontObj = obj;
return SCE_FONT_OK;
}
int APS5_VABI LibraryCloseFont(FontObj* obj, std::uint32_t flags) {
(void)flags;
if (!obj) return SCE_FONT_ERROR_FATAL;
if (obj->refcount > 1) {
--obj->refcount;
return SCE_FONT_OK;
}
FT_Face face = FaceOf(obj);
FtLibraryContext* ctx = face && face->memory ? static_cast<FtLibraryContext*>(face->memory->user) : nullptr;
const auto freeFn = ctx && ctx->iface ? ctx->iface->dealloc : nullptr;
if (face) {
FT_Done_Face(face);
obj->ft_face = nullptr;
}
if (!freeFn) return SCE_FONT_ERROR_FATAL;
FontObj* next = obj->next;
FontObj* prev = obj->prev;
if (prev) prev->next = next;
if (next) next->prev = prev;
freeFn(ctx->allocCtx, obj);
return SCE_FONT_OK;
}
int APS5_VABI LibraryGetFaceScale(FontObj* obj, std::uint16_t* outUnitsPerEm, float* outScale) {
if (!obj || !outUnitsPerEm || !outScale) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face) return SCE_FONT_ERROR_FATAL;
const std::uint16_t units = face->units_per_EM;
*outUnitsPerEm = units;
*outScale = static_cast<float>(units) * ONE_OVER_64;
return SCE_FONT_OK;
}
int APS5_VABI LibraryGetFaceMetric(FontObj* obj, std::uint32_t metricId, std::uint16_t* outMetric) {
if (!obj || !outMetric) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face) return SCE_FONT_ERROR_FATAL;
const std::uint16_t units = face->units_per_EM;
if (metricId == 0x0E00) {
*outMetric = units;
return SCE_FONT_OK;
}
if (metricId == 0xEA00) {
if (const auto* os2 = static_cast<const TT_OS2*>(FT_Get_Sfnt_Table(face, FT_SFNT_OS2))) {
*outMetric = static_cast<std::uint16_t>(os2->sTypoAscender);
return SCE_FONT_OK;
}
*outMetric = units;
return SCE_FONT_ERROR_NO_SUPPORT_FUNCTION;
}
*outMetric = 0;
return SCE_FONT_OK;
}
int APS5_VABI LibraryGetGlyphIndex(FontObj* obj, std::uint32_t codepoint, std::uint32_t* outGlyphIndex) {
if (!obj || !outGlyphIndex) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face) {
*outGlyphIndex = 0;
return SCE_FONT_ERROR_FATAL;
}
const std::uint32_t glyphIndex = ResolveGlyphIndexWithFallback(face, codepoint);
*outGlyphIndex = glyphIndex;
return glyphIndex ? SCE_FONT_OK : SCE_FONT_ERROR_NO_SUPPORT_GLYPH;
}
int APS5_VABI LibrarySetCharSizeWithDpi(FontObj* obj, std::uint32_t dpiX, std::uint32_t dpiY, float scaleX, float scaleY, float* outScaleX, float* outScaleY) {
if (!obj || !outScaleX || !outScaleY) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face) return SCE_FONT_ERROR_FATAL;
const auto charW = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(scaleX * 64.0f));
const auto charH = static_cast<FT_F26Dot6>(static_cast<std::int32_t>(scaleY * 64.0f));
if (SetCharSizeCompat(face, charW, charH, dpiX, dpiY) != 0) return SCE_FONT_ERROR_FATAL;
if (!face->size) return SCE_FONT_ERROR_FATAL;
const std::uint16_t units = face->units_per_EM;
*outScaleX = FixedMulUnitsToF26Dot6(static_cast<std::int64_t>(face->size->metrics.x_scale), units);
*outScaleY = FixedMulUnitsToF26Dot6(static_cast<std::int64_t>(face->size->metrics.y_scale), units);
return SCE_FONT_OK;
}
int APS5_VABI LibrarySetCharSizeDefaultDpi(FontObj* obj, float scaleX, float scaleY, float* outScaleX, float* outScaleY) {
if (!obj || !outScaleX || !outScaleY) return SCE_FONT_ERROR_FATAL;
return LibrarySetCharSizeWithDpi(obj, 0x48, 0x48, scaleX, scaleY, outScaleX, outScaleY);
}
int APS5_VABI LibraryComputeLayout(FontObj* obj, const StyleStateBlock* style, std::uint8_t* outWords) {
if (!obj || !style || !outWords) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face || !face->size) return SCE_FONT_ERROR_FATAL;
const float effectWidth = style->effect_weight_x;
const float effectHeight = style->effect_weight_y;
const float slant = style->slant_ratio;
const auto yScale = static_cast<std::int64_t>(face->size->metrics.y_scale);
const auto xScale = static_cast<std::int64_t>(face->size->metrics.x_scale);
const std::int64_t yShift = obj->shift_cache_y;
const std::int64_t xShift = obj->shift_cache_x;
const auto unitsPerEm = static_cast<std::int64_t>(static_cast<std::uint16_t>(face->units_per_EM));
std::int32_t yMinPx = RoundMulFixed(static_cast<std::int64_t>(face->bbox.yMin) + yShift, yScale);
std::int32_t yMaxPx = RoundMulFixed(static_cast<std::int64_t>(face->bbox.yMax) + yShift, yScale);
std::int32_t halfEffectWidthPx = 0;
std::int32_t leftAdjustPx = 0;
if (effectWidth != 0.0f) {
const std::int32_t unitsScaledX = TruncFixed(unitsPerEm * xScale);
halfEffectWidthPx = TruncateFloatToInt(effectWidth * static_cast<float>(unitsScaledX)) / 2;
leftAdjustPx = -halfEffectWidthPx;
}
float outEffectHeight = 0.0f;
if (effectHeight != 0.0f) {
const std::int32_t unitsScaledY = TruncFixed(unitsPerEm * yScale);
const std::int32_t halfEffectHeightPx = TruncateFloatToInt(effectHeight * static_cast<float>(unitsScaledY)) / 2;
outEffectHeight = static_cast<float>(halfEffectHeightPx) * ONE_OVER_64;
yMinPx -= halfEffectHeightPx;
yMaxPx += halfEffectHeightPx;
}
if (slant != 0.0f) {
const auto shear = static_cast<std::int64_t>(TruncateFloatToInt(slant * 65536.0f));
leftAdjustPx += RoundMulFixed(yMinPx, shear);
halfEffectWidthPx += RoundMulFixed(yMaxPx, shear);
}
StoreFloat(outWords, HORIZONTAL_EFFECT_HEIGHT, outEffectHeight);
StoreFloat(outWords, HORIZONTAL_LEFT_ADJUST, static_cast<float>(leftAdjustPx) * ONE_OVER_64);
StoreFloat(outWords, HORIZONTAL_HALF_EFFECT_WIDTH, static_cast<float>(halfEffectWidthPx) * ONE_OVER_64);
const std::int32_t xMinPx = RoundMulFixed(static_cast<std::int64_t>(face->bbox.xMin) + xShift, xScale);
const std::int32_t xMaxPx = RoundMulFixed(static_cast<std::int64_t>(face->bbox.xMax) + xShift, xScale);
StoreFloat(outWords, HORIZONTAL_LINE_ADVANCE, static_cast<float>(yMaxPx - yMinPx) * ONE_OVER_64);
StoreFloat(outWords, HORIZONTAL_BASELINE, static_cast<float>(yMaxPx) * ONE_OVER_64);
StoreFloat(outWords, HORIZONTAL_X_BOUND_LO, static_cast<float>(xMinPx + leftAdjustPx) * ONE_OVER_64);
StoreFloat(outWords, HORIZONTAL_X_BOUND_HI, static_cast<float>(xMaxPx + halfEffectWidthPx) * ONE_OVER_64);
const std::int32_t maxAdvanceWidthPx = RoundMulFixed(static_cast<std::int64_t>(face->max_advance_width) + xShift, xScale);
StoreFloat(outWords, HORIZONTAL_MAX_ADVANCE_WIDTH, static_cast<float>(maxAdvanceWidthPx) * ONE_OVER_64);
float caretRiseAdjust = 0.0f;
if (const auto* hhea = static_cast<const TT_HoriHeader*>(FT_Get_Sfnt_Table(face, FT_SFNT_HHEA))) {
const std::int64_t caretRiseUnits = xShift + static_cast<std::int64_t>(hhea->caret_Slope_Rise);
const std::int32_t caretRisePx = TruncFixed(caretRiseUnits * xScale);
caretRiseAdjust = static_cast<float>(caretRisePx - halfEffectWidthPx) * ONE_OVER_64;
}
StoreFloat(outWords, HORIZONTAL_CARET_RISE_ADJUST, caretRiseAdjust);
return SCE_FONT_OK;
}
int APS5_VABI LibraryComputeLayoutAlt(FontObj* obj, const StyleStateBlock* style, std::uint8_t* outWords) {
if (!obj || !style || !outWords) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face || !face->size) return SCE_FONT_ERROR_FATAL;
const float effectWidth = style->effect_weight_x;
const float effectHeight = style->effect_weight_y;
const float slant = style->slant_ratio;
const auto xScale = static_cast<std::int64_t>(face->size->metrics.x_scale);
const auto yScale = static_cast<std::int64_t>(face->size->metrics.y_scale);
const auto* vhea = static_cast<const TT_VertHeader*>(FT_Get_Sfnt_Table(face, FT_SFNT_VHEA));
const std::int64_t yShift = obj->shift_cache_y;
const std::int64_t xShift = obj->shift_cache_x;
const auto units = static_cast<std::uint16_t>(face->units_per_EM);
std::int64_t yAscender = 0;
std::int64_t yDescender = 0;
if (vhea) {
yAscender = static_cast<std::int64_t>(vhea->Ascender) + yShift;
yDescender = static_cast<std::int64_t>(vhea->Descender) + yShift;
}
const std::int32_t scaledLeft = RoundMulFixed(static_cast<std::int64_t>(face->bbox.xMin) + xShift, xScale);
const std::int32_t scaledRight = RoundMulFixed(static_cast<std::int64_t>(face->bbox.xMax) + xShift, xScale);
std::int32_t xMinScaled = scaledLeft;
std::int32_t xAbsMax = -xMinScaled;
if (xAbsMax < scaledRight) {
xAbsMax = scaledRight;
xMinScaled = -scaledRight;
}
float outEffectWidth = 0.0f;
if (effectWidth != 0.0f) {
const auto unitsScaled = static_cast<std::int32_t>(TruncMulUnits(xScale, units));
const std::int32_t effect = TruncateFloatToInt(effectWidth * static_cast<float>(unitsScaled)) / 2;
outEffectWidth = static_cast<float>(effect) * ONE_OVER_64;
}
if (effectHeight != 0.0f) {
const auto unitsScaled = static_cast<std::int32_t>(TruncMulUnits(yScale, units));
const std::int32_t delta = TruncateFloatToInt(effectHeight * static_cast<float>(unitsScaled)) / 2;
yAscender -= delta;
yDescender += delta;
}
float outSlantA = 0.0f;
float outSlantB = 0.0f;
if (slant != 0.0f) {
const auto shear = static_cast<std::int64_t>(TruncateFloatToInt(slant * 65536.0f));
const std::int64_t signAdjustMin = yAscender < 1 ? 0x10000LL : 0LL;
std::int64_t roundedMin = signAdjustMin + (-yAscender) * shear - 0x8000LL;
if (roundedMin < 0) roundedMin = signAdjustMin + 0x7FFFLL + (-yAscender) * shear;
const std::int64_t signAdjustMax = yDescender >= 0 ? 0x10000LL : 0LL;
std::int64_t roundedMax = signAdjustMax + yDescender * shear - 0x8000LL;
if (roundedMax < 0) roundedMax = signAdjustMax + 0x7FFFLL + yDescender * shear;
const auto roundedMaxBits = static_cast<std::uint64_t>(roundedMax);
const auto roundedMinBits = static_cast<std::uint64_t>(roundedMin);
const std::uint64_t roundedMaxHigh = roundedMaxBits >> 16;
const bool maxRoundBitClear = ((roundedMaxBits >> 15) & 1u) == 0;
std::uint64_t selectedHigh = roundedMaxHigh;
auto selectedLow = static_cast<std::int32_t>(roundedMinBits >> 16);
if (maxRoundBitClear && roundedMaxHigh != 0) {
selectedHigh = roundedMinBits >> 16;
selectedLow = static_cast<std::int32_t>(roundedMaxBits >> 16);
}
outSlantA = static_cast<float>(selectedLow) * ONE_OVER_64;
outSlantB = static_cast<float>(static_cast<std::int32_t>(selectedHigh)) * ONE_OVER_64;
}
StoreFloat(outWords, VERTICAL_COLUMN_ADVANCE, static_cast<float>(xAbsMax - xMinScaled) * ONE_OVER_64);
StoreFloat(outWords, VERTICAL_BASELINE_OFFSET_X, static_cast<float>(-xAbsMax) * ONE_OVER_64);
StoreFloat(outWords, VERTICAL_METRICS_0X08, static_cast<float>(RoundMulFixed(yAscender, xScale)) * ONE_OVER_64);
StoreFloat(outWords, VERTICAL_METRICS_0X0C, static_cast<float>(RoundMulFixed(yDescender, xScale)) * ONE_OVER_64);
const std::int32_t advanceHeight = RoundMulFixed(static_cast<std::int64_t>(face->max_advance_height) + yShift, yScale);
StoreFloat(outWords, VERTICAL_ADVANCE_HEIGHT, static_cast<float>(advanceHeight) * ONE_OVER_64);
StoreFloat(outWords, VERTICAL_DECORATION_SPAN, outEffectWidth);
StoreFloat(outWords, VERTICAL_DECORATION_0X08, outSlantB);
StoreFloat(outWords, VERTICAL_DECORATION_0X0C, outSlantA);
return SCE_FONT_OK;
}
void StoreSeedVector(FontObj* obj) {
const auto seedLow = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair & 0xFFFFFFFFu));
const auto seedHigh = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair >> 32));
obj->layout_seed_vec[0] = static_cast<std::uint64_t>(static_cast<std::int64_t>(seedLow));
obj->layout_seed_vec[1] = static_cast<std::uint64_t>(static_cast<std::int64_t>(seedHigh));
}
void WriteOutlineSizes(FT_GlyphSlot slot, std::uint64_t* outWords) {
const auto contours = static_cast<std::uint32_t>(slot->outline.n_contours);
const auto points = static_cast<std::uint32_t>(slot->outline.n_points);
const std::uint32_t size4 = points * 0x10u + 0x68u + ((points + 0x0Fu + contours * 2u) & 0xFFFFFFF0u);
const std::uint32_t size8 = ((points + 0x2Bu + contours * 2u) & 0xFFFFFFFCu) + 0x10u + points * 4u;
auto* out = reinterpret_cast<std::uint8_t*>(outWords);
WriteU16(out, 0, static_cast<std::uint16_t>(contours));
WriteU16(out, 2, static_cast<std::uint16_t>(points));
WriteU32(out, 4, size4);
WriteU32(out, 8, size8);
}
int APS5_VABI LibraryLoadGlyphCached(FontObj* obj, std::uint32_t glyphIndex, std::int32_t mode, std::uint64_t* outWords) {
if (outWords) {
outWords[0] = 0;
outWords[1] = 0;
}
if (!obj || !outWords) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face || !face->glyph) return SCE_FONT_ERROR_FATAL;
const auto unitsPerEm = static_cast<std::uint16_t>(face->units_per_EM);
const bool cachedMatch = obj->cached_glyph_index_0x64 == static_cast<std::int32_t>(glyphIndex) && obj->cached_units_x_0x68 == unitsPerEm && obj->cached_units_y_0x70 == obj->cached_units_x_0x68 && mode == 0;
if (cachedMatch) {
obj->shift_cache_x = obj->shift_units_x;
obj->shift_cache_y = obj->shift_units_y;
StoreSeedVector(obj);
WriteOutlineSizes(face->glyph, outWords);
return SCE_FONT_OK;
}
const FT_Int32 loadFlags = (mode == 0 ? FT_LOAD_NO_SCALE : 0) | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_VERTICAL_LAYOUT;
const FT_Error error = FT_Load_Glyph(face, glyphIndex, loadFlags);
if (error != 0) {
obj->cached_glyph_index_0x64 = 0;
obj->cached_units_x_0x68 = 0;
obj->cached_units_y_0x70 = 0;
obj->layout_seed_vec = {};
obj->layout_scale_vec = {};
return error == FT_Err_Out_Of_Memory ? SCE_FONT_ERROR_ALLOCATION_FAILED : SCE_FONT_ERROR_NO_SUPPORT_GLYPH;
}
if (!face->glyph) return SCE_FONT_ERROR_FATAL;
obj->cached_glyph_index_0x64 = static_cast<std::int32_t>(glyphIndex);
if (mode == 0) {
obj->cached_units_x_0x68 = unitsPerEm;
obj->cached_units_y_0x70 = unitsPerEm;
obj->shift_cache_x = obj->shift_units_x;
obj->shift_cache_y = obj->shift_units_y;
StoreSeedVector(obj);
obj->layout_scale_vec[0] = 0x10000;
obj->layout_scale_vec[1] = 0x10000;
} else {
if (!face->size) return SCE_FONT_ERROR_FATAL;
const auto xScale = static_cast<std::int64_t>(face->size->metrics.x_scale);
const auto yScale = static_cast<std::int64_t>(face->size->metrics.y_scale);
obj->cached_units_x_0x68 = static_cast<std::uint64_t>(TruncMulUnits(xScale, unitsPerEm));
obj->cached_units_y_0x70 = static_cast<std::uint64_t>(TruncMulUnits(yScale, unitsPerEm));
obj->shift_cache_x = RoundMulFixed(obj->shift_units_x, xScale);
obj->shift_cache_y = RoundMulFixed(obj->shift_units_y, yScale);
const auto seedLow = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair & 0xFFFFFFFFu));
const auto seedHigh = static_cast<std::int32_t>(static_cast<std::uint32_t>(obj->layout_seed_pair >> 32));
obj->layout_seed_vec[0] = static_cast<std::uint64_t>(static_cast<std::int64_t>(RoundMulFixed(seedLow, xScale)));
obj->layout_seed_vec[1] = static_cast<std::uint64_t>(static_cast<std::int64_t>(RoundMulFixed(seedHigh, yScale)));
obj->layout_scale_vec[0] = static_cast<std::uint64_t>(xScale);
obj->layout_scale_vec[1] = static_cast<std::uint64_t>(yScale);
}
WriteOutlineSizes(face->glyph, outWords);
return SCE_FONT_OK;
}
std::int64_t RoundMulFixed64(std::int64_t value, std::int64_t fixed16) {
return static_cast<std::int64_t>(RoundMulFixed(value, fixed16));
}
int APS5_VABI LibraryGetGlyphMetrics(FontObj* obj, std::uint32_t* optParam2, std::uint8_t mode, std::uint8_t* outParams, FontGlyphMetrics* outMetrics) {
if (!obj || !outParams || !outMetrics) return SCE_FONT_ERROR_FATAL;
const FT_Face face = FaceOf(obj);
if (!face || !face->glyph || !face->size) return SCE_FONT_ERROR_FATAL;
const FT_GlyphSlot slot = face->glyph;
std::memset(outParams, 0, 0x48);
const void* outlineBlob = &slot->outline;
outParams[0] = 0xF2;
WritePointer(outParams, 0x08, slot);
if (optParam2) {
outlineBlob = optParam2;
outParams[0] = 0xF0;
WritePointer(outParams, 0x08, optParam2);
}
const auto faceFlagBit = static_cast<std::uint8_t>((static_cast<std::uint32_t>(face->face_flags) >> 5) & 1u);
const bool hasSeedVector = obj->layout_seed_vec[0] != 0 || obj->layout_seed_vec[1] != 0;
outParams[2] = static_cast<std::uint8_t>(0xF0u | (hasSeedVector ? 0x08u : 0u) | faceFlagBit);
outParams[3] = 0;
WriteU16(outParams, 0x04, static_cast<std::uint16_t>(face->units_per_EM));
WritePointer(outParams, 0x10, outlineBlob);
WritePointer(outParams, 0x18, outMetrics);
float styleShiftX = 0.0f;
float styleShiftY = 0.0f;
if (obj->reserved_0x04 != 0) {
styleShiftX = static_cast<float>(slot->metrics.vertBearingX - slot->metrics.horiBearingX);
styleShiftY = static_cast<float>(-(slot->metrics.horiBearingY + slot->metrics.vertBearingY));
outParams[1] = 1;
} else {
outParams[1] = 0;
}
StoreFloat(outParams, 0x20, styleShiftX);
StoreFloat(outParams, 0x24, styleShiftY);
StoreFloat(outParams, 0x28, 1.0f);
const auto shiftX = static_cast<std::int32_t>(obj->shift_cache_x);
const auto shiftY = static_cast<std::int32_t>(obj->shift_cache_y);
const auto seedX = static_cast<std::int32_t>(obj->layout_seed_vec[0]);
const auto seedY = static_cast<std::int32_t>(obj->layout_seed_vec[1]);
WriteS32(outParams, 0x2C, shiftX);
WriteS32(outParams, 0x30, shiftY);
WriteS32(outParams, 0x34, seedX);
WriteS32(outParams, 0x38, seedY);
StoreFloat(outParams, 0x3C, static_cast<float>(static_cast<std::int64_t>(obj->cached_units_x_0x68)) * ONE_OVER_64);
StoreFloat(outParams, 0x40, static_cast<float>(static_cast<std::int64_t>(obj->cached_units_y_0x70)) * ONE_OVER_64);
WriteU32(outParams, 0x44, 0);
auto xScale = static_cast<std::int64_t>(face->size->metrics.x_scale);
auto yScale = static_cast<std::int64_t>(face->size->metrics.y_scale);
const auto objScaleX = static_cast<std::int64_t>(obj->layout_scale_vec[0]);
const auto objScaleY = static_cast<std::int64_t>(obj->layout_scale_vec[1]);
if (objScaleX != 0 && objScaleX != 0x10000) xScale = (xScale << 16) / objScaleX;
if (objScaleY != 0 && objScaleY != 0x10000) yScale = (yScale << 16) / objScaleY;
const bool applyScale = (mode & 0x0Fu) != 0;
const auto scaleIf = [&](std::int64_t value, std::int64_t scale) {
return applyScale ? RoundMulFixed64(value, scale) : value;
};
const auto widthRaw = static_cast<std::int64_t>(slot->metrics.width);
const auto heightRaw = static_cast<std::int64_t>(slot->metrics.height);
const auto horiBearingXRaw = static_cast<std::int64_t>(slot->metrics.horiBearingX);
const auto horiAdvanceRaw = static_cast<std::int64_t>(slot->metrics.horiAdvance);
const auto vertBearingXRaw = static_cast<std::int64_t>(slot->metrics.vertBearingX);
const auto vertBearingYRaw = static_cast<std::int64_t>(slot->metrics.vertBearingY);
const auto vertAdvanceRaw = static_cast<std::int64_t>(slot->metrics.vertAdvance);
const std::int64_t horiAdvanceUnscaled = seedX + horiAdvanceRaw;
const std::int64_t vertAdvanceUnscaled = seedY + vertAdvanceRaw;
slot->metrics.horiAdvance = static_cast<FT_Pos>(horiAdvanceUnscaled);
slot->metrics.vertAdvance = static_cast<FT_Pos>(vertAdvanceUnscaled);
if (widthRaw == 0 && heightRaw == 0) {
*outMetrics = {};
outMetrics->Horizontal.advance = FromF26Dot6(scaleIf(horiAdvanceUnscaled, xScale));
outMetrics->Vertical.advance = FromF26Dot6(scaleIf(vertAdvanceUnscaled, yScale));
return SCE_FONT_OK;
}
const std::int64_t topShift = heightRaw == 0 ? 0 : shiftY;
std::int64_t left = horiBearingXRaw + shiftX;
std::int64_t top = vertBearingYRaw + topShift;
std::int64_t right = left + widthRaw;
std::int64_t bottom = top - heightRaw;
left = scaleIf(left, xScale);
top = scaleIf(top, yScale);
right = scaleIf(right, xScale);
bottom = scaleIf(bottom, yScale);
const std::int64_t horiAdvance = scaleIf(horiAdvanceUnscaled, xScale);
const std::int64_t vertAdvance = scaleIf(vertAdvanceUnscaled, yScale);
const std::int64_t outWidth = right - left;
const std::int64_t outHeight = top - bottom;
std::int64_t vertBearingX;
std::int64_t vertBearingY;
if ((static_cast<std::uint32_t>(face->face_flags) & 0x20u) == 0) {
vertBearingX = left - horiAdvance / 2;
vertBearingY = (vertAdvance - outHeight) / 2;
} else {
if (seedX == 0) {
vertBearingX = scaleIf(shiftX + vertBearingXRaw, xScale);
} else {
slot->metrics.vertBearingX = static_cast<FT_Pos>(horiBearingXRaw - horiAdvanceUnscaled / 2);
vertBearingX = left - horiAdvance / 2;
StoreFloat(outParams, 0x20, static_cast<float>(-(horiAdvanceUnscaled / 2)));
}
vertBearingY = scaleIf(topShift + vertBearingYRaw, yScale);
}
outMetrics->width = FromF26Dot6(outWidth);
outMetrics->height = FromF26Dot6(outHeight);
outMetrics->Horizontal.bearingX = FromF26Dot6(left);
outMetrics->Horizontal.bearingY = FromF26Dot6(top);
outMetrics->Horizontal.advance = FromF26Dot6(horiAdvance);
outMetrics->Vertical.bearingX = FromF26Dot6(vertBearingX);
outMetrics->Vertical.bearingY = FromF26Dot6(vertBearingY);
outMetrics->Vertical.advance = FromF26Dot6(vertAdvance);
if ((mode & 0x06u) != 0 && obj->font_handle) {
const auto* handle = reinterpret_cast<const FontHandleNative*>(obj->font_handle);
const StyleStateBlock* style = (mode & 0x04u) ? &handle->cached_style.state : &handle->style;
const float slantRatio = style->slant_ratio;
if ((slantRatio != 0.0f || std::isnan(slantRatio)) && slot->outline.points && slot->outline.n_points > 0) {
const auto shear = static_cast<std::int64_t>(TruncateFloatToInt(slantRatio * 65536.0f));
std::int64_t minX = std::numeric_limits<std::int64_t>::max();
std::int64_t maxX = std::numeric_limits<std::int64_t>::min();
for (int i = 0; i < slot->outline.n_points; ++i) {
const FT_Vector point = slot->outline.points[i];
const std::int64_t px = scaleIf(static_cast<std::int64_t>(point.x) + shiftX, xScale);
const std::int64_t py = scaleIf(static_cast<std::int64_t>(point.y) + shiftY, yScale);
const std::int64_t slantedX = px + RoundMulFixed64(py, shear);
minX = std::min(minX, slantedX);
maxX = std::max(maxX, slantedX);
}
if (minX <= maxX) {
outMetrics->width = FromF26Dot6(maxX - minX);
outMetrics->Horizontal.bearingX = FromF26Dot6(minX);
}
}
const float effectX = style->effect_weight_x;
const float effectY = style->effect_weight_y;
if (effectX != 0.0f || std::isnan(effectX) || effectY != 0.0f || std::isnan(effectY)) {
const float dx = effectX * obj->scale_x_0x50 * 0.5f;
const float dy = effectY * obj->scale_y_0x54 * 0.5f;
outMetrics->width = outMetrics->width + dx + dx;
outMetrics->height = outMetrics->height + dy + dy;
outMetrics->Horizontal.bearingX -= dx;
outMetrics->Horizontal.bearingY += dy;
outMetrics->Vertical.bearingX -= dx;
outMetrics->Vertical.bearingY -= dy;
if (outMetrics->Horizontal.advance != 0.0f || std::isnan(outMetrics->Horizontal.advance)) outMetrics->Horizontal.advance += dx;
if (outMetrics->Vertical.advance != 0.0f || std::isnan(outMetrics->Vertical.advance)) outMetrics->Vertical.advance += dy;
}
}
return SCE_FONT_OK;
}
int APS5_VABI LibraryApplyGlyphAdjust(FontObj* obj, std::uint32_t p2, std::uint32_t glyphIndex, std::int32_t p4, std::int32_t p5, std::uint32_t* inoutGlyphIndex) {
(void)p2;
if (!obj || !inoutGlyphIndex) return SCE_FONT_ERROR_FATAL;
*inoutGlyphIndex = glyphIndex;
obj->shift_units_x = p4;
obj->shift_units_y = p5;
obj->layout_seed_pair = 0;
return SCE_FONT_OK;
}
int APS5_VABI LibraryConfigureGlyph(FontObj* obj, std::uint32_t* inParams, std::int32_t mode, std::uint32_t* inoutState) {
if (!obj || !inParams || !inoutState) return SCE_FONT_ERROR_FATAL;
if (*inParams != 0) reinterpret_cast<std::uint8_t*>(inoutState)[3] |= 0x80;
obj->glyph_cfg_word_0x130 = *inParams;
obj->glyph_cfg_mode_0x134 = static_cast<std::uint8_t>(mode);
obj->glyph_cfg_byte_0x136 = 0;
obj->glyph_cfg_byte_0x135 = 0;
return SCE_FONT_OK;
}
int APS5_VABI RendererCreate(RendererNative* renderer) {
if (!renderer) return SCE_FONT_ERROR_INVALID_RENDERER;
auto* ft = reinterpret_cast<RendererFt*>(renderer);
ft->ft_backend.renderer_header_0x10 = &ft->base.mem_kind;
ft->ft_backend.unknown_0x08 = 0;
ft->ft_backend.unknown_0x10 = 0;
ft->ft_backend.unknown_0x18 = 0;
ft->ft_backend.initialized_marker = ft;
return SCE_FONT_OK;
}
int APS5_VABI RendererDestroy(RendererNative* renderer) {
if (!renderer) return SCE_FONT_ERROR_INVALID_RENDERER;
reinterpret_cast<RendererFt*>(renderer)->ft_backend.initialized_marker = nullptr;
return SCE_FONT_OK;
}
std::uint64_t APS5_VABI RendererQuery(RendererNative* renderer, std::uint8_t* params, std::int64_t* outPtr, std::uint8_t* outVector) {
(void)renderer;
(void)params;
if (outVector) std::memset(outVector, 0, 16);
if (outPtr) *outPtr = 0;
return 0;
}
SysDriver MakeDriverTable() {
SysDriver driver{};
driver.pixel_resolution = &LibraryGetPixelResolution;
driver.init = &LibraryInit;
driver.term = &LibraryTerm;
driver.support_formats = &LibrarySupport;
driver.open = &LibraryOpenFont;
driver.close = &LibraryCloseFont;
driver.scale = &LibraryGetFaceScale;
driver.metric = &LibraryGetFaceMetric;
driver.glyph_index = &LibraryGetGlyphIndex;
driver.set_char_with_dpi = &LibrarySetCharSizeWithDpi;
driver.set_char_default_dpi = &LibrarySetCharSizeDefaultDpi;
driver.compute_layout = &LibraryComputeLayout;
driver.compute_layout_alt = &LibraryComputeLayoutAlt;
driver.load_glyph_cached = &LibraryLoadGlyphCached;
driver.get_glyph_metrics = &LibraryGetGlyphMetrics;
driver.apply_glyph_adjust = &LibraryApplyGlyphAdjust;
driver.configure_glyph = &LibraryConfigureGlyph;
return driver;
}
const SysDriver driverTable = MakeDriverTable();
const RendererSelection rendererTable{0, sizeof(RendererFt), &RendererCreate, &RendererDestroy, &RendererQuery};
}
const Font::SysDriver* FontFt::DriverTable() {
return &driverTable;
}
const Font::RendererSelection* FontFt::RendererTable() {
return &rendererTable;
}

View File

@ -0,0 +1,75 @@
#include "prx/libSceFont/include/FontTypes.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
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 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*);
const void* APS5_VABI sceFontSelectLibraryFt(int);
const void* APS5_VABI sceFontSelectRendererFt(int);
}
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 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);
}
int main() {
constexpr std::uint32_t SystemFontSet = 0x18070043u;
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);
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);
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);
FontRenderer renderer = nullptr;
Require(sceFontCreateRenderer(&memory, sceFontSelectRendererFt(0), &renderer) == SCE_FONT_OK && renderer != nullptr);
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);
}